<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>데굴데굴</title>
    <link>https://pixxlix.tistory.com/</link>
    <description>내가 보려고 기록하는</description>
    <language>ko</language>
    <pubDate>Sat, 18 Jul 2026 22:01:36 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>데구르르..</managingEditor>
    <image>
      <title>데굴데굴</title>
      <url>https://tistory1.daumcdn.net/tistory/7958770/attach/fd538ccae4844594a1c721b357093e20</url>
      <link>https://pixxlix.tistory.com</link>
    </image>
    <item>
      <title>Valkey 커스텀 명령어 응답 가공하기: echo2_ prefix 붙이기</title>
      <link>https://pixxlix.tistory.com/30</link>
      <description>&lt;p data-end=&quot;295&quot; data-start=&quot;248&quot; data-ke-size=&quot;size16&quot;&gt;지난 과제에서는 Valkey에 ECHOPIXXLIX라는 커스텀 명령어를 추가했다.&lt;/p&gt;
&lt;p data-end=&quot;323&quot; data-start=&quot;297&quot; data-ke-size=&quot;size16&quot;&gt;동작은 기존 ECHO 명령어처럼.&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;사용자가 abc를 입력하면 그대로 &quot;abc&quot;를 돌려주는 단순한 명령어였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;488&quot; data-start=&quot;423&quot; data-ke-size=&quot;size16&quot;&gt;이번 과제에서는 여기서 한 단계 더 나아가, 입력값 앞에 echo2_라는 prefix를 붙여 응답하도록 수정해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfIwpR/dJMcabxwFcO/dgoMkLHgWYS5KxpRT7aqH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfIwpR/dJMcabxwFcO/dgoMkLHgWYS5KxpRT7aqH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfIwpR/dJMcabxwFcO/dgoMkLHgWYS5KxpRT7aqH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfIwpR%2FdJMcabxwFcO%2FdgoMkLHgWYS5KxpRT7aqH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;583&quot; height=&quot;81&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;661&quot; data-start=&quot;547&quot; data-ke-size=&quot;size16&quot;&gt;겉으로 보면 단순히 문자열 앞에 echo2_만 붙이는 작업처럼 보인다.&lt;br /&gt;하지만 Valkey 내부에서는 기존 입력값을 그대로 반환하는 것과, 새로운 문자열을 만들어 반환하는 것이 꽤 다르게 처리된다.&lt;/p&gt;
&lt;p data-end=&quot;661&quot; data-start=&quot;547&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;791&quot; data-start=&quot;663&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 코드 흐름을 세세하게 따라가기보다는, &lt;b&gt;왜 새 문자열을 만들어야 했는지&lt;/b&gt;, &lt;b&gt;Valkey는 문자열을 어떻게 다루는지&lt;/b&gt;, &lt;b&gt;응답을 만들 때 메모리 소유권은 어떻게 처리되는지&lt;/b&gt;를 중심으로 정리해보려고 한다.&lt;/p&gt;
&lt;hr data-end=&quot;796&quot; data-start=&quot;793&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;821&quot; data-start=&quot;798&quot; data-ke-size=&quot;size26&quot;&gt;1. 기존 방식: 입력값을 그대로 돌려주기&lt;/h2&gt;
&lt;p data-end=&quot;869&quot; data-start=&quot;823&quot; data-ke-size=&quot;size16&quot;&gt;지난 과제에서 구현했던 echoPixxlixCommand는 이런 형태였다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void echoPixxlixCommand(client *c) {
    addReplyBulk(c, c-&amp;gt;argv[1]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1181&quot; data-start=&quot;1132&quot; data-ke-size=&quot;size16&quot;&gt;기존 구현에서는 c-&amp;gt;argv[1]을 그대로 addReplyBulk()에 넘겼다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;사용자가 보낸 값 &amp;rarr; 그대로 bulk string 응답&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1239&quot; data-start=&quot;1231&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1318&quot; data-start=&quot;1241&quot; data-ke-size=&quot;size16&quot;&gt;이 방식은 이미 존재하는 object를 그대로 응답할 때는 적합하다.&lt;br /&gt;하지만 이번 과제에서는 &quot;abc&quot;를 그대로 돌려주면 안 된다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;기존 입력값 앞에 새로운 문자열을 붙인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;새 응답 문자열&lt;/b&gt;이 필요하다.&lt;/p&gt;
&lt;hr data-end=&quot;1411&quot; data-start=&quot;1408&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1436&quot; data-start=&quot;1413&quot; data-ke-size=&quot;size26&quot;&gt;2. 새 문자열 만들기&lt;/h2&gt;
&lt;p data-end=&quot;1459&quot; data-start=&quot;1438&quot; data-ke-size=&quot;size16&quot;&gt;이번 과제의 전체 로직은 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;사용자 입력: abc
&amp;darr;
c-&amp;gt;argv[1]에서 실제 문자열 값 꺼내기
&amp;darr;
빈 sds 문자열 만들기
&amp;darr;
echo2_ + 입력값 붙이기
&amp;darr;
새로 만든 문자열을 bulk string 응답으로 반환&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void echoPixxlixCommand(client *c) {
    sds reply = sdsempty();

    reply = sdscatfmt(reply, &quot;echo2_%S&quot;, objectGetVal(c-&amp;gt;argv[1]));

    addReplyBulkSds(c, reply);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1842&quot; data-start=&quot;1777&quot; data-ke-size=&quot;size16&quot;&gt;코드 줄 수만 보면 짧은 수정이다.&lt;br /&gt;하지만 이 안에는 Valkey의 문자열 처리 방식과 응답 처리 방식이 함께 들어 있다.&lt;/p&gt;
&lt;hr data-end=&quot;1847&quot; data-start=&quot;1844&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1877&quot; data-start=&quot;1849&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;2.1 c-&amp;gt;argv[1]은 그냥 문자열이 아니다&lt;/span&gt;&lt;/h2&gt;
&lt;p data-end=&quot;1920&quot; data-start=&quot;1879&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 c-&amp;gt;argv[1]을 그냥 char *처럼 생각하기 쉽다.&lt;/p&gt;
&lt;p data-end=&quot;1985&quot; data-start=&quot;1922&quot; data-ke-size=&quot;size16&quot;&gt;하지만 Valkey에서 command argument는 단순 C 문자열이 아니라 robj * 형태로 관리된다.&lt;/p&gt;
&lt;p data-end=&quot;1985&quot; data-start=&quot;1922&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2060&quot; data-start=&quot;1987&quot; data-ke-size=&quot;size16&quot;&gt;그래서 입력값 자체를 그대로 문자열처럼 이어 붙이는 것이 아니라, 먼저 object 안에 들어 있는 실제 문자열 값을 꺼내야 한다.&lt;/p&gt;
&lt;p data-end=&quot;2103&quot; data-start=&quot;2062&quot; data-ke-size=&quot;size16&quot;&gt;이 역할을 하는 함수가 objectGetVal()이다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;objectGetVal(c-&amp;gt;argv[1])&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2162&quot; data-start=&quot;2140&quot; data-ke-size=&quot;size16&quot;&gt;즉, 이 부분은 이렇게 이해할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;ocaml&quot;&gt;&lt;code&gt;c-&amp;gt;argv[1]이라는 Valkey object에서
실제 문자열 값을 꺼낸다&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2303&quot; data-start=&quot;2221&quot; data-ke-size=&quot;size16&quot;&gt;기존에는 object 자체를 그대로 addReplyBulk()에 넘겼지만, 이번에는 그 안의 실제 값을 꺼내서 새 문자열을 만드는 데 사용했다.&lt;/p&gt;
&lt;hr data-end=&quot;2308&quot; data-start=&quot;2305&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2334&quot; data-start=&quot;2310&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;2.2 Valkey의 문자열 타입, sds&lt;/span&gt;&lt;/h2&gt;
&lt;p data-end=&quot;2366&quot; data-start=&quot;2336&quot; data-ke-size=&quot;size16&quot;&gt;이번 과제에서 가장 중요하게 본 개념은 sds였다.&lt;/p&gt;
&lt;p data-end=&quot;2451&quot; data-start=&quot;2368&quot; data-ke-size=&quot;size16&quot;&gt;C에서는 보통 문자열을 char *로 다룬다.&lt;br /&gt;하지만 일반 C 문자열은 길이를 알려면 strlen()처럼 문자열 끝까지 직접 훑어야 한다.&lt;/p&gt;
&lt;p data-end=&quot;2511&quot; data-start=&quot;2453&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 &quot;hello&quot;의 길이를 구하려면, 내부적으로는 \0을 만날 때까지 한 글자씩 확인한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;h &amp;rarr; e &amp;rarr; l &amp;rarr; l &amp;rarr; o &amp;rarr; \0&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2621&quot; data-start=&quot;2549&quot; data-ke-size=&quot;size16&quot;&gt;문자열이 짧을 때는 큰 문제가 없지만, Valkey처럼 문자열 데이터를 매우 자주 다루는 서버에서는 이 비용이 중요해질 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;2665&quot; data-start=&quot;2623&quot; data-ke-size=&quot;size16&quot;&gt;그래서 Valkey는 문자열을 다룰 때 주로 sds라는 구조를 사용한다.&lt;/p&gt;
&lt;p data-end=&quot;2714&quot; data-start=&quot;2667&quot; data-ke-size=&quot;size16&quot;&gt;sds는 겉으로는 char *처럼 보이지만, 실제 메모리 구조는 조금 다르다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;[ len | alloc | flags ][ 실제 문자열 buf ][ \0 ]
                         &amp;uarr;
                       sds 포인터&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2892&quot; data-start=&quot;2831&quot; data-ke-size=&quot;size16&quot;&gt;sds의 앞쪽에는 header가 있고, 그 안에는 문자열 길이, 할당된 크기, 타입 정보 등이 들어 있다.&lt;/p&gt;
&lt;p data-end=&quot;2892&quot; data-start=&quot;2831&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2963&quot; data-start=&quot;2894&quot; data-ke-size=&quot;size16&quot;&gt;재밌는 점은 sds 포인터가 header를 가리키는 것이 아니라, 실제 문자열이 시작되는 buf를 가리킨다는 것이다.&lt;/p&gt;
&lt;p data-end=&quot;2996&quot; data-start=&quot;2965&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;3183&quot; data-end=&quot;3201&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 조금 이상하게 느껴졌다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #666666; text-align: start;&quot; data-start=&quot;3203&quot; data-end=&quot;3245&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p style=&quot;color: #666666;&quot; data-start=&quot;3205&quot; data-end=&quot;3245&quot; data-ke-size=&quot;size16&quot;&gt;header가 중요하면 포인터가 header를 가리켜야 하는 것 아닌가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;3293&quot; data-end=&quot;3307&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;3293&quot; data-end=&quot;3307&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 장점이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;3293&quot; data-end=&quot;3307&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;3309&quot; data-end=&quot;3329&quot; data-ke-size=&quot;size16&quot;&gt;1. C 문자열처럼 사용하기 쉽다. 바로 문자열 시작 위치를 가리키기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;3393&quot; data-end=&quot;3448&quot; data-ke-size=&quot;size16&quot;&gt;2. 길이나 할당 크기 같은 정보가 필요하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;문자열 전체를 훑지 않고, 포인터 앞쪽으로 이동해서 header를 읽으면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;3393&quot; data-end=&quot;3448&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;3450&quot; data-end=&quot;3523&quot; data-ke-size=&quot;size16&quot;&gt;즉, 사용하기에는 C 문자열처럼 편하게 쓰고, 내부적으로는 header를 통해 길이와 메모리 정보를 빠르게 확인할 수 있는 구조다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-start=&quot;3525&quot; data-end=&quot;3584&quot; data-ke-size=&quot;size16&quot;&gt;이 부분이 처음에는 낯설었지만, Valkey가 왜 자체 문자열 구조를 사용하는지 이해하는 데 도움이 됐다.&lt;/p&gt;
&lt;hr data-end=&quot;3589&quot; data-start=&quot;3586&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3612&quot; data-start=&quot;3591&quot; data-ke-size=&quot;size26&quot;&gt;2.3 빈 문자열을 만들고, 뒤에 붙이기&lt;/h2&gt;
&lt;p data-end=&quot;3644&quot; data-start=&quot;3614&quot; data-ke-size=&quot;size16&quot;&gt;이번 과제에서는 먼저 빈 sds 문자열을 만들고, 이후 sdscatfmt()를 사용해 echo2_와 입력값을 이어 붙였다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;sds reply = sdsempty();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;reply = sdscatfmt(reply, &quot;echo2_%S&quot;, objectGetVal(c-&amp;gt;argv[1]));&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3853&quot; data-start=&quot;3800&quot; data-ke-size=&quot;size16&quot;&gt;중요한 점은 sdscatfmt()의 반환값을 다시 reply에 저장한다는 것이다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3971&quot; data-start=&quot;3889&quot; data-ke-size=&quot;size16&quot;&gt;문자열을 뒤에 붙이다 보면 기존 공간이 부족할 수 있다.&lt;br /&gt;그 경우 내부적으로 더 큰 공간을 새로 할당하거나 realloc이 일어날 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;4002&quot; data-start=&quot;3973&quot; data-ke-size=&quot;size16&quot;&gt;그러면 기존 reply 포인터가 바뀔 수도 있다.&lt;/p&gt;
&lt;p data-end=&quot;4024&quot; data-start=&quot;4004&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4132&quot; data-start=&quot;4100&quot; data-ke-size=&quot;size16&quot;&gt;그냥 아래처럼 반환값을 받지 않는 방식은 위험할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;sdscatfmt(reply, &quot;echo2_%S&quot;, objectGetVal(c-&amp;gt;argv[1]));&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4291&quot; data-start=&quot;4200&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-end=&quot;4296&quot; data-start=&quot;4293&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4350&quot; data-start=&quot;4298&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;3. addReplyBulkSds() 로 응답하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-end=&quot;4366&quot; data-start=&quot;4352&quot; data-ke-size=&quot;size16&quot;&gt;기존에는 이렇게 응답했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;addReplyBulk(c, c-&amp;gt;argv[1]);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4463&quot; data-start=&quot;4407&quot; data-ke-size=&quot;size16&quot;&gt;기존 방식은 이미 존재하는 robj를 그대로 bulk string 응답으로 보낼 때 사용하기 좋다.&lt;/p&gt;
&lt;p data-end=&quot;4494&quot; data-start=&quot;4465&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이번에는 새로 만든 sds 문자열이 있으니 addReplyBulkSds()를 사용했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;sds reply = sdsempty();
reply = sdscatfmt(reply, &quot;echo2_%S&quot;, objectGetVal(c-&amp;gt;argv[1]));&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4643&quot; data-start=&quot;4594&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;addReplyBulkSds(c, reply);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;5012&quot; data-start=&quot;4958&quot; data-ke-size=&quot;size16&quot;&gt;조심해야 할 점은 addReplyBulkSds()의 메모리 소유권이다.&lt;/p&gt;
&lt;p data-end=&quot;5098&quot; data-start=&quot;5084&quot; data-ke-size=&quot;size16&quot;&gt;addReplyBulkSds()는 단순히 응답만 추가하는 함수가 아니라, 전달받은 sds의 메모리 소유권까지 가져간다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;따라서 reply를 다시 사용하거나 직접 해제하면 안 된다.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;sdsfree(reply); // 하면 안 됨&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5271&quot; data-start=&quot;5213&quot; data-ke-size=&quot;size16&quot;&gt;왜냐하면 addReplyBulkSds() 내부에서 이미 해당 sds를 처리하고 해제하기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;5409&quot; data-start=&quot;5273&quot; data-ke-size=&quot;size16&quot;&gt;이걸 모르고 직접 sdsfree(reply)를 한 번 더 호출하면 double free 문제가 생길 수 있다.&lt;br /&gt;또 호출 이후에 reply를 다시 사용하면 이미 해제된 메모리를 참조하는 use-after-free 문제가 생길 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;5409&quot; data-start=&quot;5273&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;5418&quot; data-start=&quot;5411&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말하면,&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;addReplyBulkSds()에 reply를 넘기는 순간
reply의 책임은 함수 쪽으로 넘어간다&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5500&quot; data-start=&quot;5489&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-end=&quot;5505&quot; data-start=&quot;5502&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5516&quot; data-start=&quot;5507&quot; data-ke-size=&quot;size26&quot;&gt;테스트 결과&lt;/h2&gt;
&lt;p data-end=&quot;5546&quot; data-start=&quot;5518&quot; data-ke-size=&quot;size16&quot;&gt;수정 후 valkey-cli에서 직접 확인했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; echoPixxlix abc
&quot;echo2_abc&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5618&quot; data-start=&quot;5605&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; echoPixxlix hello
&quot;echo2_hello&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5735&quot; data-start=&quot;5681&quot; data-ke-size=&quot;size16&quot;&gt;인자 없이 실행했을 때는 command metadata의 arity 검증에 따라 에러가 발생한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; echoPixxlix
(error) ERR wrong number of arguments for 'echopixxlix' command&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;5845&quot; data-start=&quot;5842&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5854&quot; data-start=&quot;5847&quot; data-ke-size=&quot;size26&quot;&gt;정리하며&lt;/h2&gt;
&lt;p data-end=&quot;6375&quot; data-start=&quot;6249&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 단순히 문자열 앞에 echo2_만 붙이면 되는 과제라고 생각했는데, Valkey 내부에서는 기존 object를 그대로 반환하는 것과, 새 문자열을 만들어 반환하는 것이 다르게 처리된다는 점을 알게 되었다.&lt;/p&gt;
&lt;p data-end=&quot;6375&quot; data-start=&quot;6249&quot; data-ke-size=&quot;size16&quot;&gt;Valkey의 문자열 처리 방식과 응답 생성 방식을 조금 더 구체적으로 이해할 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;6375&quot; data-start=&quot;6249&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;6375&quot; data-start=&quot;6249&quot; data-ke-size=&quot;size16&quot;&gt;추가로 Valkey 네트워크 처리과정에 대해서도 배웠는데, 다음 포스팅에서 이어서 작성하겠다.(epoll과 멀티스레딩의 개념을 이해하고 있다고 생각했는데, 너무 어려워서 PPT 슬라이드를 이해하는데만 3일정도 걸렸다)&lt;/p&gt;</description>
      <category>CS STUDY</category>
      <category>C언어</category>
      <category>ossca</category>
      <category>redis</category>
      <category>redis명령어</category>
      <category>RESP</category>
      <category>sds</category>
      <category>Valkey</category>
      <category>Valkey명령어</category>
      <category>시스템프로그래밍</category>
      <category>오픈소스기여</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/30</guid>
      <comments>https://pixxlix.tistory.com/30#entry30comment</comments>
      <pubDate>Sun, 24 May 2026 11:59:42 +0900</pubDate>
    </item>
    <item>
      <title>Redis/Valkey는 Command를 어떻게 실행할까? - Valkey 커스텀 명령어를 만들어보자</title>
      <link>https://pixxlix.tistory.com/29</link>
      <description>&lt;h2 data-end=&quot;204&quot; data-start=&quot;197&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt; 들어가며&lt;/h2&gt;
&lt;p data-end=&quot;261&quot; data-start=&quot;206&quot; data-ke-size=&quot;size16&quot;&gt;OSSCA Valkey 4주차에서는 Valkey에 직접 command를 추가해보는 실습을 진행했다.&lt;/p&gt;
&lt;p data-end=&quot;538&quot; data-start=&quot;371&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 단순히 함수 하나를 추가하면 되는 과제처럼 보였다. 하지만 실제로 따라가 보니 Valkey 내부에서 command가 어떻게 정의되고, 서버 시작 시 어떻게 등록되며, client 요청이 어떤 흐름을 거쳐 실제 C 함수까지 도달하는지 이해할 수 있는 실습이었다.&lt;/p&gt;
&lt;p data-end=&quot;538&quot; data-start=&quot;371&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;631&quot; data-start=&quot;540&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 수업에서 다룬 Valkey command 등록 및 실행 흐름과, 내가 ECHOPIXXLIX command를 추가하고 PR을 보낸 과정을 정리한다.&lt;/p&gt;
&lt;hr data-end=&quot;636&quot; data-start=&quot;633&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;666&quot; data-start=&quot;638&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt; Valkey command는 어디에 정의될까?&lt;/h2&gt;
&lt;p data-end=&quot;720&quot; data-start=&quot;668&quot; data-ke-size=&quot;size16&quot;&gt;Valkey에서 command를 추가하려면 단순히 C 함수 하나만 작성하면 되는 것이 아니다.&lt;/p&gt;
&lt;p data-end=&quot;806&quot; data-start=&quot;722&quot; data-ke-size=&quot;size16&quot;&gt;command의 이름, 설명, 인자 수, 실행할 함수, command 성격 등이 함께 정의되어야 한다. 이번 실습에서 수정한 주요 파일은 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;파일&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;역할
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1107&quot; data-start=&quot;808&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;1107&quot; data-start=&quot;830&quot;&gt;
&lt;tr data-end=&quot;902&quot; data-start=&quot;830&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;865&quot; data-start=&quot;830&quot;&gt;src/commands/echo-pixxlix.json&lt;/td&gt;
&lt;td data-end=&quot;902&quot; data-start=&quot;865&quot; data-col-size=&quot;sm&quot;&gt;ECHOPIXXLIX command metadata 정의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;955&quot; data-start=&quot;903&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;924&quot; data-start=&quot;903&quot;&gt;src/commands.def&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;955&quot; data-start=&quot;924&quot;&gt;ECHOPIXXLIX command 항목 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1003&quot; data-start=&quot;956&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;973&quot; data-start=&quot;956&quot;&gt;src/server.h&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1003&quot; data-start=&quot;973&quot;&gt;echoPixxlixCommand 함수 선언&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1051&quot; data-start=&quot;1004&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1021&quot; data-start=&quot;1004&quot;&gt;src/server.c&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1051&quot; data-start=&quot;1021&quot;&gt;echoPixxlixCommand 함수 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1107&quot; data-start=&quot;1052&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1077&quot; data-start=&quot;1052&quot;&gt;tests/unit/other.tcl&lt;/td&gt;
&lt;td data-end=&quot;1107&quot; data-start=&quot;1077&quot; data-col-size=&quot;sm&quot;&gt;ECHOPIXXLIX unit test 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1221&quot; data-start=&quot;1109&quot; data-ke-size=&quot;size16&quot;&gt;Valkey command는 &amp;ldquo;어떤 이름의 command가 들어왔을 때 어떤 함수를 실행할 것인지&amp;rdquo;를 미리 등록해두고, client 요청이 들어오면 해당 command를 찾아 실행하는 구조로 동작한다.&lt;/p&gt;
&lt;hr data-end=&quot;1226&quot; data-start=&quot;1223&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1248&quot; data-start=&quot;1228&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt; Command 등록 흐름 4단계&lt;/h2&gt;
&lt;p data-end=&quot;1284&quot; data-start=&quot;1250&quot; data-ke-size=&quot;size16&quot;&gt;수업에서 가장 먼저 본 부분은 command 등록 흐름이었다.&lt;/p&gt;
&lt;p data-end=&quot;1361&quot; data-start=&quot;1286&quot; data-ke-size=&quot;size16&quot;&gt;Valkey command는 요청이 들어올 때마다 새로 만들어지는 것이 아니라, 서버가 시작될 때 command table에 등록된다.&lt;/p&gt;
&lt;p data-end=&quot;1376&quot; data-start=&quot;1363&quot; data-ke-size=&quot;size16&quot;&gt;큰 흐름은 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;commands.def
&amp;darr;
serverCommandTable[]
&amp;darr;
populateCommandTable()
&amp;darr;
server.commands&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1547&quot; data-start=&quot;1470&quot; data-ke-size=&quot;size16&quot;&gt;commands.def에는 command 이름, 설명, 실행 함수, 인자 수, flag, ACL category 같은 정보가 들어간다.&lt;/p&gt;
&lt;p data-end=&quot;1684&quot; data-start=&quot;1549&quot; data-ke-size=&quot;size16&quot;&gt;이후 서버가 시작되면 populateCommandTable()을 통해 command들이 command table에 등록된다. client가 command를 보내면 Valkey는 이 command table에서 해당 command를 찾는다.&lt;/p&gt;
&lt;p data-end=&quot;1806&quot; data-start=&quot;1686&quot; data-ke-size=&quot;size16&quot;&gt;이번에 추가한 ECHOPIXXLIX도 이 흐름에 포함되어야 한다. 즉, command metadata와 실행 함수가 제대로 연결되어 있어야 client가 ECHOPIXXLIX를 보냈을 때 정상적으로 실행된다.&lt;/p&gt;
&lt;hr data-end=&quot;1811&quot; data-start=&quot;1808&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1846&quot; data-start=&quot;1813&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt; Client 요청 이후 command 실행 흐름 7단계&lt;/h2&gt;
&lt;p data-end=&quot;1880&quot; data-start=&quot;1848&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트가 다음과 같이 command를 보냈다고 해보자.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;ECHO abc&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2008&quot; data-start=&quot;1904&quot; data-ke-size=&quot;size16&quot;&gt;겉으로 보기에는 서버가 곧바로 echoCommand()를 호출하는 것처럼 보인다. 이 장면만 보면 &amp;ldquo;소켓에서 문자열을 읽고 바로 함수 하나를 실행하는 단순한 구조&amp;rdquo;라고 생각하기 쉽다.&lt;/p&gt;
&lt;p data-end=&quot;2008&quot; data-start=&quot;1904&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2162&quot; data-start=&quot;2010&quot; data-ke-size=&quot;size16&quot;&gt;하지만 실제 Valkey 내부에서는 command 함수가 호출되기 전에 여러 단계를 거친다. 네트워크에서 데이터를 읽고, 프로토콜을 parsing하고, command를 lookup하고, 인증과 ACL, 인자 수 검사 등을 통과한 뒤에야 실제 command 함수가 실행된다.&lt;/p&gt;
&lt;p data-end=&quot;2162&quot; data-start=&quot;2010&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2202&quot; data-start=&quot;2164&quot; data-ke-size=&quot;size16&quot;&gt;수업에서 정리한 Valkey command 실행 흐름은 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;client 요청
&amp;darr;
readQueryFromClient()
&amp;darr;
processInputBuffer()
&amp;darr;
parseMultibulkBuffer() / parseInlineBuffer()
&amp;darr;
lookupCommand()
&amp;darr;
processCommand()
&amp;darr;
call()
&amp;darr;
c-&amp;gt;cmd-&amp;gt;proc(c)
&amp;darr;
addReply()
&amp;darr;
client 응답&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2442&quot; data-start=&quot;2410&quot; data-ke-size=&quot;size16&quot;&gt;이 흐름을 역할별로 나누면 다음 7단계로 정리할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;2442&quot; data-start=&quot;2410&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;단계&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 함수&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3174&quot; data-start=&quot;2444&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;3174&quot; data-start=&quot;2478&quot;&gt;
&lt;tr data-end=&quot;2573&quot; data-start=&quot;2478&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2489&quot; data-start=&quot;2478&quot;&gt;1. 연결 수락&lt;/td&gt;
&lt;td data-col-size=&quot;md&quot; data-end=&quot;2534&quot; data-start=&quot;2489&quot;&gt;acceptCommonHandler() &amp;rarr; createClient()&lt;/td&gt;
&lt;td data-end=&quot;2573&quot; data-start=&quot;2534&quot; data-col-size=&quot;md&quot;&gt;client 객체를 생성하고 read 이벤트 핸들러를 등록한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2676&quot; data-start=&quot;2574&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2586&quot; data-start=&quot;2574&quot;&gt;2. 데이터 수신&lt;/td&gt;
&lt;td data-end=&quot;2633&quot; data-start=&quot;2586&quot; data-col-size=&quot;md&quot;&gt;readQueryFromClient() &amp;rarr; readToQueryBuf()&lt;/td&gt;
&lt;td data-end=&quot;2676&quot; data-start=&quot;2633&quot; data-col-size=&quot;md&quot;&gt;소켓에서 데이터를 읽어 client query buffer에 저장한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2807&quot; data-start=&quot;2677&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2690&quot; data-start=&quot;2677&quot;&gt;3. 프로토콜 파싱&lt;/td&gt;
&lt;td data-col-size=&quot;md&quot; data-end=&quot;2741&quot; data-start=&quot;2690&quot;&gt;parseMultibulkBuffer() / parseInlineBuffer()&lt;/td&gt;
&lt;td data-end=&quot;2807&quot; data-start=&quot;2741&quot; data-col-size=&quot;md&quot;&gt;RESP 또는 inline command를 parsing하여 c-&amp;gt;argc, c-&amp;gt;argv[]를 채운다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2929&quot; data-start=&quot;2808&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2824&quot; data-start=&quot;2808&quot;&gt;4. 명령어 lookup&lt;/td&gt;
&lt;td data-end=&quot;2864&quot; data-start=&quot;2824&quot; data-col-size=&quot;md&quot;&gt;lookupCommand() &amp;rarr; hashtableFind()&lt;/td&gt;
&lt;td data-end=&quot;2929&quot; data-start=&quot;2864&quot; data-col-size=&quot;md&quot;&gt;등록된 server.commands hashtable에서 command를 찾고 c-&amp;gt;cmd에 연결한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3019&quot; data-start=&quot;2930&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2943&quot; data-start=&quot;2930&quot;&gt;5. 실행 전 검증&lt;/td&gt;
&lt;td data-col-size=&quot;md&quot; data-end=&quot;2964&quot; data-start=&quot;2943&quot;&gt;processCommand()&lt;/td&gt;
&lt;td data-end=&quot;3019&quot; data-start=&quot;2964&quot; data-col-size=&quot;md&quot;&gt;인증, ACL, arity, cluster redirection, 서버 상태 등을 검사한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3087&quot; data-start=&quot;3020&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3032&quot; data-start=&quot;3020&quot;&gt;6. 명령어 실행&lt;/td&gt;
&lt;td data-col-size=&quot;md&quot; data-end=&quot;3063&quot; data-start=&quot;3032&quot;&gt;call() &amp;rarr; c-&amp;gt;cmd-&amp;gt;proc(c)&lt;/td&gt;
&lt;td data-end=&quot;3087&quot; data-start=&quot;3063&quot; data-col-size=&quot;md&quot;&gt;실제 command 함수가 호출된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3174&quot; data-start=&quot;3088&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3099&quot; data-start=&quot;3088&quot;&gt;7. 응답 전송&lt;/td&gt;
&lt;td data-end=&quot;3138&quot; data-start=&quot;3099&quot; data-col-size=&quot;md&quot;&gt;addReply() &amp;rarr; sendReplyToClient()&lt;/td&gt;
&lt;td data-end=&quot;3174&quot; data-start=&quot;3138&quot; data-col-size=&quot;md&quot;&gt;응답을 client buffer에 쌓고 소켓으로 전송한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3196&quot; data-start=&quot;3176&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3196&quot; data-start=&quot;3176&quot; data-ke-size=&quot;size16&quot;&gt;이 중 가장 중요한 지점은 6단계다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;c-&amp;gt;cmd-&amp;gt;proc(c)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3293&quot; data-start=&quot;3224&quot; data-ke-size=&quot;size16&quot;&gt;c-&amp;gt;cmd에는 lookup된 command 정보가 들어 있고, proc에는 실제로 실행할 C 함수가 연결되어 있다.&lt;/p&gt;
&lt;p data-end=&quot;3418&quot; data-start=&quot;3295&quot; data-ke-size=&quot;size16&quot;&gt;즉, 커스텀 command를 추가한다는 것은 단순히 함수 하나를 작성하는 일이 아니다. 새 command 이름이 lookup될 수 있도록 등록하고, 그 command의 proc에 연결될 실제 함수를 작성하는 과정이다.&lt;/p&gt;
&lt;hr data-end=&quot;3423&quot; data-start=&quot;3420&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3433&quot; data-start=&quot;3425&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt; 과제 목표&lt;/h2&gt;
&lt;p data-end=&quot;3476&quot; data-start=&quot;3435&quot; data-ke-size=&quot;size16&quot;&gt;이번 과제는 Valkey에 새로운 command를 하나 추가하는 것이었다.&lt;/p&gt;
&lt;p data-end=&quot;3559&quot; data-start=&quot;3478&quot; data-ke-size=&quot;size16&quot;&gt;추가할 command는 기존 ECHO와 같은 방식으로 동작한다. 사용자가 전달한 문자열을 그대로 다시 반환하는 command를 구현하면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;3761&quot; data-start=&quot;3758&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3784&quot; data-start=&quot;3763&quot; data-ke-size=&quot;size26&quot;&gt;기존 ECHO command 분석&lt;/h2&gt;
&lt;p data-end=&quot;3825&quot; data-start=&quot;3786&quot; data-ke-size=&quot;size16&quot;&gt;이번 과제는 기존 ECHO command를 참고하면 이해하기 쉽다.&lt;/p&gt;
&lt;p data-end=&quot;3861&quot; data-start=&quot;3827&quot; data-ke-size=&quot;size16&quot;&gt;ECHO command의 핵심 구현은 다음과 같은 구조다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void echoCommand(client *c) {
    addReplyBulk(c, c-&amp;gt;argv[1]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3963&quot; data-start=&quot;3938&quot; data-ke-size=&quot;size16&quot;&gt;client가 다음과 같이 입력했다고 해보자.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;ECHO abc&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4033&quot; data-start=&quot;3987&quot; data-ke-size=&quot;size16&quot;&gt;Valkey 내부에서는 parsing 이후 argument가 다음과 같이 정리된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;c-&amp;gt;argv[0] = &quot;ECHO&quot;
c-&amp;gt;argv[1] = &quot;abc&quot;
c-&amp;gt;argc = 2&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4163&quot; data-start=&quot;4099&quot; data-ke-size=&quot;size16&quot;&gt;여기서 c-&amp;gt;argv[0]은 command 이름이고, c-&amp;gt;argv[1]은 사용자가 전달한 첫 번째 인자다.&lt;/p&gt;
&lt;p data-end=&quot;4236&quot; data-start=&quot;4165&quot; data-ke-size=&quot;size16&quot;&gt;따라서 echoCommand()에서 c-&amp;gt;argv[1]을 응답으로 반환하면 사용자가 입력한 abc가 그대로 출력된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;addReplyBulk(c, c-&amp;gt;argv[1]);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4349&quot; data-start=&quot;4277&quot; data-ke-size=&quot;size16&quot;&gt;이 부분을 통해 Valkey command 함수는 parsing된 argv 배열을 기반으로 동작한다는 것을 확인할 수 있었다.&lt;/p&gt;
&lt;hr data-end=&quot;4354&quot; data-start=&quot;4351&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4381&quot; data-start=&quot;4356&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt; ECHOPIXXLIX command 구현&lt;/h2&gt;
&lt;p data-end=&quot;4437&quot; data-start=&quot;4383&quot; data-ke-size=&quot;size16&quot;&gt;이제 기존 ECHO command를 참고해 ECHOPIXXLIX command를 추가했다.&lt;/p&gt;
&lt;h3 data-end=&quot;4465&quot; data-start=&quot;4439&quot; data-ke-size=&quot;size23&quot;&gt;1. command metadata 추가&lt;/h3&gt;
&lt;p data-end=&quot;4512&quot; data-start=&quot;4467&quot; data-ke-size=&quot;size16&quot;&gt;먼저 src/commands/echo-pixxlix.json 파일을 추가했다.&lt;/p&gt;
&lt;p data-end=&quot;4556&quot; data-start=&quot;4514&quot; data-ke-size=&quot;size16&quot;&gt;이 파일에는 ECHOPIXXLIX command의 기본 정보가 들어간다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
    &quot;ECHOPIXXLIX&quot;: {
        &quot;summary&quot;: &quot;Returns the given string.&quot;,
        &quot;complexity&quot;: &quot;O(1)&quot;,
        &quot;group&quot;: &quot;connection&quot;,
        &quot;since&quot;: &quot;8.1.0&quot;,
        &quot;arity&quot;: 2,
        &quot;function&quot;: &quot;echoPixxlixCommand&quot;,
        &quot;command_flags&quot;: [
            &quot;LOADING&quot;,
            &quot;STALE&quot;,
            &quot;FAST&quot;
        ],
        &quot;acl_categories&quot;: [
            &quot;CONNECTION&quot;,
            &quot;FAST&quot;
        ],
        &quot;reply_schema&quot;: {
            &quot;description&quot;: &quot;The given string&quot;,
            &quot;type&quot;: &quot;string&quot;
        },
        &quot;arguments&quot;: [
            {
                &quot;name&quot;: &quot;message&quot;,
                &quot;type&quot;: &quot;string&quot;
            }
        ]
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5245&quot; data-start=&quot;5226&quot; data-ke-size=&quot;size16&quot;&gt;여기서 중요한 필드는 다음과 같다.&lt;/p&gt;
&lt;div&gt;필드&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 의미
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;5520&quot; data-start=&quot;5247&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;5520&quot; data-start=&quot;5269&quot;&gt;
&lt;tr data-end=&quot;5295&quot; data-start=&quot;5269&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5281&quot; data-start=&quot;5269&quot;&gt;summary&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5295&quot; data-start=&quot;5281&quot;&gt;command 설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5320&quot; data-start=&quot;5296&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5311&quot; data-start=&quot;5296&quot;&gt;complexity&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5320&quot; data-start=&quot;5311&quot;&gt;시간복잡도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5345&quot; data-start=&quot;5321&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5331&quot; data-start=&quot;5321&quot;&gt;group&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5345&quot; data-start=&quot;5331&quot;&gt;command 분류&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5380&quot; data-start=&quot;5346&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5356&quot; data-start=&quot;5346&quot;&gt;arity&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5380&quot; data-start=&quot;5356&quot;&gt;command 이름을 포함한 인자 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5412&quot; data-start=&quot;5381&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5394&quot; data-start=&quot;5381&quot;&gt;function&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5412&quot; data-start=&quot;5394&quot;&gt;실제 실행될 C 함수 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5448&quot; data-start=&quot;5413&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5431&quot; data-start=&quot;5413&quot;&gt;command_flags&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5448&quot; data-start=&quot;5431&quot;&gt;command 실행 성격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5484&quot; data-start=&quot;5449&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5468&quot; data-start=&quot;5449&quot;&gt;acl_categories&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5484&quot; data-start=&quot;5468&quot;&gt;ACL category&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5520&quot; data-start=&quot;5485&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5499&quot; data-start=&quot;5485&quot;&gt;arguments&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5520&quot; data-start=&quot;5499&quot;&gt;command가 받는 인자 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5593&quot; data-start=&quot;5522&quot; data-ke-size=&quot;size16&quot;&gt;ECHOPIXXLIX abc는 내부적으로 command 이름과 message를 포함해 총 2개의 argument로 처리된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;argv[0] = &quot;ECHOPIXXLIX&quot;
argv[1] = &quot;abc&quot;
argc = 2&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5680&quot; data-start=&quot;5657&quot; data-ke-size=&quot;size16&quot;&gt;따라서 arity는 2로 설정했다.&lt;/p&gt;
&lt;hr data-end=&quot;5685&quot; data-start=&quot;5682&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-start=&quot;4356&quot; data-end=&quot;4381&quot; data-ke-size=&quot;size26&quot;&gt;❓명령어 처리 속도는 왜 O(1)일까?&lt;/h2&gt;
&lt;p data-end=&quot;5765&quot; data-start=&quot;5712&quot; data-ke-size=&quot;size16&quot;&gt;ECHOPIXXLIX command의 metadata에는 시간복잡도를 다음과 같이 작성했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;complexity&quot;: &quot;O(1)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5899&quot; data-start=&quot;5801&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 문자열을 응답으로 돌려주는 command인데 왜 O(1)인지 헷갈릴 수 있다. 입력 문자열의 길이가 길어지면 실제로 전송해야 하는 byte 수도 늘어나기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;5957&quot; data-start=&quot;5901&quot; data-ke-size=&quot;size16&quot;&gt;여기서 O(1)은 &lt;b&gt;Valkey 내부 command 처리 로직 기준&lt;/b&gt;으로 이해하는 것이 좋다.&lt;/p&gt;
&lt;p data-end=&quot;6082&quot; data-start=&quot;5959&quot; data-ke-size=&quot;size16&quot;&gt;ECHOPIXXLIX는 key를 조회하지 않고, hash table이나 skip list 같은 자료구조를 탐색하지도 않는다. 단순히 parsing이 끝난 뒤 c-&amp;gt;argv[1]에 들어 있는 값을 응답으로 추가한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void echoPixxlixCommand(client *c) {
    addReplyBulk(c, c-&amp;gt;argv[1]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6200&quot; data-start=&quot;6166&quot; data-ke-size=&quot;size16&quot;&gt;즉, command 함수 입장에서 하는 일은 다음 한 가지다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;사용자가 전달한 첫 번째 인자 c-&amp;gt;argv[1]을 응답으로 등록한다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6352&quot; data-start=&quot;6255&quot; data-ke-size=&quot;size16&quot;&gt;GET, HGET, ZRANGE처럼 특정 key나 자료구조 내부를 찾아가는 과정이 없기 때문에 command 자체의 논리적 처리 비용은 상수 시간으로 볼 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;6352&quot; data-start=&quot;6255&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;6496&quot; data-start=&quot;6354&quot; data-ke-size=&quot;size16&quot;&gt;다만 실제 네트워크 응답을 만들고 전송하는 비용은 문자열 길이에 영향을 받을 수 있다. 예를 들어 &quot;abc&quot;를 돌려주는 것과 매우 긴 문자열을 돌려주는 것은 전송해야 하는 byte 수가 다르다. 따라서 응답 크기에 따른 I/O 비용은 별도로 존재한다.&lt;/p&gt;
&lt;p data-end=&quot;6496&quot; data-start=&quot;6354&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;6510&quot; data-start=&quot;6498&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;command 로직 기준: O(1)
응답 데이터 전송 비용: 문자열 길이에 영향받을 수 있음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6674&quot; data-start=&quot;6577&quot; data-ke-size=&quot;size16&quot;&gt;이번 command는 DB 조회나 자료구조 순회 없이 인자를 그대로 응답하는 echo 계열 command이므로 metadata의 complexity를 O(1)로 작성했다.&lt;/p&gt;
&lt;hr data-end=&quot;6679&quot; data-start=&quot;6676&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6715&quot; data-start=&quot;6681&quot; data-ke-size=&quot;size23&quot;&gt;2. commands.def에 command 항목 추가&lt;/h3&gt;
&lt;p data-end=&quot;6773&quot; data-start=&quot;6717&quot; data-ke-size=&quot;size16&quot;&gt;다음으로 src/commands.def에 ECHOPIXXLIX command 항목을 추가했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;objectivec&quot;&gt;&lt;code&gt;{MAKE_CMD(&quot;echopixxlix&quot;,&quot;Returns the given string.&quot;,&quot;O(1)&quot;,&quot;8.1.0&quot;,CMD_DOC_NONE,NULL,NULL,&quot;connection&quot;,COMMAND_GROUP_CONNECTION,ECHOPIXXLIX_History,0,ECHOPIXXLIX_Tips,0,echoPixxlixCommand,2,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_CONNECTION|ACL_CATEGORY_FAST,NULL,ECHOPIXXLIX_Keyspecs,0,NULL,1),.args=ECHOPIXXLIX_Args},&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;7533&quot; data-start=&quot;7530&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7560&quot; data-start=&quot;7535&quot; data-ke-size=&quot;size23&quot;&gt;3. server.h에 함수 선언 추가&lt;/h3&gt;
&lt;p data-end=&quot;7632&quot; data-start=&quot;7562&quot; data-ke-size=&quot;size16&quot;&gt;commands.def에서 echoPixxlixCommand를 참조하므로, server.h에 함수 선언을 추가했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void echoPixxlixCommand(client *c);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7734&quot; data-start=&quot;7680&quot; data-ke-size=&quot;size16&quot;&gt;이 선언을 통해 다른 파일에서도 echoPixxlixCommand 함수의 존재를 알 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;7739&quot; data-start=&quot;7736&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7766&quot; data-start=&quot;7741&quot; data-ke-size=&quot;size23&quot;&gt;4. server.c에 함수 구현 추가&lt;/h3&gt;
&lt;p data-end=&quot;7792&quot; data-start=&quot;7768&quot; data-ke-size=&quot;size16&quot;&gt;실제 동작은 server.c에 구현했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void echoPixxlixCommand(client *c) {
    addReplyBulk(c, c-&amp;gt;argv[1]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7951&quot; data-start=&quot;7876&quot; data-ke-size=&quot;size16&quot;&gt;기존 echoCommand와 동일하게 사용자가 전달한 첫 번째 인자인 c-&amp;gt;argv[1]을 그대로 응답으로 반환하도록 구현했다.&lt;/p&gt;
&lt;p data-end=&quot;7971&quot; data-start=&quot;7953&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 명령을 실행하면,&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ECHOPIXXLIX abc&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8021&quot; data-start=&quot;8002&quot; data-ke-size=&quot;size16&quot;&gt;내부적으로는 다음과 같이 처리된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;c-&amp;gt;argv[0] = &quot;ECHOPIXXLIX&quot;
c-&amp;gt;argv[1] = &quot;abc&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8137&quot; data-start=&quot;8082&quot; data-ke-size=&quot;size16&quot;&gt;그리고 addReplyBulk(c, c-&amp;gt;argv[1])에 의해 abc가 응답으로 반환된다.&lt;/p&gt;
&lt;hr data-end=&quot;8142&quot; data-start=&quot;8139&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;4381&quot; data-start=&quot;4356&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt;테스트 코드 추가&lt;/h2&gt;
&lt;p data-end=&quot;8227&quot; data-start=&quot;8158&quot; data-ke-size=&quot;size16&quot;&gt;command가 정상적으로 동작하는지 확인하기 위해 tests/unit/other.tcl에 unit test를 추가했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;subunit&quot;&gt;&lt;code&gt;test {Coverage: ECHOPIXXLIX} {
    assert_equal bang [r ECHOPIXXLIX bang]
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8517&quot; data-start=&quot;8448&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;8624&quot; data-start=&quot;8519&quot; data-ke-size=&quot;size16&quot;&gt;만약 command가 제대로 등록되지 않았다면 command를 찾지 못해 실패할 것이고, 함수 구현에서 잘못된 argument를 반환했다면 기대값과 실제값이 달라져 테스트가 실패할 것이다.&lt;/p&gt;
&lt;p data-end=&quot;8701&quot; data-start=&quot;8626&quot; data-ke-size=&quot;size16&quot;&gt;즉, 이 테스트는 단순히 문자열 하나가 잘 출력되는지만 확인하는 것이 아니라, 다음 흐름이 정상적으로 이어졌는지 확인하는 역할도 한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. ECHOPIXXLIX command가 등록되어 있다.
2. lookupCommand()가 command를 찾을 수 있다.
3. c-&amp;gt;cmd에 command 정보가 연결된다.
4. call() 내부에서 echoPixxlixCommand()가 호출된다.
5. c-&amp;gt;argv[1] 값이 응답으로 반환된다.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;8890&quot; data-start=&quot;8887&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;4381&quot; data-start=&quot;4356&quot; data-ke-size=&quot;size26&quot;&gt;빌드 및 테스트&lt;/h2&gt;
&lt;p data-end=&quot;8922&quot; data-start=&quot;8905&quot; data-ke-size=&quot;size16&quot;&gt;구현 후 먼저 빌드를 진행했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;make -j$(nproc)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8972&quot; data-start=&quot;8953&quot; data-ke-size=&quot;size16&quot;&gt;이후 unit test를 실행했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;./runtest --single unit/other&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9039&quot; data-start=&quot;9017&quot; data-ke-size=&quot;size16&quot;&gt;결과는 다음과 같이 정상적으로 통과했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;[ok]: Coverage: ECHOPIXXLIX (0 ms)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;9092&quot; data-start=&quot;9089&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;9113&quot; data-start=&quot;9094&quot; data-ke-size=&quot;size26&quot;&gt;valkey-cli 수동 검증&lt;/h2&gt;
&lt;p data-end=&quot;9165&quot; data-start=&quot;9115&quot; data-ke-size=&quot;size16&quot;&gt;unit test 외에도 valkey-cli를 통해 직접 command를 실행해보았다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;accesslog&quot;&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ECHOPIXXLIX abc
&quot;abc&quot;

127.0.0.1:6379&amp;gt; ECHOPIXXLIX &quot;hello world&quot;
&quot;hello world&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9306&quot; data-start=&quot;9275&quot; data-ke-size=&quot;size16&quot;&gt;입력한 문자열이 그대로 반환되는 것을 확인할 수 있었다.&lt;/p&gt;
&lt;hr data-end=&quot;9311&quot; data-start=&quot;9308&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;4381&quot; data-start=&quot;4356&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt;PR 과정 정리&lt;/h2&gt;
&lt;p data-end=&quot;9348&quot; data-start=&quot;9326&quot; data-ke-size=&quot;size16&quot;&gt;이번 구현 과정은 다음 순서로 진행했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 기존 ECHO command 구조 확인
2. ECHOPIXXLIX command metadata 추가
3. commands.def에 command 항목 추가
4. server.h에 함수 선언 추가
5. server.c에 echoPixxlixCommand 구현
6. tests/unit/other.tcl에 unit test 추가
7. make로 빌드
8. unit test 실행
9. valkey-cli로 수동 검증
10. commit 후 PR 생성&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9726&quot; data-start=&quot;9617&quot; data-ke-size=&quot;size16&quot;&gt;작은 command 하나를 추가하는 과정이었지만, 실제로는 command metadata, command table 등록 정보, 함수 선언, 함수 구현, 테스트까지 여러 지점이 함께 맞아야 했다.&lt;/p&gt;
&lt;hr data-end=&quot;9731&quot; data-start=&quot;9728&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-end=&quot;4381&quot; data-start=&quot;4356&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt; &lt;/span&gt;배운 점&lt;/h2&gt;
&lt;p data-end=&quot;9806&quot; data-start=&quot;9742&quot; data-ke-size=&quot;size16&quot;&gt;이번 실습을 통해 Valkey command가 단순히 C 함수 하나로만 이루어진 것이 아니라는 점을 알 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;10066&quot; data-start=&quot;9808&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 ECHOPIXXLIX abc를 입력하면 곧바로 echoPixxlixCommand()가 호출될 것처럼 생각했다. 하지만 실제로는 client 연결 수락, 데이터 수신, 프로토콜 parsing, command lookup, 실행 전 검증, 실제 함수 호출, 응답 전송이라는 여러 단계를 거친다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;10066&quot; data-start=&quot;9808&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;10066&quot; data-start=&quot;9808&quot; data-ke-size=&quot;size16&quot;&gt;학기 초 운영체제 tiny shell 과제에서 명령어 만들기를 했었는데, 그때와 유사한 C 코드를 작성하여 기억이 새록새록 나고 재밌었다. 역시 전공수업 과제를 딸깍 안하고 열심히 하면 언젠가는 도움이 된다.&lt;/p&gt;
&lt;p data-end=&quot;10066&quot; data-start=&quot;9808&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;10318&quot; data-start=&quot;10068&quot; data-ke-size=&quot;size16&quot;&gt;command 등록 흐름과 실행 흐름이 server.commands hashtable에서 만난다는 점을 이해한 것도 큰 수확이었다.&lt;/p&gt;
&lt;p data-end=&quot;10318&quot; data-start=&quot;10068&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;10318&quot; data-start=&quot;10068&quot; data-ke-size=&quot;size16&quot;&gt;서버 시작 시 command가 table에 등록되고, client 요청이 들어오면 lookupCommand()가 이 table에서 command를 찾는다. command를 찾은 뒤에는 c-&amp;gt;cmd에 command 정보가 연결되고, 최종적으로 call() 함수 내부에서 다음 코드가 실행된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;c-&amp;gt;cmd-&amp;gt;proc(c)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10471&quot; data-start=&quot;10346&quot; data-ke-size=&quot;size16&quot;&gt;즉, 커스텀 command를 추가한다는 것은 단순히 새로운 C 함수를 작성하는 일이 아니라, &amp;ldquo;이 command 이름이 들어왔을 때 어떤 함수가 실행되어야 하는가&amp;rdquo;를 Valkey의 command 등록 구조에 연결하는 일이다.&lt;/p&gt;
&lt;p data-end=&quot;10683&quot; data-start=&quot;10473&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;10915&quot; data-start=&quot;10685&quot; data-ke-size=&quot;size16&quot;&gt;마지막으로, 아주 작은 echo command를 추가하는 실습이었지만 Valkey 코드를 읽는 방법을 익힐 수 있었다. 그동안 한 파일에 16000줄이 넘어가는 코드들을 보며 대체 어디에 PR을 시도해야할지, 구조를 어떻게 이해할지 어려웠는데 해당 실습을 통해 코드를 어떻게 읽어야 할지 감이 잡혔다.&lt;/p&gt;
&lt;p data-end=&quot;10915&quot; data-start=&quot;10685&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;10915&quot; data-start=&quot;10685&quot; data-ke-size=&quot;size16&quot;&gt;앞으로 다른 command를 분석할 때도 먼저 command 정의를 찾고, 연결된 함수 이름을 확인한 뒤, 실제 구현으로 따라 들어가면 된다. 예를 들어 GET, HGET, ZRANGE 같은 command도 같은 방식으로 command 정의에서 출발해 내부 구현을 추적할 수 있다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;11104&quot; data-start=&quot;10917&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS STUDY</category>
      <category>ossca</category>
      <category>redis</category>
      <category>RedisCommand</category>
      <category>Valkey</category>
      <category>ValkeyCommand</category>
      <category>오픈소스</category>
      <category>오픈소스컨트리뷰션</category>
      <category>커스텀명령어</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/29</guid>
      <comments>https://pixxlix.tistory.com/29#entry29comment</comments>
      <pubDate>Thu, 21 May 2026 04:36:06 +0900</pubDate>
    </item>
    <item>
      <title>Redis, Valkey는 왜 빠를까? - 자료구조, 메모리 최적화, RESP 프로토콜 실습</title>
      <link>https://pixxlix.tistory.com/28</link>
      <description>&lt;div&gt;
&lt;div data-turn-start-message=&quot;true&quot; data-message-model-slug=&quot;gpt-5-5-thinking&quot; data-message-id=&quot;6487e529-95f0-47b0-aa2c-4590e6f24526&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-end=&quot;383&quot; data-start=&quot;248&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;OSSCA 2026 - Valkey 수업 내용을 바탕으로 정리한 글입니다.&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1196&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEauBr/dJMcab5dfpR/fQXp8PL0qXoM2KLqWskZa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEauBr/dJMcab5dfpR/fQXp8PL0qXoM2KLqWskZa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEauBr/dJMcab5dfpR/fQXp8PL0qXoM2KLqWskZa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEauBr%2FdJMcab5dfpR%2FfQXp8PL0qXoM2KLqWskZa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1196&quot; height=&quot;464&quot; data-origin-width=&quot;1196&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-end=&quot;383&quot; data-start=&quot;248&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;383&quot; data-start=&quot;248&quot; data-ke-size=&quot;size16&quot;&gt;누가 redis가 뭔가요? 라고 물어보면 in-memory에 데이터를 저장하는 빠른 key-value store라고 대답했다. 실제로 Valkey를 사용하는 가장 큰 이유도 DB나 disk보다 memory 접근이 훨씬 빠르기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;383&quot; data-start=&quot;248&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;605&quot; data-start=&quot;385&quot; data-ke-size=&quot;size16&quot;&gt;하지만 Valkey의 성능은 단순히 &amp;ldquo;memory라서 빠르다&amp;rdquo;로만 설명되지 않는다. 내부적으로는 Hash Table, Skip List, Listpack, QuickList 같은 자료구조가 상황에 맞게 사용되고, command 실행 구조와 network I/O 처리 방식도 성능에 큰 영향을 준다. 또한 client와 server는 RESP라는 protocol을 통해 명령과 응답을 주고받는다.&lt;/p&gt;
&lt;p data-end=&quot;605&quot; data-start=&quot;385&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;698&quot; data-start=&quot;607&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 Valkey가 왜 빠른지, 그 빠름이 어떤 내부 구조에서 나오는지, 그리고 RESP 실습을 통해 명령어가 실제로 어떻게 전달되는지 정리해보려고 한다.&lt;/p&gt;
&lt;hr data-end=&quot;703&quot; data-start=&quot;700&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;721&quot; data-start=&quot;705&quot; data-ke-size=&quot;size26&quot;&gt;Valkey를 쓰는 이유&lt;/h2&gt;
&lt;p data-end=&quot;762&quot; data-start=&quot;723&quot; data-ke-size=&quot;size16&quot;&gt;Valkey를 사용하는 가장 직접적인 이유는 &lt;b&gt;빠른 응답 속도&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;911&quot; data-start=&quot;764&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 DB는 data를 조회할 때 disk 접근, index 탐색, query execution, network 통신 등 여러 과정을 거친다. 반면 Valkey는 data를 memory에 저장하기 때문에 자주 접근하는 data를 훨씬 빠르게 읽고 쓸 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;979&quot; data-start=&quot;913&quot; data-ke-size=&quot;size16&quot;&gt;그래서 Valkey는 DB를 완전히 대체하기보다는, DB 앞단이나 옆에서 빠른 처리가 필요한 역할을 맡는 경우가 많다.&lt;/p&gt;
&lt;p data-end=&quot;979&quot; data-start=&quot;913&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;사용처&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1269&quot; data-start=&quot;981&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;1269&quot; data-start=&quot;1004&quot;&gt;
&lt;tr data-end=&quot;1044&quot; data-start=&quot;1004&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1012&quot; data-start=&quot;1004&quot;&gt;Cache&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1044&quot; data-start=&quot;1012&quot;&gt;DB 조회 결과를 memory에 저장해 빠르게 응답&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1097&quot; data-start=&quot;1045&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1061&quot; data-start=&quot;1045&quot;&gt;Session Store&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1097&quot; data-start=&quot;1061&quot;&gt;여러 Web Server가 login session을 공유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1161&quot; data-start=&quot;1098&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1117&quot; data-start=&quot;1098&quot;&gt;Distributed Lock&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1161&quot; data-start=&quot;1117&quot;&gt;여러 process가 같은 resource를 동시에 수정하지 못하게 제어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1202&quot; data-start=&quot;1162&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1177&quot; data-start=&quot;1162&quot;&gt;Rate Limiter&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1202&quot; data-start=&quot;1177&quot;&gt;특정 user나 IP의 요청 횟수 제한&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1247&quot; data-start=&quot;1203&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1217&quot; data-start=&quot;1203&quot;&gt;Leaderboard&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1247&quot; data-start=&quot;1217&quot;&gt;Sorted Set을 이용해 ranking 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1269&quot; data-start=&quot;1248&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1256&quot; data-start=&quot;1248&quot;&gt;Queue&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1269&quot; data-start=&quot;1256&quot;&gt;작업 대기열 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1422&quot; data-start=&quot;1271&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 게시글 상세 조회 API가 있다고 하자. 매번 DB에서 게시글 정보를 읽으면 DB 부하가 커지고 응답 속도도 느려질 수 있다. 이때 자주 조회되는 게시글을 Valkey에 cache해두면 다음 요청부터는 DB까지 가지 않고 Valkey에서 바로 응답할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;SET post:1 &quot;{title:'Valkey 정리', content:'...'}&quot;
GET post:1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1560&quot; data-start=&quot;1497&quot; data-ke-size=&quot;size16&quot;&gt;이처럼 Valkey는 &lt;b&gt;자주 조회되거나 빠르게 처리해야 하는 data를 memory에 올려두는 역할&lt;/b&gt;을 한다.&lt;/p&gt;
&lt;p data-end=&quot;1671&quot; data-start=&quot;1562&quot; data-ke-size=&quot;size16&quot;&gt;다만 Valkey를 잘 사용하려면 단순히 명령어 사용법만 아는 것으로는 부족하다. 내부 자료구조와 처리 방식을 이해해야 어떤 상황에서 빠르고, 어떤 상황에서 성능 문제가 생기는지 판단할 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;1676&quot; data-start=&quot;1673&quot; data-ke-style=&quot;style1&quot; /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;1126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH7IKD/dJMcab5dfgC/d3Hnh4HjRUVKDsqJwzG6j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH7IKD/dJMcab5dfgC/d3Hnh4HjRUVKDsqJwzG6j0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH7IKD/dJMcab5dfgC/d3Hnh4HjRUVKDsqJwzG6j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH7IKD%2FdJMcab5dfgC%2Fd3Hnh4HjRUVKDsqJwzG6j0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1266&quot; height=&quot;1126&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;1126&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;h2 data-end=&quot;1702&quot; data-start=&quot;1678&quot; data-ke-size=&quot;size26&quot;&gt;Sorted Set과 Skip List&lt;/h2&gt;
&lt;p data-end=&quot;1745&quot; data-start=&quot;1704&quot; data-ke-size=&quot;size16&quot;&gt;Valkey의 대표적인 자료구조 중 하나는 &lt;b&gt;Sorted Set&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;1889&quot; data-start=&quot;1747&quot; data-ke-size=&quot;size16&quot;&gt;Sorted Set은 일반 Set처럼 member를 저장하지만, 각 member에 score가 붙는다. 이 score를 기준으로 정렬된 상태를 유지할 수 있기 때문에 ranking, leaderboard, 인기 게시글 순위 같은 기능에 자주 사용된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ZADD ranking 100 user1
ZADD ranking 90 user2
ZADD ranking 120 user3
ZRANGE ranking 0 -1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2002&quot; data-start=&quot;1993&quot; data-ke-size=&quot;size16&quot;&gt;문제는 정렬이다.&lt;/p&gt;
&lt;p data-end=&quot;2116&quot; data-start=&quot;2004&quot; data-ke-size=&quot;size16&quot;&gt;score 기준으로 빠르게 삽입하고 조회하려면 내부 자료구조가 중요하다. 단순 linked list를 사용하면 원하는 위치를 찾기 위해 처음부터 순차 탐색해야 하므로 최악의 경우 O(N)이 걸린다.&lt;/p&gt;
&lt;p data-end=&quot;2116&quot; data-start=&quot;2004&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2185&quot; data-start=&quot;2118&quot; data-ke-size=&quot;size16&quot;&gt;이를 개선하기 위해 Redis/Valkey의 Sorted Set은 큰 data에서는 &lt;b&gt;Skip List&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;p data-end=&quot;2365&quot; data-start=&quot;2187&quot; data-ke-size=&quot;size16&quot;&gt;Skip List는 linked list에 여러 단계의 &amp;ldquo;급행 구간&amp;rdquo;을 추가한 구조라고 이해하면 쉽다. 가장 아래 level에는 모든 node가 있고, 위 level로 갈수록 일부 node만 남는다. 탐색할 때는 가장 높은 level에서 시작해 오른쪽으로 이동하다가, 더 이상 갈 수 없으면 아래 level로 내려간다.&lt;/p&gt;
&lt;p data-end=&quot;2410&quot; data-start=&quot;2367&quot; data-ke-size=&quot;size16&quot;&gt;일반 linked list에서 30을 찾으려면 다음처럼 하나씩 봐야 한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1 &amp;rarr; 5 &amp;rarr; 10 &amp;rarr; 15 &amp;rarr; 20 &amp;rarr; 30&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2500&quot; data-start=&quot;2451&quot; data-ke-size=&quot;size16&quot;&gt;반면 Skip List는 위쪽 level에서 일부 node를 건너뛰면서 탐색할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;상위 level에서 크게 이동
&amp;rarr; 목표값보다 커질 것 같으면 아래 level로 내려감
&amp;rarr; 다시 오른쪽으로 이동&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2612&quot; data-start=&quot;2577&quot; data-ke-size=&quot;size16&quot;&gt;이 과정을 통해 평균적으로 O(log N) 탐색이 가능하다.&lt;/p&gt;
&lt;p data-end=&quot;2612&quot; data-start=&quot;2577&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2641&quot; data-start=&quot;2614&quot; data-ke-size=&quot;size16&quot;&gt;Skip List의 특징을 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2786&quot; data-start=&quot;2643&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2680&quot; data-start=&quot;2643&quot;&gt;linked list에 빠르게 건너뛰는 level을 추가한 구조&lt;/li&gt;
&lt;li data-end=&quot;2706&quot; data-start=&quot;2681&quot;&gt;평균 탐색 시간복잡도는 O(log N)&lt;/li&gt;
&lt;li data-end=&quot;2731&quot; data-start=&quot;2707&quot;&gt;node의 높이는 random하게 결정됨&lt;/li&gt;
&lt;li data-end=&quot;2750&quot; data-start=&quot;2732&quot;&gt;트리보다 구현이 비교적 단순함&lt;/li&gt;
&lt;li data-end=&quot;2786&quot; data-start=&quot;2751&quot;&gt;Sorted Set에서 score 기반 탐색과 삽입에 적합함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2876&quot; data-start=&quot;2788&quot; data-ke-size=&quot;size16&quot;&gt;즉, Sorted Set은 단순히 &amp;ldquo;score가 있는 Set&amp;rdquo;이 아니라, 내부적으로 빠른 정렬 조회를 위해 Skip List 같은 자료구조를 사용하는 구조다.&lt;/p&gt;
&lt;hr data-end=&quot;2881&quot; data-start=&quot;2878&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2908&quot; data-start=&quot;2883&quot; data-ke-size=&quot;size26&quot;&gt;Hash 자료구조와 field 단위 접근&lt;/h2&gt;
&lt;p data-end=&quot;3005&quot; data-start=&quot;2910&quot; data-ke-size=&quot;size16&quot;&gt;Valkey의 Hash는 key 안에 다시 field-value 쌍을 저장하는 자료구조다. 쉽게 말하면 main key 안에 sub key를 가지는 구조라고 볼 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;3032&quot; data-start=&quot;3007&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 user 정보를 저장한다고 해보자.&lt;/p&gt;
&lt;p data-end=&quot;3071&quot; data-start=&quot;3034&quot; data-ke-size=&quot;size16&quot;&gt;String으로 저장하면 다음처럼 JSON 형태로 저장할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;SET user:1 &quot;{name:'kim', age:20, email:'a@test.com'}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3210&quot; data-start=&quot;3141&quot; data-ke-size=&quot;size16&quot;&gt;이 방식은 전체 profile을 한 번에 읽고 쓸 때는 편하다. 하지만 email 하나만 수정하고 싶다면 문제가 생긴다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. GET user:1
2. JSON parsing
3. email 수정
4. 다시 JSON serialize
5. SET user:1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3380&quot; data-start=&quot;3302&quot; data-ke-size=&quot;size16&quot;&gt;이 과정은 read-modify-write 구조다. 여러 요청이 동시에 같은 값을 읽고 수정하면 race condition이 생길 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;3415&quot; data-start=&quot;3382&quot; data-ke-size=&quot;size16&quot;&gt;반면 Hash를 사용하면 field 단위로 접근할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;HSET user:1 name kim
HSET user:1 age 20
HSET user:1 email a@test.com&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3527&quot; data-start=&quot;3500&quot; data-ke-size=&quot;size16&quot;&gt;email만 바꾸고 싶다면 다음 한 줄이면 된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;HSET user:1 email b@test.com&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3660&quot; data-start=&quot;3572&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 전체 JSON을 다시 읽고 쓰지 않아도 된다. 따라서 &lt;b&gt;개별 field를 자주 변경하는 data라면 String보다 Hash가 더 적합&lt;/b&gt;하다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;방식&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 장점&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;단점
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3815&quot; data-start=&quot;3662&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;3815&quot; data-start=&quot;3693&quot;&gt;
&lt;tr data-end=&quot;3769&quot; data-start=&quot;3693&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3707&quot; data-start=&quot;3693&quot;&gt;String JSON&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3730&quot; data-start=&quot;3707&quot;&gt;전체 data를 한 번에 다루기 편함&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3769&quot; data-start=&quot;3730&quot;&gt;field 단위 수정이 불편하고 race condition 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3815&quot; data-start=&quot;3770&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3777&quot; data-start=&quot;3770&quot;&gt;Hash&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3795&quot; data-start=&quot;3777&quot;&gt;field 단위 수정이 쉬움&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3815&quot; data-start=&quot;3795&quot;&gt;구조 설계를 미리 고려해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3946&quot; data-start=&quot;3817&quot; data-ke-size=&quot;size16&quot;&gt;Valkey를 cache로 사용할 때도 이 차이가 중요하다. 단순히 조회 결과 전체를 cache하는 경우라면 String JSON이 편할 수 있지만, 개별 field가 자주 바뀐다면 Hash를 사용하는 것이 더 안전하고 효율적이다.&lt;/p&gt;
&lt;hr data-end=&quot;3951&quot; data-start=&quot;3948&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3985&quot; data-start=&quot;3953&quot; data-ke-size=&quot;size26&quot;&gt;Single-threaded command 처리 구조&lt;/h2&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGKndy/dJMcagk8G0g/QQ6kq6FZW46cFfRokAbPhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGKndy/dJMcagk8G0g/QQ6kq6FZW46cFfRokAbPhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGKndy/dJMcagk8G0g/QQ6kq6FZW46cFfRokAbPhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGKndy%2FdJMcagk8G0g%2FQQ6kq6FZW46cFfRokAbPhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;467&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;467&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-end=&quot;4016&quot; data-start=&quot;3987&quot; data-ke-size=&quot;size16&quot;&gt;Valkey를 공부하면서 자주 헷갈리는 부분이 있다.&lt;/p&gt;
&lt;blockquote data-end=&quot;4064&quot; data-start=&quot;4018&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;p data-end=&quot;4064&quot; data-start=&quot;4020&quot; data-ke-size=&quot;size16&quot;&gt;Valkey는 single-threaded인가, multi-threaded인가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;4086&quot; data-start=&quot;4066&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4086&quot; data-start=&quot;4066&quot; data-ke-size=&quot;size16&quot;&gt;정확히는 이렇게 이해하는 것이 좋다.&lt;/p&gt;
&lt;blockquote data-end=&quot;4177&quot; data-start=&quot;4088&quot; data-ke-style=&quot;style2&quot;&gt;
&lt;p data-end=&quot;4177&quot; data-start=&quot;4090&quot; data-ke-size=&quot;size16&quot;&gt;Valkey는 여러 thread를 사용할 수 있지만, 실제 data를 조회하거나 변경하는 command 실행은 main thread에서 순차적으로 처리된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;4362&quot; data-start=&quot;4179&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4362&quot; data-start=&quot;4179&quot; data-ke-size=&quot;size16&quot;&gt;즉, Valkey가 프로그램 전체에서 thread 하나만 사용한다는 뜻은 아니다. network I/O나 background 작업 등은 다른 thread가 도울 수 있다. 하지만 GET, SET, ZADD, HSET처럼 실제 자료구조를 읽고 수정하는 command 실행은 main thread 중심으로 이루어진다.&lt;/p&gt;
&lt;p data-end=&quot;4388&quot; data-start=&quot;4364&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4388&quot; data-start=&quot;4364&quot; data-ke-size=&quot;size16&quot;&gt;이 구조의 장점은 &lt;b&gt;atomic성&lt;/b&gt;이다. 예를 들어 두 client가 동시에 다음 명령을 보냈다고 하자.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;INCR count
INCR count&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4512&quot; data-start=&quot;4463&quot; data-ke-size=&quot;size16&quot;&gt;Valkey 내부에서는 두 명령이 완전히 동시에 실행되는 것이 아니라 순서대로 처리된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;INCR count 실행 완료
&amp;rarr; 다음 INCR count 실행 완료&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4683&quot; data-start=&quot;4566&quot; data-ke-size=&quot;size16&quot;&gt;이 때문에 단일 command는 중간에 다른 command가 끼어들지 않고 atomic하게 처리될 수 있다. 내부 자료구조에 복잡한 lock을 많이 걸지 않아도 data 일관성을 유지할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p data-end=&quot;4696&quot; data-start=&quot;4685&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4696&quot; data-start=&quot;4685&quot; data-ke-size=&quot;size16&quot;&gt;하지만 단점도 있다.&lt;/p&gt;
&lt;p data-end=&quot;4739&quot; data-start=&quot;4698&quot; data-ke-size=&quot;size16&quot;&gt;명령 하나가 오래 걸리면 그동안 뒤에 들어온 다른 명령들이 기다려야 한다. 예를 들어 다음과 같은 명령은 조심해야 한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;HGETALL huge_hash
SMEMBERS huge_set
ZRANGE huge_zset 0 -1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4962&quot; data-start=&quot;4840&quot; data-ke-size=&quot;size16&quot;&gt;data가 적을 때는 괜찮지만, collection이 매우 크면 main thread를 오래 점유할 수 있다. 이 경우 단순한 GET, SET 요청까지 뒤에서 대기하게 되어 전체 latency가 증가할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;4962&quot; data-start=&quot;4840&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;5030&quot; data-start=&quot;4964&quot; data-ke-size=&quot;size16&quot;&gt;따라서 Valkey를 사용할 때는 &amp;ldquo;명령 하나가 얼마나 오래 main thread를 점유하는가&amp;rdquo;를 항상 생각해야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;5035&quot; data-start=&quot;5032&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5071&quot; data-start=&quot;5037&quot; data-ke-size=&quot;size26&quot;&gt;Multi-threaded I/O와 main thread&lt;/h2&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HqBqG/dJMcac36khu/do85Exwk2gK55ziGHTSx91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HqBqG/dJMcac36khu/do85Exwk2gK55ziGHTSx91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HqBqG/dJMcac36khu/do85Exwk2gK55ziGHTSx91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHqBqG%2FdJMcac36khu%2Fdo85Exwk2gK55ziGHTSx91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;948&quot; height=&quot;723&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-end=&quot;5122&quot; data-start=&quot;5073&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면 Valkey가 multi-threaded I/O를 지원한다는 말은 무슨 뜻일까?&lt;/p&gt;
&lt;p data-end=&quot;5165&quot; data-start=&quot;5124&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;network 처리와 command 실행을 구분하는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;5165&quot; data-start=&quot;5124&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;5336&quot; data-start=&quot;5167&quot; data-ke-size=&quot;size16&quot;&gt;Valkey는 client와 TCP connection을 맺고 요청을 받는다. 이 과정에는 socket read, request parsing, response write 같은 network I/O 작업이 포함된다. client 수가 많거나 응답 크기가 크면 이 I/O 작업 자체가 부담이 될 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;5400&quot; data-start=&quot;5338&quot; data-ke-size=&quot;size16&quot;&gt;그래서 multi-threaded I/O에서는 worker thread가 network I/O를 도울 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;5400&quot; data-start=&quot;5338&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;영역&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 처리방식
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;5598&quot; data-start=&quot;5402&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;5598&quot; data-start=&quot;5427&quot;&gt;
&lt;tr data-end=&quot;5481&quot; data-start=&quot;5427&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5455&quot; data-start=&quot;5427&quot;&gt;client network read/write&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5481&quot; data-start=&quot;5455&quot;&gt;worker thread가 도울 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5526&quot; data-start=&quot;5482&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5500&quot; data-start=&quot;5482&quot;&gt;command parsing&lt;/td&gt;
&lt;td data-end=&quot;5526&quot; data-start=&quot;5500&quot; data-col-size=&quot;sm&quot;&gt;일부 I/O thread가 도울 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5566&quot; data-start=&quot;5527&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5543&quot; data-start=&quot;5527&quot;&gt;실제 command 실행&lt;/td&gt;
&lt;td data-end=&quot;5566&quot; data-start=&quot;5543&quot; data-col-size=&quot;sm&quot;&gt;main thread에서 순차 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5598&quot; data-start=&quot;5567&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5580&quot; data-start=&quot;5567&quot;&gt;data 조회/변경&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5598&quot; data-start=&quot;5580&quot;&gt;main thread 중심&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5702&quot; data-start=&quot;5600&quot; data-ke-size=&quot;size16&quot;&gt;즉, multi-threaded I/O는 network 병목을 줄이기 위한 구조다. 하지만 실제 data를 변경하는 command 실행은 여전히 main thread에서 처리된다.&lt;/p&gt;
&lt;p data-end=&quot;5785&quot; data-start=&quot;5704&quot; data-ke-size=&quot;size16&quot;&gt;이 설계는 Valkey의 atomic한 command 처리 특성을 유지하면서도, network I/O 부담을 줄이기 위한 절충이라고 볼 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;5790&quot; data-start=&quot;5787&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5832&quot; data-start=&quot;5792&quot; data-ke-size=&quot;size26&quot;&gt;메모리 최적화: Ziplist, Listpack, QuickList&lt;/h2&gt;
&lt;p data-end=&quot;5965&quot; data-start=&quot;5834&quot; data-ke-size=&quot;size16&quot;&gt;Valkey는 in-memory 기반이다. 모든 data가 memory에 올라가기 때문에 메모리 사용량은 매우 중요하다. 같은 data를 저장하더라도 어떤 내부 encoding을 사용하는지에 따라 메모리 사용량이 크게 달라질 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;5965&quot; data-start=&quot;5834&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;6061&quot; data-start=&quot;5967&quot; data-ke-size=&quot;size16&quot;&gt;작은 data를 저장할 때는 Hash Table이나 Skip List처럼 무거운 자료구조를 바로 쓰는 대신, 더 compact한 구조를 사용하는 방식이 유리할 수 있다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J0n50/dJMcafzNsG7/A9nJRN9YkSCdnHBz0IkiLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J0n50/dJMcafzNsG7/A9nJRN9YkSCdnHBz0IkiLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J0n50/dJMcafzNsG7/A9nJRN9YkSCdnHBz0IkiLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ0n50%2FdJMcafzNsG7%2FA9nJRN9YkSCdnHBz0IkiLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;934&quot; height=&quot;480&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;h3 data-end=&quot;6074&quot; data-start=&quot;6063&quot; data-ke-size=&quot;size23&quot;&gt;Ziplist&lt;/h3&gt;
&lt;p data-end=&quot;6158&quot; data-start=&quot;6076&quot; data-ke-size=&quot;size16&quot;&gt;Ziplist는 Redis 6.x까지 사용되던 compact한 자료구조다. field와 value를 연속된 memory 공간에 순차적으로 저장한다.&lt;/p&gt;
&lt;p data-end=&quot;6185&quot; data-start=&quot;6160&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 Hash data가 다음과 같다면,&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;name &amp;rarr; kim
age &amp;rarr; 20
email &amp;rarr; a@test.com&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6275&quot; data-start=&quot;6239&quot; data-ke-size=&quot;size16&quot;&gt;Ziplist에서는 대략 다음처럼 연속된 memory에 저장된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;[name][kim][age][20][email][a@test.com]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6425&quot; data-start=&quot;6330&quot; data-ke-size=&quot;size16&quot;&gt;이 방식은 pointer overhead가 적어 메모리를 아낄 수 있다. 하지만 중간에 값을 삽입하거나 크기가 변하면 뒤쪽 data를 이동해야 할 수 있어 비용이 커진다.&lt;/p&gt;
&lt;h3 data-end=&quot;6439&quot; data-start=&quot;6427&quot; data-ke-size=&quot;size23&quot;&gt;Listpack&lt;/h3&gt;
&lt;p data-end=&quot;6552&quot; data-start=&quot;6441&quot; data-ke-size=&quot;size16&quot;&gt;Listpack은 Redis 7.x부터 ziplist를 대체하는 구조다. ziplist보다 구현을 단순화하고, 값 크기 변경 시 뒤쪽 entry들이 연쇄적으로 수정되는 문제를 줄이기 위해 도입되었다.&lt;/p&gt;
&lt;p data-end=&quot;6643&quot; data-start=&quot;6554&quot; data-ke-size=&quot;size16&quot;&gt;Listpack도 연속된 memory에 data를 촘촘하게 저장한다. 작은 collection에서는 Hash Table보다 메모리를 훨씬 적게 사용할 수 있다.&lt;/p&gt;
&lt;h3 data-end=&quot;6658&quot; data-start=&quot;6645&quot; data-ke-size=&quot;size23&quot;&gt;QuickList&lt;/h3&gt;
&lt;p data-end=&quot;6701&quot; data-start=&quot;6660&quot; data-ke-size=&quot;size16&quot;&gt;QuickList는 Redis List에서 내부적으로 사용하는 자료구조다.&lt;/p&gt;
&lt;p data-end=&quot;6824&quot; data-start=&quot;6703&quot; data-ke-size=&quot;size16&quot;&gt;완전히 하나의 큰 연속 배열로 저장하면 realloc이나 memmove 비용이 커질 수 있고, 완전히 linked list로 저장하면 pointer overhead가 커진다. QuickList는 이 둘의 중간 형태다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;quicklistNode &amp;rarr; quicklistNode &amp;rarr; quicklistNode
     &amp;darr;               &amp;darr;               &amp;darr;
  listpack        listpack        listpack&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7087&quot; data-start=&quot;6967&quot; data-ke-size=&quot;size16&quot;&gt;즉, QuickList는 node 하나에 여러 값을 담고, 그런 node들을 linked list로 연결한다.&lt;br /&gt;이 구조는 linked list의 유연성과 listpack의 메모리 효율을 함께 얻기 위한 방식이다.&lt;/p&gt;
&lt;hr data-end=&quot;7092&quot; data-start=&quot;7089&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7115&quot; data-start=&quot;7094&quot; data-ke-size=&quot;size26&quot;&gt;성능과 메모리의 trade-off&lt;/h2&gt;
&lt;p data-end=&quot;7176&quot; data-start=&quot;7117&quot; data-ke-size=&quot;size16&quot;&gt;Valkey 내부 자료구조를 이해할 때 가장 중요한 것은 &lt;b&gt;메모리와 속도 사이의 trade-off&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;7344&quot; data-start=&quot;7178&quot; data-ke-size=&quot;size16&quot;&gt;실습 자료의 예시 벤치마크에서는 Hash 10만 개를 저장할 때 Hash Table은 약 8.99MB를 사용하지만 listpack은 약 2MB 정도만 사용하는 것으로 제시되었다. 같은 data를 저장하더라도 compact encoding을 사용하면 메모리를 4~5배 정도 절약할 수 있다는 의미다.&lt;/p&gt;
&lt;p data-end=&quot;7344&quot; data-start=&quot;7178&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;7373&quot; data-start=&quot;7346&quot; data-ke-size=&quot;size16&quot;&gt;하지만 메모리를 아낀다고 항상 좋은 것은 아니다.&lt;/p&gt;
&lt;p data-end=&quot;7531&quot; data-start=&quot;7375&quot; data-ke-size=&quot;size16&quot;&gt;Listpack은 data를 연속된 memory에 저장하므로 data 개수가 많아지면 insert/update 과정에서 realloc이나 memmove 비용이 커질 수 있다. 그래서 매우 큰 collection에 listpack을 사용하는 것은 오히려 성능에 좋지 않을 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;7565&quot; data-start=&quot;7533&quot; data-ke-size=&quot;size16&quot;&gt;작은 collection에서는 listpack이 유리하다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;data 개수가 적음
&amp;rarr; 순차 탐색 비용이 크지 않음
&amp;rarr; 메모리 절약 효과가 큼
&amp;rarr; listpack 사용이 유리&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7696&quot; data-start=&quot;7643&quot; data-ke-size=&quot;size16&quot;&gt;반대로 data 개수가 많아지면 Hash Table이나 Skip List 같은 구조가 유리하다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;data 개수가 많음
&amp;rarr; 순차 탐색 비용 증가
&amp;rarr; 삽입/삭제 비용 증가
&amp;rarr; Hash Table, Skip List 사용이 유리&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7814&quot; data-start=&quot;7782&quot; data-ke-size=&quot;size16&quot;&gt;Sorted Set에서도 비슷한 trade-off가 있다.&lt;/p&gt;
&lt;p data-end=&quot;7814&quot; data-start=&quot;7782&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;8022&quot; data-start=&quot;7816&quot; data-ke-size=&quot;size16&quot;&gt;실습 자료에서 제시된 500개짜리 Sorted Set 기준 예시에서는 ZADD 성능이 Skip List는 약 0.49&amp;mu;s, listpack은 약 5~6&amp;mu;s 정도로 비교되었다. 정확한 수치는 실험 환경과 설정에 따라 달라질 수 있지만, 이 예시는 Skip List가 연산 성능 면에서 더 빠르고, listpack은 메모리 절약 면에서 유리하다는 점을 보여준다.&lt;/p&gt;
&lt;p data-end=&quot;8022&quot; data-start=&quot;7816&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;구조&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 장점&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;단점
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;8193&quot; data-start=&quot;8024&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;8193&quot; data-start=&quot;8055&quot;&gt;
&lt;tr data-end=&quot;8098&quot; data-start=&quot;8055&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;8066&quot; data-start=&quot;8055&quot;&gt;Listpack&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;8075&quot; data-start=&quot;8066&quot;&gt;메모리 절약&lt;/td&gt;
&lt;td data-end=&quot;8098&quot; data-start=&quot;8075&quot; data-col-size=&quot;sm&quot;&gt;data가 많아지면 탐색/삽입 느림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;8141&quot; data-start=&quot;8099&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;8112&quot; data-start=&quot;8099&quot;&gt;Hash Table&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;8123&quot; data-start=&quot;8112&quot;&gt;조회/삽입 빠름&lt;/td&gt;
&lt;td data-end=&quot;8141&quot; data-start=&quot;8123&quot; data-col-size=&quot;sm&quot;&gt;메모리 overhead 큼&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;8193&quot; data-start=&quot;8142&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;8154&quot; data-start=&quot;8142&quot;&gt;Skip List&lt;/td&gt;
&lt;td data-end=&quot;8165&quot; data-start=&quot;8154&quot; data-col-size=&quot;sm&quot;&gt;정렬 탐색 빠름&lt;/td&gt;
&lt;td data-end=&quot;8193&quot; data-start=&quot;8165&quot; data-col-size=&quot;sm&quot;&gt;pointer와 node overhead 큼&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8275&quot; data-start=&quot;8195&quot; data-ke-size=&quot;size16&quot;&gt;Valkey는 기본적으로 작은 collection에는 compact encoding을 사용하고, 일정 임계값을 넘으면 원래 자료구조로 전환한다.&lt;/p&gt;
&lt;hr data-end=&quot;8280&quot; data-start=&quot;8277&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;8312&quot; data-start=&quot;8282&quot; data-ke-size=&quot;size26&quot;&gt;설정: compact encoding과 압축 설정&lt;/h2&gt;
&lt;p data-end=&quot;8391&quot; data-start=&quot;8314&quot; data-ke-size=&quot;size16&quot;&gt;Valkey의 내부 encoding은 설정값에 따라 달라질 수 있다. 다만 설정마다 제어하는 대상이 다르기 때문에 구분해서 이해해야 한다.&lt;/p&gt;
&lt;p data-end=&quot;8483&quot; data-start=&quot;8393&quot; data-ke-size=&quot;size16&quot;&gt;먼저 Hash나 Sorted Set이 listpack으로 유지될지, 아니면 Hash Table이나 Skip List 같은 구조로 전환될지를 결정하는 설정이 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;hash-max-listpack-entries
hash-max-listpack-value
zset-max-listpack-entries
zset-max-listpack-value&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8620&quot; data-start=&quot;8598&quot; data-ke-size=&quot;size16&quot;&gt;이런 설정은 대략 다음 기준을 조정한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;collection의 entry 개수가 일정 이하인가?
각 value 또는 member의 크기가 일정 이하인가?&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8769&quot; data-start=&quot;8698&quot; data-ke-size=&quot;size16&quot;&gt;조건을 만족하면 compact한 listpack encoding을 사용하고, 임계값을 넘으면 더 빠른 일반 자료구조로 전환된다.&lt;/p&gt;
&lt;p data-end=&quot;8821&quot; data-start=&quot;8771&quot; data-ke-size=&quot;size16&quot;&gt;List의 경우에는 listpack node의 크기와 관련된 설정으로 다음이 더 적절하다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;list-max-listpack-size&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8926&quot; data-start=&quot;8859&quot; data-ke-size=&quot;size16&quot;&gt;이 설정은 QuickList 내부에서 각 listpack node가 어느 정도 크기까지 저장될 수 있는지에 영향을 준다.&lt;/p&gt;
&lt;p data-end=&quot;9015&quot; data-start=&quot;8928&quot; data-ke-size=&quot;size16&quot;&gt;한편 다음 설정은 listpack과 Hash Table/Skip List 간 전환 기준이 아니라, QuickList node 압축과 관련된 별도의 설정이다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;list-compression-depth&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9230&quot; data-start=&quot;9053&quot; data-ke-size=&quot;size16&quot;&gt;list-compression-depth는 QuickList에서 head와 tail 쪽 몇 개 node를 압축하지 않고 유지할지 결정한다. 중간 node는 LZF 알고리즘으로 압축될 수 있다. 즉, 이 설정은 compact encoding 전환 임계값이라기보다는 &lt;b&gt;List 내부 node 압축 정책&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;p data-end=&quot;9244&quot; data-start=&quot;9232&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;9244&quot; data-start=&quot;9232&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;설정&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;역할
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;9525&quot; data-start=&quot;9246&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;9525&quot; data-start=&quot;9268&quot;&gt;
&lt;tr data-end=&quot;9333&quot; data-start=&quot;9268&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9298&quot; data-start=&quot;9268&quot;&gt;hash-max-listpack-entries&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9333&quot; data-start=&quot;9298&quot;&gt;Hash가 listpack으로 유지될 최대 field 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9406&quot; data-start=&quot;9334&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9364&quot; data-start=&quot;9334&quot;&gt;zset-max-listpack-entries&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9406&quot; data-start=&quot;9364&quot;&gt;Sorted Set이 listpack으로 유지될 최대 member 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9470&quot; data-start=&quot;9407&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9434&quot; data-start=&quot;9407&quot;&gt;list-max-listpack-size&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9470&quot; data-start=&quot;9434&quot;&gt;QuickList 내부 listpack node 크기 제어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9525&quot; data-start=&quot;9471&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9498&quot; data-start=&quot;9471&quot;&gt;list-compression-depth&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9525&quot; data-start=&quot;9498&quot;&gt;QuickList node 압축 깊이 제어&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9641&quot; data-start=&quot;9527&quot; data-ke-size=&quot;size16&quot;&gt;설정을 메모리 절약 방향으로만 조정하면 성능이 나빠질 수 있고, 반대로 빠른 자료구조를 너무 빨리 사용하면 메모리 사용량이 증가할 수 있다. 결국 workload에 맞게 적절한 균형을 잡는 것이 중요하다.&lt;/p&gt;
&lt;hr data-end=&quot;9646&quot; data-start=&quot;9643&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;9690&quot; data-start=&quot;9648&quot; data-ke-size=&quot;size26&quot;&gt;하나의 collection에 너무 많은 data를 넣으면 안 되는 이유&lt;/h2&gt;
&lt;p data-end=&quot;9763&quot; data-start=&quot;9692&quot; data-ke-size=&quot;size16&quot;&gt;Valkey를 사용할 때 특히 조심해야 할 점은 &lt;b&gt;많은 수의 data를 하나의 collection에 몰아넣지 않는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;9792&quot; data-start=&quot;9765&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 구조를 생각해볼 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;HSET all_users user1 ...
HSET all_users user2 ...
HSET all_users user3 ...
...&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9978&quot; data-start=&quot;9887&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 all_users라는 하나의 Hash에 모든 user 정보를 넣으면, 해당 key 하나가 매우 커진다. 이런 big key는 여러 문제를 만들 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;10121&quot; data-start=&quot;9980&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;10013&quot; data-start=&quot;9980&quot;&gt;특정 command가 main thread를 오래 점유함&lt;/li&gt;
&lt;li data-end=&quot;10030&quot; data-start=&quot;10014&quot;&gt;전체 조회 명령이 위험해짐&lt;/li&gt;
&lt;li data-end=&quot;10053&quot; data-start=&quot;10031&quot;&gt;network response가 커짐&lt;/li&gt;
&lt;li data-end=&quot;10099&quot; data-start=&quot;10054&quot;&gt;rehash, realloc, encoding conversion 비용이 커짐&lt;/li&gt;
&lt;li data-end=&quot;10121&quot; data-start=&quot;10100&quot;&gt;p99 latency가 튈 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;10179&quot; data-start=&quot;10123&quot; data-ke-size=&quot;size16&quot;&gt;따라서 data가 많다면 하나의 collection에 몰아넣기보다는 여러 key로 나누는 것이 좋다.&lt;/p&gt;
&lt;p data-end=&quot;10215&quot; data-start=&quot;10181&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 user id 기준으로 range를 나눌 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;users:0      &amp;rarr; user_id 0~9999
users:1      &amp;rarr; user_id 10000~19999
users:2      &amp;rarr; user_id 20000~29999&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10350&quot; data-start=&quot;10330&quot; data-ke-size=&quot;size16&quot;&gt;또는 날짜 기준으로 나눌 수도 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;viewcount:2026-05-01
viewcount:2026-05-02&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10532&quot; data-start=&quot;10407&quot; data-ke-size=&quot;size16&quot;&gt;Valkey는 빠르지만, 모든 data를 무작정 하나의 큰 collection에 넣는 방식은 위험하다.&lt;br /&gt;작은 collection을 적절히 나누고, 필요한 경우 TTL이나 trim을 사용해 크기를 제한하는 설계가 필요하다.&lt;/p&gt;
&lt;hr data-end=&quot;10537&quot; data-start=&quot;10534&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;10572&quot; data-start=&quot;10539&quot; data-ke-size=&quot;size26&quot;&gt;RESP: Valkey 명령어가 오가는 protocol&lt;/h2&gt;
&lt;p data-end=&quot;10645&quot; data-start=&quot;10574&quot; data-ke-size=&quot;size16&quot;&gt;지금까지는 Valkey 내부 자료구조와 command 처리 구조를 정리했다.&lt;br /&gt;이제 실습과 직접 연결되는 RESP를 살펴보자.&lt;/p&gt;
&lt;p data-end=&quot;10753&quot; data-start=&quot;10647&quot; data-ke-size=&quot;size16&quot;&gt;RESP는 &lt;b&gt;REdis Serialization Protocol&lt;/b&gt;의 약자다.&lt;br /&gt;Valkey client와 server가 명령어와 응답을 주고받기 위한 network protocol이다.&lt;/p&gt;
&lt;p data-end=&quot;10788&quot; data-start=&quot;10755&quot; data-ke-size=&quot;size16&quot;&gt;우리가 valkey-cli에서 다음처럼 입력한다고 하자.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET a&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10851&quot; data-start=&quot;10810&quot; data-ke-size=&quot;size16&quot;&gt;사람이 보기에는 단순한 문자열이지만, 실제로는 RESP 형식으로 표현된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;*2\r\n$3\r\nGET\r\n$1\r\na\r\n&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10912&quot; data-start=&quot;10897&quot; data-ke-size=&quot;size16&quot;&gt;이를 해석하면 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;*2   &amp;rarr; 인자가 2개인 배열
$3   &amp;rarr; 길이 3인 문자열
GET  &amp;rarr; 첫 번째 인자
$1   &amp;rarr; 길이 1인 문자열
a    &amp;rarr; 두 번째 인자&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;11051&quot; data-start=&quot;11009&quot; data-ke-size=&quot;size16&quot;&gt;즉, server 입장에서는 다음과 같은 command 배열을 받는 것이다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;GET&quot;, &quot;a&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;11100&quot; data-start=&quot;11079&quot; data-ke-size=&quot;size16&quot;&gt;RESP2의 주요 타입은 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;타입&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 시작문자&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;예시
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;11297&quot; data-start=&quot;11102&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;11297&quot; data-start=&quot;11136&quot;&gt;
&lt;tr data-end=&quot;11167&quot; data-start=&quot;11136&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11152&quot; data-start=&quot;11136&quot;&gt;Simple String&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11158&quot; data-start=&quot;11152&quot;&gt;+&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11167&quot; data-start=&quot;11158&quot;&gt;+OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;11192&quot; data-start=&quot;11168&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11176&quot; data-start=&quot;11168&quot;&gt;Error&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11182&quot; data-start=&quot;11176&quot;&gt;-&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11192&quot; data-start=&quot;11182&quot;&gt;-ERR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;11217&quot; data-start=&quot;11193&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11203&quot; data-start=&quot;11193&quot;&gt;Integer&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11209&quot; data-start=&quot;11203&quot;&gt;:&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11217&quot; data-start=&quot;11209&quot;&gt;:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;11257&quot; data-start=&quot;11218&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11232&quot; data-start=&quot;11218&quot;&gt;Bulk String&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11238&quot; data-start=&quot;11232&quot;&gt;$&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11257&quot; data-start=&quot;11238&quot;&gt;$3\r\nabc\r\n&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;11297&quot; data-start=&quot;11258&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11279&quot; data-start=&quot;11258&quot;&gt;Array / Multi Bulk&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11285&quot; data-start=&quot;11279&quot;&gt;*&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11297&quot; data-start=&quot;11285&quot;&gt;*2 ...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;11406&quot; data-start=&quot;11299&quot; data-ke-size=&quot;size16&quot;&gt;RESP에서 각 line은 \r\n, 즉 carriage return과 line feed로 구분된다.&lt;br /&gt;Bulk String은 문자열 길이를 먼저 보내고, 그 다음 실제 문자열을 보낸다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;$3\r\nabc\r\n&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;11461&quot; data-start=&quot;11435&quot; data-ke-size=&quot;size16&quot;&gt;이는 길이가 3인 문자열 abc를 의미한다.&lt;/p&gt;
&lt;p data-end=&quot;11636&quot; data-start=&quot;11463&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 길이 기반으로 문자열을 표현하는 이유는 공백이나 특수문자가 포함된 data도 정확히 처리하기 위해서다. 예를 들어 value가 hello world라면 공백 기준 parsing만으로는 하나의 문자열인지 두 개의 인자인지 구분하기 어렵다. RESP는 길이를 명시하기 때문에 이런 문제를 피할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;11782&quot; data-start=&quot;11638&quot; data-ke-size=&quot;size16&quot;&gt;RESP3도 있다. RESP3는 RESP2보다 표현력이 더 좋고, double, null, boolean, big number 같은 타입을 더 명확하게 표현할 수 있다. 기본 접속은 RESP2지만, HELLO 3 명령을 통해 RESP3로 전환할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;HELLO 3&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;11809&quot; data-start=&quot;11806&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;11837&quot; data-start=&quot;11811&quot; data-ke-size=&quot;size26&quot;&gt;실습: valkey-cli로 명령 실행하기&lt;/h2&gt;
&lt;p data-end=&quot;11888&quot; data-start=&quot;11839&quot; data-ke-size=&quot;size16&quot;&gt;먼저 valkey-cli로 Valkey server에 접속해 기본 명령어를 실행했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;get a
set a 123
set a abc
get a
zadd z1 100 OSSCA
zrange z1 0 -1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;11984&quot; data-start=&quot;11969&quot; data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같았다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; get a
(nil)

127.0.0.1:6379&amp;gt; set a 123
OK

127.0.0.1:6379&amp;gt; set a abc
OK

127.0.0.1:6379&amp;gt; get a
&quot;abc&quot;

127.0.0.1:6379&amp;gt; zadd z1 100 OSSCA
(integer) 1

127.0.0.1:6379&amp;gt; zrange z1 0 -1
1) &quot;OSSCA&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;12399&quot; data-start=&quot;12268&quot; data-ke-size=&quot;size16&quot;&gt;처음 get a를 실행했을 때는 a라는 key가 없었기 때문에 (nil)이 반환되었다. 이후 set a 123으로 값을 저장했고, 다시 set a abc를 실행하면서 같은 key의 value가 abc로 덮어써졌다.&lt;/p&gt;
&lt;p data-end=&quot;12399&quot; data-start=&quot;12268&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;12533&quot; data-start=&quot;12401&quot; data-ke-size=&quot;size16&quot;&gt;zadd z1 100 OSSCA는 z1이라는 Sorted Set에 OSSCA라는 member를 score 100으로 추가하는 명령이다. 결과로 나온 (integer) 1은 새로운 member가 1개 추가되었다는 뜻이다.&lt;/p&gt;
&lt;p data-end=&quot;12533&quot; data-start=&quot;12401&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;12637&quot; data-start=&quot;12535&quot; data-ke-size=&quot;size16&quot;&gt;zrange z1 0 -1은 z1의 0번째 index부터 마지막 index까지 조회하는 명령이다. 실습에서는 OSSCA 하나만 들어 있었기 때문에 결과도 하나만 출력되었다.&lt;/p&gt;
&lt;p data-end=&quot;12637&quot; data-start=&quot;12535&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;12743&quot; data-start=&quot;12639&quot; data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은, valkey-cli가 보여주는 출력은 사람이 보기 좋게 가공된 형태라는 것이다. 실제 server와 client 사이에서는 RESP 형식의 요청과 응답이 오간다.&lt;/p&gt;
&lt;hr data-end=&quot;12748&quot; data-start=&quot;12745&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;12776&quot; data-start=&quot;12750&quot; data-ke-size=&quot;size26&quot;&gt;실습: RESP 형식으로 직접 명령 보내기&lt;/h2&gt;
&lt;p data-end=&quot;12803&quot; data-start=&quot;12778&quot; data-ke-size=&quot;size16&quot;&gt;이제 RESP 형식으로 명령을 직접 보내보자.&lt;/p&gt;
&lt;p data-end=&quot;12836&quot; data-start=&quot;12805&quot; data-ke-size=&quot;size16&quot;&gt;telnet으로 6379 port에 접속할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;telnet 127.0.0.1 6379&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;12921&quot; data-start=&quot;12873&quot; data-ke-size=&quot;size16&quot;&gt;또는 \r\n을 정확히 보내기 위해 printf와 nc를 사용할 수도 있다.&lt;/p&gt;
&lt;p data-end=&quot;12959&quot; data-start=&quot;12923&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 GET a를 RESP 형식으로 보내면 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;printf '*2\r\n$3\r\nGET\r\n$1\r\na\r\n' | nc 127.0.0.1 6379&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13057&quot; data-start=&quot;13034&quot; data-ke-size=&quot;size16&quot;&gt;이 요청은 다음 command를 의미한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;GET&quot;, &quot;a&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13101&quot; data-start=&quot;13085&quot; data-ke-size=&quot;size16&quot;&gt;응답은 다음처럼 올 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;$3
abc&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13156&quot; data-start=&quot;13123&quot; data-ke-size=&quot;size16&quot;&gt;이는 길이 3인 bulk string abc를 의미한다.&lt;/p&gt;
&lt;p data-end=&quot;13189&quot; data-start=&quot;13158&quot; data-ke-size=&quot;size16&quot;&gt;SET c 789를 RESP로 표현하면 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;printf '*3\r\n$3\r\nSET\r\n$1\r\nc\r\n$3\r\n789\r\n' | nc 127.0.0.1 6379&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13290&quot; data-start=&quot;13277&quot; data-ke-size=&quot;size16&quot;&gt;이는 다음 배열과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;SET&quot;, &quot;c&quot;, &quot;789&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13358&quot; data-start=&quot;13325&quot; data-ke-size=&quot;size16&quot;&gt;성공하면 server는 다음과 같은 RESP 응답을 보낸다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;diff&quot;&gt;&lt;code&gt;+OK&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13413&quot; data-start=&quot;13377&quot; data-ke-size=&quot;size16&quot;&gt;ZADD z2 100 OSSCA도 RESP로 표현할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;printf '*4\r\n$4\r\nZADD\r\n$2\r\nz2\r\n$3\r\n100\r\n$5\r\nOSSCA\r\n' | nc 127.0.0.1 6379&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13544&quot; data-start=&quot;13518&quot; data-ke-size=&quot;size16&quot;&gt;응답은 다음과 같이 integer 형태로 온다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;:1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13605&quot; data-start=&quot;13562&quot; data-ke-size=&quot;size16&quot;&gt;valkey-cli에서는 이 응답을 (integer) 1처럼 보여준다.&lt;/p&gt;
&lt;p data-end=&quot;13644&quot; data-start=&quot;13607&quot; data-ke-size=&quot;size16&quot;&gt;마지막으로 ZRANGE z2 0 -1은 다음처럼 보낼 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;printf '*4\r\n$6\r\nZRANGE\r\n$2\r\nz2\r\n$1\r\n0\r\n$2\r\n-1\r\n' | nc 127.0.0.1 6379&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13774&quot; data-start=&quot;13746&quot; data-ke-size=&quot;size16&quot;&gt;응답은 다음과 같은 array 형태로 올 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;*1
$5
OSSCA&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13816&quot; data-start=&quot;13801&quot; data-ke-size=&quot;size16&quot;&gt;이를 해석하면 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;*1    &amp;rarr; 원소가 1개인 배열
$5    &amp;rarr; 길이 5인 문자열
OSSCA &amp;rarr; 실제 반환된 member&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;13954&quot; data-start=&quot;13890&quot; data-ke-size=&quot;size16&quot;&gt;즉, valkey-cli에서 보던 1) &quot;OSSCA&quot;는 실제 RESP 응답을 사람이 읽기 좋게 바꾼 결과다.&lt;/p&gt;
&lt;hr data-end=&quot;13959&quot; data-start=&quot;13956&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;13976&quot; data-start=&quot;13961&quot; data-ke-size=&quot;size26&quot;&gt;실습을 통해 확인한 점&lt;/h2&gt;
&lt;p data-end=&quot;14060&quot; data-start=&quot;13978&quot; data-ke-size=&quot;size16&quot;&gt;이번 실습을 통해 가장 먼저 확인한 것은, valkey-cli에서 입력하는 명령이 server에 그대로 문자열로 전달되는 것이 아니라는 점이다.&lt;/p&gt;
&lt;p data-end=&quot;14075&quot; data-start=&quot;14062&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 명령은,&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET a&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;14126&quot; data-start=&quot;14097&quot; data-ke-size=&quot;size16&quot;&gt;실제로는 다음과 같은 RESP 배열 구조로 전달된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;GET&quot;, &quot;a&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;14216&quot; data-start=&quot;14154&quot; data-ke-size=&quot;size16&quot;&gt;또한 ZADD, ZRANGE처럼 인자가 여러 개인 명령도 Multi Bulk 형식으로 표현할 수 있었다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;ZADD&quot;, &quot;z2&quot;, &quot;100&quot;, &quot;OSSCA&quot;]
[&quot;ZRANGE&quot;, &quot;z2&quot;, &quot;0&quot;, &quot;-1&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;14490&quot; data-start=&quot;14290&quot; data-ke-size=&quot;size16&quot;&gt;Valkey server는 TCP socket을 통해 들어온 byte stream을 input buffer에 저장하고, RESP parser를 통해 command를 완성한 뒤 실행한다. 이 과정을 생각하면 Valkey는 단순히 명령어를 실행하는 프로그램이 아니라, &lt;b&gt;network protocol을 기반으로 동작하는 server&lt;/b&gt;라는 점이 더 분명해진다.&lt;/p&gt;
&lt;hr data-end=&quot;14495&quot; data-start=&quot;14492&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;14519&quot; data-start=&quot;14497&quot; data-ke-size=&quot;size26&quot;&gt;Valkey를 사용할 때 주의할 점&lt;/h2&gt;
&lt;p data-end=&quot;14550&quot; data-start=&quot;14521&quot; data-ke-size=&quot;size16&quot;&gt;Valkey를 사용할 때는 몇 가지를 조심해야 한다.&lt;/p&gt;
&lt;p data-end=&quot;14550&quot; data-start=&quot;14521&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;14677&quot; data-start=&quot;14552&quot; data-ke-size=&quot;size16&quot;&gt;첫째, 하나의 collection에 너무 많은 data를 넣지 않는 것이 중요하다.&lt;br /&gt;Hash, Set, Sorted Set, List 모두 data가 너무 커지면 특정 명령이 main thread를 오래 점유할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;14677&quot; data-start=&quot;14552&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;14701&quot; data-start=&quot;14679&quot; data-ke-size=&quot;size16&quot;&gt;둘째, 전체 조회 명령을 조심해야 한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ZRANGE z1 0 -1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;14921&quot; data-start=&quot;14732&quot; data-ke-size=&quot;size16&quot;&gt;실습에서는 z1에 OSSCA 하나만 들어 있었기 때문에 문제가 없었다. 하지만 실제 서비스에서 수십만 개의 member가 들어 있는 Sorted Set에 대해 ZRANGE key 0 -1을 실행하면 전체 data를 한 번에 반환하게 된다. 이 경우 command 실행 시간뿐 아니라 network response 크기도 커진다.&lt;/p&gt;
&lt;p data-end=&quot;14921&quot; data-start=&quot;14732&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;15058&quot; data-start=&quot;14923&quot; data-ke-size=&quot;size16&quot;&gt;셋째, Valkey의 성능은 내부 자료구조와 설정에 따라 달라진다.&lt;br /&gt;작은 collection에서는 listpack을 사용해 메모리를 아낄 수 있지만, data가 커지면 Hash Table이나 Skip List로 전환되어야 성능이 유지된다.&lt;/p&gt;
&lt;p data-end=&quot;15058&quot; data-start=&quot;14923&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;15176&quot; data-start=&quot;15060&quot; data-ke-size=&quot;size16&quot;&gt;넷째, Valkey가 memory 기반이라 빠르다는 말은 맞지만, 무거운 command나 big key가 생기면 single-threaded command 처리 구조 때문에 전체 latency가 튈 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;15181&quot; data-start=&quot;15178&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;15189&quot; data-start=&quot;15183&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-end=&quot;15233&quot; data-start=&quot;15191&quot; data-ke-size=&quot;size16&quot;&gt;이번 정리를 통해 Valkey를 조금 더 시스템 관점에서 바라볼 수 있었다.&lt;/p&gt;
&lt;p data-end=&quot;15523&quot; data-start=&quot;15235&quot; data-ke-size=&quot;size16&quot;&gt;처음에는 Valkey를 단순히 빠른 cache 정도로 생각했지만, 실제로는 다양한 내부 구조가 함께 동작하고 있었다.&lt;/p&gt;
&lt;p data-end=&quot;15523&quot; data-start=&quot;15235&quot; data-ke-size=&quot;size16&quot;&gt;Sorted Set은 Skip List를 통해 score 기반 탐색을 빠르게 처리하고, Hash는 field 단위 접근을 제공해 개별 값 수정에 유리하다. 작은 collection에서는 Ziplist나 Listpack 같은 compact encoding을 사용해 메모리를 절약하고, List는 QuickList를 통해 linked list와 compact structure의 장점을 함께 활용한다.&lt;/p&gt;
&lt;p data-end=&quot;15523&quot; data-start=&quot;15235&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;15720&quot; data-start=&quot;15525&quot; data-ke-size=&quot;size16&quot;&gt;또한 Valkey는 command 실행을 main thread에서 순차적으로 처리해 atomic성을 유지한다. 동시에 network I/O는 worker thread가 도울 수 있어 I/O 병목을 줄일 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;15720&quot; data-start=&quot;15525&quot; data-ke-size=&quot;size16&quot;&gt;즉, Valkey의 single-threaded 특성은 단순한 한계가 아니라 data 일관성과 성능 사이의 설계 선택이라고 볼 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;15720&quot; data-start=&quot;15525&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;15824&quot; data-start=&quot;15722&quot; data-ke-size=&quot;size16&quot;&gt;마지막으로 RESP 실습을 통해 GET, SET, ZADD, ZRANGE 같은 명령이 실제로는 RESP protocol 형식으로 server에 전달된다는 점을 확인했다.&lt;/p&gt;
&lt;p data-end=&quot;15824&quot; data-start=&quot;15722&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;15959&quot; data-start=&quot;15826&quot; data-ke-size=&quot;size16&quot;&gt;정리하면, Valkey를 잘 사용하려면 단순히 명령어 사용법만 아는 것으로는 부족하다.&lt;br /&gt;내부 자료구조, 메모리 encoding, command 처리 방식, network protocol이 성능에 어떤 영향을 주는지 함께 이해해야 한다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;16035&quot; data-start=&quot;15961&quot; data-ke-size=&quot;size16&quot;&gt;Valkey는 단순한 in-memory 저장소가 아니라, 자료구조와 protocol, OS-level 처리 방식이 결합된 시스템이다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;16035&quot; data-start=&quot;15961&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;16035&quot; data-start=&quot;15961&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;16035&quot; data-start=&quot;15961&quot; data-ke-size=&quot;size16&quot;&gt;참고한 글 - &lt;a href=&quot;https://serverwizard.tistory.com/117&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://serverwizard.tistory.com/117&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;figure id=&quot;og_1778393818894&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Redis&quot; data-og-description=&quot;Redis - in memory 데이터 저장소 (key-value형태의 데이터) - 싱글 스레드 (1번에 1개의 명령어만 처리) =&amp;gt; 여러 문제의 원인이 됨 https://www.zdnet.co.kr/view/?no=20131119174125 - 초당 10만~15만 get/set 가능 Redis의 자&quot; data-og-host=&quot;serverwizard.tistory.com&quot; data-og-source-url=&quot;https://serverwizard.tistory.com/117&quot; data-og-url=&quot;https://serverwizard.tistory.com/117&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbNLJs/dJMb9kT7nvS/00a9WqnsdRlN8wAtkbdJqK/img.png?width=800&amp;amp;height=318&amp;amp;face=0_0_800_318,https://scrap.kakaocdn.net/dn/cHekUd/dJMb84X3arR/GHsxoptGEH8cLkWmEx44x0/img.png?width=800&amp;amp;height=318&amp;amp;face=0_0_800_318,https://scrap.kakaocdn.net/dn/SoCP4/dJMb89573YK/KkaGeDQibgf2MgCCKExPn0/img.png?width=1266&amp;amp;height=1126&amp;amp;face=0_0_1266_1126&quot;&gt;&lt;a href=&quot;https://serverwizard.tistory.com/117&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://serverwizard.tistory.com/117&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbNLJs/dJMb9kT7nvS/00a9WqnsdRlN8wAtkbdJqK/img.png?width=800&amp;amp;height=318&amp;amp;face=0_0_800_318,https://scrap.kakaocdn.net/dn/cHekUd/dJMb84X3arR/GHsxoptGEH8cLkWmEx44x0/img.png?width=800&amp;amp;height=318&amp;amp;face=0_0_800_318,https://scrap.kakaocdn.net/dn/SoCP4/dJMb89573YK/KkaGeDQibgf2MgCCKExPn0/img.png?width=1266&amp;amp;height=1126&amp;amp;face=0_0_1266_1126');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Redis&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Redis - in memory 데이터 저장소 (key-value형태의 데이터) - 싱글 스레드 (1번에 1개의 명령어만 처리) =&amp;gt; 여러 문제의 원인이 됨 https://www.zdnet.co.kr/view/?no=20131119174125 - 초당 10만~15만 get/set 가능 Redis의 자&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;serverwizard.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@banggeunho/%EB%A0%88%EB%94%94%EC%8A%A4Redis-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90.-%EC%A0%95%EC%9D%98-%EC%A0%80%EC%9E%A5%EB%B0%A9%EC%8B%9D-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9C%A0%ED%9A%A8-%EA%B8%B0%EA%B0%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@banggeunho/%EB%A0%88%EB%94%94%EC%8A%A4Redis-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90.-%EC%A0%95%EC%9D%98-%EC%A0%80%EC%9E%A5%EB%B0%A9%EC%8B%9D-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9C%A0%ED%9A%A8-%EA%B8%B0%EA%B0%84&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778394089695&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Redis] 레디스 알고 쓰자. - 정의, 저장방식, 아키텍처, 자료구조, 유효 기간&quot; data-og-description=&quot;레디스(Redis)는 메모리 기반의 데이터 저장소이다. 키-밸류(key-value) 데이터 구조에 기반한 다양한 형태의 자료 구조를 제공하며, 데이터들을 저장할 수 있는 저장소이다. 최신 버전의 레디스는 PU&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@banggeunho/%EB%A0%88%EB%94%94%EC%8A%A4Redis-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90.-%EC%A0%95%EC%9D%98-%EC%A0%80%EC%9E%A5%EB%B0%A9%EC%8B%9D-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9C%A0%ED%9A%A8-%EA%B8%B0%EA%B0%84&quot; data-og-url=&quot;https://velog.io/@banggeunho/레디스Redis-알고-쓰자.-정의-저장방식-아키텍처-자료구조-유효-기간&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bPsZ2Z/dJMb8XSabnh/KCHgFqni6iOvrcdjMU6CLk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bfqvhU/dJMb8YpZPOA/j0tnJn1wuUikNslwQQK4J0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/vxYzC/dJMb8RRWjAc/ueW5HtHWs1K9U9HpH3z8J1/img.png?width=828&amp;amp;height=410&amp;amp;face=0_0_828_410&quot;&gt;&lt;a href=&quot;https://velog.io/@banggeunho/%EB%A0%88%EB%94%94%EC%8A%A4Redis-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90.-%EC%A0%95%EC%9D%98-%EC%A0%80%EC%9E%A5%EB%B0%A9%EC%8B%9D-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9C%A0%ED%9A%A8-%EA%B8%B0%EA%B0%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@banggeunho/%EB%A0%88%EB%94%94%EC%8A%A4Redis-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90.-%EC%A0%95%EC%9D%98-%EC%A0%80%EC%9E%A5%EB%B0%A9%EC%8B%9D-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9C%A0%ED%9A%A8-%EA%B8%B0%EA%B0%84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bPsZ2Z/dJMb8XSabnh/KCHgFqni6iOvrcdjMU6CLk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bfqvhU/dJMb8YpZPOA/j0tnJn1wuUikNslwQQK4J0/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/vxYzC/dJMb8RRWjAc/ueW5HtHWs1K9U9HpH3z8J1/img.png?width=828&amp;amp;height=410&amp;amp;face=0_0_828_410');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Redis] 레디스 알고 쓰자. - 정의, 저장방식, 아키텍처, 자료구조, 유효 기간&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;레디스(Redis)는 메모리 기반의 데이터 저장소이다. 키-밸류(key-value) 데이터 구조에 기반한 다양한 형태의 자료 구조를 제공하며, 데이터들을 저장할 수 있는 저장소이다. 최신 버전의 레디스는 PU&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>CS STUDY</category>
      <category>redis</category>
      <category>Valkey</category>
      <category>자료구조</category>
      <category>프로토콜</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/28</guid>
      <comments>https://pixxlix.tistory.com/28#entry28comment</comments>
      <pubDate>Sun, 10 May 2026 15:05:56 +0900</pubDate>
    </item>
    <item>
      <title>Git-X-Ray : 깃 구조 살펴보기</title>
      <link>https://pixxlix.tistory.com/27</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 실습 개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습에서는 Git 저장소를 직접 생성하고, 파일을 추가한 뒤 두 번의 commit을 수행하면서 .git 내부, 특히 .git/objects 디렉터리에 어떤 object가 생성되는지 관찰하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Git object 파일을 Python의 zlib.decompress()로 직접 열어 보고, commit / tree / blob object가 각각 어떤 정보를 담고 있는지도 확인하였다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 진행한 작업&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습은 PowerShell 환경에서 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실습 순서는 다음과 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-1. 저장소 생성 및 파일 추가&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;git init
echo &quot;ossca&quot; &amp;gt; a.txt
git add a.txt
git commit -m &quot;첫번째 커밋&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-2. 파일 수정 후 두 번째 커밋&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;&quot;ossca study&quot; | Out-File -Encoding utf8 a.txt
git add a.txt
git commit -m &quot;두번째 커밋&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-3. 생성된 object 확인&lt;/h3&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Get-ChildItem .git\\objects-Recurse-File
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2-4. object 압축 해제 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 두 번째 commit object는 아래와 같이 확인하였다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;python -c &quot;import zlib; OBJECT_PATH=r'.git\\objects\\7b\\8819c6d32d120877db51b459cd3894e3c057fe'; data=zlib.decompress(open(OBJECT_PATH,'rb').read()); print(data); print(); print(data.decode(errors='replace'))&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tree object는 Git 명령으로 더 쉽게 확인할 수 있었다.&lt;/p&gt;
&lt;pre class=&quot;llvm&quot;&gt;&lt;code&gt;git cat-file -p f261758d74fb919ad6b381d6f7be894d4b6c034c
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과:&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;100644 blob f853bacec969b422f71054a8b1810b9bb8f7ba29    a.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 두 번째 tree가 a.txt와 두 번째 blob object를 가리키고 있음을 확인하였다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. object 관계 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습에서 생성된 object들은 다음과 같은 관계를 가진다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;첫 번째 상태
a.txt 내용 -&amp;gt; blob 9e0176...
           &amp;darr;
        tree 2cdbd2...
           &amp;darr;
      commit c137bd...   (&quot;첫번째 커밋&quot;)

두 번째 상태
a.txt 내용 -&amp;gt; blob f853ba...
           &amp;darr;
        tree f26175...
           &amp;darr;
      commit 7b8819...   (&quot;두번째 커밋&quot;)
           &amp;uarr;
        parent = c137bd...
&lt;/code&gt;&lt;/pre&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;파일 내용을 blob으로 저장하고&lt;/li&gt;
&lt;li&gt;파일 목록과 blob 연결을 tree로 묶은 뒤&lt;/li&gt;
&lt;li&gt;이를 가리키는 commit object를 생성하며&lt;/li&gt;
&lt;li&gt;이전 commit과는 parent로 연결&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 생성된 object 표 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 타입 의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;9e0176bd24d00fdb5b837f3a7bdd4db7611efd25&lt;/td&gt;
&lt;td&gt;blob&lt;/td&gt;
&lt;td&gt;첫 번째 a.txt 내용 (ossca)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2cdbd2c5688b466cc9287982a9fc0ef26c3898b6&lt;/td&gt;
&lt;td&gt;tree&lt;/td&gt;
&lt;td&gt;첫 번째 시점의 디렉터리 스냅샷&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;c137bde3269577c083c813cf30a9afdcd5504c6b&lt;/td&gt;
&lt;td&gt;commit&lt;/td&gt;
&lt;td&gt;첫 번째 커밋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;f853bacec969b422f71054a8b1810b9bb8f7ba29&lt;/td&gt;
&lt;td&gt;blob&lt;/td&gt;
&lt;td&gt;수정된 a.txt 내용 (ossca study)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;f261758d74fb919ad6b381d6f7be894d4b6c034c&lt;/td&gt;
&lt;td&gt;tree&lt;/td&gt;
&lt;td&gt;두 번째 시점의 디렉터리 스냅샷&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7b8819c6d32d120877db51b459cd3894e3c057fe&lt;/td&gt;
&lt;td&gt;commit&lt;/td&gt;
&lt;td&gt;두 번째 커밋&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 6개의 object가 생성되었고, 구성은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;blob 2개&lt;/li&gt;
&lt;li&gt;tree 2개&lt;/li&gt;
&lt;li&gt;commit 2개&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 느낀 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습을 통해 Git의 commit이 단순한 파일 저장이나 메시지 기록이 아니라는 점을 확인할 수 있었다. object 파일을 직접 열어본 결과, Git은 파일 내용을 blob으로 저장하고, 이를 tree로 묶은 뒤, commit object가 해당 tree와 부모 commit을 가리키는 방식으로 동작하고 있었다. 특히 두 번째 commit에서 parent 항목이 나타나는 것을 보고, Git의 버전 관리가 단순한 덮어쓰기가 아니라 이전 상태와 연결된 구조라는 점이 인상적이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로는 간단한 명령어 몇 줄에 불과하지만, 내부적으로는 체계적인 객체 구조를 기반으로 버전 관리가 이루어진다는 점을 이해할 수 있었다. 이번 실습을 통해 앞으로 Git을 사용할 때 commit의 의미를 더 정확히 인식하면서 활용할 수 있을 것 같다.&lt;/p&gt;</description>
      <category>CS STUDY</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/27</guid>
      <comments>https://pixxlix.tistory.com/27#entry27comment</comments>
      <pubDate>Sun, 10 May 2026 14:10:25 +0900</pubDate>
    </item>
    <item>
      <title>[데이터베이스 시스템 7판 10장] 빅데이터 정리 | HDFS, MapReduce, Spark, 스트림 처리까지</title>
      <link>https://pixxlix.tistory.com/26</link>
      <description>&lt;h2 data-end=&quot;1344&quot; data-start=&quot;1325&quot; data-section-id=&quot;117r74z&quot; data-ke-size=&quot;size26&quot;&gt;1. 왜 빅데이터가 등장했을까&lt;/h2&gt;
&lt;p data-end=&quot;1589&quot; data-start=&quot;1346&quot; data-ke-size=&quot;size16&quot;&gt;빅데이터는 단순히 &amp;ldquo;데이터가 많다&amp;rdquo;는 뜻이 아니다. &lt;b&gt;웹, SNS, IoT, 로그 시스템의 성장으로 데이터 양이 너무 커지고 너무 빠르게 들어와서 기존 방식만으로 처리하기 어려워졌다는 점&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;1589&quot; data-start=&quot;1346&quot; data-ke-size=&quot;size16&quot;&gt;그래서 한 대의 강한 서버보다, 여러 서버를 묶어 병렬로 저장하고 처리하는 방식이 중요해졌다.&lt;/p&gt;
&lt;p data-end=&quot;1589&quot; data-start=&quot;1346&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1589&quot; data-start=&quot;1346&quot; data-ke-size=&quot;size16&quot;&gt;클러스터를 구성하는 개별 서버를 보통 &lt;b&gt;node&lt;/b&gt;라고 부른다.&lt;/p&gt;
&lt;h2 data-end=&quot;1605&quot; data-start=&quot;1591&quot; data-section-id=&quot;1c0fpud&quot; data-ke-size=&quot;size26&quot;&gt;2. 빅데이터의 3V&lt;/h2&gt;
&lt;p data-end=&quot;1737&quot; data-start=&quot;1607&quot; data-ke-size=&quot;size16&quot;&gt;빅데이터를 설명할 때 가장 자주 나오는 개념이 &lt;b&gt;3V&lt;/b&gt;다.&amp;nbsp;&lt;/p&gt;
&lt;div&gt;요소&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;의미&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 예시&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1925&quot; data-start=&quot;1739&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;1925&quot; data-start=&quot;1770&quot;&gt;
&lt;tr data-end=&quot;1817&quot; data-start=&quot;1770&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1779&quot; data-start=&quot;1770&quot;&gt;Volume&lt;/td&gt;
&lt;td data-end=&quot;1794&quot; data-start=&quot;1779&quot; data-col-size=&quot;sm&quot;&gt;데이터 규모가 매우 큼&lt;/td&gt;
&lt;td data-end=&quot;1817&quot; data-start=&quot;1794&quot; data-col-size=&quot;sm&quot;&gt;TB, PB 단위 로그/센서 데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1873&quot; data-start=&quot;1818&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1829&quot; data-start=&quot;1818&quot;&gt;Velocity&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1847&quot; data-start=&quot;1829&quot;&gt;데이터가 매우 빠르게 생성됨&lt;/td&gt;
&lt;td data-end=&quot;1873&quot; data-start=&quot;1847&quot; data-col-size=&quot;sm&quot;&gt;클릭 로그, 실시간 거래, IoT 이벤트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1925&quot; data-start=&quot;1874&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1884&quot; data-start=&quot;1874&quot;&gt;Variety&lt;/td&gt;
&lt;td data-end=&quot;1898&quot; data-start=&quot;1884&quot; data-col-size=&quot;sm&quot;&gt;데이터 형태가 다양함&lt;/td&gt;
&lt;td data-end=&quot;1925&quot; data-start=&quot;1898&quot; data-col-size=&quot;sm&quot;&gt;테이블, JSON, 텍스트, 이미지, 영상&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 data-end=&quot;2005&quot; data-start=&quot;1984&quot; data-section-id=&quot;urtrul&quot; data-ke-size=&quot;size26&quot;&gt;3. 분산 저장 방식 한눈에 보기&lt;/h2&gt;
&lt;p data-end=&quot;2203&quot; data-start=&quot;2007&quot; data-ke-size=&quot;size16&quot;&gt;빅데이터 환경에서는 한 가지 저장 방식으로 모든 문제를 해결하지 않는다. 데이터 형태와 처리 목적에 따라 &lt;b&gt;분산 파일 시스템, 샤딩, Key-Value 저장소, 병렬/분산 DB&lt;/b&gt; 같은 방식을 나눠 쓴다. 관계형 저장소가 강한 일도 있고, NoSQL 계열이 더 잘 맞는 일도 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;방식&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 핵심 아이디어&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 강점&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 아쉬운 점
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;2529&quot; data-start=&quot;2205&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;2529&quot; data-start=&quot;2253&quot;&gt;
&lt;tr data-end=&quot;2327&quot; data-start=&quot;2253&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2265&quot; data-start=&quot;2253&quot;&gt;분산 파일 시스템&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2286&quot; data-start=&quot;2265&quot;&gt;큰 파일을 여러 노드에 분산 저장&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2299&quot; data-start=&quot;2286&quot;&gt;확장성, 장애 내성&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2327&quot; data-start=&quot;2299&quot;&gt;작은 레코드 대량 처리엔 비효율적일 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2387&quot; data-start=&quot;2328&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2333&quot; data-start=&quot;2328&quot;&gt;샤딩&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2357&quot; data-start=&quot;2333&quot;&gt;데이터를 키 기준으로 여러 DB에 분할&lt;/td&gt;
&lt;td data-end=&quot;2370&quot; data-start=&quot;2357&quot; data-col-size=&quot;sm&quot;&gt;구현이 비교적 단순&lt;/td&gt;
&lt;td data-end=&quot;2387&quot; data-start=&quot;2370&quot; data-col-size=&quot;sm&quot;&gt;라우팅/복제 책임이 커짐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2458&quot; data-start=&quot;2388&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2404&quot; data-start=&quot;2388&quot;&gt;Key-Value 저장소&lt;/td&gt;
&lt;td data-end=&quot;2427&quot; data-start=&quot;2404&quot; data-col-size=&quot;sm&quot;&gt;key로 빠르게 읽고 쓰는 단순 모델&lt;/td&gt;
&lt;td data-end=&quot;2440&quot; data-start=&quot;2427&quot; data-col-size=&quot;sm&quot;&gt;고확장성, 고가용성&lt;/td&gt;
&lt;td data-end=&quot;2458&quot; data-start=&quot;2440&quot; data-col-size=&quot;sm&quot;&gt;join/복잡한 질의 제약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2529&quot; data-start=&quot;2459&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2470&quot; data-start=&quot;2459&quot;&gt;병렬/분산 DB&lt;/td&gt;
&lt;td data-end=&quot;2492&quot; data-start=&quot;2470&quot; data-col-size=&quot;sm&quot;&gt;여러 노드에서 DB 연산 병렬 처리&lt;/td&gt;
&lt;td data-end=&quot;2504&quot; data-start=&quot;2492&quot; data-col-size=&quot;sm&quot;&gt;SQL 지원 강함&lt;/td&gt;
&lt;td data-end=&quot;2529&quot; data-start=&quot;2504&quot; data-col-size=&quot;sm&quot;&gt;초대규모 확장성엔 한계가 있을 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-section-id=&quot;1kvlh23&quot; data-start=&quot;1672&quot; data-end=&quot;1703&quot;&gt;4. Distributed File System이란&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-start=&quot;1705&quot; data-end=&quot;1777&quot;&gt;분산 파일 시스템은 데이터를 여러 머신에 나눠 저장하지만, 사용자 입장에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;하나의 파일시스템처럼 보이게 하는 구조&lt;/b&gt;다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-section-id=&quot;1hrlj9m&quot; data-start=&quot;1779&quot; data-end=&quot;1785&quot;&gt;특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;1786&quot; data-end=&quot;1855&quot;&gt;
&lt;li data-section-id=&quot;471v9t&quot; data-start=&quot;1786&quot; data-end=&quot;1800&quot;&gt;여러 머신에 분산 저장&lt;/li&gt;
&lt;li data-section-id=&quot;r0p1tn&quot; data-start=&quot;1801&quot; data-end=&quot;1809&quot;&gt;높은 확장성&lt;/li&gt;
&lt;li data-section-id=&quot;sw5wn2&quot; data-start=&quot;1810&quot; data-end=&quot;1828&quot;&gt;값싼 서버 여러 대 사용 가능&lt;/li&gt;
&lt;li data-section-id=&quot;180qbnk&quot; data-start=&quot;1829&quot; data-end=&quot;1855&quot;&gt;일부 서버가 고장 나도 복제로 버틸 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-start=&quot;1857&quot; data-end=&quot;1863&quot;&gt;대표 예시:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;1864&quot; data-end=&quot;1938&quot;&gt;
&lt;li data-section-id=&quot;2rc7gx&quot; data-start=&quot;1864&quot; data-end=&quot;1894&quot;&gt;&lt;b&gt;GFS (Google File System)&lt;/b&gt;&lt;/li&gt;
&lt;li data-section-id=&quot;6wwavx&quot; data-start=&quot;1895&quot; data-end=&quot;1938&quot;&gt;&lt;b&gt;HDFS (Hadoop Distributed File System)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;2546&quot; data-start=&quot;2531&quot; data-section-id=&quot;1pac8a6&quot; data-ke-size=&quot;size26&quot;&gt;4-1. HDFS 이해하기&lt;/h2&gt;
&lt;p data-end=&quot;2834&quot; data-start=&quot;2548&quot; data-ke-size=&quot;size16&quot;&gt;HDFS는 Hadoop의 대표적인 분산 파일 시스템이다. 핵심 아이디어는 &lt;b&gt;큰 파일을 block 단위로 나눠 여러 DataNode에 저장하고, 메타데이터는 NameNode가 관리하는 구조&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;2834&quot; data-start=&quot;2548&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2834&quot; data-start=&quot;2548&quot; data-ke-size=&quot;size16&quot;&gt;사용자는 하나의 파일시스템처럼 보지만, 실제론 파일이 여러 block으로 쪼개져 분산 저장된다.&amp;nbsp; &lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3025&quot; data-start=&quot;2850&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2893&quot; data-start=&quot;2850&quot; data-section-id=&quot;1z00ewc&quot;&gt;&lt;b&gt;NameNode&lt;/b&gt;: 파일명, block 위치 같은 메타데이터 관리&lt;/li&gt;
&lt;li data-end=&quot;2923&quot; data-start=&quot;2894&quot; data-section-id=&quot;1wfumqe&quot;&gt;&lt;b&gt;DataNode&lt;/b&gt;: 실제 block 저장&lt;/li&gt;
&lt;li data-end=&quot;3025&quot; data-start=&quot;2924&quot; data-section-id=&quot;1a9luq4&quot;&gt;&lt;b&gt;Client&lt;/b&gt;: NameNode에 block 위치를 묻고, 실제 데이터는 DataNode와 직접 주고받음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/omoFQ/dJMcaaLg7YE/7YbNnIrIOMCbC9OTuhglP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/omoFQ/dJMcaaLg7YE/7YbNnIrIOMCbC9OTuhglP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/omoFQ/dJMcaaLg7YE/7YbNnIrIOMCbC9OTuhglP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FomoFQ%2FdJMcaaLg7YE%2F7YbNnIrIOMCbC9OTuhglP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;874&quot; height=&quot;604&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;3362&quot; data-start=&quot;3133&quot; data-ke-size=&quot;size16&quot;&gt;HDFS는 &lt;b&gt;write-once-read-many&lt;/b&gt; 모델에 잘 맞고, 큰 파일을 저장하고 순차적으로 읽는 작업에 강하다. 반면 작은 파일이 지나치게 많거나, 잦은 랜덤 업데이트가 필요한 워크로드엔 잘 맞지 않을 수 있다. 그래서 HDFS는 일반 관계형 DB를 대체한다기보다, &lt;b&gt;대규모 파일 저장소&lt;/b&gt;에 더 가깝게 보는 편이 자연스럽다.&lt;/p&gt;
&lt;h2 data-end=&quot;3382&quot; data-start=&quot;3364&quot; data-section-id=&quot;19kbd2g&quot; data-ke-size=&quot;size26&quot;&gt;5. 복제, 일관성, CAP&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dYeCY0/dJMcagkpbdI/kOOYkn1g3C6O8xA6JQtjW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dYeCY0/dJMcagkpbdI/kOOYkn1g3C6O8xA6JQtjW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dYeCY0/dJMcagkpbdI/kOOYkn1g3C6O8xA6JQtjW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdYeCY0%2FdJMcagkpbdI%2FkOOYkn1g3C6O8xA6JQtjW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;3625&quot; data-start=&quot;3384&quot; data-ke-size=&quot;size16&quot;&gt;분산 시스템은 일부 노드가 죽어도 계속 동작해야 하니까 보통 데이터를 복제해둔다. 이때 중요한 개념이 &lt;b&gt;Availability(가용성)&lt;/b&gt; 과 &lt;b&gt;Consistency(일관성)&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-end=&quot;3625&quot; data-start=&quot;3384&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 복제본이 3개일 때 read/write 시 다수 복제본을 확인하는 majority 방식은 일관성을 확보하는 대표적인 아이디어다.&lt;/p&gt;
&lt;p data-end=&quot;3625&quot; data-start=&quot;3384&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3849&quot; data-start=&quot;3627&quot; data-ke-size=&quot;size16&quot;&gt;여기서 유명한 게 &lt;b&gt;CAP 정리&lt;/b&gt;다. 네트워크 파티션이 생긴 상황에서는, 분산 시스템이 &lt;b&gt;Consistency와 Availability를 동시에 완벽히 만족시키기 어렵다!! 둘 중 하나를 선택해야한다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;3849&quot; data-start=&quot;3627&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;4901&quot; data-end=&quot;4966&quot;&gt;
&lt;li data-section-id=&quot;yn856w&quot; data-start=&quot;4901&quot; data-end=&quot;4929&quot;&gt;전통적인 DB 시스템:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;일관성&lt;/b&gt;을 더 중시&lt;/li&gt;
&lt;li data-section-id=&quot;1ig03kc&quot; data-start=&quot;4930&quot; data-end=&quot;4966&quot;&gt;대규모 Web 서비스:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;가용성&lt;/b&gt;을 더 중시하는 경우 많음&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;3970&quot; data-start=&quot;3944&quot; data-section-id=&quot;5njafg&quot; data-ke-size=&quot;size26&quot;&gt;6. MapReduce와 WordCount&lt;/h2&gt;
&lt;p data-end=&quot;4192&quot; data-start=&quot;3972&quot; data-ke-size=&quot;size16&quot;&gt;MapReduce는 대규모 병렬 계산을 단순하게 만들기 위한 프로그래밍 모델이다. 개발자는 기본적으로 &lt;b&gt;map 함수와 reduce 함수&lt;/b&gt;만 작성하고, 분산 실행, 장애 처리, 스케줄링, 중간 데이터 정렬 같은 복잡한 일은 시스템이 맡는다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zf4sL/dJMcagEKks7/gO6INpse4XLwmzskXXqdjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zf4sL/dJMcagEKks7/gO6INpse4XLwmzskXXqdjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zf4sL/dJMcagEKks7/gO6INpse4XLwmzskXXqdjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzf4sL%2FdJMcagEKks7%2FgO6INpse4XLwmzskXXqdjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;790&quot; height=&quot;414&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;4369&quot; data-start=&quot;4194&quot; data-ke-size=&quot;size16&quot;&gt;가장 대표적인 예제가 &lt;b&gt;WordCount&lt;/b&gt;다. 문서에서 단어를 뽑아 (word, 1) 형태로 내보내고, 같은 단어끼리 묶은 뒤, 마지막에 합을 계산한다. 이때 &lt;b&gt;shuffle&lt;/b&gt; 단계는 같은 key를 자동으로 모아주는 시스템 단계다.&lt;/p&gt;
&lt;p data-end=&quot;4493&quot; data-start=&quot;4371&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;단계&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;하는 일
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;4618&quot; data-start=&quot;4495&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;4618&quot; data-start=&quot;4519&quot;&gt;
&lt;tr data-end=&quot;4555&quot; data-start=&quot;4519&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4525&quot; data-start=&quot;4519&quot;&gt;map&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4555&quot; data-start=&quot;4525&quot;&gt;입력에서 단어를 꺼내 (word, 1) 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4584&quot; data-start=&quot;4556&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4566&quot; data-start=&quot;4556&quot;&gt;shuffle&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4584&quot; data-start=&quot;4566&quot;&gt;같은 단어끼리 자동 그룹핑&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4618&quot; data-start=&quot;4585&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4594&quot; data-start=&quot;4585&quot;&gt;reduce&lt;/td&gt;
&lt;td data-end=&quot;4618&quot; data-start=&quot;4594&quot; data-col-size=&quot;sm&quot;&gt;각 단어별 값을 합쳐 최종 개수 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4813&quot; data-start=&quot;4620&quot; data-ke-size=&quot;size16&quot;&gt;MapReduce의 장점은 &lt;b&gt;초대규모 배치 처리와 장애 내성&lt;/b&gt;이다. 하지만 단순한 조회나 join, aggregation 같은 작업까지 전부 MapReduce 코드로 직접 짜면 너무 장황해진다. 그래서 이후에는 더 높은 수준의 대수 연산을 지원하는 엔진이 발전하게 된다.&lt;/p&gt;
&lt;p data-end=&quot;4813&quot; data-start=&quot;4620&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;4813&quot; data-start=&quot;4620&quot; data-ke-size=&quot;size16&quot;&gt;자세한 코드를 보고싶다면 &lt;a href=&quot;https://hadoop.apache.org/docs/stable/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html?utm_source=chatgpt.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hadoop.apache.org/docs/stable/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-end=&quot;4837&quot; data-start=&quot;4815&quot; data-section-id=&quot;1mfmkdb&quot; data-ke-size=&quot;size26&quot;&gt;7. Spark와 고수준 분산 처리&lt;/h2&gt;
&lt;p data-end=&quot;5146&quot; data-start=&quot;4839&quot; data-ke-size=&quot;size16&quot;&gt;MapReduce 다음 세대 시스템들은 map/reduce 두 단계만이 아니라, &lt;b&gt;join, groupBy, filter, agg&lt;/b&gt; 같은 더 풍부한 연산을 기본 지원한다. 그 대표적인 예가 Spark다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-start=&quot;7558&quot; data-end=&quot;7618&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-start=&quot;7558&quot; data-end=&quot;7618&quot;&gt;Spark의 핵심 개념 중 하나가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;RDD(Resilient Distributed Dataset)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-section-id=&quot;oy69iw&quot; data-start=&quot;7620&quot; data-end=&quot;7630&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-section-id=&quot;oy69iw&quot; data-start=&quot;7620&quot; data-end=&quot;7630&quot;&gt;RDD 특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;7631&quot; data-end=&quot;7726&quot;&gt;
&lt;li data-section-id=&quot;1v6jucr&quot; data-start=&quot;7631&quot; data-end=&quot;7661&quot;&gt;데이터가 여러 노드에 partition 되어 저장됨&lt;/li&gt;
&lt;li data-section-id=&quot;1b3icvq&quot; data-start=&quot;7662&quot; data-end=&quot;7683&quot;&gt;연산을 적용하면 새로운 RDD 생성&lt;/li&gt;
&lt;li data-section-id=&quot;1k8bgjb&quot; data-start=&quot;7684&quot; data-end=&quot;7709&quot;&gt;immutable에 가까운 변환 중심 모델&lt;/li&gt;
&lt;li data-section-id=&quot;1c3a30t&quot; data-start=&quot;7710&quot; data-end=&quot;7726&quot;&gt;장애 발생 시 재계산 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;5482&quot; data-start=&quot;5148&quot; data-ke-size=&quot;size16&quot;&gt;또 Spark 계열 연산은 보통 &lt;b&gt;lazy evaluation&lt;/b&gt; 방식으로 동작해서, collect() 같은 action이 호출되기 전까지는 실행 계획만 쌓아둘 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;5482&quot; data-start=&quot;5148&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-section-id=&quot;1lcdlps&quot; data-start=&quot;10508&quot; data-end=&quot;10536&quot;&gt;MapReduce vs Database&lt;/h2&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-section-id=&quot;blxy0e&quot; data-start=&quot;10551&quot; data-end=&quot;10570&quot;&gt;MapReduce가 강한 쪽&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;10571&quot; data-end=&quot;10621&quot;&gt;
&lt;li data-section-id=&quot;1m30lm9&quot; data-start=&quot;10571&quot; data-end=&quot;10586&quot;&gt;매우 큰 규모 병렬 처리&lt;/li&gt;
&lt;li data-section-id=&quot;13zh1js&quot; data-start=&quot;10587&quot; data-end=&quot;10596&quot;&gt;비정형 데이터&lt;/li&gt;
&lt;li data-section-id=&quot;1lx4llt&quot; data-start=&quot;10597&quot; data-end=&quot;10609&quot;&gt;복잡한 절차형 처리&lt;/li&gt;
&lt;li data-section-id=&quot;qihejl&quot; data-start=&quot;10610&quot; data-end=&quot;10621&quot;&gt;대규모 로그 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-section-id=&quot;p11mfp&quot; data-start=&quot;10623&quot; data-end=&quot;10639&quot;&gt;SQL/DB가 강한 쪽&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;10640&quot; data-end=&quot;10699&quot;&gt;
&lt;li data-section-id=&quot;1xs5pis&quot; data-start=&quot;10640&quot; data-end=&quot;10673&quot;&gt;select/project/join/aggregation&lt;/li&gt;
&lt;li data-section-id=&quot;2wgdku&quot; data-start=&quot;10674&quot; data-end=&quot;10686&quot;&gt;짧고 선언적인 질의&lt;/li&gt;
&lt;li data-section-id=&quot;1bo22tp&quot; data-start=&quot;10687&quot; data-end=&quot;10699&quot;&gt;구조화 데이터 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;항목&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;MapReduce&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Spark
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;5725&quot; data-start=&quot;5484&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;5725&quot; data-start=&quot;5525&quot;&gt;
&lt;tr data-end=&quot;5580&quot; data-start=&quot;5525&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5533&quot; data-start=&quot;5525&quot;&gt;처리 방식&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5561&quot; data-start=&quot;5533&quot;&gt;map / shuffle / reduce 중심&lt;/td&gt;
&lt;td data-end=&quot;5580&quot; data-start=&quot;5561&quot; data-col-size=&quot;sm&quot;&gt;더 다양한 고수준 연산 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5627&quot; data-start=&quot;5581&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5587&quot; data-start=&quot;5581&quot;&gt;표현력&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5597&quot; data-start=&quot;5587&quot;&gt;비교적 저수준&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5627&quot; data-start=&quot;5597&quot;&gt;RDD, DataFrame, Dataset 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5672&quot; data-start=&quot;5628&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5636&quot; data-start=&quot;5628&quot;&gt;실행 특징&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5644&quot; data-start=&quot;5636&quot;&gt;배치 중심&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5672&quot; data-start=&quot;5644&quot;&gt;lazy execution 기반 최적화 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5725&quot; data-start=&quot;5673&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5679&quot; data-start=&quot;5673&quot;&gt;사용감&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5697&quot; data-start=&quot;5679&quot;&gt;단순하지만 장황해질 수 있음&lt;/td&gt;
&lt;td data-end=&quot;5725&quot; data-start=&quot;5697&quot; data-col-size=&quot;sm&quot;&gt;분석/ETL 파이프라인 작성이 더 자연스러움&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-end=&quot;5745&quot; data-start=&quot;5727&quot; data-section-id=&quot;ch840r&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-end=&quot;5745&quot; data-start=&quot;5727&quot; data-section-id=&quot;ch840r&quot; data-ke-size=&quot;size26&quot;&gt;8. 스트림 데이터와 윈도우&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRiOhQ/dJMcabwC7OG/7ahzEjGACTXWDYjCKmqlFK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRiOhQ/dJMcabwC7OG/7ahzEjGACTXWDYjCKmqlFK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRiOhQ/dJMcabwC7OG/7ahzEjGACTXWDYjCKmqlFK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRiOhQ%2FdJMcabwC7OG%2F7ahzEjGACTXWDYjCKmqlFK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;408&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;5967&quot; data-start=&quot;5747&quot; data-ke-size=&quot;size16&quot;&gt;빅데이터가 꼭 저장된 데이터만 뜻하는 건 아니다. 현실에서는 데이터가 &lt;b&gt;끊임없이 들어오는 스트림 형태&lt;/b&gt;인 경우가 많다. 주식 거래, 검색 로그, IoT 센서, 네트워크 이벤트, SNS 피드 같은 데이터가 여기에 해당한다.&lt;/p&gt;
&lt;p data-end=&quot;5967&quot; data-start=&quot;5747&quot; data-ke-size=&quot;size16&quot;&gt;이런 데이터는 쌓아두고 나중에 보는 것보다 &lt;b&gt;즉시 계산하고 반응하는 것&lt;/b&gt;이 중요할 때가 많다.&lt;/p&gt;
&lt;p data-end=&quot;6185&quot; data-start=&quot;5969&quot; data-ke-size=&quot;size16&quot;&gt;문제는 스트림이 사실상 무한하다는 점이다.&lt;/p&gt;
&lt;p data-end=&quot;6185&quot; data-start=&quot;5969&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;6185&quot; data-start=&quot;5969&quot; data-ke-size=&quot;size16&quot;&gt;그래서 스트림 처리 시스템은 보통 &lt;b&gt;window&lt;/b&gt; 개념으로 무한 스트림을 유한 구간으로 잘라서 계산한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;윈도우 종류&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;의미&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;예시
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;6523&quot; data-start=&quot;6296&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;6523&quot; data-start=&quot;6331&quot;&gt;
&lt;tr data-end=&quot;6369&quot; data-start=&quot;6331&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6342&quot; data-start=&quot;6331&quot;&gt;Tumbling&lt;/td&gt;
&lt;td data-end=&quot;6357&quot; data-start=&quot;6342&quot; data-col-size=&quot;sm&quot;&gt;겹치지 않는 고정 구간&lt;/td&gt;
&lt;td data-end=&quot;6369&quot; data-start=&quot;6357&quot; data-col-size=&quot;sm&quot;&gt;매 1시간 집계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;6417&quot; data-start=&quot;6370&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6380&quot; data-start=&quot;6370&quot;&gt;Hopping&lt;/td&gt;
&lt;td data-end=&quot;6396&quot; data-start=&quot;6380&quot; data-col-size=&quot;sm&quot;&gt;겹칠 수 있는 고정 구간&lt;/td&gt;
&lt;td data-end=&quot;6417&quot; data-start=&quot;6396&quot; data-col-size=&quot;sm&quot;&gt;1시간 윈도우를 20분마다 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;6470&quot; data-start=&quot;6418&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6428&quot; data-start=&quot;6418&quot;&gt;Sliding&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6451&quot; data-start=&quot;6428&quot;&gt;현재 이벤트를 중심으로 이동하며 계산&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6470&quot; data-start=&quot;6451&quot;&gt;최근 N분 이벤트 기준 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;6523&quot; data-start=&quot;6471&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6481&quot; data-start=&quot;6471&quot;&gt;Session&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6498&quot; data-start=&quot;6481&quot;&gt;활동 간격을 기준으로 묶음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;6523&quot; data-start=&quot;6498&quot;&gt;사용자가 일정 시간 쉬면 새 세션 시작&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6707&quot; data-start=&quot;6525&quot; data-ke-size=&quot;size16&quot;&gt;스트림 처리에서는 &lt;b&gt;continuous query&lt;/b&gt;, 늦게 들어오는 이벤트 문제, 그리고 특정 시점 이전 데이터가 다 도착했음을 알려주는 메타 정보 같은 개념도 중요하다. 결국 핵심은 &lt;b&gt;무한한 흐름을 의미 있는 구간으로 잘라서 계산하는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;6707&quot; data-start=&quot;6525&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;6739&quot; data-start=&quot;6709&quot; data-section-id=&quot;1yjnu88&quot; data-ke-size=&quot;size26&quot;&gt;9. Kafka와 Publish-Subscribe&lt;/h2&gt;
&lt;p data-end=&quot;7105&quot; data-start=&quot;6741&quot; data-ke-size=&quot;size16&quot;&gt;스트림 파이프라인에서 데이터를 한 시스템에서 다른 시스템으로 흘려보내려면 &lt;b&gt;producer와 consumer를 느슨하게 연결&lt;/b&gt;하는 구조가 필요하다.&lt;/p&gt;
&lt;p data-end=&quot;7105&quot; data-start=&quot;6741&quot; data-ke-size=&quot;size16&quot;&gt;이때 자주 쓰는 게 &lt;b&gt;Publish-Subscribe&lt;/b&gt; 모델이다. 생산자는 topic에 데이터를 publish하고, 소비자는 관심 있는 topic을 subscribe 해서 가져간다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;7105&quot; data-start=&quot;6741&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;7295&quot; data-start=&quot;7107&quot; data-ke-size=&quot;size16&quot;&gt;즉, Kafka는 단순 메시지 큐라기보다 &lt;b&gt;현대 데이터 파이프라인의 허브&lt;/b&gt; 역할에 가깝다. topic, partition, replication 개념을 바탕으로 생산자와 소비자를 이어주고, 스트림 처리 엔진과 저장 시스템 사이를 연결하는 중심축처럼 자주 쓰인다.&lt;/p&gt;
&lt;hr data-end=&quot;11463&quot; data-start=&quot;11460&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-section-id=&quot;klq8rj&quot; data-start=&quot;10819&quot; data-end=&quot;10831&quot;&gt;10. 핵심 요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-start=&quot;10853&quot; data-end=&quot;11458&quot;&gt;
&lt;li data-section-id=&quot;qaogox&quot; data-start=&quot;10853&quot; data-end=&quot;10927&quot;&gt;빅데이터는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Volume, Velocity, Variety&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;때문에 기존 DBMS 방식만으로 처리하기 어려운 데이터 환경이다.&lt;/li&gt;
&lt;li data-section-id=&quot;w3se2o&quot; data-start=&quot;10928&quot; data-end=&quot;10966&quot;&gt;이를 해결하려고&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;클러스터 기반 분산 저장/처리&lt;/b&gt;가 등장했다.&lt;/li&gt;
&lt;li data-section-id=&quot;15ecvgi&quot; data-start=&quot;10967&quot; data-end=&quot;11017&quot;&gt;&lt;b&gt;HDFS&lt;/b&gt;는 큰 파일을 block 단위로 나눠 분산 저장하는 대표적인 시스템이다.&lt;/li&gt;
&lt;li data-section-id=&quot;ma9mls&quot; data-start=&quot;11018&quot; data-end=&quot;11070&quot;&gt;&lt;b&gt;샤딩&lt;/b&gt;은 데이터를 여러 DB로 나누는 방식이지만, 애플리케이션 책임이 커질 수 있다.&lt;/li&gt;
&lt;li data-section-id=&quot;15m256d&quot; data-start=&quot;11071&quot; data-end=&quot;11127&quot;&gt;&lt;b&gt;Key-Value 저장소&lt;/b&gt;는 고확장성을 위해 많은 DB 기능을 단순화한 NoSQL 계열이다.&lt;/li&gt;
&lt;li data-section-id=&quot;luolyd&quot; data-start=&quot;11128&quot; data-end=&quot;11174&quot;&gt;분산 시스템에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;복제, 가용성, 일관성, CAP&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;문제가 매우 중요하다.&lt;/li&gt;
&lt;li data-section-id=&quot;xlms39&quot; data-start=&quot;11175&quot; data-end=&quot;11230&quot;&gt;&lt;b&gt;MapReduce&lt;/b&gt;는 대규모 병렬 처리를 단순한 map/reduce 모델로 추상화했다.&lt;/li&gt;
&lt;li data-section-id=&quot;nwe6mz&quot; data-start=&quot;11231&quot; data-end=&quot;11280&quot;&gt;이후에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Spark, Tez&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 더 고수준 대수 연산 기반 엔진이 발전했다.&lt;/li&gt;
&lt;li data-section-id=&quot;1ngfial&quot; data-start=&quot;11281&quot; data-end=&quot;11341&quot;&gt;&lt;b&gt;RDD, DataFrame, DataSet&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;은 Spark에서 분산 데이터를 다루는 핵심 개념이다.&lt;/li&gt;
&lt;li data-section-id=&quot;13g2nqm&quot; data-start=&quot;11342&quot; data-end=&quot;11388&quot;&gt;스트림 데이터는 저장된 데이터와 달리&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;실시간으로 계속 들어오는 데이터&lt;/b&gt;다.&lt;/li&gt;
&lt;li data-section-id=&quot;1dzwrx9&quot; data-start=&quot;11389&quot; data-end=&quot;11458&quot;&gt;스트림 처리에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;window, continuous query, pub-sub, Kafka&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 개념이 중요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;7303&quot; data-start=&quot;7297&quot; data-section-id=&quot;1h9nj85&quot; data-ke-size=&quot;size26&quot;&gt;11. 전체 흐름&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7552&quot; data-start=&quot;7326&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7347&quot; data-start=&quot;7326&quot; data-section-id=&quot;1wk8mfq&quot;&gt;데이터가 너무 많아지고 빨라짐, 한 대 서버와 기존 DBMS만으로는 부족&lt;/li&gt;
&lt;li data-end=&quot;7406&quot; data-start=&quot;7378&quot; data-section-id=&quot;ug2e0e&quot;&gt;분산 저장(HDFS, NoSQL)이 등장&lt;/li&gt;
&lt;li data-end=&quot;7433&quot; data-start=&quot;7407&quot; data-section-id=&quot;1c6bswd&quot;&gt;분산 처리(MapReduce) 등장&lt;/li&gt;
&lt;li data-end=&quot;7466&quot; data-start=&quot;7434&quot; data-section-id=&quot;reqzy5&quot;&gt;더 높은 수준의 실행 엔진(Spark)으로 발전&lt;/li&gt;
&lt;li data-end=&quot;7552&quot; data-start=&quot;7467&quot; data-section-id=&quot;14xuj1s&quot;&gt;이제는 실시간 스트림 처리와 Kafka 같은 데이터 흐름 플랫폼까지 중요해졌다.&lt;/li&gt;
&lt;li data-end=&quot;7552&quot; data-start=&quot;7467&quot; data-section-id=&quot;14xuj1s&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;7746&quot; data-start=&quot;7733&quot; data-section-id=&quot;1huywmi&quot; data-ke-size=&quot;size23&quot;&gt;이미지 출처 모음&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8118&quot; data-start=&quot;7747&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7837&quot; data-start=&quot;7747&quot; data-section-id=&quot;1enwrp1&quot;&gt;HDFS 구조도: Apache Hadoop HDFS Architecture Guide&lt;/li&gt;
&lt;li data-end=&quot;7939&quot; data-start=&quot;7838&quot; data-section-id=&quot;1ihqcig&quot;&gt;WordCount / MapReduce 흐름: Apache Hadoop MapReduce Tutorial&lt;/li&gt;
&lt;li data-end=&quot;8118&quot; data-start=&quot;8023&quot; data-section-id=&quot;dvtqn4&quot;&gt;스트림 윈도우 종류: Azure Stream Analytics / Apache Flink 문서&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS STUDY/DB</category>
      <category>HDFS</category>
      <category>Kafka</category>
      <category>MapReduce</category>
      <category>NoSQL</category>
      <category>Spark</category>
      <category>데이터베이스정리</category>
      <category>빅데이터</category>
      <category>스트림처리</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/26</guid>
      <comments>https://pixxlix.tistory.com/26#entry26comment</comments>
      <pubDate>Thu, 12 Mar 2026 03:14:39 +0900</pubDate>
    </item>
    <item>
      <title>[데이터베이스 시스템 7판 6장] E-R 모델 정리 | ERD 기본 개념부터 관계형 스키마 변환까지 한 번에 이해하기</title>
      <link>https://pixxlix.tistory.com/24</link>
      <description>&lt;h2 data-end=&quot;470&quot; data-start=&quot;445&quot; data-section-id=&quot;y5amdk&quot; data-ke-size=&quot;size26&quot;&gt;1. 데이터베이스 설계는 어떻게 진행될까&lt;/h2&gt;
&lt;p data-end=&quot;522&quot; data-start=&quot;472&quot; data-ke-size=&quot;size16&quot;&gt;데이터베이스 설계는 그냥 테이블부터 만드는 작업이 아니다. 보통 다음 순서로 진행된다.&lt;/p&gt;
&lt;h3 data-end=&quot;538&quot; data-start=&quot;524&quot; data-section-id=&quot;1clgev2&quot; data-ke-size=&quot;size23&quot;&gt;1) 요구사항 분석&lt;/h3&gt;
&lt;p data-end=&quot;569&quot; data-start=&quot;539&quot; data-ke-size=&quot;size16&quot;&gt;사용자가 어떤 데이터를 필요로 하는지 정리하는 단계다.&lt;/p&gt;
&lt;p data-end=&quot;573&quot; data-start=&quot;571&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;623&quot; data-start=&quot;574&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;587&quot; data-start=&quot;574&quot; data-section-id=&quot;1hryzy9&quot;&gt;학생 정보가 필요한가&lt;/li&gt;
&lt;li data-end=&quot;601&quot; data-start=&quot;588&quot; data-section-id=&quot;13mxwlk&quot;&gt;수강 정보가 필요한가&lt;/li&gt;
&lt;li data-end=&quot;623&quot; data-start=&quot;602&quot; data-section-id=&quot;uuzj3o&quot;&gt;교수와 지도 관계를 관리해야 하는가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;650&quot; data-start=&quot;625&quot; data-ke-size=&quot;size16&quot;&gt;이 단계의 결과물은 &lt;b&gt;요구사항 명세서&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-end=&quot;655&quot; data-start=&quot;652&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;670&quot; data-start=&quot;657&quot; data-section-id=&quot;10s3muo&quot; data-ke-size=&quot;size23&quot;&gt;2) 개념적 설계&lt;/h3&gt;
&lt;p data-end=&quot;759&quot; data-start=&quot;671&quot; data-ke-size=&quot;size16&quot;&gt;요구사항을 바탕으로 데이터를 개념적으로 표현한다.&lt;br /&gt;이때 사용하는 대표적인 도구가 &lt;b&gt;E-R 모델(Entity-Relationship Model)&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-end=&quot;791&quot; data-start=&quot;761&quot; data-ke-size=&quot;size16&quot;&gt;결과물은 보통 &lt;b&gt;E-R 다이어그램(ERD)&lt;/b&gt; 이다.&lt;/p&gt;
&lt;hr data-end=&quot;796&quot; data-start=&quot;793&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;811&quot; data-start=&quot;798&quot; data-section-id=&quot;cm8sfh&quot; data-ke-size=&quot;size23&quot;&gt;3) 논리적 설계&lt;/h3&gt;
&lt;p data-end=&quot;861&quot; data-start=&quot;812&quot; data-ke-size=&quot;size16&quot;&gt;E-R 다이어그램을 실제 DB에서 사용할 수 있는 &lt;b&gt;관계형 스키마&lt;/b&gt;로 바꾸는 단계다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;908&quot; data-start=&quot;866&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;876&quot; data-start=&quot;866&quot; data-section-id=&quot;1cfdlj2&quot;&gt;개체 &amp;rarr; 테이블&lt;/li&gt;
&lt;li data-end=&quot;886&quot; data-start=&quot;877&quot; data-section-id=&quot;k2o1bq&quot;&gt;속성 &amp;rarr; 컬럼&lt;/li&gt;
&lt;li data-end=&quot;908&quot; data-start=&quot;887&quot; data-section-id=&quot;1yi9vhz&quot;&gt;관계 &amp;rarr; 외래키 or 별도 릴레이션&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;922&quot; data-start=&quot;910&quot; data-ke-size=&quot;size16&quot;&gt;같은 식으로 변환한다.&lt;/p&gt;
&lt;hr data-end=&quot;927&quot; data-start=&quot;924&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;942&quot; data-start=&quot;929&quot; data-section-id=&quot;32ozxm&quot; data-ke-size=&quot;size23&quot;&gt;4) 물리적 설계&lt;/h3&gt;
&lt;p data-end=&quot;965&quot; data-start=&quot;943&quot; data-ke-size=&quot;size16&quot;&gt;실제로 어떻게 저장할지 결정하는 단계다.&lt;/p&gt;
&lt;p data-end=&quot;969&quot; data-start=&quot;967&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1006&quot; data-start=&quot;970&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;981&quot; data-start=&quot;970&quot; data-section-id=&quot;1mcxif9&quot;&gt;인덱스 생성 여부&lt;/li&gt;
&lt;li data-end=&quot;989&quot; data-start=&quot;982&quot; data-section-id=&quot;s6f36s&quot;&gt;파일 구조&lt;/li&gt;
&lt;li data-end=&quot;997&quot; data-start=&quot;990&quot; data-section-id=&quot;1grqfkp&quot;&gt;저장 방식&lt;/li&gt;
&lt;li data-end=&quot;1006&quot; data-start=&quot;998&quot; data-section-id=&quot;ks6sid&quot;&gt;성능 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1011&quot; data-start=&quot;1008&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1039&quot; data-start=&quot;1013&quot; data-section-id=&quot;6y52bv&quot; data-ke-size=&quot;size26&quot;&gt;2. 설계할 때 왜 E-R 모델이 필요할까&lt;/h2&gt;
&lt;p data-end=&quot;1087&quot; data-start=&quot;1041&quot; data-ke-size=&quot;size16&quot;&gt;처음부터 테이블을 바로 만들면 편해 보이지만,&lt;br /&gt;그렇게 하면 구조가 꼬이기 쉽다.&lt;/p&gt;
&lt;p data-end=&quot;1111&quot; data-start=&quot;1089&quot; data-ke-size=&quot;size16&quot;&gt;특히 다음 두 가지 문제가 자주 생긴다.&lt;/p&gt;
&lt;h3 data-end=&quot;1120&quot; data-start=&quot;1113&quot; data-section-id=&quot;1xnt9tf&quot; data-ke-size=&quot;size23&quot;&gt;중복성&lt;/h3&gt;
&lt;p data-end=&quot;1142&quot; data-start=&quot;1121&quot; data-ke-size=&quot;size16&quot;&gt;같은 정보를 여러 번 저장하는 문제다.&lt;/p&gt;
&lt;p data-end=&quot;1211&quot; data-start=&quot;1144&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 분반마다 course_id, title을 반복 저장하면&lt;br /&gt;같은 과목 제목이 여러 행에 중복 저장된다.&lt;/p&gt;
&lt;p data-end=&quot;1252&quot; data-start=&quot;1213&quot; data-ke-size=&quot;size16&quot;&gt;이러면 수정할 때 일부만 바뀌어서 &lt;b&gt;일관성 문제&lt;/b&gt;가 생길 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;1257&quot; data-start=&quot;1254&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1267&quot; data-start=&quot;1259&quot; data-section-id=&quot;il9wov&quot; data-ke-size=&quot;size23&quot;&gt;불완전성&lt;/h3&gt;
&lt;p data-end=&quot;1296&quot; data-start=&quot;1268&quot; data-ke-size=&quot;size16&quot;&gt;현재 구조로는 어떤 정보를 표현할 수 없는 문제다.&lt;/p&gt;
&lt;p data-end=&quot;1370&quot; data-start=&quot;1298&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 분반 정보만 있고 과목 자체를 나타내는 테이블이 없다면,&lt;br /&gt;아직 분반이 열리지 않은 새 과목은 DB에 넣을 수 없다.&lt;/p&gt;
&lt;p data-end=&quot;1428&quot; data-start=&quot;1372&quot; data-ke-size=&quot;size16&quot;&gt;즉, 어떤 구조는 데이터를 저장할 수는 있어도&lt;br /&gt;&lt;b&gt;현실 세계를 충분히 표현하지 못할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;1433&quot; data-start=&quot;1430&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1452&quot; data-start=&quot;1435&quot; data-section-id=&quot;191zin8&quot; data-ke-size=&quot;size23&quot;&gt;결국 E-R 모델의 역할&lt;/h3&gt;
&lt;p data-end=&quot;1505&quot; data-start=&quot;1453&quot; data-ke-size=&quot;size16&quot;&gt;E-R 모델은 이런 문제를 줄이기 위해&lt;br /&gt;현실 세계의 개체와 관계를 먼저 정리하게 도와준다.&lt;/p&gt;
&lt;p data-end=&quot;1523&quot; data-start=&quot;1507&quot; data-ke-size=&quot;size16&quot;&gt;즉, 테이블 만들기 전에 먼저&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1573&quot; data-start=&quot;1525&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1535&quot; data-start=&quot;1525&quot; data-section-id=&quot;aedysf&quot;&gt;무엇이 개체인지&lt;/li&gt;
&lt;li data-end=&quot;1546&quot; data-start=&quot;1536&quot; data-section-id=&quot;aqhi2b&quot;&gt;무엇이 관계인지&lt;/li&gt;
&lt;li data-end=&quot;1560&quot; data-start=&quot;1547&quot; data-section-id=&quot;1oddq58&quot;&gt;어떤 속성이 필요한지&lt;/li&gt;
&lt;li data-end=&quot;1573&quot; data-start=&quot;1561&quot; data-section-id=&quot;15z6gr5&quot;&gt;어떤 제약이 있는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1594&quot; data-start=&quot;1575&quot; data-ke-size=&quot;size16&quot;&gt;를 정리하는 과정이라고 보면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;1599&quot; data-start=&quot;1596&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1620&quot; data-start=&quot;1601&quot; data-section-id=&quot;vuo3bi&quot; data-ke-size=&quot;size26&quot;&gt;3. E-R 모델의 기본 개념&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HgSVp/dJMcahwSmxN/uKdCD3uJpaFxMqEnt72Y9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HgSVp/dJMcahwSmxN/uKdCD3uJpaFxMqEnt72Y9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HgSVp/dJMcahwSmxN/uKdCD3uJpaFxMqEnt72Y9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHgSVp%2FdJMcahwSmxN%2FuKdCD3uJpaFxMqEnt72Y9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-end=&quot;1636&quot; data-start=&quot;1622&quot; data-section-id=&quot;1n4buok&quot; data-ke-size=&quot;size23&quot;&gt;개체(Entity)&lt;/h3&gt;
&lt;p data-end=&quot;1661&quot; data-start=&quot;1637&quot; data-ke-size=&quot;size16&quot;&gt;현실 세계에서 &lt;b&gt;구별 가능한 대상&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;1665&quot; data-start=&quot;1663&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1685&quot; data-start=&quot;1666&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1670&quot; data-start=&quot;1666&quot; data-section-id=&quot;yii6z0&quot;&gt;학생&lt;/li&gt;
&lt;li data-end=&quot;1675&quot; data-start=&quot;1671&quot; data-section-id=&quot;yi3obk&quot;&gt;교수&lt;/li&gt;
&lt;li data-end=&quot;1680&quot; data-start=&quot;1676&quot; data-section-id=&quot;yi2zr1&quot;&gt;과목&lt;/li&gt;
&lt;li data-end=&quot;1685&quot; data-start=&quot;1681&quot; data-section-id=&quot;yiinl9&quot;&gt;학과&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1713&quot; data-start=&quot;1687&quot; data-ke-size=&quot;size16&quot;&gt;즉, 독립적으로 존재를 생각할 수 있는 객체다.&lt;/p&gt;
&lt;hr data-end=&quot;1718&quot; data-start=&quot;1715&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1741&quot; data-start=&quot;1720&quot; data-section-id=&quot;1vnpktq&quot; data-ke-size=&quot;size23&quot;&gt;개체 집합(Entity Set)&lt;/h3&gt;
&lt;p data-end=&quot;1763&quot; data-start=&quot;1742&quot; data-ke-size=&quot;size16&quot;&gt;같은 종류의 개체들을 모아둔 집합이다.&lt;/p&gt;
&lt;p data-end=&quot;1767&quot; data-start=&quot;1765&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1793&quot; data-start=&quot;1768&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1780&quot; data-start=&quot;1768&quot; data-section-id=&quot;1juxc4g&quot;&gt;모든 학생들의 집합&lt;/li&gt;
&lt;li data-end=&quot;1793&quot; data-start=&quot;1781&quot; data-section-id=&quot;v04vws&quot;&gt;모든 교수들의 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1816&quot; data-start=&quot;1795&quot; data-ke-size=&quot;size16&quot;&gt;ERD에서는 보통 사각형으로 표현한다.&lt;/p&gt;
&lt;hr data-end=&quot;1821&quot; data-start=&quot;1818&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1843&quot; data-start=&quot;1823&quot; data-section-id=&quot;1noza2v&quot; data-ke-size=&quot;size23&quot;&gt;관계(Relationship)&lt;/h3&gt;
&lt;p data-end=&quot;1857&quot; data-start=&quot;1844&quot; data-ke-size=&quot;size16&quot;&gt;개체들 사이의 연결이다.&lt;/p&gt;
&lt;p data-end=&quot;1861&quot; data-start=&quot;1859&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1909&quot; data-start=&quot;1862&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1876&quot; data-start=&quot;1862&quot; data-section-id=&quot;17ya2zg&quot;&gt;학생이 과목을 수강한다&lt;/li&gt;
&lt;li data-end=&quot;1894&quot; data-start=&quot;1877&quot; data-section-id=&quot;ynhkd7&quot;&gt;학생이 교수의 지도를 받는다&lt;/li&gt;
&lt;li data-end=&quot;1909&quot; data-start=&quot;1895&quot; data-section-id=&quot;1jqkjbg&quot;&gt;교수가 학과에 소속된다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1911&quot; data-ke-size=&quot;size16&quot;&gt;즉, 개체끼리 어떤 식으로 연관되는지를 나타낸다.&lt;/p&gt;
&lt;hr data-end=&quot;1943&quot; data-start=&quot;1940&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1972&quot; data-start=&quot;1945&quot; data-section-id=&quot;mo7lh9&quot; data-ke-size=&quot;size23&quot;&gt;관계 집합(Relationship Set)&lt;/h3&gt;
&lt;p data-end=&quot;1990&quot; data-start=&quot;1973&quot; data-ke-size=&quot;size16&quot;&gt;같은 종류의 관계들의 집합이다.&lt;/p&gt;
&lt;p data-end=&quot;1994&quot; data-start=&quot;1992&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2016&quot; data-start=&quot;1995&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2005&quot; data-start=&quot;1995&quot; data-section-id=&quot;eimeu8&quot;&gt;모든 지도 관계&lt;/li&gt;
&lt;li data-end=&quot;2016&quot; data-start=&quot;2006&quot; data-section-id=&quot;13gvznt&quot;&gt;모든 수강 관계&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2061&quot; data-start=&quot;2018&quot; data-ke-size=&quot;size16&quot;&gt;관계도 단순 연결만 있는 게 아니라&lt;br /&gt;필요하면 &lt;b&gt;속성&lt;/b&gt;을 가질 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;2065&quot; data-start=&quot;2063&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2110&quot; data-start=&quot;2066&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2088&quot; data-start=&quot;2066&quot; data-section-id=&quot;8mkhck&quot;&gt;advisor 관계의 date&lt;/li&gt;
&lt;li data-end=&quot;2110&quot; data-start=&quot;2089&quot; data-section-id=&quot;v9jkp5&quot;&gt;takes 관계의 grade&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2135&quot; data-start=&quot;2112&quot; data-ke-size=&quot;size16&quot;&gt;즉, 관계에도 설명 정보가 붙을 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;2140&quot; data-start=&quot;2137&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2162&quot; data-start=&quot;2142&quot; data-section-id=&quot;158tzus&quot; data-ke-size=&quot;size26&quot;&gt;4. E-R 다이어그램 보는 법&lt;/h2&gt;
&lt;p data-end=&quot;2200&quot; data-start=&quot;2164&quot; data-ke-size=&quot;size16&quot;&gt;E-R 다이어그램은 개체, 관계, 속성을 기호로 표현한 그림이다.&lt;/p&gt;
&lt;h3 data-end=&quot;2211&quot; data-start=&quot;2202&quot; data-section-id=&quot;17osr8m&quot; data-ke-size=&quot;size23&quot;&gt;기본 기호&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2312&quot; data-start=&quot;2212&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2228&quot; data-start=&quot;2212&quot; data-section-id=&quot;156g6be&quot;&gt;&lt;b&gt;사각형&lt;/b&gt;: 개체 집합&lt;/li&gt;
&lt;li data-end=&quot;2247&quot; data-start=&quot;2229&quot; data-section-id=&quot;ee8ega&quot;&gt;&lt;b&gt;다이아몬드&lt;/b&gt;: 관계 집합&lt;/li&gt;
&lt;li data-end=&quot;2259&quot; data-start=&quot;2248&quot; data-section-id=&quot;r02u0u&quot;&gt;&lt;b&gt;원&lt;/b&gt;: 속성&lt;/li&gt;
&lt;li data-end=&quot;2273&quot; data-start=&quot;2260&quot; data-section-id=&quot;jupxgf&quot;&gt;&lt;b&gt;밑줄&lt;/b&gt;: 주 키&lt;/li&gt;
&lt;li data-end=&quot;2290&quot; data-start=&quot;2274&quot; data-section-id=&quot;1hwjltf&quot;&gt;&lt;b&gt;이중선&lt;/b&gt;: 전체 참여&lt;/li&gt;
&lt;li data-end=&quot;2312&quot; data-start=&quot;2291&quot; data-section-id=&quot;tggsu&quot;&gt;&lt;b&gt;이중 다이아몬드&lt;/b&gt;: 식별 관계&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2317&quot; data-start=&quot;2314&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2334&quot; data-start=&quot;2319&quot; data-section-id=&quot;1cph0zl&quot; data-ke-size=&quot;size23&quot;&gt;왜 그림으로 그릴까?&lt;/h3&gt;
&lt;p data-end=&quot;2397&quot; data-start=&quot;2335&quot; data-ke-size=&quot;size16&quot;&gt;ERD는 단순히 예쁘게 그리려고 쓰는 게 아니다.&lt;br /&gt;테이블 만들기 전에 구조를 눈으로 검토할 수 있어서 좋다.&lt;/p&gt;
&lt;p data-end=&quot;2401&quot; data-start=&quot;2399&quot; data-ke-size=&quot;size16&quot;&gt;특히&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2462&quot; data-start=&quot;2402&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2414&quot; data-start=&quot;2402&quot; data-section-id=&quot;u7t3j6&quot;&gt;빠진 개체는 없는지&lt;/li&gt;
&lt;li data-end=&quot;2427&quot; data-start=&quot;2415&quot; data-section-id=&quot;wzmflq&quot;&gt;관계 방향이 맞는지&lt;/li&gt;
&lt;li data-end=&quot;2444&quot; data-start=&quot;2428&quot; data-section-id=&quot;1o2ejym&quot;&gt;중복 저장이 생기지 않는지&lt;/li&gt;
&lt;li data-end=&quot;2462&quot; data-start=&quot;2445&quot; data-section-id=&quot;1voztgg&quot;&gt;약한 개체인지 강한 개체인지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2481&quot; data-start=&quot;2464&quot; data-ke-size=&quot;size16&quot;&gt;이런 걸 미리 확인할 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;2553&quot; data-start=&quot;2550&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2570&quot; data-start=&quot;2555&quot; data-section-id=&quot;1say9qa&quot; data-ke-size=&quot;size26&quot;&gt;5. 관계의 세부 요소&lt;/h2&gt;
&lt;h3 data-end=&quot;2592&quot; data-start=&quot;2572&quot; data-section-id=&quot;1h6eohy&quot; data-ke-size=&quot;size23&quot;&gt;관계에도 속성이 있을 수 있다&lt;/h3&gt;
&lt;p data-end=&quot;2618&quot; data-start=&quot;2593&quot; data-ke-size=&quot;size16&quot;&gt;관계는 단순히 &amp;ldquo;연결&amp;rdquo;만 표현하는 게 아니다.&lt;/p&gt;
&lt;p data-end=&quot;2725&quot; data-start=&quot;2620&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생과 교수 사이 advisor 관계가 있다고 하면,&lt;br /&gt;&amp;ldquo;언제 지도교수로 지정되었는가&amp;rdquo; 같은 정보는&lt;br /&gt;학생 속성도 아니고 교수 속성도 아니라 &lt;b&gt;관계 자체의 속성&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;2729&quot; data-start=&quot;2727&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2747&quot; data-start=&quot;2730&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2747&quot; data-start=&quot;2730&quot; data-section-id=&quot;uqw5k9&quot;&gt;advisor(date)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2800&quot; data-start=&quot;2749&quot; data-ke-size=&quot;size16&quot;&gt;이런 속성을 &lt;b&gt;설명 속성(descriptive attribute)&lt;/b&gt; 라고 생각하면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;2805&quot; data-start=&quot;2802&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2819&quot; data-start=&quot;2807&quot; data-section-id=&quot;ij1nfi&quot; data-ke-size=&quot;size23&quot;&gt;역할(Role)&lt;/h3&gt;
&lt;p data-end=&quot;2861&quot; data-start=&quot;2820&quot; data-ke-size=&quot;size16&quot;&gt;같은 개체 집합이 하나의 관계에 두 번 이상 참여할 때는 역할이 필요하다.&lt;/p&gt;
&lt;p data-end=&quot;2888&quot; data-start=&quot;2863&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 과목과 선수과목 관계를 생각해보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2943&quot; data-start=&quot;2890&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2915&quot; data-start=&quot;2890&quot; data-section-id=&quot;1axo23t&quot;&gt;한 과목이 다른 과목의 선수과목일 수 있음&lt;/li&gt;
&lt;li data-end=&quot;2943&quot; data-start=&quot;2916&quot; data-section-id=&quot;174r1it&quot;&gt;둘 다 같은 course 개체 집합에 속함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2950&quot; data-start=&quot;2945&quot; data-ke-size=&quot;size16&quot;&gt;이럴 때는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2966&quot; data-start=&quot;2951&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2958&quot; data-start=&quot;2951&quot; data-section-id=&quot;2he7at&quot;&gt;현재 과목&lt;/li&gt;
&lt;li data-end=&quot;2966&quot; data-start=&quot;2959&quot; data-section-id=&quot;f0smed&quot;&gt;선수 과목&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2993&quot; data-start=&quot;2968&quot; data-ke-size=&quot;size16&quot;&gt;같이 역할 이름을 구분해줘야 헷갈리지 않는다.&lt;/p&gt;
&lt;hr data-end=&quot;2998&quot; data-start=&quot;2995&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3012&quot; data-start=&quot;3000&quot; data-section-id=&quot;7cxz8k&quot; data-ke-size=&quot;size26&quot;&gt;6. 속성의 종류&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HINUQ/dJMcagEJ7pA/yq2vIb5d7TtqUbp2uFJXE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HINUQ/dJMcagEJ7pA/yq2vIb5d7TtqUbp2uFJXE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HINUQ/dJMcagEJ7pA/yq2vIb5d7TtqUbp2uFJXE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHINUQ%2FdJMcagEJ7pA%2Fyq2vIb5d7TtqUbp2uFJXE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;468&quot; data-origin-width=&quot;683&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;3038&quot; data-start=&quot;3014&quot; data-ke-size=&quot;size16&quot;&gt;ER 모델에서는 속성도 여러 종류로 나뉜다.&lt;/p&gt;
&lt;h3 data-end=&quot;3052&quot; data-start=&quot;3040&quot; data-section-id=&quot;15vtys6&quot; data-ke-size=&quot;size23&quot;&gt;1) 단순 속성&lt;/h3&gt;
&lt;p data-end=&quot;3071&quot; data-start=&quot;3053&quot; data-ke-size=&quot;size16&quot;&gt;더 이상 쪼갤 수 없는 속성이다.&lt;/p&gt;
&lt;p data-end=&quot;3075&quot; data-start=&quot;3073&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3082&quot; data-start=&quot;3076&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3082&quot; data-start=&quot;3076&quot; data-section-id=&quot;1j39hwl&quot;&gt;ID&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;3087&quot; data-start=&quot;3084&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3101&quot; data-start=&quot;3089&quot; data-section-id=&quot;1qkosal&quot; data-ke-size=&quot;size23&quot;&gt;2) 복합 속성&lt;/h3&gt;
&lt;p data-end=&quot;3126&quot; data-start=&quot;3102&quot; data-ke-size=&quot;size16&quot;&gt;여러 하위 속성으로 나눌 수 있는 속성이다.&lt;/p&gt;
&lt;p data-end=&quot;3130&quot; data-start=&quot;3128&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3199&quot; data-start=&quot;3131&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3165&quot; data-start=&quot;3131&quot; data-section-id=&quot;18wewtk&quot;&gt;name = {first_name, last_name}&lt;/li&gt;
&lt;li data-end=&quot;3199&quot; data-start=&quot;3166&quot; data-section-id=&quot;xonzi8&quot;&gt;address = {city, street, zip}&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3231&quot; data-start=&quot;3201&quot; data-ke-size=&quot;size16&quot;&gt;실제 관계형 스키마로 바꿀 때는 보통 쪼개서 저장한다.&lt;/p&gt;
&lt;hr data-end=&quot;3236&quot; data-start=&quot;3233&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3251&quot; data-start=&quot;3238&quot; data-section-id=&quot;enf7ty&quot; data-ke-size=&quot;size23&quot;&gt;3) 단일값 속성&lt;/h3&gt;
&lt;p data-end=&quot;3274&quot; data-start=&quot;3252&quot; data-ke-size=&quot;size16&quot;&gt;한 개체가 하나의 값만 가지는 속성이다.&lt;/p&gt;
&lt;p data-end=&quot;3278&quot; data-start=&quot;3276&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3287&quot; data-start=&quot;3279&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3287&quot; data-start=&quot;3279&quot; data-section-id=&quot;16yodxh&quot;&gt;학생의 학번&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;3292&quot; data-start=&quot;3289&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3307&quot; data-start=&quot;3294&quot; data-section-id=&quot;12xq2rk&quot; data-ke-size=&quot;size23&quot;&gt;4) 다중값 속성&lt;/h3&gt;
&lt;p data-end=&quot;3333&quot; data-start=&quot;3308&quot; data-ke-size=&quot;size16&quot;&gt;한 개체가 여러 값을 가질 수 있는 속성이다.&lt;/p&gt;
&lt;p data-end=&quot;3337&quot; data-start=&quot;3335&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3356&quot; data-start=&quot;3338&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3344&quot; data-start=&quot;3338&quot; data-section-id=&quot;cezc94&quot;&gt;전화번호&lt;/li&gt;
&lt;li data-end=&quot;3350&quot; data-start=&quot;3345&quot; data-section-id=&quot;2jk8s4&quot;&gt;이메일&lt;/li&gt;
&lt;li data-end=&quot;3356&quot; data-start=&quot;3351&quot; data-section-id=&quot;2jnfak&quot;&gt;자격증&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3371&quot; data-start=&quot;3358&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 교수 한 명이&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3395&quot; data-start=&quot;3372&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3380&quot; data-start=&quot;3372&quot; data-section-id=&quot;1sxiojk&quot;&gt;연구실 번호&lt;/li&gt;
&lt;li data-end=&quot;3387&quot; data-start=&quot;3381&quot; data-section-id=&quot;bv9wll&quot;&gt;집 번호&lt;/li&gt;
&lt;li data-end=&quot;3395&quot; data-start=&quot;3388&quot; data-section-id=&quot;1v3z7fg&quot;&gt;개인 번호&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3426&quot; data-start=&quot;3397&quot; data-ke-size=&quot;size16&quot;&gt;를 모두 가질 수 있다면 전화번호는 다중값 속성이다.&lt;/p&gt;
&lt;p data-end=&quot;3468&quot; data-start=&quot;3428&quot; data-ke-size=&quot;size16&quot;&gt;관계형 모델로 변환할 때는 보통 &lt;b&gt;별도 릴레이션&lt;/b&gt;으로 분리해야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;3473&quot; data-start=&quot;3470&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3487&quot; data-start=&quot;3475&quot; data-section-id=&quot;135zcjm&quot; data-ke-size=&quot;size23&quot;&gt;5) 유도 속성&lt;/h3&gt;
&lt;p data-end=&quot;3510&quot; data-start=&quot;3488&quot; data-ke-size=&quot;size16&quot;&gt;다른 속성으로부터 계산 가능한 속성이다.&lt;/p&gt;
&lt;p data-end=&quot;3514&quot; data-start=&quot;3512&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3543&quot; data-start=&quot;3515&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3543&quot; data-start=&quot;3515&quot; data-section-id=&quot;dxil6a&quot;&gt;age = today - birth_date&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3579&quot; data-start=&quot;3545&quot; data-ke-size=&quot;size16&quot;&gt;이건 굳이 DB에 저장하지 않고 필요할 때 계산할 수도 있다.&lt;/p&gt;
&lt;hr data-end=&quot;3671&quot; data-start=&quot;3668&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3693&quot; data-start=&quot;3673&quot; data-section-id=&quot;15dd3gl&quot; data-ke-size=&quot;size26&quot;&gt;7. 관계의 차수(Degree)&lt;/h2&gt;
&lt;p data-end=&quot;3725&quot; data-start=&quot;3695&quot; data-ke-size=&quot;size16&quot;&gt;관계에 몇 개의 개체 집합이 참여하는지에 따라 나뉜다.&lt;/p&gt;
&lt;h3 data-end=&quot;3744&quot; data-start=&quot;3727&quot; data-section-id=&quot;ci8pjw&quot; data-ke-size=&quot;size23&quot;&gt;이진 관계(Binary)&lt;/h3&gt;
&lt;p data-end=&quot;3763&quot; data-start=&quot;3745&quot; data-ke-size=&quot;size16&quot;&gt;두 개체 집합이 참여하는 관계다.&lt;/p&gt;
&lt;p data-end=&quot;3767&quot; data-start=&quot;3765&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3791&quot; data-start=&quot;3768&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3779&quot; data-start=&quot;3768&quot; data-section-id=&quot;1cr6hzo&quot;&gt;학생 &amp;mdash; 지도교수&lt;/li&gt;
&lt;li data-end=&quot;3791&quot; data-start=&quot;3780&quot; data-section-id=&quot;1djbyf4&quot;&gt;학생 &amp;mdash; 수강과목&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3810&quot; data-start=&quot;3793&quot; data-ke-size=&quot;size16&quot;&gt;대부분의 관계가 여기에 속한다.&lt;/p&gt;
&lt;hr data-end=&quot;3815&quot; data-start=&quot;3812&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3835&quot; data-start=&quot;3817&quot; data-section-id=&quot;jmfnfg&quot; data-ke-size=&quot;size23&quot;&gt;삼진 관계(Ternary)&lt;/h3&gt;
&lt;p data-end=&quot;3858&quot; data-start=&quot;3836&quot; data-ke-size=&quot;size16&quot;&gt;세 개체 집합이 동시에 참여하는 관계다.&lt;/p&gt;
&lt;p data-end=&quot;3862&quot; data-start=&quot;3860&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3879&quot; data-start=&quot;3863&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3879&quot; data-start=&quot;3863&quot; data-section-id=&quot;5jcdzt&quot;&gt;학생 &amp;mdash; 교수 &amp;mdash; 프로젝트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3969&quot; data-start=&quot;3881&quot; data-ke-size=&quot;size16&quot;&gt;이건 단순히 이진 관계 세 개로 쪼개면 의미가 달라질 수 있어서&lt;br /&gt;정말로 세 개가 함께 묶여야 하는 관계라면 ternary 관계로 표현하는 게 더 정확하다.&lt;/p&gt;
&lt;hr data-end=&quot;3974&quot; data-start=&quot;3971&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4011&quot; data-start=&quot;3976&quot; data-section-id=&quot;h6s5uh&quot; data-ke-size=&quot;size26&quot;&gt;8. 대응 카디널리티(Mapping Cardinality)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RvHLQ/dJMcabXIpBd/hwBZy1TJwADJD9u0D7f3qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RvHLQ/dJMcabXIpBd/hwBZy1TJwADJD9u0D7f3qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RvHLQ/dJMcabXIpBd/hwBZy1TJwADJD9u0D7f3qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRvHLQ%2FdJMcabXIpBd%2FhwBZy1TJwADJD9u0D7f3qk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1073&quot; height=&quot;308&quot; data-origin-width=&quot;1073&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;4039&quot; data-start=&quot;4013&quot; data-ke-size=&quot;size16&quot;&gt;카디널리티는 &amp;ldquo;몇 대 몇 관계인가&amp;rdquo;를 나타낸다.&lt;/p&gt;
&lt;p data-end=&quot;4063&quot; data-start=&quot;4041&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생과 지도교수 관계를 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4111&quot; data-start=&quot;4065&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4090&quot; data-start=&quot;4065&quot; data-section-id=&quot;8imzb1&quot;&gt;교수 한 명이 여러 학생을 지도할 수 있다&lt;/li&gt;
&lt;li data-end=&quot;4111&quot; data-start=&quot;4091&quot; data-section-id=&quot;1pdz8lp&quot;&gt;학생은 한 명의 지도교수만 가진다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;4133&quot; data-start=&quot;4113&quot; data-ke-size=&quot;size16&quot;&gt;이건 &lt;b&gt;일대다(1:N)&lt;/b&gt; 관계다.&lt;/p&gt;
&lt;hr data-end=&quot;4138&quot; data-start=&quot;4135&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4154&quot; data-start=&quot;4140&quot; data-section-id=&quot;x6kpan&quot; data-ke-size=&quot;size23&quot;&gt;대표적인 카디널리티&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4218&quot; data-start=&quot;4155&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4170&quot; data-start=&quot;4155&quot; data-section-id=&quot;1bk9a2b&quot;&gt;&lt;b&gt;일대일 (1:1)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;4186&quot; data-start=&quot;4171&quot; data-section-id=&quot;1sou0hg&quot;&gt;&lt;b&gt;일대다 (1:N)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;4202&quot; data-start=&quot;4187&quot; data-section-id=&quot;1q7c778&quot;&gt;&lt;b&gt;다대일 (N:1)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;4218&quot; data-start=&quot;4203&quot; data-section-id=&quot;cbp74g&quot;&gt;&lt;b&gt;다대다 (M:N)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;4223&quot; data-start=&quot;4220&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4237&quot; data-start=&quot;4225&quot; data-section-id=&quot;1z0al52&quot; data-ke-size=&quot;size23&quot;&gt;예시로 이해하기&lt;/h3&gt;
&lt;p data-end=&quot;4252&quot; data-start=&quot;4238&quot; data-ke-size=&quot;size16&quot;&gt;advisor 관계에서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4318&quot; data-start=&quot;4254&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4283&quot; data-start=&quot;4254&quot; data-section-id=&quot;vle4yk&quot;&gt;교수: 지도학생이 없을 수도 있음 &amp;rarr; 0..*&lt;/li&gt;
&lt;li data-end=&quot;4318&quot; data-start=&quot;4284&quot; data-section-id=&quot;1tb8l6p&quot;&gt;학생: 지도교수가 반드시 한 명 있어야 함 &amp;rarr; 1..1&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;4333&quot; data-start=&quot;4320&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 생각할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;4387&quot; data-start=&quot;4335&quot; data-ke-size=&quot;size16&quot;&gt;즉, 카디널리티는 단순히 숫자 문제가 아니라&lt;br /&gt;현실 세계의 규칙을 모델에 반영하는 과정이다.&lt;/p&gt;
&lt;hr data-end=&quot;4392&quot; data-start=&quot;4389&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4412&quot; data-start=&quot;4394&quot; data-section-id=&quot;1y3ofom&quot; data-ke-size=&quot;size26&quot;&gt;9. 전체 참여와 부분 참여&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byg8T4/dJMb99MkTn9/BcCfSurilM5go2IO0EDdu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byg8T4/dJMb99MkTn9/BcCfSurilM5go2IO0EDdu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byg8T4/dJMb99MkTn9/BcCfSurilM5go2IO0EDdu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbyg8T4%2FdJMb99MkTn9%2FBcCfSurilM5go2IO0EDdu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;857&quot; height=&quot;258&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;4434&quot; data-start=&quot;4414&quot; data-ke-size=&quot;size16&quot;&gt;이건 카디널리티와 함께 자주 나온다.&lt;/p&gt;
&lt;h3 data-end=&quot;4466&quot; data-start=&quot;4436&quot; data-section-id=&quot;eqyu7s&quot; data-ke-size=&quot;size23&quot;&gt;전체 참여(Total Participation)&lt;/h3&gt;
&lt;p data-end=&quot;4525&quot; data-start=&quot;4467&quot; data-ke-size=&quot;size16&quot;&gt;모든 개체가 반드시 어떤 관계에 참여해야 하는 경우다.&lt;br /&gt;ERD에서는 보통 &lt;b&gt;이중선&lt;/b&gt;으로 표시한다.&lt;/p&gt;
&lt;p data-end=&quot;4529&quot; data-start=&quot;4527&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4555&quot; data-start=&quot;4530&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4555&quot; data-start=&quot;4530&quot; data-section-id=&quot;jrt4i0&quot;&gt;모든 학생은 반드시 지도교수가 있어야 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;4560&quot; data-start=&quot;4557&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4594&quot; data-start=&quot;4562&quot; data-section-id=&quot;wqn35p&quot; data-ke-size=&quot;size23&quot;&gt;부분 참여(Partial Participation)&lt;/h3&gt;
&lt;p data-end=&quot;4646&quot; data-start=&quot;4595&quot; data-ke-size=&quot;size16&quot;&gt;일부 개체만 관계에 참여해도 되는 경우다.&lt;br /&gt;ERD에서는 보통 &lt;b&gt;단일선&lt;/b&gt;으로 표시한다.&lt;/p&gt;
&lt;p data-end=&quot;4650&quot; data-start=&quot;4648&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4674&quot; data-start=&quot;4651&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4674&quot; data-start=&quot;4651&quot; data-section-id=&quot;9m3pya&quot;&gt;어떤 교수는 지도학생이 없을 수도 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;4679&quot; data-start=&quot;4676&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4692&quot; data-start=&quot;4681&quot; data-section-id=&quot;nbx15o&quot; data-ke-size=&quot;size23&quot;&gt;왜 중요한가?&lt;/h3&gt;
&lt;p data-end=&quot;4753&quot; data-start=&quot;4693&quot; data-ke-size=&quot;size16&quot;&gt;이건 나중에 관계형 스키마로 바꿀 때&lt;br /&gt;외래키가 NULL 가능인지, NOT NULL인지와 연결될 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;4757&quot; data-start=&quot;4755&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4803&quot; data-start=&quot;4758&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4780&quot; data-start=&quot;4758&quot; data-section-id=&quot;axg6zj&quot;&gt;전체 참여 &amp;rarr; 보통 반드시 참조 필요&lt;/li&gt;
&lt;li data-end=&quot;4803&quot; data-start=&quot;4781&quot; data-section-id=&quot;enrl4o&quot;&gt;부분 참여 &amp;rarr; 참조가 없을 수도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;4808&quot; data-start=&quot;4805&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4823&quot; data-start=&quot;4810&quot; data-section-id=&quot;18tlf5k&quot; data-ke-size=&quot;size26&quot;&gt;10. 키(Key)&lt;/h2&gt;
&lt;h3 data-end=&quot;4837&quot; data-start=&quot;4825&quot; data-section-id=&quot;5vvpxm&quot; data-ke-size=&quot;size23&quot;&gt;개체 집합의 키&lt;/h3&gt;
&lt;p data-end=&quot;4859&quot; data-start=&quot;4838&quot; data-ke-size=&quot;size16&quot;&gt;각 개체를 고유하게 식별하는 속성이다.&lt;/p&gt;
&lt;p data-end=&quot;4863&quot; data-start=&quot;4861&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4903&quot; data-start=&quot;4864&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4874&quot; data-start=&quot;4864&quot; data-section-id=&quot;148czjt&quot;&gt;학생의 ID&lt;/li&gt;
&lt;li data-end=&quot;4885&quot; data-start=&quot;4875&quot; data-section-id=&quot;10dpoph&quot;&gt;교수의 ID&lt;/li&gt;
&lt;li data-end=&quot;4903&quot; data-start=&quot;4886&quot; data-section-id=&quot;1kh16h4&quot;&gt;학과의 dept_name&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;4908&quot; data-start=&quot;4905&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4922&quot; data-start=&quot;4910&quot; data-section-id=&quot;1axgyli&quot; data-ke-size=&quot;size23&quot;&gt;관계 집합의 키&lt;/h3&gt;
&lt;p data-end=&quot;4965&quot; data-start=&quot;4923&quot; data-ke-size=&quot;size16&quot;&gt;관계를 고유하게 식별하는 키다.&lt;br /&gt;이건 관계의 카디널리티에 따라 달라진다.&lt;/p&gt;
&lt;p data-end=&quot;4992&quot; data-start=&quot;4967&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생(s)과 교수(i)의 관계라면:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;관계 유형관계 키
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;5106&quot; data-start=&quot;4994&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;5106&quot; data-start=&quot;5021&quot;&gt;
&lt;tr data-end=&quot;5037&quot; data-start=&quot;5021&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5027&quot; data-start=&quot;5021&quot;&gt;다대일&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5037&quot; data-start=&quot;5027&quot;&gt;s_id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5054&quot; data-start=&quot;5038&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5044&quot; data-start=&quot;5038&quot;&gt;일대다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5054&quot; data-start=&quot;5044&quot;&gt;i_id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5081&quot; data-start=&quot;5055&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5061&quot; data-start=&quot;5055&quot;&gt;일대일&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5081&quot; data-start=&quot;5061&quot;&gt;s_id 또는 i_id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5106&quot; data-start=&quot;5082&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5088&quot; data-start=&quot;5082&quot;&gt;다대다&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5106&quot; data-start=&quot;5088&quot;&gt;(s_id, i_id)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5147&quot; data-start=&quot;5108&quot; data-ke-size=&quot;size16&quot;&gt;즉, 어떤 쪽이 여러 번 나타날 수 있는지에 따라 키 구성이 달라진다.&lt;/p&gt;
&lt;hr data-end=&quot;5152&quot; data-start=&quot;5149&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5186&quot; data-start=&quot;5154&quot; data-section-id=&quot;1cslxwa&quot; data-ke-size=&quot;size26&quot;&gt;11. 약한 개체 집합(Weak Entity Set)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bITvPf/dJMcaio0N3P/0yGNzzOiYYijfyxC6WeKyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bITvPf/dJMcaio0N3P/0yGNzzOiYYijfyxC6WeKyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bITvPf/dJMcaio0N3P/0yGNzzOiYYijfyxC6WeKyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbITvPf%2FdJMcaio0N3P%2F0yGNzzOiYYijfyxC6WeKyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1221&quot; height=&quot;340&quot; data-origin-width=&quot;1221&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;5209&quot; data-start=&quot;5188&quot; data-ke-size=&quot;size16&quot;&gt;이 부분은 ER 모델에서 꽤 중요하다.&lt;/p&gt;
&lt;p data-end=&quot;5286&quot; data-start=&quot;5211&quot; data-ke-size=&quot;size16&quot;&gt;약한 개체는 &lt;b&gt;자기 자신의 속성만으로는 유일하게 구별할 수 없는 개체&lt;/b&gt;다.&lt;br /&gt;즉, 다른 개체에 의존해서만 존재를 구분할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;5290&quot; data-start=&quot;5288&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5306&quot; data-start=&quot;5291&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5306&quot; data-start=&quot;5291&quot; data-section-id=&quot;7mezlo&quot;&gt;section(분반)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;5373&quot; data-start=&quot;5308&quot; data-ke-size=&quot;size16&quot;&gt;분반은 보통 course 없이 단독으로 존재한다고 보기 어렵다.&lt;br /&gt;어떤 과목의 분반인지가 있어야 의미가 생긴다.&lt;/p&gt;
&lt;hr data-end=&quot;5378&quot; data-start=&quot;5375&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5392&quot; data-start=&quot;5380&quot; data-section-id=&quot;163fcr3&quot; data-ke-size=&quot;size23&quot;&gt;식별 개체 집합&lt;/h3&gt;
&lt;p data-end=&quot;5417&quot; data-start=&quot;5393&quot; data-ke-size=&quot;size16&quot;&gt;약한 개체를 식별해주는 강한 개체 집합이다.&lt;/p&gt;
&lt;p data-end=&quot;5421&quot; data-start=&quot;5419&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5454&quot; data-start=&quot;5422&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5454&quot; data-start=&quot;5422&quot; data-section-id=&quot;14gxv4d&quot;&gt;course 가 section의 식별 개체 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;5459&quot; data-start=&quot;5456&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5470&quot; data-start=&quot;5461&quot; data-section-id=&quot;ttmuij&quot; data-ke-size=&quot;size23&quot;&gt;식별 관계&lt;/h3&gt;
&lt;p data-end=&quot;5523&quot; data-start=&quot;5471&quot; data-ke-size=&quot;size16&quot;&gt;약한 개체와 식별 개체를 연결하는 관계다.&lt;br /&gt;ERD에서는 &lt;b&gt;이중 다이아몬드&lt;/b&gt;로 표시한다.&lt;/p&gt;
&lt;p data-end=&quot;5527&quot; data-start=&quot;5525&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5542&quot; data-start=&quot;5528&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5542&quot; data-start=&quot;5528&quot; data-section-id=&quot;140kazj&quot;&gt;sec_course&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;5547&quot; data-start=&quot;5544&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5561&quot; data-start=&quot;5549&quot; data-section-id=&quot;j55yhf&quot; data-ke-size=&quot;size23&quot;&gt;약한 개체의 키&lt;/h3&gt;
&lt;p data-end=&quot;5576&quot; data-start=&quot;5562&quot; data-ke-size=&quot;size16&quot;&gt;약한 개체의 주 키는 보통&lt;/p&gt;
&lt;p data-end=&quot;5619&quot; data-start=&quot;5578&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;식별 개체의 키 + 약한 개체의 부분 키(discriminator)&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;5628&quot; data-start=&quot;5621&quot; data-ke-size=&quot;size16&quot;&gt;로 구성된다.&lt;/p&gt;
&lt;p data-end=&quot;5646&quot; data-start=&quot;5630&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;br /&gt;section의 경우&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5694&quot; data-start=&quot;5648&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5661&quot; data-start=&quot;5648&quot; data-section-id=&quot;xz0ief&quot;&gt;course_id&lt;/li&gt;
&lt;li data-end=&quot;5672&quot; data-start=&quot;5662&quot; data-section-id=&quot;1o2dl3j&quot;&gt;sec_id&lt;/li&gt;
&lt;li data-end=&quot;5685&quot; data-start=&quot;5673&quot; data-section-id=&quot;5uu2w6&quot;&gt;semester&lt;/li&gt;
&lt;li data-end=&quot;5694&quot; data-start=&quot;5686&quot; data-section-id=&quot;18j04xz&quot;&gt;year&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;5721&quot; data-start=&quot;5696&quot; data-ke-size=&quot;size16&quot;&gt;이 합쳐져야 분반을 유일하게 식별할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;5725&quot; data-start=&quot;5723&quot; data-ke-size=&quot;size16&quot;&gt;즉:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;section(course_id, sec_id, semester, year)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;5785&quot; data-start=&quot;5782&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5808&quot; data-start=&quot;5787&quot; data-section-id=&quot;iq5a94&quot; data-ke-size=&quot;size23&quot;&gt;약한 개체에서 헷갈리기 쉬운 점&lt;/h3&gt;
&lt;p data-end=&quot;5900&quot; data-start=&quot;5809&quot; data-ke-size=&quot;size16&quot;&gt;ER 다이어그램에서는 약한 개체 안에 식별 개체의 키를 직접 속성처럼 안 그릴 수도 있다.&lt;br /&gt;하지만 실제 관계형 DB로 구현할 때는 &lt;b&gt;반드시 포함&lt;/b&gt;해야 한다.&lt;/p&gt;
&lt;p data-end=&quot;5904&quot; data-start=&quot;5902&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5946&quot; data-start=&quot;5905&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5923&quot; data-start=&quot;5905&quot; data-section-id=&quot;1ysma1g&quot;&gt;개념 모델에서는 종속성을 강조&lt;/li&gt;
&lt;li data-end=&quot;5946&quot; data-start=&quot;5924&quot; data-section-id=&quot;pmihog&quot;&gt;구현 모델에서는 실제 키 컬럼이 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;5962&quot; data-start=&quot;5948&quot; data-ke-size=&quot;size16&quot;&gt;이 차이를 구분해야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;6028&quot; data-start=&quot;6025&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;6064&quot; data-start=&quot;6030&quot; data-section-id=&quot;1g2p3vz&quot; data-ke-size=&quot;size26&quot;&gt;12. E-R 다이어그램을 관계형 스키마로 변환하는 방법&lt;/h2&gt;
&lt;p data-end=&quot;6117&quot; data-start=&quot;6066&quot; data-ke-size=&quot;size16&quot;&gt;이제 ERD를 실제 테이블 구조로 바꾸는 단계다.&lt;br /&gt;이 부분이 바로 &lt;b&gt;논리적 설계&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;6147&quot; data-start=&quot;6119&quot; data-ke-size=&quot;size16&quot;&gt;변환은 크게 몇 가지 경우로 나눠 생각하면 편하다.&lt;/p&gt;
&lt;hr data-end=&quot;6152&quot; data-start=&quot;6149&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6179&quot; data-start=&quot;6154&quot; data-section-id=&quot;m9wapz&quot; data-ke-size=&quot;size23&quot;&gt;1) 단순 속성만 가진 강한 개체 집합&lt;/h3&gt;
&lt;p data-end=&quot;6191&quot; data-start=&quot;6180&quot; data-ke-size=&quot;size16&quot;&gt;가장 간단한 경우다.&lt;/p&gt;
&lt;p data-end=&quot;6220&quot; data-start=&quot;6193&quot; data-ke-size=&quot;size16&quot;&gt;ER 개체 하나를 그대로 릴레이션 하나로 만든다.&lt;/p&gt;
&lt;p data-end=&quot;6224&quot; data-start=&quot;6222&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;student(ID, name, tot_cred)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6268&quot; data-start=&quot;6266&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6292&quot; data-start=&quot;6269&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6282&quot; data-start=&quot;6269&quot; data-section-id=&quot;1rwe7nq&quot;&gt;개체 집합 &amp;rarr; 테이블&lt;/li&gt;
&lt;li data-end=&quot;6292&quot; data-start=&quot;6283&quot; data-section-id=&quot;k2o1bq&quot;&gt;속성 &amp;rarr; 컬럼&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;6307&quot; data-start=&quot;6294&quot; data-ke-size=&quot;size16&quot;&gt;으로 바로 옮기면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;6312&quot; data-start=&quot;6309&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6339&quot; data-start=&quot;6314&quot; data-section-id=&quot;kinep8&quot; data-ke-size=&quot;size23&quot;&gt;2) 복합 속성이 있는 강한 개체 집합&lt;/h3&gt;
&lt;p data-end=&quot;6384&quot; data-start=&quot;6340&quot; data-ke-size=&quot;size16&quot;&gt;복합 속성은 그대로 둘 수 없고&lt;br /&gt;&lt;b&gt;하위 단순 속성으로 분해&lt;/b&gt;해서 넣는다.&lt;/p&gt;
&lt;p data-end=&quot;6388&quot; data-start=&quot;6386&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6423&quot; data-start=&quot;6389&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6423&quot; data-start=&quot;6389&quot; data-section-id=&quot;18wewtk&quot;&gt;name = {first_name, last_name}&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;6446&quot; data-start=&quot;6425&quot; data-ke-size=&quot;size16&quot;&gt;그러면 스키마는 이렇게 바뀔 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;student(ID, first_name, last_name, tot_cred)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6526&quot; data-start=&quot;6506&quot; data-ke-size=&quot;size16&quot;&gt;필요하면 prefix를 붙여도 된다.&lt;/p&gt;
&lt;p data-end=&quot;6530&quot; data-start=&quot;6528&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6559&quot; data-start=&quot;6531&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6545&quot; data-start=&quot;6531&quot; data-section-id=&quot;f4qrwq&quot;&gt;name_first&lt;/li&gt;
&lt;li data-end=&quot;6559&quot; data-start=&quot;6546&quot; data-section-id=&quot;73ryfu&quot;&gt;name_last&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;6564&quot; data-start=&quot;6561&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6582&quot; data-start=&quot;6566&quot; data-section-id=&quot;5x7wab&quot; data-ke-size=&quot;size23&quot;&gt;3) 다중값 속성 처리&lt;/h3&gt;
&lt;p data-end=&quot;6608&quot; data-start=&quot;6583&quot; data-ke-size=&quot;size16&quot;&gt;다중값 속성은 별도 릴레이션을 만들어야 한다.&lt;/p&gt;
&lt;p data-end=&quot;6685&quot; data-start=&quot;6610&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 교수 한 명이 여러 전화번호를 가진다면&lt;br /&gt;전화번호를 instructor 테이블 안에 반복 컬럼으로 넣는 건 적절하지 않다.&lt;/p&gt;
&lt;p data-end=&quot;6703&quot; data-start=&quot;6687&quot; data-ke-size=&quot;size16&quot;&gt;그래서 보통 이렇게 분리한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;instructor(ID, name, dept_name, salary)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;inst_phone(ID, phone_number)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6812&quot; data-start=&quot;6787&quot; data-ke-size=&quot;size16&quot;&gt;여기서 inst_phone의 기본키는 보통&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;(ID, phone_number)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6851&quot; data-start=&quot;6846&quot; data-ke-size=&quot;size16&quot;&gt;가 된다.&lt;/p&gt;
&lt;p data-end=&quot;6881&quot; data-start=&quot;6853&quot; data-ke-size=&quot;size16&quot;&gt;즉, 한 교수에게 여러 전화번호를 연결하는 구조다.&lt;/p&gt;
&lt;hr data-end=&quot;6886&quot; data-start=&quot;6883&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6906&quot; data-start=&quot;6888&quot; data-section-id=&quot;1sup5le&quot; data-ke-size=&quot;size23&quot;&gt;4) 약한 개체 집합 변환&lt;/h3&gt;
&lt;p data-end=&quot;6941&quot; data-start=&quot;6907&quot; data-ke-size=&quot;size16&quot;&gt;약한 개체는 식별 개체의 주 키를 포함해서 릴레이션을 만든다.&lt;/p&gt;
&lt;p data-end=&quot;6945&quot; data-start=&quot;6943&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;section(course_id, sec_id, semester, year)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7069&quot; data-start=&quot;7002&quot; data-ke-size=&quot;size16&quot;&gt;여기서 course_id는 식별 개체인 course에서 온 키고,&lt;br /&gt;나머지는 section을 구분하는 속성이다.&lt;/p&gt;
&lt;hr data-end=&quot;7074&quot; data-start=&quot;7071&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7091&quot; data-start=&quot;7076&quot; data-section-id=&quot;906r6m&quot; data-ke-size=&quot;size23&quot;&gt;5) 관계 집합 표현&lt;/h3&gt;
&lt;h4 data-end=&quot;7104&quot; data-start=&quot;7093&quot; data-ke-size=&quot;size20&quot;&gt;다대다 관계&lt;/h4&gt;
&lt;p data-end=&quot;7134&quot; data-start=&quot;7105&quot; data-ke-size=&quot;size16&quot;&gt;참여하는 개체 집합들의 주 키를 모두 가져와야 한다.&lt;/p&gt;
&lt;p data-end=&quot;7138&quot; data-start=&quot;7136&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;advisor(s_id, i_id, date)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7202&quot; data-start=&quot;7178&quot; data-ke-size=&quot;size16&quot;&gt;여기서 date는 관계의 설명 속성이다.&lt;/p&gt;
&lt;hr data-end=&quot;7207&quot; data-start=&quot;7204&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;7220&quot; data-start=&quot;7209&quot; data-ke-size=&quot;size20&quot;&gt;다대일 관계&lt;/h4&gt;
&lt;p data-end=&quot;7255&quot; data-start=&quot;7221&quot; data-ke-size=&quot;size16&quot;&gt;보통 &amp;ldquo;다&amp;rdquo; 쪽에 &amp;ldquo;일&amp;rdquo; 쪽의 기본키를 외래키로 넣으면 된다.&lt;/p&gt;
&lt;p data-end=&quot;7292&quot; data-start=&quot;7257&quot; data-ke-size=&quot;size16&quot;&gt;즉, 굳이 별도 관계 릴레이션을 안 만들어도 되는 경우가 많다.&lt;/p&gt;
&lt;p data-end=&quot;7296&quot; data-start=&quot;7294&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7351&quot; data-start=&quot;7297&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7316&quot; data-start=&quot;7297&quot; data-section-id=&quot;it2fww&quot;&gt;여러 학생이 한 학과에 속한다면&lt;/li&gt;
&lt;li data-end=&quot;7351&quot; data-start=&quot;7317&quot; data-section-id=&quot;1ui2x1q&quot;&gt;student 테이블에 dept_name을 넣는 식&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;7356&quot; data-start=&quot;7353&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7374&quot; data-start=&quot;7358&quot; data-section-id=&quot;1h7dfyp&quot; data-ke-size=&quot;size26&quot;&gt;13. 스키마 중복 처리&lt;/h2&gt;
&lt;h3 data-end=&quot;7392&quot; data-start=&quot;7376&quot; data-section-id=&quot;1ghqg4t&quot; data-ke-size=&quot;size23&quot;&gt;일대다 / 다대일 관계&lt;/h3&gt;
&lt;p data-end=&quot;7463&quot; data-start=&quot;7393&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;다&amp;rdquo; 쪽이 전체 참여라면&lt;br /&gt;별도 관계 릴레이션을 만들지 않고 &amp;ldquo;다&amp;rdquo; 쪽 릴레이션에 &amp;ldquo;일&amp;rdquo; 쪽 키를 넣는 방식이 효율적이다.&lt;/p&gt;
&lt;p data-end=&quot;7467&quot; data-start=&quot;7465&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7523&quot; data-start=&quot;7468&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7486&quot; data-start=&quot;7468&quot; data-section-id=&quot;1r7ze1k&quot;&gt;학생은 반드시 한 학과에 속함&lt;/li&gt;
&lt;li data-end=&quot;7523&quot; data-start=&quot;7487&quot; data-section-id=&quot;r7ezoo&quot;&gt;그러면 student 릴레이션에 dept_name 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7554&quot; data-start=&quot;7525&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 관계 릴레이션을 따로 만들 필요가 없다.&lt;/p&gt;
&lt;hr data-end=&quot;7559&quot; data-start=&quot;7556&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7571&quot; data-start=&quot;7561&quot; data-section-id=&quot;1p22t2a&quot; data-ke-size=&quot;size23&quot;&gt;일대일 관계&lt;/h3&gt;
&lt;p data-end=&quot;7616&quot; data-start=&quot;7572&quot; data-ke-size=&quot;size16&quot;&gt;한쪽 릴레이션에 다른 쪽의 주 키를 넣고 관계 집합 릴레이션을 생략할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;7631&quot; data-start=&quot;7618&quot; data-ke-size=&quot;size16&quot;&gt;다만 어느 쪽에 넣을지는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7663&quot; data-start=&quot;7632&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7642&quot; data-start=&quot;7632&quot; data-section-id=&quot;1luut5s&quot;&gt;전체 참여 여부&lt;/li&gt;
&lt;li data-end=&quot;7655&quot; data-start=&quot;7643&quot; data-section-id=&quot;tcj3by&quot;&gt;NULL 허용 여부&lt;/li&gt;
&lt;li data-end=&quot;7663&quot; data-start=&quot;7656&quot; data-section-id=&quot;sawsxg&quot;&gt;설계 의도&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7680&quot; data-start=&quot;7665&quot; data-ke-size=&quot;size16&quot;&gt;를 보고 결정하는 게 좋다.&lt;/p&gt;
&lt;hr data-end=&quot;7685&quot; data-start=&quot;7682&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7712&quot; data-start=&quot;7687&quot; data-section-id=&quot;17eutw1&quot; data-ke-size=&quot;size26&quot;&gt;14. E-R 설계에서 자주 생기는 고민&lt;/h2&gt;
&lt;p data-end=&quot;7773&quot; data-start=&quot;7714&quot; data-ke-size=&quot;size16&quot;&gt;이 부분은 진짜 중요하다.&lt;br /&gt;ER 모델은 &amp;ldquo;정답 하나&amp;rdquo;가 아니라 &lt;b&gt;설계 선택의 문제&lt;/b&gt;가 많이 나온다.&lt;/p&gt;
&lt;hr data-end=&quot;7778&quot; data-start=&quot;7775&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7805&quot; data-start=&quot;7780&quot; data-section-id=&quot;6wdpwq&quot; data-ke-size=&quot;size23&quot;&gt;1) 속성으로 넣을까, 개체로 만들까?&lt;/h3&gt;
&lt;p data-end=&quot;7816&quot; data-start=&quot;7806&quot; data-ke-size=&quot;size16&quot;&gt;예: 교수 전화번호&lt;/p&gt;
&lt;p data-end=&quot;7829&quot; data-start=&quot;7818&quot; data-ke-size=&quot;size16&quot;&gt;선택지는 여러 개다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7896&quot; data-start=&quot;7831&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7853&quot; data-start=&quot;7831&quot; data-section-id=&quot;duu60w&quot;&gt;단일 속성 phone_number&lt;/li&gt;
&lt;li data-end=&quot;7877&quot; data-start=&quot;7854&quot; data-section-id=&quot;vlce37&quot;&gt;다중값 속성 phone_number&lt;/li&gt;
&lt;li data-end=&quot;7896&quot; data-start=&quot;7878&quot; data-section-id=&quot;az3kw4&quot;&gt;별도 phone 개체 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7923&quot; data-start=&quot;7898&quot; data-ke-size=&quot;size16&quot;&gt;어떤 구조가 맞는지는 요구사항에 따라 다르다.&lt;/p&gt;
&lt;p data-end=&quot;7927&quot; data-start=&quot;7925&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7977&quot; data-start=&quot;7928&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7949&quot; data-start=&quot;7928&quot; data-section-id=&quot;1npkijy&quot;&gt;전화번호 하나만 필요 &amp;rarr; 단일 속성&lt;/li&gt;
&lt;li data-end=&quot;7977&quot; data-start=&quot;7950&quot; data-section-id=&quot;1eraqtp&quot;&gt;여러 개 가능 &amp;rarr; 다중값 속성 or 별도 개체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8002&quot; data-start=&quot;7979&quot; data-ke-size=&quot;size16&quot;&gt;즉, 현실 세계 규칙을 보고 정해야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;8007&quot; data-start=&quot;8004&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8034&quot; data-start=&quot;8009&quot; data-section-id=&quot;1u6et5e&quot; data-ke-size=&quot;size23&quot;&gt;2) 관계로 표현할까, 개체로 만들까?&lt;/h3&gt;
&lt;p data-end=&quot;8051&quot; data-start=&quot;8035&quot; data-ke-size=&quot;size16&quot;&gt;예: 학생과 분반의 수강 관계&lt;/p&gt;
&lt;p data-end=&quot;8100&quot; data-start=&quot;8053&quot; data-ke-size=&quot;size16&quot;&gt;단순히 수강 여부만 저장하면 takes 관계로 충분할 수 있다.&lt;br /&gt;하지만 여기에&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8136&quot; data-start=&quot;8101&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8109&quot; data-start=&quot;8101&quot; data-section-id=&quot;12ek06d&quot;&gt;재수강 여부&lt;/li&gt;
&lt;li data-end=&quot;8117&quot; data-start=&quot;8110&quot; data-section-id=&quot;1aj637d&quot;&gt;등록 상태&lt;/li&gt;
&lt;li data-end=&quot;8125&quot; data-start=&quot;8118&quot; data-section-id=&quot;c18g2h&quot;&gt;결제 상태&lt;/li&gt;
&lt;li data-end=&quot;8136&quot; data-start=&quot;8126&quot; data-section-id=&quot;1dxi6o&quot;&gt;수강 신청 시간&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8210&quot; data-start=&quot;8138&quot; data-ke-size=&quot;size16&quot;&gt;같은 정보가 많이 붙으면&lt;br /&gt;단순 관계보다는 &lt;b&gt;registration 같은 별도 개체&lt;/b&gt;로 분리하는 게 더 자연스러울 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;8215&quot; data-start=&quot;8212&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8245&quot; data-start=&quot;8217&quot; data-section-id=&quot;pitc8l&quot; data-ke-size=&quot;size23&quot;&gt;3) 이진 관계로 할까, n진 관계로 할까?&lt;/h3&gt;
&lt;p data-end=&quot;8310&quot; data-start=&quot;8246&quot; data-ke-size=&quot;size16&quot;&gt;세 개 이상의 개체가 하나의 사실을 이루는 경우는&lt;br /&gt;단순한 이진 관계 몇 개보다 n진 관계가 더 정확할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;8314&quot; data-start=&quot;8312&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8350&quot; data-start=&quot;8315&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8333&quot; data-start=&quot;8315&quot; data-section-id=&quot;xtpej4&quot;&gt;부품 &amp;mdash; 공급업체 &amp;mdash; 프로젝트&lt;/li&gt;
&lt;li data-end=&quot;8350&quot; data-start=&quot;8334&quot; data-section-id=&quot;5jcdzt&quot;&gt;학생 &amp;mdash; 교수 &amp;mdash; 프로젝트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8375&quot; data-start=&quot;8352&quot; data-ke-size=&quot;size16&quot;&gt;이건 셋이 동시에 묶이는 의미가 중요하다.&lt;/p&gt;
&lt;hr data-end=&quot;8380&quot; data-start=&quot;8377&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8403&quot; data-start=&quot;8382&quot; data-section-id=&quot;uaoev0&quot; data-ke-size=&quot;size23&quot;&gt;4) 관계 속성을 어디에 둘까?&lt;/h3&gt;
&lt;p data-end=&quot;8450&quot; data-start=&quot;8404&quot; data-ke-size=&quot;size16&quot;&gt;일대다 관계에서는 관계의 설명 속성을 &amp;ldquo;다&amp;rdquo; 쪽 개체에 옮길 수 있는 경우가 있다.&lt;/p&gt;
&lt;p data-end=&quot;8454&quot; data-start=&quot;8452&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8477&quot; data-start=&quot;8455&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8477&quot; data-start=&quot;8455&quot; data-section-id=&quot;8mkhck&quot;&gt;advisor 관계의 date&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8538&quot; data-start=&quot;8479&quot; data-ke-size=&quot;size16&quot;&gt;학생이 지도교수를 정확히 한 명만 가진다면&lt;br /&gt;그 날짜를 학생 쪽 속성으로 옮겨도 의미가 유지될 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;8572&quot; data-start=&quot;8540&quot; data-ke-size=&quot;size16&quot;&gt;하지만 다대다 관계에서는 이런 이동이 어려운 경우가 많다.&lt;/p&gt;
&lt;hr data-end=&quot;8577&quot; data-start=&quot;8574&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8609&quot; data-start=&quot;8579&quot; data-section-id=&quot;1nom55r&quot; data-ke-size=&quot;size23&quot;&gt;5) n진 관계를 이진 관계로 쪼갤 수 있을까?&lt;/h3&gt;
&lt;p data-end=&quot;8637&quot; data-start=&quot;8610&quot; data-ke-size=&quot;size16&quot;&gt;가능한 경우도 있지만, 의미가 달라질 수도 있다.&lt;/p&gt;
&lt;p data-end=&quot;8659&quot; data-start=&quot;8639&quot; data-ke-size=&quot;size16&quot;&gt;필요하면 새로운 개체 E를 만들고&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8683&quot; data-start=&quot;8660&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8667&quot; data-start=&quot;8660&quot; data-section-id=&quot;1682mf5&quot;&gt;E-A&lt;/li&gt;
&lt;li data-end=&quot;8675&quot; data-start=&quot;8668&quot; data-section-id=&quot;1682mle&quot;&gt;E-B&lt;/li&gt;
&lt;li data-end=&quot;8683&quot; data-start=&quot;8676&quot; data-section-id=&quot;1682mkj&quot;&gt;E-C&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8705&quot; data-start=&quot;8685&quot; data-ke-size=&quot;size16&quot;&gt;형태의 이진 관계로 변환할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;8731&quot; data-start=&quot;8707&quot; data-ke-size=&quot;size16&quot;&gt;그리고 원래 관계의 속성은 E에 넣는다.&lt;/p&gt;
&lt;p data-end=&quot;8776&quot; data-start=&quot;8733&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이건 단순 변환이 아니라&lt;br /&gt;원래 의미가 유지되는지 꼭 따져봐야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;8781&quot; data-start=&quot;8778&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;8795&quot; data-start=&quot;8783&quot; data-section-id=&quot;v1i69r&quot; data-ke-size=&quot;size26&quot;&gt;15. 핵심 요약&lt;/h2&gt;
&lt;p data-end=&quot;8818&quot; data-start=&quot;8797&quot; data-ke-size=&quot;size16&quot;&gt;이번 장 핵심만 다시 정리하면 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;9227&quot; data-start=&quot;8820&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8881&quot; data-start=&quot;8820&quot; data-section-id=&quot;ixxe0w&quot;&gt;데이터베이스 설계는 &lt;b&gt;요구사항 분석 &amp;rarr; 개념적 설계 &amp;rarr; 논리적 설계 &amp;rarr; 물리적 설계&lt;/b&gt; 순서로 진행된다.&lt;/li&gt;
&lt;li data-end=&quot;8928&quot; data-start=&quot;8882&quot; data-section-id=&quot;1irub2t&quot;&gt;E-R 모델은 현실 세계의 데이터 구조를 개체와 관계 중심으로 표현하는 도구다.&lt;/li&gt;
&lt;li data-end=&quot;8977&quot; data-start=&quot;8929&quot; data-section-id=&quot;1tq3yn6&quot;&gt;&lt;b&gt;개체, 관계, 속성, 키, 카디널리티, 참여 제약&lt;/b&gt;은 ERD의 핵심 요소다.&lt;/li&gt;
&lt;li data-end=&quot;9030&quot; data-start=&quot;8978&quot; data-section-id=&quot;yzqvon&quot;&gt;속성은 &lt;b&gt;단순 / 복합 / 단일값 / 다중값 / 유도 속성&lt;/b&gt;으로 나눠 이해할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;9079&quot; data-start=&quot;9031&quot; data-section-id=&quot;1s36xsi&quot;&gt;약한 개체는 다른 개체에 의존해서 식별되며, 식별 개체의 키를 함께 사용해야 한다.&lt;/li&gt;
&lt;li data-end=&quot;9139&quot; data-start=&quot;9080&quot; data-section-id=&quot;xmwa8c&quot;&gt;ERD를 관계형 스키마로 변환할 때는 개체는 테이블로, 관계는 외래키 또는 별도 릴레이션으로 변환한다.&lt;/li&gt;
&lt;li data-end=&quot;9174&quot; data-start=&quot;9140&quot; data-section-id=&quot;11n9763&quot;&gt;다중값 속성은 별도 릴레이션으로 분리하는 것이 일반적이다.&lt;/li&gt;
&lt;li data-end=&quot;9227&quot; data-start=&quot;9175&quot; data-section-id=&quot;dhiczy&quot;&gt;설계에서는 &amp;ldquo;속성으로 둘지 / 개체로 분리할지 / 관계로 표현할지&amp;rdquo; 같은 선택이 중요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;9232&quot; data-start=&quot;9229&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;9240&quot; data-start=&quot;9234&quot; data-section-id=&quot;1h9nj85&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-end=&quot;9308&quot; data-start=&quot;9242&quot; data-ke-size=&quot;size16&quot;&gt;6장은 &lt;b&gt;&amp;ldquo;좋은 테이블 구조를 만들기 위한 사고방식&amp;rdquo;&lt;/b&gt; 을 배우는 장이라고 보면 된다.&lt;/p&gt;
&lt;p data-end=&quot;9359&quot; data-start=&quot;9310&quot; data-ke-size=&quot;size16&quot;&gt;지금까지는 이미 만들어진 테이블에 질의하는 느낌이었다면, ER 모델부터는 이제 반대로&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;9404&quot; data-start=&quot;9361&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;9374&quot; data-start=&quot;9361&quot; data-section-id=&quot;vflysc&quot;&gt;어떤 개체가 필요한지&lt;/li&gt;
&lt;li data-end=&quot;9388&quot; data-start=&quot;9375&quot; data-section-id=&quot;1yhn07h&quot;&gt;어떻게 관계를 맺을지&lt;/li&gt;
&lt;li data-end=&quot;9404&quot; data-start=&quot;9389&quot; data-section-id=&quot;12ys04o&quot;&gt;어떤 속성을 어디에 둘지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;9419&quot; data-start=&quot;9406&quot; data-ke-size=&quot;size16&quot;&gt;를 먼저 고민하게 된다.&lt;/p&gt;
&lt;p data-end=&quot;9608&quot; data-start=&quot;9551&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS STUDY/DB</category>
      <category>DB설계</category>
      <category>ERD</category>
      <category>ER모델</category>
      <category>개체관계모델</category>
      <category>관계형스키마</category>
      <category>데이터베이스설계</category>
      <category>데이터베이스정리</category>
      <category>약한개체</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/24</guid>
      <comments>https://pixxlix.tistory.com/24#entry24comment</comments>
      <pubDate>Thu, 12 Mar 2026 02:57:22 +0900</pubDate>
    </item>
    <item>
      <title>[데이터베이스 시스템 7판 5장] JDBC, 트리거, 재귀 질의, 윈도우 함수까지 | 데이터베이스 고급 SQL 한 번에 정리</title>
      <link>https://pixxlix.tistory.com/23</link>
      <description>&lt;h2 data-end=&quot;516&quot; data-start=&quot;487&quot; data-section-id=&quot;1tx5dk9&quot; data-ke-size=&quot;size26&quot;&gt;1. 왜 범용 프로그래밍 언어로 DB에 접근할까&lt;/h2&gt;
&lt;p data-end=&quot;586&quot; data-start=&quot;518&quot; data-ke-size=&quot;size16&quot;&gt;SQL은 데이터베이스를 다루는 데 아주 강력한 언어지만,&lt;br /&gt;현실에서는 &lt;b&gt;SQL만으로 모든 작업을 처리할 수는 없다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;619&quot; data-start=&quot;588&quot; data-ke-size=&quot;size16&quot;&gt;왜냐면 실제 서비스에서는 이런 작업도 필요하기 때문이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;708&quot; data-start=&quot;621&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;632&quot; data-start=&quot;621&quot; data-section-id=&quot;5cm6xi&quot;&gt;사용자 입력 받기&lt;/li&gt;
&lt;li data-end=&quot;643&quot; data-start=&quot;633&quot; data-section-id=&quot;1p9lql9&quot;&gt;버튼 클릭 처리&lt;/li&gt;
&lt;li data-end=&quot;652&quot; data-start=&quot;644&quot; data-section-id=&quot;r447uh&quot;&gt;보고서 출력&lt;/li&gt;
&lt;li data-end=&quot;670&quot; data-start=&quot;653&quot; data-section-id=&quot;14upyvv&quot;&gt;GUI 화면에 결과 보여주기&lt;/li&gt;
&lt;li data-end=&quot;693&quot; data-start=&quot;671&quot; data-section-id=&quot;1hluxpa&quot;&gt;조건에 따라 SQL을 동적으로 바꾸기&lt;/li&gt;
&lt;li data-end=&quot;708&quot; data-start=&quot;694&quot; data-section-id=&quot;s4uiad&quot;&gt;외부 시스템과 통신하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;795&quot; data-start=&quot;710&quot; data-ke-size=&quot;size16&quot;&gt;즉, SQL은 &lt;b&gt;데이터를 다루는 언어&lt;/b&gt;이고&lt;br /&gt;Java, Python, C 같은 범용 프로그래밍 언어는 &lt;b&gt;프로그램 전체 흐름을 제어하는 언어&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;812&quot; data-start=&quot;797&quot; data-ke-size=&quot;size16&quot;&gt;그래서 실제 개발에서는 보통&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;856&quot; data-start=&quot;814&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;836&quot; data-start=&quot;814&quot; data-section-id=&quot;1sdjdps&quot;&gt;프로그램 언어가 전체 로직을 담당하고&lt;/li&gt;
&lt;li data-end=&quot;856&quot; data-start=&quot;837&quot; data-section-id=&quot;jalmlx&quot;&gt;SQL이 데이터 조회/수정 담당&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;873&quot; data-start=&quot;858&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 역할을 나눠서 쓴다.&lt;/p&gt;
&lt;hr data-end=&quot;878&quot; data-start=&quot;875&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;913&quot; data-start=&quot;880&quot; data-section-id=&quot;19su09k&quot; data-ke-size=&quot;size26&quot;&gt;2. SQL 접근 방식: 동적 SQL vs 내장 SQL&lt;/h2&gt;
&lt;p data-end=&quot;944&quot; data-start=&quot;915&quot; data-ke-size=&quot;size16&quot;&gt;DB에 접근하는 방식은 크게 두 가지로 볼 수 있다.&lt;/p&gt;
&lt;h3 data-end=&quot;959&quot; data-start=&quot;946&quot; data-section-id=&quot;8145m0&quot; data-ke-size=&quot;size23&quot;&gt;1) 동적 SQL&lt;/h3&gt;
&lt;p data-end=&quot;995&quot; data-start=&quot;960&quot; data-ke-size=&quot;size16&quot;&gt;프로그램 실행 중에 SQL문을 만들어서 DB에 보내는 방식이다.&lt;/p&gt;
&lt;p data-end=&quot;999&quot; data-start=&quot;997&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1013&quot; data-start=&quot;1000&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1006&quot; data-start=&quot;1000&quot; data-section-id=&quot;1j3wrdz&quot;&gt;JDBC&lt;/li&gt;
&lt;li data-end=&quot;1013&quot; data-start=&quot;1007&quot; data-section-id=&quot;1j3xchu&quot;&gt;ODBC&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1035&quot; data-start=&quot;1015&quot; data-ke-size=&quot;size16&quot;&gt;즉, 실행 시점에 SQL이 결정된다.&lt;/p&gt;
&lt;p data-end=&quot;1080&quot; data-start=&quot;1037&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 검색 조건이 사용자 입력에 따라 달라진다면 동적 SQL이 필요하다.&lt;/p&gt;
&lt;hr data-end=&quot;1085&quot; data-start=&quot;1082&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1115&quot; data-start=&quot;1087&quot; data-section-id=&quot;1y31whv&quot; data-ke-size=&quot;size23&quot;&gt;2) 내장 SQL (Embedded SQL)&lt;/h3&gt;
&lt;p data-end=&quot;1145&quot; data-start=&quot;1116&quot; data-ke-size=&quot;size16&quot;&gt;프로그램 코드 안에 SQL을 직접 포함하는 방식이다.&lt;/p&gt;
&lt;p data-end=&quot;1149&quot; data-start=&quot;1147&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;EXEC&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SQL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; ...;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1225&quot; data-start=&quot;1183&quot; data-ke-size=&quot;size16&quot;&gt;이 방식은 전처리기가 SQL을 일반 함수 호출 코드로 변환한 뒤 컴파일한다.&lt;/p&gt;
&lt;p data-end=&quot;1229&quot; data-start=&quot;1227&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1273&quot; data-start=&quot;1230&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1253&quot; data-start=&quot;1230&quot; data-section-id=&quot;nmkqzq&quot;&gt;SQL이 코드 안에 고정적으로 들어가고&lt;/li&gt;
&lt;li data-end=&quot;1273&quot; data-start=&quot;1254&quot; data-section-id=&quot;rryg11&quot;&gt;컴파일 전에 변환 과정을 거친다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1278&quot; data-start=&quot;1275&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1303&quot; data-start=&quot;1280&quot; data-section-id=&quot;1vcthkm&quot; data-ke-size=&quot;size23&quot;&gt;동적 SQL vs 내장 SQL 차이&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;구분&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 동적 SQL&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 내장 SQL
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1474&quot; data-start=&quot;1305&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;1474&quot; data-start=&quot;1344&quot;&gt;
&lt;tr data-end=&quot;1378&quot; data-start=&quot;1344&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1356&quot; data-start=&quot;1344&quot;&gt;SQL 생성 시점&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1363&quot; data-start=&quot;1356&quot;&gt;실행 시&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1378&quot; data-start=&quot;1363&quot;&gt;컴파일 전/전처리 시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1396&quot; data-start=&quot;1379&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1385&quot; data-start=&quot;1379&quot;&gt;유연성&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1390&quot; data-start=&quot;1385&quot;&gt;높음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1396&quot; data-start=&quot;1390&quot;&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1434&quot; data-start=&quot;1397&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1405&quot; data-start=&quot;1397&quot;&gt;대표 예시&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1418&quot; data-start=&quot;1405&quot;&gt;JDBC, ODBC&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1434&quot; data-start=&quot;1418&quot;&gt;Embedded SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1474&quot; data-start=&quot;1435&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1440&quot; data-start=&quot;1435&quot;&gt;장점&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1458&quot; data-start=&quot;1440&quot;&gt;상황에 따라 쿼리 변경 가능&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1474&quot; data-start=&quot;1458&quot;&gt;고정 쿼리 처리에 유리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1523&quot; data-start=&quot;1476&quot; data-ke-size=&quot;size16&quot;&gt;요즘 실무에서는 보통 &lt;b&gt;JDBC 같은 동적 SQL 방식&lt;/b&gt;이 훨씬 더 많이 쓰인다.&lt;/p&gt;
&lt;hr data-end=&quot;1528&quot; data-start=&quot;1525&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1542&quot; data-start=&quot;1530&quot; data-section-id=&quot;1oliz0o&quot; data-ke-size=&quot;size26&quot;&gt;3. JDBC란?&lt;/h2&gt;
&lt;p data-end=&quot;1628&quot; data-start=&quot;1544&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JDBC(Java Database Connectivity)&lt;/b&gt; 는&lt;br /&gt;Java에서 데이터베이스에 접속하고 SQL을 실행할 수 있게 해주는 API다.&lt;/p&gt;
&lt;p data-end=&quot;1675&quot; data-start=&quot;1630&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말하면&lt;br /&gt;&lt;b&gt;Java &amp;harr; DB를 연결하는 표준 통로&lt;/b&gt;라고 생각하면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;1680&quot; data-start=&quot;1677&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1696&quot; data-start=&quot;1682&quot; data-section-id=&quot;1uu7s69&quot; data-ke-size=&quot;size23&quot;&gt;JDBC 기본 절차&lt;/h3&gt;
&lt;p data-end=&quot;1720&quot; data-start=&quot;1698&quot; data-ke-size=&quot;size16&quot;&gt;JDBC 코드는 보통 이 흐름으로 간다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;1774&quot; data-start=&quot;1722&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;1730&quot; data-start=&quot;1722&quot; data-section-id=&quot;1vbze4c&quot;&gt;DB 연결&lt;/li&gt;
&lt;li data-end=&quot;1746&quot; data-start=&quot;1731&quot; data-section-id=&quot;u3lxcv&quot;&gt;SQL 실행 객체 생성&lt;/li&gt;
&lt;li data-end=&quot;1756&quot; data-start=&quot;1747&quot; data-section-id=&quot;bee9mj&quot;&gt;SQL 실행&lt;/li&gt;
&lt;li data-end=&quot;1765&quot; data-start=&quot;1757&quot; data-section-id=&quot;17bcsdj&quot;&gt;결과 처리&lt;/li&gt;
&lt;li data-end=&quot;1774&quot; data-start=&quot;1766&quot; data-section-id=&quot;1bfmexz&quot;&gt;자원 정리&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;1779&quot; data-start=&quot;1776&quot; data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre id=&quot;code_1773250841783&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Connection conn = DriverManager.getConnection(url, user, pass);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(
&quot;SELECT dept_name, avg(salary) FROM instructor GROUP BY dept_name&quot;
);

while (rs.next()) {
System.out.println(rs.getString(&quot;dept_name&quot;) + &quot; &quot; + rs.getFloat(2));
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2107&quot; data-start=&quot;2104&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2123&quot; data-start=&quot;2109&quot; data-section-id=&quot;18ruqmy&quot; data-ke-size=&quot;size23&quot;&gt;각 객체 역할 정리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2223&quot; data-start=&quot;2125&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2156&quot; data-start=&quot;2125&quot; data-section-id=&quot;2nytnr&quot;&gt;&lt;b&gt;Connection&lt;/b&gt;&lt;br /&gt;DB와 연결된 통로&lt;/li&gt;
&lt;li data-end=&quot;2189&quot; data-start=&quot;2158&quot; data-section-id=&quot;h8j3ss&quot;&gt;&lt;b&gt;Statement&lt;/b&gt;&lt;br /&gt;SQL문 실행용 객체&lt;/li&gt;
&lt;li data-end=&quot;2223&quot; data-start=&quot;2191&quot; data-section-id=&quot;35s0ss&quot;&gt;&lt;b&gt;ResultSet&lt;/b&gt;&lt;br /&gt;조회 결과를 담는 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결&amp;gt; 실행&amp;gt; 결과 읽는 구조&lt;/p&gt;
&lt;hr data-end=&quot;2260&quot; data-start=&quot;2257&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2276&quot; data-start=&quot;2262&quot; data-section-id=&quot;1gxwmod&quot; data-ke-size=&quot;size23&quot;&gt;NULL 처리 주의&lt;/h3&gt;
&lt;p data-end=&quot;2318&quot; data-start=&quot;2278&quot; data-ke-size=&quot;size16&quot;&gt;JDBC에서 숫자 컬럼을 읽을 때는 NULL 처리가 헷갈릴 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;2322&quot; data-start=&quot;2320&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1773250885039&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int a = rs.getInt(&quot;a&quot;);
if (rs.wasNull()) {
// NULL 처리
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2492&quot; data-start=&quot;2397&quot; data-ke-size=&quot;size16&quot;&gt;왜냐면 getInt()는 NULL이어도 자바 기본형 int를 반환해야 해서&lt;br /&gt;0처럼 보일 수 있기 때문이다.&lt;br /&gt;그래서 wasNull()로 꼭 확인해야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;2553&quot; data-start=&quot;2550&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2586&quot; data-start=&quot;2555&quot; data-section-id=&quot;i2ea5u&quot; data-ke-size=&quot;size26&quot;&gt;4. PreparedStatement가 중요한 이유&lt;/h2&gt;
&lt;p data-end=&quot;2620&quot; data-start=&quot;2588&quot; data-ke-size=&quot;size16&quot;&gt;SQL을 문자열 이어붙이기로 만들면 편해 보여도 위험하다.&lt;/p&gt;
&lt;p data-end=&quot;2624&quot; data-start=&quot;2622&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;&quot;SELECT * FROM instructor WHERE name='&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;'&quot;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2733&quot; data-start=&quot;2692&quot; data-ke-size=&quot;size16&quot;&gt;이런 방식은 사용자가 이상한 입력을 넣으면 SQL 자체가 변형될 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;2738&quot; data-start=&quot;2735&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2760&quot; data-start=&quot;2740&quot; data-section-id=&quot;5o81o4&quot; data-ke-size=&quot;size23&quot;&gt;SQL Injection이란?&lt;/h3&gt;
&lt;p data-end=&quot;2791&quot; data-start=&quot;2762&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자가 이름에 이렇게 입력했다고 해보자.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;X' OR 'Y'='Y&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2865&quot; data-start=&quot;2819&quot; data-ke-size=&quot;size16&quot;&gt;그러면 원래 의도했던 쿼리가 아니라&lt;br /&gt;항상 참이 되는 SQL이 만들어질 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;2873&quot; data-start=&quot;2867&quot; data-ke-size=&quot;size16&quot;&gt;결과적으로:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2905&quot; data-start=&quot;2874&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2882&quot; data-start=&quot;2874&quot; data-section-id=&quot;6qacq0&quot;&gt;로그인 우회&lt;/li&gt;
&lt;li data-end=&quot;2894&quot; data-start=&quot;2883&quot; data-section-id=&quot;wuorpk&quot;&gt;전체 데이터 노출&lt;/li&gt;
&lt;li data-end=&quot;2905&quot; data-start=&quot;2895&quot; data-section-id=&quot;15o0ac6&quot;&gt;권한 없는 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2922&quot; data-start=&quot;2907&quot; data-ke-size=&quot;size16&quot;&gt;같은 문제가 생길 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;2958&quot; data-start=&quot;2924&quot; data-ke-size=&quot;size16&quot;&gt;이게 바로 &lt;b&gt;SQL Injection Attack&lt;/b&gt; 이다.&lt;/p&gt;
&lt;hr data-end=&quot;2963&quot; data-start=&quot;2960&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2991&quot; data-start=&quot;2965&quot; data-section-id=&quot;1m2vvgd&quot; data-ke-size=&quot;size23&quot;&gt;해결책: PreparedStatement&lt;/h3&gt;
&lt;p data-end=&quot;3051&quot; data-start=&quot;2993&quot; data-ke-size=&quot;size16&quot;&gt;PreparedStatement는 SQL 구조를 먼저 고정하고,&lt;br /&gt;값만 나중에 바인딩하는 방식이다.&lt;/p&gt;
&lt;p data-end=&quot;3055&quot; data-start=&quot;3053&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre id=&quot;code_1773250895728&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PreparedStatement ps =
conn.prepareStatement(&quot;INSERT INTO instructor VALUES (?,?,?,?)&quot;);

ps.setString(1, &quot;88877&quot;);
ps.setString(2, &quot;Perry&quot;);
ps.setString(3, &quot;Finance&quot;);
ps.setInt(4, 125000);
ps.executeUpdate();&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;3292&quot; data-start=&quot;3286&quot; data-section-id=&quot;1hrqpoz&quot; data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3355&quot; data-start=&quot;3293&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3315&quot; data-start=&quot;3293&quot; data-section-id=&quot;135ixtk&quot;&gt;&lt;b&gt;SQL Injection 방지&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3343&quot; data-start=&quot;3316&quot; data-section-id=&quot;161gqgz&quot;&gt;같은 형태의 SQL 반복 실행 시 성능상 이점&lt;/li&gt;
&lt;li data-end=&quot;3355&quot; data-start=&quot;3344&quot; data-section-id=&quot;1rjjjjk&quot;&gt;코드 가독성 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3423&quot; data-start=&quot;3357&quot; data-ke-size=&quot;size16&quot;&gt;실무에서는 사용자 입력이 들어가는 SQL은 거의 무조건&lt;br /&gt;PreparedStatement를 쓴다고 생각하면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;3428&quot; data-start=&quot;3425&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3463&quot; data-start=&quot;3430&quot; data-section-id=&quot;1ql4kmc&quot; data-ke-size=&quot;size23&quot;&gt;보안은 PreparedStatement만으로 끝일까?&lt;/h3&gt;
&lt;p data-end=&quot;3482&quot; data-start=&quot;3465&quot; data-ke-size=&quot;size16&quot;&gt;아니다. 이것도 같이 중요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3533&quot; data-start=&quot;3484&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3494&quot; data-start=&quot;3484&quot; data-section-id=&quot;1pmbeox&quot;&gt;최소 권한 원칙&lt;/li&gt;
&lt;li data-end=&quot;3503&quot; data-start=&quot;3495&quot; data-section-id=&quot;13g3tl3&quot;&gt;입력값 검증&lt;/li&gt;
&lt;li data-end=&quot;3519&quot; data-start=&quot;3504&quot; data-section-id=&quot;1e70mql&quot;&gt;화이트리스트 기반 필터링&lt;/li&gt;
&lt;li data-end=&quot;3533&quot; data-start=&quot;3520&quot; data-section-id=&quot;4yxf1j&quot;&gt;DB 계정 권한 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3588&quot; data-start=&quot;3535&quot; data-ke-size=&quot;size16&quot;&gt;즉, PreparedStatement는 핵심 방어 수단이지만&lt;br /&gt;보안 전체의 전부는 아니다.&lt;/p&gt;
&lt;hr data-end=&quot;3593&quot; data-start=&quot;3590&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3618&quot; data-start=&quot;3595&quot; data-section-id=&quot;2055cd&quot; data-ke-size=&quot;size26&quot;&gt;5. 메타데이터(Metadata)란?&lt;/h2&gt;
&lt;p data-end=&quot;3650&quot; data-start=&quot;3620&quot; data-ke-size=&quot;size16&quot;&gt;메타데이터는 말 그대로 &lt;b&gt;데이터에 대한 데이터&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;3654&quot; data-start=&quot;3652&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3694&quot; data-start=&quot;3655&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3663&quot; data-start=&quot;3655&quot; data-section-id=&quot;1w38zpw&quot;&gt;테이블 이름&lt;/li&gt;
&lt;li data-end=&quot;3671&quot; data-start=&quot;3664&quot; data-section-id=&quot;wkitew&quot;&gt;컬럼 이름&lt;/li&gt;
&lt;li data-end=&quot;3676&quot; data-start=&quot;3672&quot; data-section-id=&quot;yiizu5&quot;&gt;타입&lt;/li&gt;
&lt;li data-end=&quot;3685&quot; data-start=&quot;3677&quot; data-section-id=&quot;cg2mzp&quot;&gt;기본키 정보&lt;/li&gt;
&lt;li data-end=&quot;3694&quot; data-start=&quot;3686&quot; data-section-id=&quot;1rnwhuk&quot;&gt;스키마 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3708&quot; data-start=&quot;3696&quot; data-ke-size=&quot;size16&quot;&gt;같은 정보를 의미한다.&lt;/p&gt;
&lt;p data-end=&quot;3739&quot; data-start=&quot;3710&quot; data-ke-size=&quot;size16&quot;&gt;이건 단순히 &amp;ldquo;보기용&amp;rdquo;이 아니라 실제로 꽤 유용하다.&lt;/p&gt;
&lt;h3 data-end=&quot;3749&quot; data-start=&quot;3741&quot; data-section-id=&quot;oass63&quot; data-ke-size=&quot;size23&quot;&gt;활용 예&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3814&quot; data-start=&quot;3750&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3760&quot; data-start=&quot;3750&quot; data-section-id=&quot;1vxx7nw&quot;&gt;동적 쿼리 생성&lt;/li&gt;
&lt;li data-end=&quot;3769&quot; data-start=&quot;3761&quot; data-section-id=&quot;1d5mru9&quot;&gt;자동 문서화&lt;/li&gt;
&lt;li data-end=&quot;3786&quot; data-start=&quot;3770&quot; data-section-id=&quot;hgxy11&quot;&gt;마이그레이션 스크립트 작성&lt;/li&gt;
&lt;li data-end=&quot;3797&quot; data-start=&quot;3787&quot; data-section-id=&quot;h4ghb6&quot;&gt;DB 구조 점검&lt;/li&gt;
&lt;li data-end=&quot;3814&quot; data-start=&quot;3798&quot; data-section-id=&quot;12xuqh5&quot;&gt;컬럼 기반 UI 자동 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;3819&quot; data-start=&quot;3816&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3842&quot; data-start=&quot;3821&quot; data-section-id=&quot;1e591nk&quot; data-ke-size=&quot;size23&quot;&gt;ResultSetMetaData&lt;/h3&gt;
&lt;p data-end=&quot;3867&quot; data-start=&quot;3844&quot; data-ke-size=&quot;size16&quot;&gt;조회 결과의 컬럼 정보를 확인할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;3871&quot; data-start=&quot;3869&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;ResultSetMetaData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rsmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getMetaData&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rsmd&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getColumnCount&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rsmd&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getColumnName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rsmd&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getColumnTypeName&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;br /&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4121&quot; data-start=&quot;4080&quot; data-ke-size=&quot;size16&quot;&gt;이건 &amp;ldquo;지금 조회된 결과가 어떤 컬럼들로 구성돼 있는지&amp;rdquo;를 보는 데 쓴다.&lt;/p&gt;
&lt;hr data-end=&quot;4126&quot; data-start=&quot;4123&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4148&quot; data-start=&quot;4128&quot; data-section-id=&quot;xz23vy&quot; data-ke-size=&quot;size23&quot;&gt;DatabaseMetaData&lt;/h3&gt;
&lt;p data-end=&quot;4172&quot; data-start=&quot;4150&quot; data-ke-size=&quot;size16&quot;&gt;DB 전체 구조에 대한 정보를 가져온다.&lt;/p&gt;
&lt;p data-end=&quot;4176&quot; data-start=&quot;4174&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;DatabaseMetaData&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dbmd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;conn&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getMetaData&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span&gt;ResultSet&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dbmd&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getTables&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;%&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;[]{&lt;/span&gt;&lt;span&gt;&quot;TABLE&quot;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;br /&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;rs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;()) {&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;System&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;rs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;TABLE_NAME&quot;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;br /&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4400&quot; data-start=&quot;4376&quot; data-ke-size=&quot;size16&quot;&gt;즉, 스키마 수준의 정보 확인에 더 가깝다.&lt;/p&gt;
&lt;hr data-end=&quot;4405&quot; data-start=&quot;4402&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4429&quot; data-start=&quot;4407&quot; data-section-id=&quot;1teuizz&quot; data-ke-size=&quot;size26&quot;&gt;6. JDBC에서 트랜잭션 제어하기&lt;/h2&gt;
&lt;p data-end=&quot;4498&quot; data-start=&quot;4431&quot; data-ke-size=&quot;size16&quot;&gt;PostgreSQL은 기본적으로 &lt;b&gt;auto commit&lt;/b&gt; 상태다.&lt;br /&gt;즉, SQL 하나 실행할 때마다 바로 반영된다.&lt;/p&gt;
&lt;p data-end=&quot;4549&quot; data-start=&quot;4500&quot; data-ke-size=&quot;size16&quot;&gt;그런데 여러 작업을 하나의 묶음으로 처리해야 할 때는&lt;br /&gt;직접 트랜잭션을 제어해야 한다.&lt;/p&gt;
&lt;p data-end=&quot;4553&quot; data-start=&quot;4551&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;conn&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setAutoCommit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;// SQL 작업들 수행&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;conn&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;br /&gt;&lt;span&gt;conn&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;rollback&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;4654&quot; data-start=&quot;4643&quot; data-section-id=&quot;16jfcjd&quot; data-ke-size=&quot;size23&quot;&gt;왜 필요할까?&lt;/h3&gt;
&lt;p data-end=&quot;4676&quot; data-start=&quot;4655&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 계좌 이체는 보통 2단계다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;4706&quot; data-start=&quot;4678&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;4692&quot; data-start=&quot;4678&quot; data-section-id=&quot;1iwttu7&quot;&gt;A 계좌에서 돈 차감&lt;/li&gt;
&lt;li data-end=&quot;4706&quot; data-start=&quot;4693&quot; data-section-id=&quot;189lt92&quot;&gt;B 계좌에 돈 증가&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;4776&quot; data-start=&quot;4708&quot; data-ke-size=&quot;size16&quot;&gt;이때 중간에 오류가 나면 둘 중 하나만 반영되면 안 된다.&lt;br /&gt;그래서 &lt;b&gt;둘 다 성공하거나, 둘 다 취소&lt;/b&gt;되어야 한다.&lt;/p&gt;
&lt;p data-end=&quot;4795&quot; data-start=&quot;4778&quot; data-ke-size=&quot;size16&quot;&gt;그걸 보장하는 게 트랜잭션이다.&lt;/p&gt;
&lt;hr data-end=&quot;4800&quot; data-start=&quot;4797&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4816&quot; data-start=&quot;4802&quot; data-section-id=&quot;115pnhk&quot; data-ke-size=&quot;size26&quot;&gt;7. 함수와 프로시저&lt;/h2&gt;
&lt;p data-end=&quot;4897&quot; data-start=&quot;4818&quot; data-ke-size=&quot;size16&quot;&gt;데이터베이스에도 반복해서 쓰는 로직을 저장해둘 수 있다.&lt;br /&gt;이게 &lt;b&gt;함수(Function)&lt;/b&gt; 와 &lt;b&gt;프로시저(Procedure)&lt;/b&gt; 다.&lt;/p&gt;
&lt;p data-end=&quot;4947&quot; data-start=&quot;4899&quot; data-ke-size=&quot;size16&quot;&gt;즉, 비즈니스 로직을 애플리케이션 코드에만 두지 않고&lt;br /&gt;DB 내부에도 넣을 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;4952&quot; data-start=&quot;4949&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4974&quot; data-start=&quot;4954&quot; data-section-id=&quot;1ah1t5&quot; data-ke-size=&quot;size23&quot;&gt;왜 DB 안에 로직을 넣을까?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5064&quot; data-start=&quot;4976&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4998&quot; data-start=&quot;4976&quot; data-section-id=&quot;1scwagw&quot;&gt;여러 프로그램이 같은 로직 공유 가능&lt;/li&gt;
&lt;li data-end=&quot;5009&quot; data-start=&quot;4999&quot; data-section-id=&quot;109uke0&quot;&gt;중복 코드 감소&lt;/li&gt;
&lt;li data-end=&quot;5038&quot; data-start=&quot;5010&quot; data-section-id=&quot;cmghl5&quot;&gt;로직 변경 시 애플리케이션 전체 수정 필요 감소&lt;/li&gt;
&lt;li data-end=&quot;5064&quot; data-start=&quot;5039&quot; data-section-id=&quot;1v0u6vs&quot;&gt;데이터 가까이에서 실행돼 효율적일 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;5069&quot; data-start=&quot;5066&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5088&quot; data-start=&quot;5071&quot; data-section-id=&quot;1ln1jrf&quot; data-ke-size=&quot;size23&quot;&gt;함수 vs 프로시저 차이&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;구분&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;함수(Function)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 프로시저(Procedure)
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;5271&quot; data-start=&quot;5090&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;5271&quot; data-start=&quot;5144&quot;&gt;
&lt;tr data-end=&quot;5171&quot; data-start=&quot;5144&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5150&quot; data-start=&quot;5144&quot;&gt;반환값&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5155&quot; data-start=&quot;5150&quot;&gt;있음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5171&quot; data-start=&quot;5155&quot;&gt;보통 직접 반환 안 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5207&quot; data-start=&quot;5172&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5180&quot; data-start=&quot;5172&quot;&gt;사용 위치&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5198&quot; data-start=&quot;5180&quot;&gt;SQL 식 안에서 사용 가능&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5207&quot; data-start=&quot;5198&quot;&gt;독립 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5239&quot; data-start=&quot;5208&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5213&quot; data-start=&quot;5208&quot;&gt;용도&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5224&quot; data-start=&quot;5213&quot;&gt;계산 결과 반환&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5239&quot; data-start=&quot;5224&quot;&gt;여러 단계 작업 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;5271&quot; data-start=&quot;5240&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5250&quot; data-start=&quot;5240&quot;&gt;트랜잭션 제어&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5256&quot; data-start=&quot;5250&quot;&gt;제한적&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5271&quot; data-start=&quot;5256&quot;&gt;DBMS에 따라 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;5276&quot; data-start=&quot;5273&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5298&quot; data-start=&quot;5278&quot; data-section-id=&quot;zccwkk&quot; data-ke-size=&quot;size23&quot;&gt;PostgreSQL 함수 예시&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1773250925931&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create function function_name(param_list)
returns return_type
language plpgsql
as $$
declare
begin
-- 로직
end;
$$;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;5433&quot; data-start=&quot;5430&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5445&quot; data-start=&quot;5435&quot; data-section-id=&quot;1xpu7ui&quot; data-ke-size=&quot;size23&quot;&gt;테이블 함수&lt;/h3&gt;
&lt;p data-end=&quot;5466&quot; data-start=&quot;5446&quot; data-ke-size=&quot;size16&quot;&gt;테이블 자체를 반환하는 함수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1773250934441&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create function function_name(param_list)
returns table (column_list)
language plpgsql
as $$
begin
end;
$$;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5611&quot; data-start=&quot;5588&quot; data-ke-size=&quot;size16&quot;&gt;이건 결과를 하나의 릴레이션처럼 반환한다.&lt;/p&gt;
&lt;hr data-end=&quot;5616&quot; data-start=&quot;5613&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5640&quot; data-start=&quot;5618&quot; data-section-id=&quot;42ba1c&quot; data-ke-size=&quot;size23&quot;&gt;PostgreSQL 프로시저 예시&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1773250942346&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create procedure procedure_name(parameter_list)
language plpgsql
as $$
declare
begin
end;
$$;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5810&quot; data-start=&quot;5748&quot; data-ke-size=&quot;size16&quot;&gt;PostgreSQL 11+부터 프로시저를 지원하고,&lt;br /&gt;프로시저는 함수보다 &lt;b&gt;트랜잭션 제어&lt;/b&gt;에 더 적합하다.&lt;/p&gt;
&lt;hr data-end=&quot;5815&quot; data-start=&quot;5812&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5829&quot; data-start=&quot;5817&quot; data-section-id=&quot;10ztm0e&quot; data-ke-size=&quot;size23&quot;&gt;외부 언어 루틴&lt;/h3&gt;
&lt;p data-end=&quot;5841&quot; data-start=&quot;5830&quot; data-ke-size=&quot;size16&quot;&gt;plpgsql 말고도&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5886&quot; data-start=&quot;5842&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5845&quot; data-start=&quot;5842&quot; data-section-id=&quot;374357&quot;&gt;C&lt;/li&gt;
&lt;li data-end=&quot;5852&quot; data-start=&quot;5846&quot; data-section-id=&quot;1j3vxn8&quot;&gt;Java&lt;/li&gt;
&lt;li data-end=&quot;5886&quot; data-start=&quot;5853&quot; data-section-id=&quot;1q01wh1&quot;&gt;C++&lt;br /&gt;같은 외부 언어로 함수/프로시저를 만들 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;5910&quot; data-start=&quot;5888&quot; data-ke-size=&quot;size16&quot;&gt;이건 강력하지만 보안상 위험할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;5942&quot; data-start=&quot;5912&quot; data-ke-size=&quot;size16&quot;&gt;특히 C는 메모리 접근이 자유로워서 더 조심해야 한다.&lt;/p&gt;
&lt;h3 data-end=&quot;5953&quot; data-start=&quot;5944&quot; data-section-id=&quot;1qkdwbj&quot; data-ke-size=&quot;size23&quot;&gt;보안 대책&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5993&quot; data-start=&quot;5954&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5963&quot; data-start=&quot;5954&quot; data-section-id=&quot;qi2gqs&quot;&gt;샌드박스 실행&lt;/li&gt;
&lt;li data-end=&quot;5978&quot; data-start=&quot;5964&quot; data-section-id=&quot;q834il&quot;&gt;메모리 안전 언어 사용&lt;/li&gt;
&lt;li data-end=&quot;5993&quot; data-start=&quot;5979&quot; data-section-id=&quot;1bzdutp&quot;&gt;별도 프로세스에서 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;6022&quot; data-start=&quot;5995&quot; data-ke-size=&quot;size16&quot;&gt;대신 이런 방식은 성능 오버헤드가 생길 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;6027&quot; data-start=&quot;6024&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;6049&quot; data-start=&quot;6029&quot; data-section-id=&quot;n69y9d&quot; data-ke-size=&quot;size26&quot;&gt;8. 트리거(Trigger)란?&lt;/h2&gt;
&lt;p data-end=&quot;6094&quot; data-start=&quot;6051&quot; data-ke-size=&quot;size16&quot;&gt;트리거는 &lt;b&gt;DB에서 특정 사건이 발생했을 때 자동으로 실행되는 작업&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;6098&quot; data-start=&quot;6096&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6131&quot; data-start=&quot;6099&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6109&quot; data-start=&quot;6099&quot; data-section-id=&quot;dorilb&quot;&gt;INSERT&lt;/li&gt;
&lt;li data-end=&quot;6120&quot; data-start=&quot;6110&quot; data-section-id=&quot;q60uyh&quot;&gt;UPDATE&lt;/li&gt;
&lt;li data-end=&quot;6131&quot; data-start=&quot;6121&quot; data-section-id=&quot;o4m3pt&quot;&gt;DELETE&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;6156&quot; data-start=&quot;6133&quot; data-ke-size=&quot;size16&quot;&gt;같은 이벤트가 일어나면 자동으로 실행된다.&lt;/p&gt;
&lt;p data-end=&quot;6177&quot; data-start=&quot;6158&quot; data-ke-size=&quot;size16&quot;&gt;트리거는 보통 두 가지를 정의한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;6282&quot; data-start=&quot;6179&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;6216&quot; data-start=&quot;6179&quot; data-section-id=&quot;133d626&quot;&gt;&lt;b&gt;언제 실행할지&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6216&quot; data-start=&quot;6197&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6205&quot; data-start=&quot;6197&quot; data-section-id=&quot;1wbba9d&quot;&gt;BEFORE&lt;/li&gt;
&lt;li data-end=&quot;6216&quot; data-start=&quot;6209&quot; data-section-id=&quot;16y7j0s&quot;&gt;AFTER&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;6282&quot; data-start=&quot;6218&quot; data-section-id=&quot;1ht44ni&quot;&gt;&lt;b&gt;무슨 동작을 할지&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6282&quot; data-start=&quot;6238&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6245&quot; data-start=&quot;6238&quot; data-section-id=&quot;4r51u1&quot;&gt;로그 저장&lt;/li&gt;
&lt;li data-end=&quot;6258&quot; data-start=&quot;6249&quot; data-section-id=&quot;soz19a&quot;&gt;값 자동 수정&lt;/li&gt;
&lt;li data-end=&quot;6271&quot; data-start=&quot;6262&quot; data-section-id=&quot;aodtrx&quot;&gt;제약조건 보완&lt;/li&gt;
&lt;li data-end=&quot;6282&quot; data-start=&quot;6275&quot; data-section-id=&quot;1ueasb1&quot;&gt;연쇄 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;6287&quot; data-start=&quot;6284&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6304&quot; data-start=&quot;6289&quot; data-section-id=&quot;26bsir&quot; data-ke-size=&quot;size23&quot;&gt;트리거가 유용한 이유&lt;/h3&gt;
&lt;p data-end=&quot;6349&quot; data-start=&quot;6305&quot; data-ke-size=&quot;size16&quot;&gt;트리거는 단순 자동화가 아니라&lt;br /&gt;&lt;b&gt;데이터 무결성 보조 장치&lt;/b&gt; 역할도 한다.&lt;/p&gt;
&lt;p data-end=&quot;6414&quot; data-start=&quot;6351&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 잘못된 입력을 자동으로 바꾸거나,&lt;br /&gt;변경 이력을 남기거나,&lt;br /&gt;관련 데이터 갱신을 자동화할 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;6419&quot; data-start=&quot;6416&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6451&quot; data-start=&quot;6421&quot; data-section-id=&quot;1ku26pe&quot; data-ke-size=&quot;size23&quot;&gt;예시 1: 빈 문자열을 NULL로 바꾸는 트리거&lt;/h3&gt;
&lt;pre id=&quot;code_1773250805014&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;create trigger setnull_trigger before update of takes
referencing new row as nrow
for each row
when (nrow.grade = '')
begin atomic
set nrow.grade = null;
end;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6661&quot; data-start=&quot;6627&quot; data-ke-size=&quot;size16&quot;&gt;이건 성적이 빈 문자열로 갱신되면 자동으로 NULL로 바꾼다.&lt;/p&gt;
&lt;hr data-end=&quot;6666&quot; data-start=&quot;6663&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6687&quot; data-start=&quot;6668&quot; data-section-id=&quot;1m1aqwa&quot; data-ke-size=&quot;size23&quot;&gt;예시 2: 감사 로그 남기기&lt;/h3&gt;
&lt;p data-end=&quot;6747&quot; data-start=&quot;6689&quot; data-ke-size=&quot;size16&quot;&gt;실습 예시처럼 last_name 변경 전 값을 로그 테이블에 저장하는 것도 대표적인 트리거 활용이다.&lt;/p&gt;
&lt;p data-end=&quot;6756&quot; data-start=&quot;6749&quot; data-ke-size=&quot;size16&quot;&gt;트리거 함수:&lt;/p&gt;
&lt;pre id=&quot;code_1773250795422&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE OR REPLACE FUNCTION log_last_name_changes()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
IF NEW.last_name &amp;lt;&amp;gt; OLD.last_name THEN
INSERT INTO employee_audits(employee_id, last_name, changed_on)
VALUES(OLD.id, OLD.last_name, now());
END IF;

RETURN NEW;
END;
$$;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7088&quot; data-start=&quot;7051&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 UPDATE가 일어날 때마다 이전 성이 자동 저장된다.&lt;/p&gt;
&lt;hr data-end=&quot;7093&quot; data-start=&quot;7090&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7108&quot; data-start=&quot;7095&quot; data-section-id=&quot;thhyj6&quot; data-ke-size=&quot;size23&quot;&gt;OLD / NEW&lt;/h3&gt;
&lt;p data-end=&quot;7130&quot; data-start=&quot;7109&quot; data-ke-size=&quot;size16&quot;&gt;트리거에서 자주 나오는 핵심 개념이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7167&quot; data-start=&quot;7132&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7149&quot; data-start=&quot;7132&quot; data-section-id=&quot;1c0x8lh&quot;&gt;&lt;b&gt;OLD&lt;/b&gt;: 변경 전 행&lt;/li&gt;
&lt;li data-end=&quot;7167&quot; data-start=&quot;7150&quot; data-section-id=&quot;go39xa&quot;&gt;&lt;b&gt;NEW&lt;/b&gt;: 변경 후 행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7195&quot; data-start=&quot;7169&quot; data-ke-size=&quot;size16&quot;&gt;즉, UPDATE 트리거에서는 둘 다 중요하다.&lt;/p&gt;
&lt;hr data-end=&quot;7200&quot; data-start=&quot;7197&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7217&quot; data-start=&quot;7202&quot; data-section-id=&quot;doob1b&quot; data-ke-size=&quot;size23&quot;&gt;트리거 사용 시 주의&lt;/h3&gt;
&lt;p data-end=&quot;7235&quot; data-start=&quot;7218&quot; data-ke-size=&quot;size16&quot;&gt;트리거는 편리하지만 과하게 쓰면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7278&quot; data-start=&quot;7237&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7258&quot; data-start=&quot;7237&quot; data-section-id=&quot;1vj0zx0&quot;&gt;로직 흐름이 숨겨져서 디버깅 어려움&lt;/li&gt;
&lt;li data-end=&quot;7266&quot; data-start=&quot;7259&quot; data-section-id=&quot;wx0xyc&quot;&gt;성능 저하&lt;/li&gt;
&lt;li data-end=&quot;7278&quot; data-start=&quot;7267&quot; data-section-id=&quot;oyawri&quot;&gt;예상 못한 부작용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7290&quot; data-start=&quot;7280&quot; data-ke-size=&quot;size16&quot;&gt;이 생길 수 있다. 그래서 보통&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7327&quot; data-start=&quot;7299&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7306&quot; data-start=&quot;7299&quot; data-section-id=&quot;1vv80wg&quot;&gt;감사 로그&lt;/li&gt;
&lt;li data-end=&quot;7315&quot; data-start=&quot;7307&quot; data-section-id=&quot;1p5qizd&quot;&gt;무결성 보조&lt;/li&gt;
&lt;li data-end=&quot;7327&quot; data-start=&quot;7316&quot; data-section-id=&quot;100xskz&quot;&gt;자동 파생값 갱신&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7343&quot; data-start=&quot;7329&quot; data-ke-size=&quot;size16&quot;&gt;같은 곳에 신중하게 쓴다.&lt;/p&gt;
&lt;hr data-end=&quot;7416&quot; data-start=&quot;7413&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7448&quot; data-start=&quot;7418&quot; data-section-id=&quot;14yc9ls&quot; data-ke-size=&quot;size26&quot;&gt;9. 재귀 질의(Recursive Query)란?&lt;/h2&gt;
&lt;p data-end=&quot;7483&quot; data-start=&quot;7450&quot; data-ke-size=&quot;size16&quot;&gt;재귀 질의는 &lt;b&gt;계층 구조 데이터&lt;/b&gt;를 처리할 때 사용한다.&lt;/p&gt;
&lt;p data-end=&quot;7487&quot; data-start=&quot;7485&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7533&quot; data-start=&quot;7488&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7493&quot; data-start=&quot;7488&quot; data-section-id=&quot;2ix6nx&quot;&gt;조직도&lt;/li&gt;
&lt;li data-end=&quot;7501&quot; data-start=&quot;7494&quot; data-section-id=&quot;1f7tvin&quot;&gt;댓글 트리&lt;/li&gt;
&lt;li data-end=&quot;7512&quot; data-start=&quot;7502&quot; data-section-id=&quot;1rnkqj5&quot;&gt;부품 조립 구조&lt;/li&gt;
&lt;li data-end=&quot;7522&quot; data-start=&quot;7513&quot; data-section-id=&quot;1ok503l&quot;&gt;선수과목 관계&lt;/li&gt;
&lt;li data-end=&quot;7533&quot; data-start=&quot;7523&quot; data-section-id=&quot;lyufh0&quot;&gt;상사-부하 관계&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7598&quot; data-start=&quot;7535&quot; data-ke-size=&quot;size16&quot;&gt;이런 구조는 &amp;ldquo;부모를 찾고, 그 부모의 부모를 또 찾고...&amp;rdquo; 같은 방식이 필요하다.&lt;br /&gt;이때 재귀 질의를 쓴다.&lt;/p&gt;
&lt;hr data-end=&quot;7603&quot; data-start=&quot;7600&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7614&quot; data-start=&quot;7605&quot; data-section-id=&quot;17ow5kz&quot; data-ke-size=&quot;size23&quot;&gt;기본 문법&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;WITH&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RECURSIVE&lt;/span&gt;&lt;span&gt; cte_name &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;base query&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UNION&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ALL&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;recursive&lt;/span&gt;&lt;span&gt; query&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; cte_name;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7751&quot; data-start=&quot;7736&quot; data-ke-size=&quot;size16&quot;&gt;구조는 두 부분으로 나뉜다.&lt;/p&gt;
&lt;h3 data-end=&quot;7767&quot; data-start=&quot;7753&quot; data-section-id=&quot;1nywf2x&quot; data-ke-size=&quot;size23&quot;&gt;base query&lt;/h3&gt;
&lt;p data-end=&quot;7774&quot; data-start=&quot;7768&quot; data-ke-size=&quot;size16&quot;&gt;처음 시작점&lt;/p&gt;
&lt;h3 data-end=&quot;7795&quot; data-start=&quot;7776&quot; data-section-id=&quot;wuli7q&quot; data-ke-size=&quot;size23&quot;&gt;recursive query&lt;/h3&gt;
&lt;p data-end=&quot;7816&quot; data-start=&quot;7796&quot; data-ke-size=&quot;size16&quot;&gt;이전 결과를 이용해서 다음 단계 확장&lt;/p&gt;
&lt;p data-end=&quot;7838&quot; data-start=&quot;7818&quot; data-ke-size=&quot;size16&quot;&gt;더 이상 새 결과가 없으면 종료된다.&lt;/p&gt;
&lt;hr data-end=&quot;7843&quot; data-start=&quot;7840&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7857&quot; data-start=&quot;7845&quot; data-section-id=&quot;1ffkp8h&quot; data-ke-size=&quot;size23&quot;&gt;숫자 세기 예시&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;WITH&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RECURSIVE&lt;/span&gt;&lt;span&gt; cnt(n) &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UNION&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ALL&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; n &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; cnt&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; n &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;br /&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; n &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; cnt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8000&quot; data-start=&quot;7982&quot; data-ke-size=&quot;size16&quot;&gt;결과는 1, 2, 3 이 나온다.&lt;/p&gt;
&lt;p data-end=&quot;8032&quot; data-start=&quot;8002&quot; data-ke-size=&quot;size16&quot;&gt;이건 재귀 질의 구조를 가장 단순하게 보여주는 예시다.&lt;/p&gt;
&lt;hr data-end=&quot;8037&quot; data-start=&quot;8034&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8060&quot; data-start=&quot;8039&quot; data-section-id=&quot;1uw7wmn&quot; data-ke-size=&quot;size23&quot;&gt;조직도 / 부하 직원 찾기 예시&lt;/h3&gt;
&lt;pre id=&quot;code_1773250957536&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;WITH RECURSIVE subordinates AS (
SELECT employee_id, manager_id, full_name
FROM employees
WHERE employee_id = 2

UNION

SELECT e.employee_id, e.manager_id, e.full_name
FROM employees e
INNER JOIN subordinates s ON s.employee_id = e.manager_id
)
SELECT * FROM subordinates;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8398&quot; data-start=&quot;8361&quot; data-ke-size=&quot;size16&quot;&gt;이 쿼리는 특정 직원 아래의 모든 하위 직원들을 단계적으로 찾는다.&lt;/p&gt;
&lt;p data-end=&quot;8402&quot; data-start=&quot;8400&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8463&quot; data-start=&quot;8403&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8416&quot; data-start=&quot;8403&quot; data-section-id=&quot;10ok84b&quot;&gt;base: 시작 직원&lt;/li&gt;
&lt;li data-end=&quot;8438&quot; data-start=&quot;8417&quot; data-section-id=&quot;gzsrxd&quot;&gt;recursive: 그 직원의 부하&lt;/li&gt;
&lt;li data-end=&quot;8452&quot; data-start=&quot;8439&quot; data-section-id=&quot;1v1cbc0&quot;&gt;그다음은 부하의 부하&lt;/li&gt;
&lt;li data-end=&quot;8463&quot; data-start=&quot;8453&quot; data-section-id=&quot;1d19daz&quot;&gt;더 없으면 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;8468&quot; data-start=&quot;8465&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8487&quot; data-start=&quot;8470&quot; data-section-id=&quot;qtasle&quot; data-ke-size=&quot;size23&quot;&gt;재귀 질의에서 주의할 점&lt;/h3&gt;
&lt;p data-end=&quot;8530&quot; data-start=&quot;8488&quot; data-ke-size=&quot;size16&quot;&gt;사이클이 있으면 무한 반복 문제가 생길 수 있다.&lt;br /&gt;그래서 실제 실무에서는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8558&quot; data-start=&quot;8532&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8539&quot; data-start=&quot;8532&quot; data-section-id=&quot;1rfh7c0&quot;&gt;중복 제거&lt;/li&gt;
&lt;li data-end=&quot;8550&quot; data-start=&quot;8540&quot; data-section-id=&quot;1vto2n4&quot;&gt;방문 여부 추적&lt;/li&gt;
&lt;li data-end=&quot;8558&quot; data-start=&quot;8551&quot; data-section-id=&quot;7o788m&quot;&gt;깊이 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8574&quot; data-start=&quot;8560&quot; data-ke-size=&quot;size16&quot;&gt;같은 장치를 넣기도 한다.&lt;/p&gt;
&lt;hr data-end=&quot;8579&quot; data-start=&quot;8576&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;8601&quot; data-start=&quot;8581&quot; data-section-id=&quot;15xbuq6&quot; data-ke-size=&quot;size26&quot;&gt;10. 순위 함수와 윈도우 함수&lt;/h2&gt;
&lt;p data-end=&quot;8667&quot; data-start=&quot;8603&quot; data-ke-size=&quot;size16&quot;&gt;이번 파트는 SQL에서 꽤 재밌는 부분이다.&lt;br /&gt;왜냐면 &lt;b&gt;집계처럼 계산하면서도 행을 유지할 수 있기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-end=&quot;8672&quot; data-start=&quot;8669&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8691&quot; data-start=&quot;8674&quot; data-section-id=&quot;143oz3a&quot; data-ke-size=&quot;size23&quot;&gt;먼저, 기존 방식의 한계&lt;/h3&gt;
&lt;p data-end=&quot;8724&quot; data-start=&quot;8692&quot; data-ke-size=&quot;size16&quot;&gt;예전 방식으로 순위를 구하려면 보통 서브쿼리를 써야 했다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt; ID, (&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; student_grades B&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;where&lt;/span&gt;&lt;span&gt; B.GPA &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; A.GPA)) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; s_rank&lt;/span&gt;&lt;br /&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; student_grades A&lt;/span&gt;&lt;br /&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; s_rank;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8940&quot; data-start=&quot;8898&quot; data-ke-size=&quot;size16&quot;&gt;이 방식도 맞긴 한데, 학생마다 서브쿼리가 한 번씩 실행되니까 비효율적이다.&lt;/p&gt;
&lt;hr data-end=&quot;8945&quot; data-start=&quot;8942&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8966&quot; data-start=&quot;8947&quot; data-section-id=&quot;cwqx99&quot; data-ke-size=&quot;size23&quot;&gt;rank() 윈도우 함수&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt; ID, rank() over (&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; GPA &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; s_rank&lt;/span&gt;&lt;br /&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; student_grades&lt;/span&gt;&lt;br /&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; s_rank;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9092&quot; data-start=&quot;9070&quot; data-ke-size=&quot;size16&quot;&gt;이제 DB가 자동으로 순위를 계산해준다.&lt;/p&gt;
&lt;hr data-end=&quot;9097&quot; data-start=&quot;9094&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;9117&quot; data-start=&quot;9099&quot; data-section-id=&quot;1exa3jb&quot; data-ke-size=&quot;size23&quot;&gt;OVER가 의미하는 것&lt;/h3&gt;
&lt;p data-end=&quot;9148&quot; data-start=&quot;9118&quot; data-ke-size=&quot;size16&quot;&gt;윈도우 함수는 항상 OVER(...)와 함께 쓴다.&lt;/p&gt;
&lt;p data-end=&quot;9152&quot; data-start=&quot;9150&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;rank() over (&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; GPA &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9248&quot; data-start=&quot;9197&quot; data-ke-size=&quot;size16&quot;&gt;여기서 OVER는 &amp;ldquo;어떤 기준으로 계산할지&amp;rdquo;를 정하는 창(window) 역할을 한다.&lt;/p&gt;
&lt;hr data-end=&quot;9253&quot; data-start=&quot;9250&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;9273&quot; data-start=&quot;9255&quot; data-section-id=&quot;c0yhun&quot; data-ke-size=&quot;size23&quot;&gt;PARTITION BY&lt;/h3&gt;
&lt;p data-end=&quot;9305&quot; data-start=&quot;9274&quot; data-ke-size=&quot;size16&quot;&gt;이건 그룹을 나눠서 각 그룹 안에서 따로 계산하게 한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt; ID, dept_name,&lt;/span&gt;&lt;br /&gt;&lt;span&gt; rank() over (partition &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; GPA &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; dept_rank&lt;/span&gt;&lt;br /&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; dept_grades&lt;/span&gt;&lt;br /&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; dept_name, dept_rank;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9485&quot; data-start=&quot;9464&quot; data-ke-size=&quot;size16&quot;&gt;이 쿼리는 학과별 순위를 따로 구한다.&lt;/p&gt;
&lt;p data-end=&quot;9489&quot; data-start=&quot;9487&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;9537&quot; data-start=&quot;9490&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;9515&quot; data-start=&quot;9490&quot; data-section-id=&quot;1ggnpvr&quot;&gt;partition 안 하면 전체 대상 순위&lt;/li&gt;
&lt;li data-end=&quot;9537&quot; data-start=&quot;9516&quot; data-section-id=&quot;p6ex23&quot;&gt;partition 하면 그룹별 순위&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;9542&quot; data-start=&quot;9539&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;9556&quot; data-start=&quot;9544&quot; data-section-id=&quot;1ni0zie&quot; data-ke-size=&quot;size23&quot;&gt;주요 순위 함수&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;함수&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;의미
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;9731&quot; data-start=&quot;9558&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;9731&quot; data-start=&quot;9580&quot;&gt;
&lt;tr data-end=&quot;9613&quot; data-start=&quot;9580&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9591&quot; data-start=&quot;9580&quot;&gt;rank()&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9613&quot; data-start=&quot;9591&quot;&gt;공동순위 허용, 다음 순위 건너뜀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9647&quot; data-start=&quot;9614&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9631&quot; data-start=&quot;9614&quot;&gt;row_number()&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9647&quot; data-start=&quot;9631&quot;&gt;무조건 1,2,3...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9677&quot; data-start=&quot;9648&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9667&quot; data-start=&quot;9648&quot;&gt;percent_rank()&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9677&quot; data-start=&quot;9667&quot;&gt;백분위 순위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9703&quot; data-start=&quot;9678&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9694&quot; data-start=&quot;9678&quot;&gt;cume_dist()&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9703&quot; data-start=&quot;9694&quot;&gt;누적 분포&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9731&quot; data-start=&quot;9704&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9717&quot; data-start=&quot;9704&quot;&gt;ntile(n)&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9731&quot; data-start=&quot;9717&quot;&gt;n개 구간으로 나눔&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;9736&quot; data-start=&quot;9733&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;9768&quot; data-start=&quot;9738&quot; data-section-id=&quot;1rs1cyr&quot; data-ke-size=&quot;size23&quot;&gt;rank() vs row_number()&lt;/h3&gt;
&lt;p data-end=&quot;9780&quot; data-start=&quot;9769&quot; data-ke-size=&quot;size16&quot;&gt;이건 자주 비교된다.&lt;/p&gt;
&lt;p data-end=&quot;9791&quot; data-start=&quot;9782&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 점수가&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;9812&quot; data-start=&quot;9792&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;9797&quot; data-start=&quot;9792&quot; data-section-id=&quot;1o2h7d&quot;&gt;100&lt;/li&gt;
&lt;li data-end=&quot;9802&quot; data-start=&quot;9798&quot; data-section-id=&quot;yhmrgh&quot;&gt;90&lt;/li&gt;
&lt;li data-end=&quot;9807&quot; data-start=&quot;9803&quot; data-section-id=&quot;yhmrgh&quot;&gt;90&lt;/li&gt;
&lt;li data-end=&quot;9812&quot; data-start=&quot;9808&quot; data-section-id=&quot;yhmrhc&quot;&gt;80&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;9816&quot; data-start=&quot;9814&quot; data-ke-size=&quot;size16&quot;&gt;이면&lt;/p&gt;
&lt;p data-end=&quot;9830&quot; data-start=&quot;9818&quot; data-ke-size=&quot;size16&quot;&gt;rank() 결과:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;9846&quot; data-start=&quot;9831&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;9834&quot; data-start=&quot;9831&quot; data-section-id=&quot;37432x&quot;&gt;1&lt;/li&gt;
&lt;li data-end=&quot;9838&quot; data-start=&quot;9835&quot; data-section-id=&quot;37432y&quot;&gt;2&lt;/li&gt;
&lt;li data-end=&quot;9842&quot; data-start=&quot;9839&quot; data-section-id=&quot;37432y&quot;&gt;2&lt;/li&gt;
&lt;li data-end=&quot;9846&quot; data-start=&quot;9843&quot; data-section-id=&quot;374330&quot;&gt;4&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;9866&quot; data-start=&quot;9848&quot; data-ke-size=&quot;size16&quot;&gt;row_number() 결과:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;9882&quot; data-start=&quot;9867&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;9870&quot; data-start=&quot;9867&quot; data-section-id=&quot;37432x&quot;&gt;1&lt;/li&gt;
&lt;li data-end=&quot;9874&quot; data-start=&quot;9871&quot; data-section-id=&quot;37432y&quot;&gt;2&lt;/li&gt;
&lt;li data-end=&quot;9878&quot; data-start=&quot;9875&quot; data-section-id=&quot;37432z&quot;&gt;3&lt;/li&gt;
&lt;li data-end=&quot;9882&quot; data-start=&quot;9879&quot; data-section-id=&quot;374330&quot;&gt;4&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;9886&quot; data-start=&quot;9884&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;9944&quot; data-start=&quot;9887&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;9911&quot; data-start=&quot;9887&quot; data-section-id=&quot;4xam0w&quot;&gt;&lt;b&gt;공동순위 반영&lt;/b&gt; &amp;rarr; rank()&lt;/li&gt;
&lt;li data-end=&quot;9944&quot; data-start=&quot;9912&quot; data-section-id=&quot;s2d18k&quot;&gt;&lt;b&gt;그냥 번호 매기기&lt;/b&gt; &amp;rarr; row_number()&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;9949&quot; data-start=&quot;9946&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;9965&quot; data-start=&quot;9951&quot; data-section-id=&quot;1filk4z&quot; data-ke-size=&quot;size23&quot;&gt;ntile(n)&lt;/h3&gt;
&lt;p data-end=&quot;9982&quot; data-start=&quot;9966&quot; data-ke-size=&quot;size16&quot;&gt;전체를 n개 구간으로 나눈다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt; ID, ntile(&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;) over (&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; GPA &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; quartile&lt;/span&gt;&lt;br /&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; student_grades;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10097&quot; data-start=&quot;10074&quot; data-ke-size=&quot;size16&quot;&gt;이건 GPA 기준으로 학생들을 4등분한다.&lt;/p&gt;
&lt;p data-end=&quot;10118&quot; data-start=&quot;10099&quot; data-ke-size=&quot;size16&quot;&gt;즉, 사분위처럼 나눌 때 유용하다.&lt;/p&gt;
&lt;hr data-end=&quot;10123&quot; data-start=&quot;10120&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;10155&quot; data-start=&quot;10125&quot; data-section-id=&quot;1h5of80&quot; data-ke-size=&quot;size26&quot;&gt;11. 윈도우 함수의 진짜 핵심: 행을 안 합친다&lt;/h2&gt;
&lt;p data-end=&quot;10168&quot; data-start=&quot;10157&quot; data-ke-size=&quot;size16&quot;&gt;이 부분이 중요하다.&lt;/p&gt;
&lt;p data-end=&quot;10240&quot; data-start=&quot;10170&quot; data-ke-size=&quot;size16&quot;&gt;GROUP BY는 여러 행을 하나로 합쳐버린다.&lt;br /&gt;반면 윈도우 함수는 &lt;b&gt;원래 행을 유지한 채 계산 결과를 덧붙인다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;10252&quot; data-start=&quot;10242&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 이동합:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;, sum(&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;) over&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rows&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;between&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; preceding &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; following)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; sales;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10378&quot; data-start=&quot;10366&quot; data-ke-size=&quot;size16&quot;&gt;이건 현재 행 기준으로&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;10402&quot; data-start=&quot;10380&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;10387&quot; data-start=&quot;10380&quot; data-section-id=&quot;6auehs&quot;&gt;이전 1행&lt;/li&gt;
&lt;li data-end=&quot;10394&quot; data-start=&quot;10388&quot; data-section-id=&quot;au5quh&quot;&gt;현재 행&lt;/li&gt;
&lt;li data-end=&quot;10402&quot; data-start=&quot;10395&quot; data-section-id=&quot;1uzan20&quot;&gt;다음 1행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;10421&quot; data-start=&quot;10404&quot; data-ke-size=&quot;size16&quot;&gt;까지 포함해서 합계를 계산한다.&lt;/p&gt;
&lt;p data-end=&quot;10453&quot; data-start=&quot;10423&quot; data-ke-size=&quot;size16&quot;&gt;즉, 각 행마다 주변 데이터를 보고 계산하는 느낌이다.&lt;/p&gt;
&lt;hr data-end=&quot;10458&quot; data-start=&quot;10455&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;10477&quot; data-start=&quot;10460&quot; data-section-id=&quot;cmt1dx&quot; data-ke-size=&quot;size23&quot;&gt;ROWS vs RANGE&lt;/h3&gt;
&lt;h4 data-end=&quot;10490&quot; data-start=&quot;10479&quot; data-ke-size=&quot;size20&quot;&gt;ROWS&lt;/h4&gt;
&lt;p data-end=&quot;10510&quot; data-start=&quot;10491&quot; data-ke-size=&quot;size16&quot;&gt;행 개수를 기준으로 범위를 잡는다.&lt;/p&gt;
&lt;p data-end=&quot;10514&quot; data-start=&quot;10512&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;rows&lt;/span&gt;&lt;span&gt; unbounded preceding&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10565&quot; data-start=&quot;10552&quot; data-ke-size=&quot;size16&quot;&gt;처음 행부터 현재 행까지&lt;/p&gt;
&lt;hr data-end=&quot;10570&quot; data-start=&quot;10567&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;10584&quot; data-start=&quot;10572&quot; data-ke-size=&quot;size20&quot;&gt;RANGE&lt;/h4&gt;
&lt;p data-end=&quot;10600&quot; data-start=&quot;10585&quot; data-ke-size=&quot;size16&quot;&gt;값 범위를 기준으로 잡는다.&lt;/p&gt;
&lt;p data-end=&quot;10604&quot; data-start=&quot;10602&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;range &lt;/span&gt;&lt;span&gt;interval&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;'7 days'&lt;/span&gt;&lt;span&gt; preceding&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10671&quot; data-start=&quot;10651&quot; data-ke-size=&quot;size16&quot;&gt;현재 날짜 기준 7일 전부터 현재까지&lt;/p&gt;
&lt;p data-end=&quot;10675&quot; data-start=&quot;10673&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;10720&quot; data-start=&quot;10676&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;10697&quot; data-start=&quot;10676&quot; data-section-id=&quot;rentfo&quot;&gt;&lt;b&gt;ROWS&lt;/b&gt; = 물리적 행 기준&lt;/li&gt;
&lt;li data-end=&quot;10720&quot; data-start=&quot;10698&quot; data-section-id=&quot;33hy8&quot;&gt;&lt;b&gt;RANGE&lt;/b&gt; = 값의 범위 기준&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;10725&quot; data-start=&quot;10722&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;10739&quot; data-start=&quot;10727&quot; data-section-id=&quot;1exlwxu&quot; data-ke-size=&quot;size23&quot;&gt;누적 잔액 예시&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1773250731019&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;select account_number, date_time,
sum(value) over
(partition by account_number
order by date_time
rows unbounded preceding) as balance
from transaction
order by account_number, date_time;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;11030&quot; data-start=&quot;10977&quot; data-ke-size=&quot;size16&quot;&gt;이건 계좌별로 거래 내역을 시간순으로 보면서&lt;br /&gt;처음 거래부터 현재까지의 누적 잔액을 계산한다.&lt;/p&gt;
&lt;p data-end=&quot;11050&quot; data-start=&quot;11032&quot; data-ke-size=&quot;size16&quot;&gt;실무적으로도 꽤 직관적인 예시다.&lt;/p&gt;
&lt;hr data-end=&quot;11198&quot; data-start=&quot;11195&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;11228&quot; data-start=&quot;11200&quot; data-section-id=&quot;12o02m4&quot; data-ke-size=&quot;size26&quot;&gt;12. ODBC는 뭐고 JDBC랑 뭐가 다를까&lt;/h2&gt;
&lt;p data-end=&quot;11324&quot; data-start=&quot;11230&quot; data-ke-size=&quot;size16&quot;&gt;ODBC는 &lt;b&gt;Open Database Connectivity&lt;/b&gt; 의 약자다.&lt;br /&gt;DB 접근용 API라는 점에서는 JDBC와 비슷하지만, 더 범용적이고 오래된 표준이다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;구분&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;ODBC&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; JDBC
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;11457&quot; data-start=&quot;11326&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;11457&quot; data-start=&quot;11361&quot;&gt;
&lt;tr data-end=&quot;11390&quot; data-start=&quot;11361&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11366&quot; data-start=&quot;11361&quot;&gt;언어&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11379&quot; data-start=&quot;11366&quot;&gt;주로 C/C++ 등&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11390&quot; data-start=&quot;11379&quot;&gt;Java 전용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;11436&quot; data-start=&quot;11391&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11397&quot; data-start=&quot;11391&quot;&gt;플랫폼&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11416&quot; data-start=&quot;11397&quot;&gt;전통적으로 Windows 중심&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11436&quot; data-start=&quot;11416&quot;&gt;Java 기반이라 OS 독립적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;11457&quot; data-start=&quot;11437&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11443&quot; data-start=&quot;11437&quot;&gt;스타일&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11449&quot; data-start=&quot;11443&quot;&gt;절차적&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;11457&quot; data-start=&quot;11449&quot;&gt;객체지향&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;11487&quot; data-start=&quot;11459&quot; data-ke-size=&quot;size16&quot;&gt;지금은 Java에서는 거의 JDBC를 쓰는 편이다.&lt;/p&gt;
&lt;hr data-end=&quot;11492&quot; data-start=&quot;11489&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;11506&quot; data-start=&quot;11494&quot; data-section-id=&quot;1s696yh&quot; data-ke-size=&quot;size26&quot;&gt;13. 핵심 요약&lt;/h2&gt;
&lt;p data-end=&quot;11529&quot; data-start=&quot;11508&quot; data-ke-size=&quot;size16&quot;&gt;이번 고급 파트 핵심만 뽑으면 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;12020&quot; data-start=&quot;11531&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;11568&quot; data-start=&quot;11531&quot; data-section-id=&quot;azt3dy&quot;&gt;SQL만으로는 GUI 처리나 프로그램 제어까지 다 하긴 어렵다.&lt;/li&gt;
&lt;li data-end=&quot;11604&quot; data-start=&quot;11569&quot; data-section-id=&quot;1lcvf41&quot;&gt;그래서 Java 같은 범용 언어와 DB를 연결해서 사용한다.&lt;/li&gt;
&lt;li data-end=&quot;11641&quot; data-start=&quot;11605&quot; data-section-id=&quot;1bg55cq&quot;&gt;&lt;b&gt;JDBC&lt;/b&gt;는 Java에서 DB에 접근하는 표준 API다.&lt;/li&gt;
&lt;li data-end=&quot;11699&quot; data-start=&quot;11642&quot; data-section-id=&quot;b84587&quot;&gt;PreparedStatement는 &lt;b&gt;SQL Injection 방지&lt;/b&gt;와 성능 면에서 중요하다.&lt;/li&gt;
&lt;li data-end=&quot;11745&quot; data-start=&quot;11700&quot; data-section-id=&quot;1fkq70a&quot;&gt;메타데이터를 이용하면 테이블 구조나 결과 컬럼 정보를 동적으로 다룰 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;11803&quot; data-start=&quot;11746&quot; data-section-id=&quot;nrmsgx&quot;&gt;트랜잭션 제어는 auto commit과 commit/rollback 흐름으로 이해하면 된다.&lt;/li&gt;
&lt;li data-end=&quot;11841&quot; data-start=&quot;11804&quot; data-section-id=&quot;du66wo&quot;&gt;함수와 프로시저는 DB 내부에 비즈니스 로직을 저장하는 도구다.&lt;/li&gt;
&lt;li data-end=&quot;11874&quot; data-start=&quot;11842&quot; data-section-id=&quot;h9g9fu&quot;&gt;트리거는 특정 이벤트 발생 시 자동 실행되는 로직이다.&lt;/li&gt;
&lt;li data-end=&quot;11909&quot; data-start=&quot;11875&quot; data-section-id=&quot;4ywd9u&quot;&gt;재귀 질의는 계층 구조 데이터를 처리할 때 매우 유용하다.&lt;/li&gt;
&lt;li data-end=&quot;11959&quot; data-start=&quot;11910&quot; data-section-id=&quot;1ig1omv&quot;&gt;윈도우 함수는 &lt;b&gt;행을 유지하면서 계산&lt;/b&gt;한다는 점에서 GROUP BY와 다르다.&lt;/li&gt;
&lt;li data-end=&quot;12020&quot; data-start=&quot;11960&quot; data-section-id=&quot;1yqcln5&quot;&gt;rank(), row_number(), ntile() 같은 함수는 순위와 분석에 자주 쓰인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;12025&quot; data-start=&quot;12022&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;12033&quot; data-start=&quot;12027&quot; data-section-id=&quot;1h9nj85&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-end=&quot;12252&quot; data-start=&quot;12196&quot; data-ke-size=&quot;size16&quot;&gt;5장은&amp;nbsp;&lt;b&gt;DB를 실제 시스템 안에서 어떻게 활용하는지&lt;/b&gt;를 보여주는 장이라고 보면 된다.&lt;/p&gt;
&lt;p data-end=&quot;12275&quot; data-start=&quot;12254&quot; data-ke-size=&quot;size16&quot;&gt;개인적으로 시험에서 중요한 흐름은 이거다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;12392&quot; data-start=&quot;12277&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;12295&quot; data-start=&quot;12277&quot; data-section-id=&quot;kiykxv&quot;&gt;앱에서 DB 접근 &amp;rarr; JDBC&lt;/li&gt;
&lt;li data-end=&quot;12325&quot; data-start=&quot;12296&quot; data-section-id=&quot;vavcrz&quot;&gt;안전하게 실행 &amp;rarr; PreparedStatement&lt;/li&gt;
&lt;li data-end=&quot;12354&quot; data-start=&quot;12326&quot; data-section-id=&quot;1blrzj9&quot;&gt;DB 내부 로직 &amp;rarr; 함수 / 프로시저 / 트리거&lt;/li&gt;
&lt;li data-end=&quot;12374&quot; data-start=&quot;12355&quot; data-section-id=&quot;nvs67a&quot;&gt;복잡한 구조 처리 &amp;rarr; 재귀 질의&lt;/li&gt;
&lt;li data-end=&quot;12392&quot; data-start=&quot;12375&quot; data-section-id=&quot;15ypsoe&quot;&gt;분석형 계산 &amp;rarr; 윈도우 함수&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS STUDY/DB</category>
      <category>JDBC</category>
      <category>PostgreSQL</category>
      <category>PreparedStatement</category>
      <category>sqlInjection</category>
      <category>데이터베이스정리</category>
      <category>윈도우함수</category>
      <category>재귀질의</category>
      <category>트리거</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/23</guid>
      <comments>https://pixxlix.tistory.com/23#entry23comment</comments>
      <pubDate>Thu, 12 Mar 2026 02:43:04 +0900</pubDate>
    </item>
    <item>
      <title>[데이터베이스 시스템 7판 4장] SQL 조인, 뷰, 무결성 제약조건, 인덱스, 권한</title>
      <link>https://pixxlix.tistory.com/22</link>
      <description>&lt;h2 data-end=&quot;483&quot; data-start=&quot;470&quot; data-section-id=&quot;1uo6x2e&quot; data-ke-size=&quot;size26&quot;&gt;1. 조인 연산이란&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tmr1r/dJMcacWBq1v/M7l3MhZOHJ0PD01ShlwJn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tmr1r/dJMcacWBq1v/M7l3MhZOHJ0PD01ShlwJn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tmr1r/dJMcacWBq1v/M7l3MhZOHJ0PD01ShlwJn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTmr1r%2FdJMcacWBq1v%2FM7l3MhZOHJ0PD01ShlwJn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;474&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;538&quot; data-start=&quot;485&quot; data-ke-size=&quot;size16&quot;&gt;조인(join)은 &lt;b&gt;두 개의 릴레이션을 연결해서 하나의 결과 릴레이션으로 만드는 연산&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;625&quot; data-start=&quot;540&quot; data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스에서는 데이터가 보통 여러 테이블로 나뉘어 저장된다.&lt;br /&gt;그래서 실제로 원하는 정보를 얻으려면 테이블을 합쳐서 봐야 하는 경우가 많다.&lt;/p&gt;
&lt;p data-end=&quot;632&quot; data-start=&quot;627&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;655&quot; data-start=&quot;634&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;645&quot; data-start=&quot;634&quot; data-section-id=&quot;1thajtt&quot;&gt;student&lt;/li&gt;
&lt;li data-end=&quot;655&quot; data-start=&quot;646&quot; data-section-id=&quot;1l23dds&quot;&gt;takes&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;673&quot; data-start=&quot;657&quot; data-ke-size=&quot;size16&quot;&gt;이 두 테이블이 있다고 하면,&lt;/p&gt;
&lt;p data-end=&quot;708&quot; data-start=&quot;675&quot; data-ke-size=&quot;size16&quot;&gt;학생 이름과 수강한 과목을 함께 보고 싶을 때 조인을 쓴다.&lt;/p&gt;
&lt;p data-end=&quot;780&quot; data-start=&quot;710&quot; data-ke-size=&quot;size16&quot;&gt;조인은 카티션 곱과 비슷해 보이지만,&lt;br /&gt;아무 튜플이나 다 합치는 게 아니라 &lt;b&gt;특정 조건을 만족하는 튜플끼리만 결합&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-end=&quot;809&quot; data-start=&quot;782&quot; data-ke-size=&quot;size16&quot;&gt;실제 SQL에서는 보통 FROM 절에서 쓴다.&lt;/p&gt;
&lt;hr data-end=&quot;814&quot; data-start=&quot;811&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;831&quot; data-start=&quot;816&quot; data-section-id=&quot;kq7a0k&quot; data-ke-size=&quot;size26&quot;&gt;2. 조인 연산의 종류&lt;/h2&gt;
&lt;p data-end=&quot;855&quot; data-start=&quot;833&quot; data-ke-size=&quot;size16&quot;&gt;조인은 크게 다음처럼 나눠서 보면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;991&quot; data-start=&quot;857&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;882&quot; data-start=&quot;857&quot; data-section-id=&quot;2pjxn6&quot;&gt;&lt;b&gt;자연 조인(NATURAL JOIN)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;906&quot; data-start=&quot;883&quot; data-section-id=&quot;4muj7t&quot;&gt;&lt;b&gt;내부 조인(INNER JOIN)&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;991&quot; data-start=&quot;907&quot; data-section-id=&quot;1grxg6&quot;&gt;&lt;b&gt;외부 조인(OUTER JOIN)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;991&quot; data-start=&quot;933&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;950&quot; data-start=&quot;933&quot; data-section-id=&quot;1lgtsqw&quot;&gt;LEFT OUTER JOIN&lt;/li&gt;
&lt;li data-end=&quot;971&quot; data-start=&quot;953&quot; data-section-id=&quot;10s45xv&quot;&gt;RIGHT OUTER JOIN&lt;/li&gt;
&lt;li data-end=&quot;991&quot; data-start=&quot;974&quot; data-section-id=&quot;18z21fk&quot;&gt;FULL OUTER JOIN&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;996&quot; data-start=&quot;993&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1023&quot; data-start=&quot;998&quot; data-section-id=&quot;td4k8y&quot; data-ke-size=&quot;size26&quot;&gt;3. 자연 조인(NATURAL JOIN)&lt;/h2&gt;
&lt;p data-end=&quot;1079&quot; data-start=&quot;1025&quot; data-ke-size=&quot;size16&quot;&gt;자연 조인은 &lt;b&gt;두 릴레이션에서 이름이 같은 모든 속성&lt;/b&gt;을 기준으로 자동으로 매칭해서 조인한다.&lt;/p&gt;
&lt;p data-end=&quot;1126&quot; data-start=&quot;1081&quot; data-ke-size=&quot;size16&quot;&gt;즉, 공통 이름의 열이 있으면&lt;br /&gt;그 열의 값이 같은 튜플끼리 자동으로 합쳐진다.&lt;/p&gt;
&lt;h3 data-end=&quot;1134&quot; data-start=&quot;1128&quot; data-section-id=&quot;1hrlj9m&quot; data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1197&quot; data-start=&quot;1135&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1155&quot; data-start=&quot;1135&quot; data-section-id=&quot;161lv1z&quot;&gt;공통 속성을 자동으로 찾아서 조인&lt;/li&gt;
&lt;li data-end=&quot;1182&quot; data-start=&quot;1156&quot; data-section-id=&quot;1qtq3vg&quot;&gt;결과에서 공통 속성은 &lt;b&gt;한 번만 나타남&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1197&quot; data-start=&quot;1183&quot; data-section-id=&quot;jo2yg&quot;&gt;결과는 하나의 릴레이션&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1205&quot; data-start=&quot;1199&quot; data-ke-size=&quot;size16&quot;&gt;예를 들면:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; student &lt;/span&gt;&lt;span&gt;NATURAL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;JOIN&lt;/span&gt;&lt;span&gt; takes;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1298&quot; data-start=&quot;1261&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 두 테이블에서 이름이 같은 속성을 전부 기준으로 조인한다.&lt;/p&gt;
&lt;hr data-end=&quot;1303&quot; data-start=&quot;1300&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1318&quot; data-start=&quot;1305&quot; data-section-id=&quot;3i1ycj&quot; data-ke-size=&quot;size23&quot;&gt;자연 조인의 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1347&quot; data-start=&quot;1319&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1327&quot; data-start=&quot;1319&quot; data-section-id=&quot;12uz0oi&quot;&gt;문법이 짧다&lt;/li&gt;
&lt;li data-end=&quot;1347&quot; data-start=&quot;1328&quot; data-section-id=&quot;zbrpws&quot;&gt;공통 속성이 명확할 때는 편하다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;1363&quot; data-start=&quot;1349&quot; data-section-id=&quot;18ijezf&quot; data-ke-size=&quot;size23&quot;&gt;자연 조인의 위험성&lt;/h3&gt;
&lt;p data-end=&quot;1406&quot; data-start=&quot;1364&quot; data-ke-size=&quot;size16&quot;&gt;문제는 &lt;b&gt;이름이 같다고 해서 항상 같은 의미의 속성은 아니라는 점&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;1414&quot; data-start=&quot;1408&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1458&quot; data-start=&quot;1416&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1436&quot; data-start=&quot;1416&quot; data-section-id=&quot;1iqu10m&quot;&gt;course.dept_name&lt;/li&gt;
&lt;li data-end=&quot;1458&quot; data-start=&quot;1437&quot; data-section-id=&quot;1n4ver6&quot;&gt;student.dept_name&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1532&quot; data-start=&quot;1460&quot; data-ke-size=&quot;size16&quot;&gt;이 둘은 이름은 같아도 의미가 다를 수 있다.&lt;br /&gt;그런데 NATURAL JOIN을 쓰면 이 둘도 자동으로 매칭해버릴 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;1554&quot; data-start=&quot;1534&quot; data-ke-size=&quot;size16&quot;&gt;이게 자연 조인의 가장 큰 위험이다.&lt;/p&gt;
&lt;p data-end=&quot;1593&quot; data-start=&quot;1556&quot; data-ke-size=&quot;size16&quot;&gt;즉, 편하긴 한데 &lt;b&gt;의도치 않은 조인 조건이 숨어들 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;1598&quot; data-start=&quot;1595&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1622&quot; data-start=&quot;1600&quot; data-section-id=&quot;1qvc120&quot; data-ke-size=&quot;size26&quot;&gt;4. USING과 ON 차이&lt;/h2&gt;
&lt;p data-end=&quot;1670&quot; data-start=&quot;1624&quot; data-ke-size=&quot;size16&quot;&gt;자연 조인 대신 더 안전하게 조인하려면 USING 이나 ON 을 많이 쓴다.&lt;/p&gt;
&lt;h3 data-end=&quot;1683&quot; data-start=&quot;1672&quot; data-section-id=&quot;kjc3z4&quot; data-ke-size=&quot;size23&quot;&gt;USING&lt;/h3&gt;
&lt;p data-end=&quot;1744&quot; data-start=&quot;1684&quot; data-ke-size=&quot;size16&quot;&gt;두 릴레이션에 &lt;b&gt;이름이 같은 속성&lt;/b&gt;이 있을 때,&lt;br /&gt;그중 어떤 공통 속성으로 조인할지 명시하는 방식이다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; student &lt;/span&gt;&lt;span&gt;JOIN&lt;/span&gt;&lt;span&gt; takes &lt;/span&gt;&lt;span&gt;USING&lt;/span&gt;&lt;span&gt; (course_id);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;1816&quot; data-start=&quot;1810&quot; data-section-id=&quot;1hrlj9m&quot; data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1871&quot; data-start=&quot;1817&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1838&quot; data-start=&quot;1817&quot; data-section-id=&quot;1d0oqfl&quot;&gt;공통 속성 이름이 같아야 사용 가능&lt;/li&gt;
&lt;li data-end=&quot;1861&quot; data-start=&quot;1839&quot; data-section-id=&quot;cb91vo&quot;&gt;결과에서 공통 속성은 한 번만 표시됨&lt;/li&gt;
&lt;li data-end=&quot;1871&quot; data-start=&quot;1862&quot; data-section-id=&quot;49d2lx&quot;&gt;문법이 간단함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1876&quot; data-start=&quot;1873&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1886&quot; data-start=&quot;1878&quot; data-section-id=&quot;yo5jyf&quot; data-ke-size=&quot;size23&quot;&gt;ON&lt;/h3&gt;
&lt;p data-end=&quot;1905&quot; data-start=&quot;1887&quot; data-ke-size=&quot;size16&quot;&gt;조인 조건을 직접 적는 방식이다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; student &lt;/span&gt;&lt;span&gt;JOIN&lt;/span&gt;&lt;span&gt; takes&lt;/span&gt;&lt;br /&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; student.ID &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; takes.ID;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;1984&quot; data-start=&quot;1978&quot; data-section-id=&quot;1hrlj9m&quot; data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2043&quot; data-start=&quot;1985&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2003&quot; data-start=&quot;1985&quot; data-section-id=&quot;xvyoro&quot;&gt;속성 이름이 달라도 사용 가능&lt;/li&gt;
&lt;li data-end=&quot;2018&quot; data-start=&quot;2004&quot; data-section-id=&quot;1olps78&quot;&gt;가장 일반적이고 유연함&lt;/li&gt;
&lt;li data-end=&quot;2043&quot; data-start=&quot;2019&quot; data-section-id=&quot;1na7txg&quot;&gt;결과에서 양쪽 속성이 모두 남을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2048&quot; data-start=&quot;2045&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2077&quot; data-start=&quot;2050&quot; data-section-id=&quot;xazny1&quot; data-ke-size=&quot;size23&quot;&gt;USING vs ON 한 번에 정리&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;구분USINGON
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;2296&quot; data-start=&quot;2079&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;2296&quot; data-start=&quot;2113&quot;&gt;
&lt;tr data-end=&quot;2152&quot; data-start=&quot;2113&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2121&quot; data-start=&quot;2113&quot;&gt;사용 조건&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2139&quot; data-start=&quot;2121&quot;&gt;같은 이름 속성이 있어야 함&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2152&quot; data-start=&quot;2139&quot;&gt;이름 달라도 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2201&quot; data-start=&quot;2153&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2161&quot; data-start=&quot;2153&quot;&gt;조건 방식&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2179&quot; data-start=&quot;2161&quot;&gt;USING(column)&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2201&quot; data-start=&quot;2179&quot;&gt;ON A.col = B.col&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2244&quot; data-start=&quot;2202&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2210&quot; data-start=&quot;2202&quot;&gt;결과 컬럼&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2225&quot; data-start=&quot;2210&quot;&gt;공통 컬럼 1번만 표시&lt;/td&gt;
&lt;td data-end=&quot;2244&quot; data-start=&quot;2225&quot; data-col-size=&quot;sm&quot;&gt;양쪽 컬럼 다 남을 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2269&quot; data-start=&quot;2245&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2250&quot; data-start=&quot;2245&quot;&gt;장점&lt;/td&gt;
&lt;td data-end=&quot;2258&quot; data-start=&quot;2250&quot; data-col-size=&quot;sm&quot;&gt;짧고 깔끔&lt;/td&gt;
&lt;td data-end=&quot;2269&quot; data-start=&quot;2258&quot; data-col-size=&quot;sm&quot;&gt;유연하고 명확&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2296&quot; data-start=&quot;2270&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2278&quot; data-start=&quot;2270&quot;&gt;실무 사용&lt;/td&gt;
&lt;td data-end=&quot;2284&quot; data-start=&quot;2278&quot; data-col-size=&quot;sm&quot;&gt;제한적&lt;/td&gt;
&lt;td data-end=&quot;2296&quot; data-start=&quot;2284&quot; data-col-size=&quot;sm&quot;&gt;훨씬 많이 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2368&quot; data-start=&quot;2298&quot; data-ke-size=&quot;size16&quot;&gt;실제로는 &lt;b&gt;ON을 가장 많이 쓴다&lt;/b&gt;고 보면 된다.&lt;br /&gt;왜냐면 조건을 명시적으로 보여줘서 실수할 가능성이 적기 때문이다.&lt;/p&gt;
&lt;hr data-end=&quot;2373&quot; data-start=&quot;2370&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2398&quot; data-start=&quot;2375&quot; data-section-id=&quot;2ympen&quot; data-ke-size=&quot;size26&quot;&gt;5. 내부 조인(INNER JOIN)&lt;/h2&gt;
&lt;p data-end=&quot;2436&quot; data-start=&quot;2400&quot; data-ke-size=&quot;size16&quot;&gt;내부 조인은 &lt;b&gt;조건에 맞는 튜플만 결과에 포함&lt;/b&gt;하는 조인이다.&lt;/p&gt;
&lt;p data-end=&quot;2477&quot; data-start=&quot;2438&quot; data-ke-size=&quot;size16&quot;&gt;사실 JOIN 이라고만 쓰면 대부분 INNER JOIN 의미다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; student&lt;/span&gt;&lt;br /&gt;&lt;span&gt;JOIN&lt;/span&gt;&lt;span&gt; takes&lt;/span&gt;&lt;br /&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; student.ID &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; takes.ID;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2584&quot; data-start=&quot;2550&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 양쪽 테이블에서 ID가 일치하는 행만 결과로 나온다.&lt;/p&gt;
&lt;p data-end=&quot;2606&quot; data-start=&quot;2586&quot; data-ke-size=&quot;size16&quot;&gt;즉, 매칭되지 않는 튜플은 버려진다.&lt;/p&gt;
&lt;hr data-end=&quot;2611&quot; data-start=&quot;2608&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2636&quot; data-start=&quot;2613&quot; data-section-id=&quot;csw1cn&quot; data-ke-size=&quot;size26&quot;&gt;6. 외부 조인(OUTER JOIN)&lt;/h2&gt;
&lt;p data-end=&quot;2701&quot; data-start=&quot;2638&quot; data-ke-size=&quot;size16&quot;&gt;외부 조인은 &lt;b&gt;매칭되지 않는 튜플도 결과에 보존&lt;/b&gt;하는 조인이다.&lt;br /&gt;일치하지 않는 쪽은 NULL로 채운다.&lt;/p&gt;
&lt;p data-end=&quot;2722&quot; data-start=&quot;2703&quot; data-ke-size=&quot;size16&quot;&gt;이게 내부 조인과 가장 큰 차이다.&lt;/p&gt;
&lt;hr data-end=&quot;2727&quot; data-start=&quot;2724&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2748&quot; data-start=&quot;2729&quot; data-section-id=&quot;8iy206&quot; data-ke-size=&quot;size23&quot;&gt;LEFT OUTER JOIN&lt;/h3&gt;
&lt;p data-end=&quot;2771&quot; data-start=&quot;2750&quot; data-ke-size=&quot;size16&quot;&gt;왼쪽 릴레이션의 모든 튜플을 유지한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; student&lt;/span&gt;&lt;br /&gt;&lt;span&gt;LEFT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OUTER&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;JOIN&lt;/span&gt;&lt;span&gt; takes&lt;/span&gt;&lt;br /&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; student.ID &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; takes.ID;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2912&quot; data-start=&quot;2855&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 수강 정보가 없는 학생도 결과에 나타난다.&lt;br /&gt;단, takes 쪽 컬럼은 NULL이 된다.&lt;/p&gt;
&lt;hr data-end=&quot;2917&quot; data-start=&quot;2914&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2939&quot; data-start=&quot;2919&quot; data-section-id=&quot;1nhxlml&quot; data-ke-size=&quot;size23&quot;&gt;RIGHT OUTER JOIN&lt;/h3&gt;
&lt;p data-end=&quot;2963&quot; data-start=&quot;2941&quot; data-ke-size=&quot;size16&quot;&gt;오른쪽 릴레이션의 모든 튜플을 유지한다.&lt;/p&gt;
&lt;hr data-end=&quot;2968&quot; data-start=&quot;2965&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2989&quot; data-start=&quot;2970&quot; data-section-id=&quot;15fyn5a&quot; data-ke-size=&quot;size23&quot;&gt;FULL OUTER JOIN&lt;/h3&gt;
&lt;p data-end=&quot;3021&quot; data-start=&quot;2991&quot; data-ke-size=&quot;size16&quot;&gt;양쪽 릴레이션에서 매칭되지 않은 튜플도 전부 보존한다.&lt;/p&gt;
&lt;p data-end=&quot;3025&quot; data-start=&quot;3023&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3063&quot; data-start=&quot;3026&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3037&quot; data-start=&quot;3026&quot; data-section-id=&quot;1fa134a&quot;&gt;왼쪽에만 있는 것&lt;/li&gt;
&lt;li data-end=&quot;3063&quot; data-start=&quot;3038&quot; data-section-id=&quot;12ihwt2&quot;&gt;오른쪽에만 있는 것&lt;br /&gt;둘 다 결과에 남는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;3068&quot; data-start=&quot;3065&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3120&quot; data-start=&quot;3070&quot; data-section-id=&quot;rvpt6a&quot; data-ke-size=&quot;size23&quot;&gt;LEFT OUTER JOIN vs NATURAL LEFT OUTER JOIN&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;구분LEFT OUTER JOINNATURAL LEFT OUTER JOIN
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3333&quot; data-start=&quot;3122&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;3333&quot; data-start=&quot;3187&quot;&gt;
&lt;tr data-end=&quot;3239&quot; data-start=&quot;3187&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3195&quot; data-start=&quot;3187&quot;&gt;조인 조건&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3221&quot; data-start=&quot;3195&quot;&gt;ON 또는 USING으로 직접 지정&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3239&quot; data-start=&quot;3221&quot;&gt;같은 이름 속성 자동 매칭&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3281&quot; data-start=&quot;3240&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3248&quot; data-start=&quot;3240&quot;&gt;결과 컬럼&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3264&quot; data-start=&quot;3248&quot;&gt;중복 속성 남을 수 있음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3281&quot; data-start=&quot;3264&quot;&gt;공통 속성 하나로 합쳐짐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3302&quot; data-start=&quot;3282&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3291&quot; data-start=&quot;3282&quot;&gt;제어 가능성&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3296&quot; data-start=&quot;3291&quot;&gt;높음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3302&quot; data-start=&quot;3296&quot;&gt;낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3333&quot; data-start=&quot;3303&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3309&quot; data-start=&quot;3303&quot;&gt;안전성&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3316&quot; data-start=&quot;3309&quot;&gt;더 안전&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3333&quot; data-start=&quot;3316&quot;&gt;자동 매칭이라 주의 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3424&quot; data-start=&quot;3335&quot; data-ke-size=&quot;size16&quot;&gt;정리하면,&lt;br /&gt;&lt;b&gt;NATURAL JOIN 계열은 편하지만 위험하고&lt;/b&gt;,&lt;br /&gt;보통은 &lt;b&gt;LEFT OUTER JOIN ... ON ...&lt;/b&gt; 형태를 더 많이 쓴다.&lt;/p&gt;
&lt;hr data-end=&quot;3509&quot; data-start=&quot;3506&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3534&quot; data-start=&quot;3511&quot; data-section-id=&quot;1m1pb3l&quot; data-ke-size=&quot;size26&quot;&gt;7. 조인 종류와 조인 조건은 다르다&lt;/h2&gt;
&lt;p data-end=&quot;3550&quot; data-start=&quot;3536&quot; data-ke-size=&quot;size16&quot;&gt;이건 은근 많이 헷갈린다.&lt;/p&gt;
&lt;h3 data-end=&quot;3572&quot; data-start=&quot;3552&quot; data-section-id=&quot;7c4a94&quot; data-ke-size=&quot;size23&quot;&gt;조인 종류(Join Type)&lt;/h3&gt;
&lt;p data-end=&quot;3599&quot; data-start=&quot;3573&quot; data-ke-size=&quot;size16&quot;&gt;매칭되지 않는 튜플을 어떻게 처리할지 결정한다.&lt;/p&gt;
&lt;p data-end=&quot;3603&quot; data-start=&quot;3601&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3659&quot; data-start=&quot;3604&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3613&quot; data-start=&quot;3604&quot; data-section-id=&quot;jrr4ba&quot;&gt;INNER&lt;/li&gt;
&lt;li data-end=&quot;3628&quot; data-start=&quot;3614&quot; data-section-id=&quot;138v93e&quot;&gt;LEFT OUTER&lt;/li&gt;
&lt;li data-end=&quot;3644&quot; data-start=&quot;3629&quot; data-section-id=&quot;1pfmbwx&quot;&gt;RIGHT OUTER&lt;/li&gt;
&lt;li data-end=&quot;3659&quot; data-start=&quot;3645&quot; data-section-id=&quot;1szedhe&quot;&gt;FULL OUTER&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;3686&quot; data-start=&quot;3661&quot; data-section-id=&quot;uf3nei&quot; data-ke-size=&quot;size23&quot;&gt;조인 조건(Join Condition)&lt;/h3&gt;
&lt;p data-end=&quot;3705&quot; data-start=&quot;3687&quot; data-ke-size=&quot;size16&quot;&gt;어떤 튜플끼리 매칭할지 결정한다.&lt;/p&gt;
&lt;p data-end=&quot;3709&quot; data-start=&quot;3707&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3738&quot; data-start=&quot;3710&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3721&quot; data-start=&quot;3710&quot; data-section-id=&quot;145egvt&quot;&gt;NATURAL&lt;/li&gt;
&lt;li data-end=&quot;3731&quot; data-start=&quot;3722&quot; data-section-id=&quot;11ehnbi&quot;&gt;USING&lt;/li&gt;
&lt;li data-end=&quot;3738&quot; data-start=&quot;3732&quot; data-section-id=&quot;1j39jjd&quot;&gt;ON&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3742&quot; data-start=&quot;3740&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3794&quot; data-start=&quot;3744&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3770&quot; data-start=&quot;3744&quot; data-section-id=&quot;j6ftbr&quot;&gt;&lt;b&gt;종류&lt;/b&gt; = 안 맞는 행을 버릴지 살릴지&lt;/li&gt;
&lt;li data-end=&quot;3794&quot; data-start=&quot;3771&quot; data-section-id=&quot;1jt0btd&quot;&gt;&lt;b&gt;조건&lt;/b&gt; = 어떤 기준으로 연결할지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3807&quot; data-start=&quot;3796&quot; data-ke-size=&quot;size16&quot;&gt;라고 이해하면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;3812&quot; data-start=&quot;3809&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3829&quot; data-start=&quot;3814&quot; data-section-id=&quot;x5a04w&quot; data-ke-size=&quot;size26&quot;&gt;8. 뷰(View)란?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUmRUL/dJMcadgVkkS/GvJWSEFirk9vVttrzE65I1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUmRUL/dJMcadgVkkS/GvJWSEFirk9vVttrzE65I1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUmRUL/dJMcadgVkkS/GvJWSEFirk9vVttrzE65I1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUmRUL%2FdJMcadgVkkS%2FGvJWSEFirk9vVttrzE65I1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;331&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;3869&quot; data-start=&quot;3831&quot; data-ke-size=&quot;size16&quot;&gt;뷰는 &lt;b&gt;기본 테이블이나 다른 뷰로부터 파생된 가상 릴레이션&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;3924&quot; data-start=&quot;3871&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말하면,&lt;br /&gt;복잡한 SELECT 문에 이름을 붙여서 &lt;b&gt;테이블처럼 사용하는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;h3 data-end=&quot;3935&quot; data-start=&quot;3926&quot; data-section-id=&quot;1b8v4w2&quot; data-ke-size=&quot;size23&quot;&gt;뷰의 특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4017&quot; data-start=&quot;3936&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3956&quot; data-start=&quot;3936&quot; data-section-id=&quot;u2nugf&quot;&gt;실제 데이터를 따로 저장하지 않음&lt;/li&gt;
&lt;li data-end=&quot;3982&quot; data-start=&quot;3957&quot; data-section-id=&quot;cfhaop&quot;&gt;질의 시점에 기본 테이블을 바탕으로 계산됨&lt;/li&gt;
&lt;li data-end=&quot;4002&quot; data-start=&quot;3983&quot; data-section-id=&quot;1xksgds&quot;&gt;복잡한 질의를 단순화할 수 있음&lt;/li&gt;
&lt;li data-end=&quot;4017&quot; data-start=&quot;4003&quot; data-section-id=&quot;1ej48mx&quot;&gt;보안 목적으로도 유용함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;4021&quot; data-start=&quot;4019&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VIEW&lt;/span&gt;&lt;span&gt; geo_instructor &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;br /&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;'Geology'&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4155&quot; data-start=&quot;4119&quot; data-ke-size=&quot;size16&quot;&gt;이제 geo_instructor를 테이블처럼 사용할 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;4160&quot; data-start=&quot;4157&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4181&quot; data-start=&quot;4162&quot; data-section-id=&quot;6aol7u&quot; data-ke-size=&quot;size26&quot;&gt;9. 뷰 정의할 때 중요한 점&lt;/h2&gt;
&lt;p data-end=&quot;4219&quot; data-start=&quot;4183&quot; data-ke-size=&quot;size16&quot;&gt;뷰를 정의한다고 해서 결과가 즉시 테이블처럼 저장되는 건 아니다.&lt;/p&gt;
&lt;p data-end=&quot;4232&quot; data-start=&quot;4221&quot; data-ke-size=&quot;size16&quot;&gt;이거 진짜 중요하다.&lt;/p&gt;
&lt;p data-end=&quot;4236&quot; data-start=&quot;4234&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VIEW&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4312&quot; data-start=&quot;4266&quot; data-ke-size=&quot;size16&quot;&gt;는 &amp;ldquo;결과를 저장&amp;rdquo;하는 게 아니라&lt;br /&gt;&lt;b&gt;SQL 표현식을 저장&lt;/b&gt;하는 것에 가깝다.&lt;/p&gt;
&lt;p data-end=&quot;4342&quot; data-start=&quot;4314&quot; data-ke-size=&quot;size16&quot;&gt;필요할 때마다 그 정의를 이용해서 결과를 계산한다.&lt;/p&gt;
&lt;h3 data-end=&quot;4352&quot; data-start=&quot;4344&quot; data-section-id=&quot;jjqbne&quot; data-ke-size=&quot;size23&quot;&gt;뷰 제거&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;DROP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VIEW&lt;/span&gt;&lt;span&gt; view_name;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;4389&quot; data-start=&quot;4386&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4403&quot; data-start=&quot;4391&quot; data-section-id=&quot;pr9j13&quot; data-ke-size=&quot;size26&quot;&gt;10. 뷰의 장점&lt;/h2&gt;
&lt;h3 data-end=&quot;4422&quot; data-start=&quot;4405&quot; data-section-id=&quot;1b74squ&quot; data-ke-size=&quot;size23&quot;&gt;1) 복잡한 질의 단순화&lt;/h3&gt;
&lt;p data-end=&quot;4456&quot; data-start=&quot;4423&quot; data-ke-size=&quot;size16&quot;&gt;긴 조인 쿼리를 뷰로 만들어 두면 이후엔 짧게 쓸 수 있다.&lt;/p&gt;
&lt;h3 data-end=&quot;4467&quot; data-start=&quot;4458&quot; data-section-id=&quot;6ld9k1&quot; data-ke-size=&quot;size23&quot;&gt;2) 보안&lt;/h3&gt;
&lt;p data-end=&quot;4510&quot; data-start=&quot;4468&quot; data-ke-size=&quot;size16&quot;&gt;사용자에게 전체 테이블을 공개하지 않고 필요한 열/행만 보이게 할 수 있다.&lt;/p&gt;
&lt;h3 data-end=&quot;4526&quot; data-start=&quot;4512&quot; data-section-id=&quot;1pg929w&quot; data-ke-size=&quot;size23&quot;&gt;3) 논리적 독립성&lt;/h3&gt;
&lt;p data-end=&quot;4555&quot; data-start=&quot;4527&quot; data-ke-size=&quot;size16&quot;&gt;사용자 입장에서는 복잡한 내부 구조를 몰라도 된다.&lt;/p&gt;
&lt;hr data-end=&quot;4560&quot; data-start=&quot;4557&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4586&quot; data-start=&quot;4562&quot; data-section-id=&quot;gstsn1&quot; data-ke-size=&quot;size26&quot;&gt;11. 갱신 가능한 뷰 / 불가능한 뷰&lt;/h2&gt;
&lt;p data-end=&quot;4633&quot; data-start=&quot;4588&quot; data-ke-size=&quot;size16&quot;&gt;모든 뷰가 INSERT, UPDATE, DELETE 가능한 건 아니다.&lt;/p&gt;
&lt;h3 data-end=&quot;4654&quot; data-start=&quot;4635&quot; data-section-id=&quot;b1rk43&quot; data-ke-size=&quot;size23&quot;&gt;일반적으로 갱신 불가능한 뷰&lt;/h3&gt;
&lt;p data-end=&quot;4678&quot; data-start=&quot;4655&quot; data-ke-size=&quot;size16&quot;&gt;다음 요소가 들어가면 보통 갱신이 어렵다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4759&quot; data-start=&quot;4680&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4704&quot; data-start=&quot;4680&quot; data-section-id=&quot;1qpdycq&quot;&gt;집계 함수 (AVG, SUM 등)&lt;/li&gt;
&lt;li data-end=&quot;4717&quot; data-start=&quot;4705&quot; data-section-id=&quot;1a0fhho&quot;&gt;GROUP BY&lt;/li&gt;
&lt;li data-end=&quot;4730&quot; data-start=&quot;4718&quot; data-section-id=&quot;q13n9u&quot;&gt;DISTINCT&lt;/li&gt;
&lt;li data-end=&quot;4742&quot; data-start=&quot;4731&quot; data-section-id=&quot;6net58&quot;&gt;여러 테이블 조인&lt;/li&gt;
&lt;li data-end=&quot;4759&quot; data-start=&quot;4743&quot; data-section-id=&quot;1qkq9bu&quot;&gt;계산식이 많이 포함된 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;4805&quot; data-start=&quot;4761&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 평균 급여를 보여주는 뷰에 값을 삽입하는 건 애초에 의미가 모호하다.&lt;/p&gt;
&lt;hr data-end=&quot;4810&quot; data-start=&quot;4807&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4828&quot; data-start=&quot;4812&quot; data-section-id=&quot;1fpqen6&quot; data-ke-size=&quot;size23&quot;&gt;갱신 가능한 뷰의 조건&lt;/h3&gt;
&lt;p data-end=&quot;4856&quot; data-start=&quot;4829&quot; data-ke-size=&quot;size16&quot;&gt;보통 다음 조건을 만족해야 비교적 갱신 가능하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4971&quot; data-start=&quot;4858&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4885&quot; data-start=&quot;4858&quot; data-section-id=&quot;xw6ln8&quot;&gt;SELECT 절에 단순한 속성 이름만 있음&lt;/li&gt;
&lt;li data-end=&quot;4909&quot; data-start=&quot;4886&quot; data-section-id=&quot;mbuf4u&quot;&gt;FROM 절에 릴레이션 하나만 있음&lt;/li&gt;
&lt;li data-end=&quot;4935&quot; data-start=&quot;4910&quot; data-section-id=&quot;moyywl&quot;&gt;GROUP BY, HAVING 없음&lt;/li&gt;
&lt;li data-end=&quot;4971&quot; data-start=&quot;4936&quot; data-section-id=&quot;1ze3pg&quot;&gt;생략된 속성이 NULL 허용 또는 DEFAULT 가능해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;5004&quot; data-start=&quot;4973&quot; data-ke-size=&quot;size16&quot;&gt;즉, &amp;ldquo;거의 기본 테이블처럼 보이는 단순 뷰&amp;rdquo;여야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;5009&quot; data-start=&quot;5006&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5037&quot; data-start=&quot;5011&quot; data-section-id=&quot;17ky04v&quot; data-ke-size=&quot;size26&quot;&gt;12. WITH CHECK OPTION&lt;/h2&gt;
&lt;p data-end=&quot;5094&quot; data-start=&quot;5039&quot; data-ke-size=&quot;size16&quot;&gt;이 옵션은 뷰를 통해 삽입/수정되는 데이터가&lt;br /&gt;&lt;b&gt;그 뷰의 조건을 반드시 만족하도록 강제&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-end=&quot;5098&quot; data-start=&quot;5096&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VIEW&lt;/span&gt;&lt;span&gt; dept_instructor &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;br /&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;'Comp. Sci.'&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WITH&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CHECK&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OPTION&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5290&quot; data-start=&quot;5218&quot; data-ke-size=&quot;size16&quot;&gt;이제 이 뷰를 통해 데이터를 넣거나 수정할 때는&lt;br /&gt;반드시 dept_name = 'Comp. Sci.' 조건을 만족해야 한다.&lt;/p&gt;
&lt;p data-end=&quot;5348&quot; data-start=&quot;5292&quot; data-ke-size=&quot;size16&quot;&gt;이 옵션이 없으면&lt;br /&gt;뷰를 통해 수정한 결과가 오히려 뷰에서 안 보이는 이상한 상황이 생길 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;5353&quot; data-start=&quot;5350&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5375&quot; data-start=&quot;5355&quot; data-section-id=&quot;19w6cbd&quot; data-ke-size=&quot;size26&quot;&gt;13. 뷰 갱신에서 생기는 문제&lt;/h2&gt;
&lt;p data-end=&quot;5396&quot; data-start=&quot;5377&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 뷰가 있다고 하자.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VIEW&lt;/span&gt;&lt;span&gt; instructor_info &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;br /&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; id, name, building&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor I, department D&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; I.dept_name &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; D.dept_name;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5535&quot; data-start=&quot;5532&quot; data-ke-size=&quot;size16&quot;&gt;여기에&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;INSERT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;INTO&lt;/span&gt;&lt;span&gt; instructor_info &lt;/span&gt;&lt;span&gt;VALUES&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;'67787'&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;'Amy'&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;'1공학관'&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5623&quot; data-start=&quot;5610&quot; data-ke-size=&quot;size16&quot;&gt;를 하면 문제가 생긴다.&lt;/p&gt;
&lt;p data-end=&quot;5689&quot; data-start=&quot;5625&quot; data-ke-size=&quot;size16&quot;&gt;왜냐면 뷰에는 dept_name이 없어서,&lt;br /&gt;실제 기본 테이블에 어떻게 넣어야 할지 정보가 부족하기 때문이다.&lt;/p&gt;
&lt;h3 data-end=&quot;5704&quot; data-start=&quot;5691&quot; data-section-id=&quot;jxc29m&quot; data-ke-size=&quot;size23&quot;&gt;발생 가능한 문제&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;5792&quot; data-start=&quot;5705&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;5746&quot; data-start=&quot;5705&quot; data-section-id=&quot;15c3qnd&quot;&gt;삽입된 튜플이 조인 조건을 만족하지 않아 뷰 결과에 안 보일 수 있음&lt;/li&gt;
&lt;li data-end=&quot;5792&quot; data-start=&quot;5747&quot; data-section-id=&quot;bc853j&quot;&gt;기본 테이블의 필수 속성이나 기본키가 NULL이 되어 삽입 불가능할 수 있음&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;5815&quot; data-start=&quot;5794&quot; data-ke-size=&quot;size16&quot;&gt;즉, 조인 뷰는 갱신이 특히 까다롭다.&lt;/p&gt;
&lt;hr data-end=&quot;5820&quot; data-start=&quot;5817&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5853&quot; data-start=&quot;5822&quot; data-section-id=&quot;1ln4men&quot; data-ke-size=&quot;size26&quot;&gt;14. 실체화 뷰(Materialized View)&lt;/h2&gt;
&lt;p data-end=&quot;5901&quot; data-start=&quot;5855&quot; data-ke-size=&quot;size16&quot;&gt;일반 뷰는 결과를 매번 계산하지만,&lt;br /&gt;실체화 뷰는 &lt;b&gt;결과를 실제로 저장&lt;/b&gt;한다.&lt;/p&gt;
&lt;h3 data-end=&quot;5909&quot; data-start=&quot;5903&quot; data-section-id=&quot;1hrqpoz&quot; data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5941&quot; data-start=&quot;5910&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5921&quot; data-start=&quot;5910&quot; data-section-id=&quot;4t5kux&quot;&gt;조회 속도가 빠름&lt;/li&gt;
&lt;li data-end=&quot;5941&quot; data-start=&quot;5922&quot; data-section-id=&quot;1ezhc3c&quot;&gt;복잡한 집계나 분석 쿼리에 유리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;5949&quot; data-start=&quot;5943&quot; data-section-id=&quot;1hrgjzy&quot; data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5997&quot; data-start=&quot;5950&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5960&quot; data-start=&quot;5950&quot; data-section-id=&quot;q18iss&quot;&gt;저장 공간 필요&lt;/li&gt;
&lt;li data-end=&quot;5986&quot; data-start=&quot;5961&quot; data-section-id=&quot;1cm7n9g&quot;&gt;원본 데이터 변경 시 최신 상태 유지 필요&lt;/li&gt;
&lt;li data-end=&quot;5997&quot; data-start=&quot;5987&quot; data-section-id=&quot;ssvd05&quot;&gt;갱신 비용 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;6031&quot; data-start=&quot;5999&quot; data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;속도와 저장/유지 비용의 트레이드오프&lt;/b&gt;가 있다.&lt;/p&gt;
&lt;hr data-end=&quot;6100&quot; data-start=&quot;6097&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;6119&quot; data-start=&quot;6102&quot; data-section-id=&quot;3jym23&quot; data-ke-size=&quot;size26&quot;&gt;15. 무결성 제약조건이란&lt;/h2&gt;
&lt;p data-end=&quot;6159&quot; data-start=&quot;6121&quot; data-ke-size=&quot;size16&quot;&gt;무결성 제약조건은 &lt;b&gt;잘못된 데이터가 들어오는 걸 막는 규칙&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;6232&quot; data-start=&quot;6161&quot; data-ke-size=&quot;size16&quot;&gt;데이터베이스는 한 번 이상한 값이 들어가면&lt;br /&gt;그 뒤로 모든 질의 결과가 오염될 수 있다.&lt;br /&gt;그래서 제약조건은 진짜 중요하다.&lt;/p&gt;
&lt;hr data-end=&quot;6237&quot; data-start=&quot;6234&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;6258&quot; data-start=&quot;6239&quot; data-section-id=&quot;1nf23n1&quot; data-ke-size=&quot;size26&quot;&gt;16. 단일 릴레이션 제약조건&lt;/h2&gt;
&lt;h3 data-end=&quot;6274&quot; data-start=&quot;6260&quot; data-section-id=&quot;gc8yug&quot; data-ke-size=&quot;size23&quot;&gt;NOT NULL&lt;/h3&gt;
&lt;p data-end=&quot;6297&quot; data-start=&quot;6275&quot; data-ke-size=&quot;size16&quot;&gt;해당 속성에 NULL을 허용하지 않는다.&lt;/p&gt;
&lt;h3 data-end=&quot;6316&quot; data-start=&quot;6299&quot; data-section-id=&quot;15ownml&quot; data-ke-size=&quot;size23&quot;&gt;PRIMARY KEY&lt;/h3&gt;
&lt;p data-end=&quot;6357&quot; data-start=&quot;6317&quot; data-ke-size=&quot;size16&quot;&gt;기본키를 지정한다.&lt;br /&gt;자동으로 유일성과 NOT NULL 성질을 가진다.&lt;/p&gt;
&lt;h3 data-end=&quot;6371&quot; data-start=&quot;6359&quot; data-section-id=&quot;1kuqiol&quot; data-ke-size=&quot;size23&quot;&gt;UNIQUE&lt;/h3&gt;
&lt;p data-end=&quot;6456&quot; data-start=&quot;6372&quot; data-ke-size=&quot;size16&quot;&gt;중복을 허용하지 않는다.&lt;br /&gt;다만 SQL에서는 NULL 처리 방식이 DBMS마다 약간 다를 수 있는데,&lt;br /&gt;보통 여러 NULL을 허용하는 경우가 많다.&lt;/p&gt;
&lt;h3 data-end=&quot;6474&quot; data-start=&quot;6458&quot; data-section-id=&quot;po4m1x&quot; data-ke-size=&quot;size23&quot;&gt;CHECK (조건)&lt;/h3&gt;
&lt;p data-end=&quot;6489&quot; data-start=&quot;6475&quot; data-ke-size=&quot;size16&quot;&gt;속성 값 범위를 제한한다.&lt;/p&gt;
&lt;p data-end=&quot;6493&quot; data-start=&quot;6491&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TABLE&lt;/span&gt;&lt;span&gt; section2(&lt;/span&gt;&lt;br /&gt;&lt;span&gt; semester &lt;/span&gt;&lt;span&gt;varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CHECK&lt;/span&gt;&lt;span&gt; (semester &lt;/span&gt;&lt;span&gt;IN&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;'Fall'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;'Winter'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;'Spring'&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;'Summer'&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;br /&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6616&quot; data-start=&quot;6614&quot; data-ke-size=&quot;size16&quot;&gt;또는&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;salary &lt;/span&gt;&lt;span&gt;numeric&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;CHECK&lt;/span&gt;&lt;span&gt; (salary &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;29000&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6685&quot; data-start=&quot;6673&quot; data-ke-size=&quot;size16&quot;&gt;이런 식으로 사용한다.&lt;/p&gt;
&lt;hr data-end=&quot;6690&quot; data-start=&quot;6687&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;6710&quot; data-start=&quot;6692&quot; data-section-id=&quot;nhgavp&quot; data-ke-size=&quot;size26&quot;&gt;17. 참조 무결성 제약조건&lt;/h2&gt;
&lt;p data-end=&quot;6763&quot; data-start=&quot;6712&quot; data-ke-size=&quot;size16&quot;&gt;참조 무결성은 &lt;b&gt;외래키 값이 반드시 다른 릴레이션의 기본키와 일치해야 한다&lt;/b&gt;는 규칙이다.&lt;/p&gt;
&lt;p data-end=&quot;6767&quot; data-start=&quot;6765&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TABLE&lt;/span&gt;&lt;span&gt; instructor(&lt;/span&gt;&lt;br /&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FOREIGN&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KEY&lt;/span&gt;&lt;span&gt; (dept_name) &lt;/span&gt;&lt;span&gt;REFERENCES&lt;/span&gt;&lt;span&gt; department(dept_name)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;6960&quot; data-start=&quot;6893&quot; data-ke-size=&quot;size16&quot;&gt;이제 instructor.dept_name에는&lt;br /&gt;반드시 department에 존재하는 학과명만 들어갈 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;6965&quot; data-start=&quot;6962&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6980&quot; data-start=&quot;6967&quot; data-section-id=&quot;bw6r3q&quot; data-ke-size=&quot;size23&quot;&gt;외래키 옵션 정리&lt;/h3&gt;
&lt;div&gt;옵션&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 의미
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;7240&quot; data-start=&quot;6982&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;7240&quot; data-start=&quot;7004&quot;&gt;
&lt;tr data-end=&quot;7047&quot; data-start=&quot;7004&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7026&quot; data-start=&quot;7004&quot;&gt;ON DELETE CASCADE&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7047&quot; data-start=&quot;7026&quot;&gt;부모 삭제 시 자식도 같이 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7099&quot; data-start=&quot;7048&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7071&quot; data-start=&quot;7048&quot;&gt;ON DELETE SET NULL&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7099&quot; data-start=&quot;7071&quot;&gt;부모 삭제 시 자식 외래키를 NULL로 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7154&quot; data-start=&quot;7100&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7126&quot; data-start=&quot;7100&quot;&gt;ON DELETE SET DEFAULT&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7154&quot; data-start=&quot;7126&quot;&gt;부모 삭제 시 자식 외래키를 기본값으로 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7200&quot; data-start=&quot;7155&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7177&quot; data-start=&quot;7155&quot;&gt;ON UPDATE CASCADE&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7200&quot; data-start=&quot;7177&quot;&gt;부모 값 변경 시 자식도 자동 반영&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7240&quot; data-start=&quot;7201&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7225&quot; data-start=&quot;7201&quot;&gt;ON UPDATE NO ACTION&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7240&quot; data-start=&quot;7225&quot;&gt;부모 값 변경을 막음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7298&quot; data-start=&quot;7242&quot; data-ke-size=&quot;size16&quot;&gt;실무에서는 특히 CASCADE를 쓸 때 조심해야 한다.&lt;br /&gt;잘못하면 연쇄 삭제가 발생할 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;7303&quot; data-start=&quot;7300&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7324&quot; data-start=&quot;7305&quot; data-section-id=&quot;1loboqq&quot; data-ke-size=&quot;size26&quot;&gt;18. SQL 날짜/시간 타입&lt;/h2&gt;
&lt;p data-end=&quot;7351&quot; data-start=&quot;7326&quot; data-ke-size=&quot;size16&quot;&gt;SQL에는 날짜와 시간을 다루는 타입도 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;타입예시설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;7546&quot; data-start=&quot;7353&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;7546&quot; data-start=&quot;7384&quot;&gt;
&lt;tr data-end=&quot;7419&quot; data-start=&quot;7384&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7393&quot; data-start=&quot;7384&quot;&gt;DATE&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7410&quot; data-start=&quot;7393&quot;&gt;'2005-07-27'&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7419&quot; data-start=&quot;7410&quot;&gt;연/월/일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7456&quot; data-start=&quot;7420&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7429&quot; data-start=&quot;7420&quot;&gt;TIME&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7447&quot; data-start=&quot;7429&quot;&gt;'09:00:30.75'&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7456&quot; data-start=&quot;7447&quot;&gt;시/분/초&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7511&quot; data-start=&quot;7457&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7471&quot; data-start=&quot;7457&quot;&gt;TIMESTAMP&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7500&quot; data-start=&quot;7471&quot;&gt;'2005-07-27 09:00:30.75'&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7511&quot; data-start=&quot;7500&quot;&gt;날짜 + 시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7546&quot; data-start=&quot;7512&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7525&quot; data-start=&quot;7512&quot;&gt;INTERVAL&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7537&quot; data-start=&quot;7525&quot;&gt;'1 day'&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7546&quot; data-start=&quot;7537&quot;&gt;시간 간격&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7588&quot; data-start=&quot;7548&quot; data-ke-size=&quot;size16&quot;&gt;이 타입들은 예약 시스템, 로그 저장, 기간 계산 같은 데서 많이 쓴다.&lt;/p&gt;
&lt;hr data-end=&quot;7593&quot; data-start=&quot;7590&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7616&quot; data-start=&quot;7595&quot; data-section-id=&quot;6micgq&quot; data-ke-size=&quot;size26&quot;&gt;19. 대형 객체 타입 (LOB)&lt;/h2&gt;
&lt;p data-end=&quot;7645&quot; data-start=&quot;7618&quot; data-ke-size=&quot;size16&quot;&gt;대용량 데이터를 저장할 때는 LOB 타입을 쓴다.&lt;/p&gt;
&lt;h3 data-end=&quot;7655&quot; data-start=&quot;7647&quot; data-section-id=&quot;yncehx&quot; data-ke-size=&quot;size23&quot;&gt;BLOB&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7700&quot; data-start=&quot;7656&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7677&quot; data-start=&quot;7656&quot; data-section-id=&quot;9c17z&quot;&gt;Binary Large Object&lt;/li&gt;
&lt;li data-end=&quot;7700&quot; data-start=&quot;7678&quot; data-section-id=&quot;y3c55d&quot;&gt;이미지, 영상, 바이너리 데이터 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;7710&quot; data-start=&quot;7702&quot; data-section-id=&quot;ynd2tg&quot; data-ke-size=&quot;size23&quot;&gt;CLOB&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7750&quot; data-start=&quot;7711&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7735&quot; data-start=&quot;7711&quot; data-section-id=&quot;1u42ggp&quot;&gt;Character Large Object&lt;/li&gt;
&lt;li data-end=&quot;7750&quot; data-start=&quot;7736&quot; data-section-id=&quot;1yh9qu8&quot;&gt;문서, 긴 텍스트 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7813&quot; data-start=&quot;7752&quot; data-ke-size=&quot;size16&quot;&gt;실제로는 대형 데이터를 DB 자체에 전부 넣기보다&lt;br /&gt;참조 포인터나 파일 경로처럼 관리하는 방식도 많이 쓴다.&lt;/p&gt;
&lt;hr data-end=&quot;7818&quot; data-start=&quot;7815&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7841&quot; data-start=&quot;7820&quot; data-section-id=&quot;ynhegw&quot; data-ke-size=&quot;size26&quot;&gt;20. 사용자 정의 타입과 도메인&lt;/h2&gt;
&lt;h3 data-end=&quot;7860&quot; data-start=&quot;7843&quot; data-section-id=&quot;1climka&quot; data-ke-size=&quot;size23&quot;&gt;CREATE TYPE&lt;/h3&gt;
&lt;p data-end=&quot;7883&quot; data-start=&quot;7861&quot; data-ke-size=&quot;size16&quot;&gt;사용자 정의 복합 타입을 만들 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;7887&quot; data-start=&quot;7885&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; TYPE Dollars &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;br /&gt;&lt;span&gt; dollars &lt;/span&gt;&lt;span&gt;numeric&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7973&quot; data-start=&quot;7955&quot; data-ke-size=&quot;size16&quot;&gt;이후 테이블에서 사용할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TABLE&lt;/span&gt;&lt;span&gt; department(&lt;/span&gt;&lt;br /&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;br /&gt;&lt;span&gt; budget Dollars&lt;/span&gt;&lt;br /&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;8060&quot; data-start=&quot;8057&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8081&quot; data-start=&quot;8062&quot; data-section-id=&quot;lxbzc2&quot; data-ke-size=&quot;size23&quot;&gt;CREATE DOMAIN&lt;/h3&gt;
&lt;p data-end=&quot;8129&quot; data-start=&quot;8082&quot; data-ke-size=&quot;size16&quot;&gt;도메인은 기존 타입에 &lt;b&gt;제약조건을 붙인 재사용 가능한 타입&lt;/b&gt;이라고 생각하면 된다.&lt;/p&gt;
&lt;p data-end=&quot;8133&quot; data-start=&quot;8131&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DOMAIN&lt;/span&gt;&lt;span&gt; degree_level &lt;/span&gt;&lt;span&gt;varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CONSTRAINT&lt;/span&gt;&lt;span&gt; degree_level_test&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CHECK&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;VALUE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IN&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;'Bachelors'&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;'Masters'&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;'Doctorate'&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;8309&quot; data-start=&quot;8275&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 만들어두면 여러 테이블에서 일관되게 재사용할 수 있다.&lt;/p&gt;
&lt;h3 data-end=&quot;8327&quot; data-start=&quot;8311&quot; data-section-id=&quot;1yn5nua&quot; data-ke-size=&quot;size23&quot;&gt;타입 vs 도메인 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8383&quot; data-start=&quot;8328&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8352&quot; data-start=&quot;8328&quot; data-section-id=&quot;na6iev&quot;&gt;&lt;b&gt;TYPE&lt;/b&gt;: 구조 자체를 새로 정의&lt;/li&gt;
&lt;li data-end=&quot;8383&quot; data-start=&quot;8353&quot; data-section-id=&quot;1uvyp04&quot;&gt;&lt;b&gt;DOMAIN&lt;/b&gt;: 기존 타입 + 제약조건 재사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8397&quot; data-start=&quot;8385&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 구분하면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;8402&quot; data-start=&quot;8399&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;8422&quot; data-start=&quot;8404&quot; data-section-id=&quot;14lvyii&quot; data-ke-size=&quot;size26&quot;&gt;21. 인덱스(Index)란&lt;/h2&gt;
&lt;p data-end=&quot;8453&quot; data-start=&quot;8424&quot; data-ke-size=&quot;size16&quot;&gt;인덱스는 &lt;b&gt;검색을 빠르게 하기 위한 자료구조&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;8525&quot; data-start=&quot;8455&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 책 뒤의 색인 같은 느낌이다.&lt;br /&gt;본문을 처음부터 끝까지 다 안 읽고도 원하는 내용을 빨리 찾을 수 있게 해준다.&lt;/p&gt;
&lt;h3 data-end=&quot;8538&quot; data-start=&quot;8527&quot; data-section-id=&quot;1sn0tsa&quot; data-ke-size=&quot;size23&quot;&gt;인덱스의 핵심&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8606&quot; data-start=&quot;8539&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8558&quot; data-start=&quot;8539&quot; data-section-id=&quot;1pnez7m&quot;&gt;속성값과 해당 튜플 위치를 저장&lt;/li&gt;
&lt;li data-end=&quot;8575&quot; data-start=&quot;8559&quot; data-section-id=&quot;166jwx0&quot;&gt;특정 조건 검색 속도 향상&lt;/li&gt;
&lt;li data-end=&quot;8606&quot; data-start=&quot;8576&quot; data-section-id=&quot;1nt23mt&quot;&gt;한 번 만들면 같은 조건의 질의에 반복적으로 도움됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8610&quot; data-start=&quot;8608&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; INDEX dept_index&lt;/span&gt;&lt;br /&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; instructor(dept_name, salary);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;8685&quot; data-start=&quot;8682&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8712&quot; data-start=&quot;8687&quot; data-section-id=&quot;ws8snx&quot; data-ke-size=&quot;size23&quot;&gt;왜 그냥 테이블을 정렬해두면 안 될까?&lt;/h3&gt;
&lt;p data-end=&quot;8727&quot; data-start=&quot;8714&quot; data-ke-size=&quot;size16&quot;&gt;이 질문도 자주 나온다.&lt;/p&gt;
&lt;p data-end=&quot;8781&quot; data-start=&quot;8729&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 테이블을 dept_name 기준으로 정렬하면&lt;br /&gt;다른 속성 기준 정렬은 깨진다.&lt;/p&gt;
&lt;p data-end=&quot;8785&quot; data-start=&quot;8783&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8852&quot; data-start=&quot;8786&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8818&quot; data-start=&quot;8786&quot; data-section-id=&quot;10xwmcb&quot;&gt;하나의 정렬 상태로 모든 검색을 빠르게 만들 수는 없음&lt;/li&gt;
&lt;li data-end=&quot;8852&quot; data-start=&quot;8819&quot; data-section-id=&quot;9rsk51&quot;&gt;그래서 검색이 자주 발생하는 속성마다 인덱스를 따로 둔다&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;8857&quot; data-start=&quot;8854&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;8874&quot; data-start=&quot;8859&quot; data-section-id=&quot;1mj1utf&quot; data-ke-size=&quot;size23&quot;&gt;인덱스의 단점도 있다&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8946&quot; data-start=&quot;8875&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8888&quot; data-start=&quot;8875&quot; data-section-id=&quot;ak85y0&quot;&gt;저장 공간 추가 필요&lt;/li&gt;
&lt;li data-end=&quot;8946&quot; data-start=&quot;8889&quot; data-section-id=&quot;ipnrmr&quot;&gt;INSERT, UPDATE, DELETE 때 인덱스도 함께 갱신해야 해서 쓰기 비용 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8977&quot; data-start=&quot;8948&quot; data-ke-size=&quot;size16&quot;&gt;즉, 조회는 빨라지지만 갱신은 더 무거워질 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;9042&quot; data-start=&quot;9039&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;9068&quot; data-start=&quot;9044&quot; data-section-id=&quot;19wiujl&quot; data-ke-size=&quot;size26&quot;&gt;22. 권한(Authorization)&lt;/h2&gt;
&lt;p data-end=&quot;9122&quot; data-start=&quot;9070&quot; data-ke-size=&quot;size16&quot;&gt;DB에서는 모든 사용자가 모든 데이터를 다 만지면 안 된다.&lt;br /&gt;그래서 권한 관리가 필요하다.&lt;/p&gt;
&lt;h3 data-end=&quot;9136&quot; data-start=&quot;9124&quot; data-section-id=&quot;1b07mr&quot; data-ke-size=&quot;size23&quot;&gt;기본 권한 종류&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;권한설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;9260&quot; data-start=&quot;9138&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;9260&quot; data-start=&quot;9160&quot;&gt;
&lt;tr data-end=&quot;9177&quot; data-start=&quot;9160&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9171&quot; data-start=&quot;9160&quot;&gt;SELECT&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9177&quot; data-start=&quot;9171&quot;&gt;조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9195&quot; data-start=&quot;9178&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9189&quot; data-start=&quot;9178&quot;&gt;INSERT&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9195&quot; data-start=&quot;9189&quot;&gt;삽입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9213&quot; data-start=&quot;9196&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9207&quot; data-start=&quot;9196&quot;&gt;UPDATE&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9213&quot; data-start=&quot;9207&quot;&gt;수정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9231&quot; data-start=&quot;9214&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9225&quot; data-start=&quot;9214&quot;&gt;DELETE&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9231&quot; data-start=&quot;9225&quot;&gt;삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;9260&quot; data-start=&quot;9232&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9251&quot; data-start=&quot;9232&quot;&gt;ALL PRIVILEGES&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;9260&quot; data-start=&quot;9251&quot;&gt;모든 권한&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9264&quot; data-start=&quot;9262&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;GRANT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; department &lt;/span&gt;&lt;span&gt;TO&lt;/span&gt;&lt;span&gt; amit, satoshi;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;GRANT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UPDATE&lt;/span&gt;&lt;span&gt; (budget) &lt;/span&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; department &lt;/span&gt;&lt;span&gt;TO&lt;/span&gt;&lt;span&gt; amit, satoshi;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9401&quot; data-start=&quot;9377&quot; data-ke-size=&quot;size16&quot;&gt;특정 컬럼에 대해서만 권한을 줄 수도 있다.&lt;/p&gt;
&lt;p data-end=&quot;9417&quot; data-start=&quot;9403&quot; data-ke-size=&quot;size16&quot;&gt;권한 회수는 이렇게 한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;REVOKE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; department &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; amit, satoshi;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;REVOKE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;UPDATE&lt;/span&gt;&lt;span&gt; (budget) &lt;/span&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; department &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; amit, satoshi;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;9539&quot; data-start=&quot;9536&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;9554&quot; data-start=&quot;9541&quot; data-section-id=&quot;lq2qq2&quot; data-ke-size=&quot;size26&quot;&gt;23. ROLE이란&lt;/h2&gt;
&lt;p data-end=&quot;9581&quot; data-start=&quot;9556&quot; data-ke-size=&quot;size16&quot;&gt;ROLE은 &lt;b&gt;권한 묶음&lt;/b&gt;이라고 보면 된다.&lt;/p&gt;
&lt;p data-end=&quot;9647&quot; data-start=&quot;9583&quot; data-ke-size=&quot;size16&quot;&gt;사용자마다 권한을 하나하나 주는 대신,&lt;br /&gt;역할을 만들고 거기에 권한을 준 다음 사용자를 그 역할에 넣는 방식이다.&lt;/p&gt;
&lt;p data-end=&quot;9651&quot; data-start=&quot;9649&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ROLE&lt;/span&gt;&lt;span&gt; instructor;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;GRANT&lt;/span&gt;&lt;span&gt; instructor &lt;/span&gt;&lt;span&gt;TO&lt;/span&gt;&lt;span&gt; amit;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ROLE&lt;/span&gt;&lt;span&gt; teaching_assistant;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;GRANT&lt;/span&gt;&lt;span&gt; teaching_assistant &lt;/span&gt;&lt;span&gt;TO&lt;/span&gt;&lt;span&gt; instructor;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;GRANT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; takes &lt;/span&gt;&lt;span&gt;TO&lt;/span&gt;&lt;span&gt; instructor;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;9847&quot; data-start=&quot;9825&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 권한 관리가 훨씬 편해진다.&lt;/p&gt;
&lt;hr data-end=&quot;9852&quot; data-start=&quot;9849&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;9869&quot; data-start=&quot;9854&quot; data-section-id=&quot;h4it8h&quot; data-ke-size=&quot;size26&quot;&gt;24. 뷰에 대한 권한&lt;/h2&gt;
&lt;p data-end=&quot;9890&quot; data-start=&quot;9871&quot; data-ke-size=&quot;size16&quot;&gt;뷰는 보안 목적으로 정말 유용하다.&lt;/p&gt;
&lt;p data-end=&quot;9894&quot; data-start=&quot;9892&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CREATE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;VIEW&lt;/span&gt;&lt;span&gt; geo_instructor &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;br /&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor &lt;/span&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;'Geology'&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;GRANT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ON&lt;/span&gt;&lt;span&gt; geo_instructor &lt;/span&gt;&lt;span&gt;TO&lt;/span&gt;&lt;span&gt; geo_staff;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;10110&quot; data-start=&quot;10038&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 geo_staff는 원본 instructor 전체를 볼 필요 없이&lt;br /&gt;뷰를 통해 필요한 데이터만 조회할 수 있다.&lt;/p&gt;
&lt;h3 data-end=&quot;10123&quot; data-start=&quot;10112&quot; data-section-id=&quot;q649lf&quot; data-ke-size=&quot;size23&quot;&gt;중요한 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;10220&quot; data-start=&quot;10124&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;10156&quot; data-start=&quot;10124&quot; data-section-id=&quot;1j57tu4&quot;&gt;뷰를 만든 사용자는 원본 테이블 접근 권한이 있어야 함&lt;/li&gt;
&lt;li data-end=&quot;10220&quot; data-start=&quot;10157&quot; data-section-id=&quot;1wk8e5v&quot;&gt;뷰를 사용하는 사람은 원본 테이블 권한이 없어도,&lt;br /&gt;뷰에 대한 권한만 있으면 사용할 수 있는 경우가 많음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;10249&quot; data-start=&quot;10222&quot; data-ke-size=&quot;size16&quot;&gt;즉, 뷰는 &lt;b&gt;보안 필터 역할&lt;/b&gt;도 할 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;10254&quot; data-start=&quot;10251&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;10301&quot; data-start=&quot;10256&quot; data-section-id=&quot;1rt217z&quot; data-ke-size=&quot;size26&quot;&gt;25. USAGE, SELECT, CREATE 헷갈리는 이유 정리&lt;/h2&gt;
&lt;p data-end=&quot;10320&quot; data-start=&quot;10303&quot; data-ke-size=&quot;size16&quot;&gt;이 부분은 진짜 많이 헷갈린다.&lt;/p&gt;
&lt;p data-end=&quot;10354&quot; data-start=&quot;10322&quot; data-ke-size=&quot;size16&quot;&gt;권한은 &lt;b&gt;대상 객체가 무엇인지에 따라 이름이 다르다.&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;대상가능한 권한
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;10558&quot; data-start=&quot;10356&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;10558&quot; data-start=&quot;10382&quot;&gt;
&lt;tr data-end=&quot;10439&quot; data-start=&quot;10382&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;10397&quot; data-start=&quot;10382&quot;&gt;TABLE / VIEW&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;10439&quot; data-start=&quot;10397&quot;&gt;SELECT, INSERT, UPDATE, DELETE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;10470&quot; data-start=&quot;10440&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;10449&quot; data-start=&quot;10440&quot;&gt;SCHEMA&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;10470&quot; data-start=&quot;10449&quot;&gt;USAGE, CREATE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;10513&quot; data-start=&quot;10471&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;10482&quot; data-start=&quot;10471&quot;&gt;DATABASE&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;10513&quot; data-start=&quot;10482&quot;&gt;CONNECT, CREATE, TEMP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;10558&quot; data-start=&quot;10514&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;10536&quot; data-start=&quot;10514&quot;&gt;FUNCTION / SEQUENCE&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;10558&quot; data-start=&quot;10536&quot;&gt;EXECUTE, USAGE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;10563&quot; data-start=&quot;10560&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;10606&quot; data-start=&quot;10565&quot; data-section-id=&quot;fn20e4&quot; data-ke-size=&quot;size23&quot;&gt;GRANT SELECT ON mini_user TO maker;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;10648&quot; data-start=&quot;10607&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;10616&quot; data-start=&quot;10607&quot; data-section-id=&quot;1db9hrz&quot;&gt;대상: 테이블&lt;/li&gt;
&lt;li data-end=&quot;10648&quot; data-start=&quot;10617&quot; data-section-id=&quot;19kjdvb&quot;&gt;의미: mini_user 데이터를 조회할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;10694&quot; data-start=&quot;10650&quot; data-section-id=&quot;1ma2s18&quot; data-ke-size=&quot;size23&quot;&gt;GRANT USAGE ON SCHEMA public TO maker;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;10746&quot; data-start=&quot;10695&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;10704&quot; data-start=&quot;10695&quot; data-section-id=&quot;1d6h7mj&quot;&gt;대상: 스키마&lt;/li&gt;
&lt;li data-end=&quot;10746&quot; data-start=&quot;10705&quot; data-section-id=&quot;t4h7p3&quot;&gt;의미: public이라는 공간 안의 객체를 인식하고 접근할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;10757&quot; data-start=&quot;10748&quot; data-ke-size=&quot;size16&quot;&gt;비유하면 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;10859&quot; data-start=&quot;10759&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;10769&quot; data-start=&quot;10759&quot; data-section-id=&quot;rvsi3h&quot;&gt;스키마 = 폴더&lt;/li&gt;
&lt;li data-end=&quot;10780&quot; data-start=&quot;10770&quot; data-section-id=&quot;1hy5qy1&quot;&gt;테이블 = 파일&lt;/li&gt;
&lt;li data-end=&quot;10803&quot; data-start=&quot;10781&quot; data-section-id=&quot;15fueo8&quot;&gt;USAGE = 폴더에 들어갈 권한&lt;/li&gt;
&lt;li data-end=&quot;10828&quot; data-start=&quot;10804&quot; data-section-id=&quot;fm7w0d&quot;&gt;SELECT = 파일 내용 읽기 권한&lt;/li&gt;
&lt;li data-end=&quot;10859&quot; data-start=&quot;10829&quot; data-section-id=&quot;1gjq52x&quot;&gt;CREATE = 폴더 안에 새 파일 만들기 권한&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;10904&quot; data-start=&quot;10861&quot; data-ke-size=&quot;size16&quot;&gt;즉, 어떤 사용자가 public 안에서 뷰를 만들고 테이블을 읽으려면 보통&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;10968&quot; data-start=&quot;10906&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;10926&quot; data-start=&quot;10906&quot; data-section-id=&quot;16yyzan&quot;&gt;USAGE ON SCHEMA&lt;/li&gt;
&lt;li data-end=&quot;10948&quot; data-start=&quot;10927&quot; data-section-id=&quot;j9cmml&quot;&gt;CREATE ON SCHEMA&lt;/li&gt;
&lt;li data-end=&quot;10968&quot; data-start=&quot;10949&quot; data-section-id=&quot;ogsz3k&quot;&gt;테이블에 대한 SELECT&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;10981&quot; data-start=&quot;10970&quot; data-ke-size=&quot;size16&quot;&gt;이 조합이 필요하다.&lt;/p&gt;
&lt;hr data-end=&quot;10986&quot; data-start=&quot;10983&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;11000&quot; data-start=&quot;10988&quot; data-section-id=&quot;klq8rj&quot; data-ke-size=&quot;size26&quot;&gt;26. 핵심 요약&lt;/h2&gt;
&lt;p data-end=&quot;11025&quot; data-start=&quot;11002&quot; data-ke-size=&quot;size16&quot;&gt;이번 4장의 핵심만 다시 정리하면 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;11522&quot; data-start=&quot;11027&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;11062&quot; data-start=&quot;11027&quot; data-section-id=&quot;114853r&quot;&gt;&lt;b&gt;조인&lt;/b&gt;은 두 릴레이션을 조건에 따라 결합하는 연산이다.&lt;/li&gt;
&lt;li data-end=&quot;11116&quot; data-start=&quot;11063&quot; data-section-id=&quot;sf7t39&quot;&gt;NATURAL JOIN은 편하지만, &lt;b&gt;동명이 속성을 자동 매칭해서 위험할 수 있다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;11156&quot; data-start=&quot;11117&quot; data-section-id=&quot;1rgboja&quot;&gt;실무에서는 보통 JOIN ... ON ... 을 더 많이 쓴다.&lt;/li&gt;
&lt;li data-end=&quot;11217&quot; data-start=&quot;11157&quot; data-section-id=&quot;cqhu9f&quot;&gt;&lt;b&gt;INNER JOIN&lt;/b&gt;은 매칭된 튜플만, &lt;b&gt;OUTER JOIN&lt;/b&gt;은 매칭되지 않은 튜플도 보존한다.&lt;/li&gt;
&lt;li data-end=&quot;11266&quot; data-start=&quot;11218&quot; data-section-id=&quot;y1k6eq&quot;&gt;&lt;b&gt;뷰(View)&lt;/b&gt; 는 가상 릴레이션으로, 복잡한 질의 단순화와 보안에 유용하다.&lt;/li&gt;
&lt;li data-end=&quot;11313&quot; data-start=&quot;11267&quot; data-section-id=&quot;vmo31x&quot;&gt;모든 뷰가 갱신 가능한 건 아니며, 특히 &lt;b&gt;조인/집계 뷰는 갱신이 어렵다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;11364&quot; data-start=&quot;11314&quot; data-section-id=&quot;63vsum&quot;&gt;WITH CHECK OPTION은 뷰 조건을 만족하는 데이터만 삽입/수정하게 한다.&lt;/li&gt;
&lt;li data-end=&quot;11440&quot; data-start=&quot;11365&quot; data-section-id=&quot;1sgtx1j&quot;&gt;제약조건에는 NOT NULL, PRIMARY KEY, UNIQUE, CHECK, FOREIGN KEY 등이 있다.&lt;/li&gt;
&lt;li data-end=&quot;11472&quot; data-start=&quot;11441&quot; data-section-id=&quot;1d855bg&quot;&gt;인덱스는 조회 속도를 높여주지만 갱신 비용도 늘린다.&lt;/li&gt;
&lt;li data-end=&quot;11522&quot; data-start=&quot;11473&quot; data-section-id=&quot;a5b1o9&quot;&gt;권한 관리는 GRANT, REVOKE, ROLE을 중심으로 이해하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;11527&quot; data-start=&quot;11524&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;11535&quot; data-start=&quot;11529&quot; data-section-id=&quot;1h9nj85&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-end=&quot;11588&quot; data-start=&quot;11537&quot; data-ke-size=&quot;size16&quot;&gt;4장은 내용이 많아서 처음 보면 좀 산만하게 느껴질 수 있는데, 큰 흐름은 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;11713&quot; data-start=&quot;11590&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;11615&quot; data-start=&quot;11590&quot; data-section-id=&quot;1xopsz4&quot;&gt;&lt;b&gt;조인&lt;/b&gt;으로 여러 테이블을 연결하고&lt;/li&gt;
&lt;li data-end=&quot;11638&quot; data-start=&quot;11616&quot; data-section-id=&quot;5asn44&quot;&gt;&lt;b&gt;뷰&lt;/b&gt;로 복잡한 질의를 감추고&lt;/li&gt;
&lt;li data-end=&quot;11668&quot; data-start=&quot;11639&quot; data-section-id=&quot;19rtt7h&quot;&gt;&lt;b&gt;제약조건&lt;/b&gt;으로 이상한 데이터 입력을 막고&lt;/li&gt;
&lt;li data-end=&quot;11692&quot; data-start=&quot;11669&quot; data-section-id=&quot;3nug9f&quot;&gt;&lt;b&gt;인덱스&lt;/b&gt;로 조회 성능을 높이고&lt;/li&gt;
&lt;li data-end=&quot;11713&quot; data-start=&quot;11693&quot; data-section-id=&quot;wvsezg&quot;&gt;&lt;b&gt;권한 관리&lt;/b&gt;로 보안을 지킨다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;11768&quot; data-start=&quot;11715&quot; data-ke-size=&quot;size16&quot;&gt;4장은 &amp;ldquo;데이터베이스를 실제로 운영하려면 필요한 기능들&amp;rdquo;을 한 번에 모아놓은 느낌이다.&lt;/p&gt;
&lt;p data-end=&quot;11851&quot; data-start=&quot;11770&quot; data-ke-size=&quot;size16&quot;&gt;여기까지 이해하면 이제 SQL이 단순 조회 언어가 아니라 &lt;b&gt;구조, 성능, 무결성, 보안까지 다루는 언어&lt;/b&gt;라는 게 확실히 보이기 시작한다.&lt;/p&gt;</description>
      <category>CS STUDY/DB</category>
      <category>db권한관리</category>
      <category>SQL기초</category>
      <category>SQL뷰</category>
      <category>sql조인</category>
      <category>데이터베이스정리</category>
      <category>데이터베이스제약조건</category>
      <category>외래키</category>
      <category>인덱스</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/22</guid>
      <comments>https://pixxlix.tistory.com/22#entry22comment</comments>
      <pubDate>Thu, 12 Mar 2026 02:34:58 +0900</pubDate>
    </item>
    <item>
      <title>[데이터베이스 시스템 7판 3장] SQL 타입, 제약조건, 하위 질의(Subquery) 완벽 정리 | IN, EXISTS, WITH까지 이해하기</title>
      <link>https://pixxlix.tistory.com/21</link>
      <description>&lt;h1 data-end=&quot;478&quot; data-start=&quot;464&quot; data-section-id=&quot;fkq2h2&quot;&gt;1. SQL 언어 구성&lt;/h1&gt;
&lt;p data-end=&quot;533&quot; data-start=&quot;480&quot; data-ke-size=&quot;size16&quot;&gt;SQL은 단순히 데이터를 조회하는 언어가 아니라&lt;br /&gt;&lt;b&gt;데이터베이스 전체를 관리하는 언어&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;564&quot; data-start=&quot;535&quot; data-ke-size=&quot;size16&quot;&gt;보통 SQL 기능은 다음 네 가지로 나눠서 이해한다.&lt;/p&gt;
&lt;h3 data-end=&quot;604&quot; data-start=&quot;566&quot; data-section-id=&quot;a3pxxd&quot; data-ke-size=&quot;size23&quot;&gt;1️⃣ DDL (Data Definition Language)&lt;/h3&gt;
&lt;p data-end=&quot;630&quot; data-start=&quot;606&quot; data-ke-size=&quot;size16&quot;&gt;데이터베이스 &lt;b&gt;구조를 정의&lt;/b&gt;하는 언어다.&lt;/p&gt;
&lt;p data-end=&quot;638&quot; data-start=&quot;632&quot; data-ke-size=&quot;size16&quot;&gt;대표 명령어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;669&quot; data-start=&quot;640&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;650&quot; data-start=&quot;640&quot; data-section-id=&quot;2674vg&quot;&gt;CREATE&lt;/li&gt;
&lt;li data-end=&quot;660&quot; data-start=&quot;651&quot; data-section-id=&quot;oonqyu&quot;&gt;ALTER&lt;/li&gt;
&lt;li data-end=&quot;669&quot; data-start=&quot;661&quot; data-section-id=&quot;17v9li9&quot;&gt;DROP&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;688&quot; data-start=&quot;671&quot; data-ke-size=&quot;size16&quot;&gt;DDL은 다음 작업을 수행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;742&quot; data-start=&quot;690&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;698&quot; data-start=&quot;690&quot; data-section-id=&quot;1rnp5bh&quot;&gt;스키마 정의&lt;/li&gt;
&lt;li data-end=&quot;707&quot; data-start=&quot;699&quot; data-section-id=&quot;1w36hbc&quot;&gt;테이블 생성&lt;/li&gt;
&lt;li data-end=&quot;721&quot; data-start=&quot;708&quot; data-section-id=&quot;1ehjxf9&quot;&gt;무결성 제약조건 정의&lt;/li&gt;
&lt;li data-end=&quot;734&quot; data-start=&quot;722&quot; data-section-id=&quot;1o79w3d&quot;&gt;뷰(View) 정의&lt;/li&gt;
&lt;li data-end=&quot;742&quot; data-start=&quot;735&quot; data-section-id=&quot;6uv62c&quot;&gt;권한 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;745&quot; data-start=&quot;744&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre id=&quot;code_1773249972976&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE student(
id INT PRIMARY KEY,
name VARCHAR(20)
);&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;832&quot; data-start=&quot;829&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;874&quot; data-start=&quot;834&quot; data-section-id=&quot;1yp95mp&quot; data-ke-size=&quot;size23&quot;&gt;2️⃣ DML (Data Manipulation Language)&lt;/h3&gt;
&lt;p data-end=&quot;907&quot; data-start=&quot;876&quot; data-ke-size=&quot;size16&quot;&gt;데이터를 &lt;b&gt;조회, 수정, 삽입, 삭제&lt;/b&gt;하는 언어다.&lt;/p&gt;
&lt;p data-end=&quot;915&quot; data-start=&quot;909&quot; data-ke-size=&quot;size16&quot;&gt;대표 명령어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;960&quot; data-start=&quot;917&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;927&quot; data-start=&quot;917&quot; data-section-id=&quot;1gasv6o&quot;&gt;SELECT&lt;/li&gt;
&lt;li data-end=&quot;938&quot; data-start=&quot;928&quot; data-section-id=&quot;dorilb&quot;&gt;INSERT&lt;/li&gt;
&lt;li data-end=&quot;949&quot; data-start=&quot;939&quot; data-section-id=&quot;q60uyh&quot;&gt;UPDATE&lt;/li&gt;
&lt;li data-end=&quot;960&quot; data-start=&quot;950&quot; data-section-id=&quot;o4m3pt&quot;&gt;DELETE&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;963&quot; data-start=&quot;962&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre id=&quot;code_1773250000371&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT * FROM student;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1003&quot; data-start=&quot;1000&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1020&quot; data-start=&quot;1005&quot; data-section-id=&quot;1trccw0&quot; data-ke-size=&quot;size23&quot;&gt;3️⃣ 트랜잭션 제어&lt;/h3&gt;
&lt;p data-end=&quot;1056&quot; data-start=&quot;1022&quot; data-ke-size=&quot;size16&quot;&gt;데이터 변경 작업을 &lt;b&gt;원자적으로 처리&lt;/b&gt;하기 위해 사용한다.&lt;/p&gt;
&lt;p data-end=&quot;1064&quot; data-start=&quot;1058&quot; data-ke-size=&quot;size16&quot;&gt;대표 명령어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1099&quot; data-start=&quot;1066&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1076&quot; data-start=&quot;1066&quot; data-section-id=&quot;1q5os55&quot;&gt;COMMIT&lt;/li&gt;
&lt;li data-end=&quot;1089&quot; data-start=&quot;1077&quot; data-section-id=&quot;ofkroe&quot;&gt;ROLLBACK&lt;/li&gt;
&lt;li data-end=&quot;1099&quot; data-start=&quot;1090&quot; data-section-id=&quot;mwvb9r&quot;&gt;BEGIN&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1102&quot; data-start=&quot;1101&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;pre id=&quot;code_1773249990725&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;BEGIN;
UPDATE account SET balance = balance - 100;
COMMIT;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1178&quot; data-start=&quot;1175&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1200&quot; data-start=&quot;1180&quot; data-section-id=&quot;1mq5igu&quot; data-ke-size=&quot;size23&quot;&gt;4️⃣ Embedded SQL&lt;/h3&gt;
&lt;p data-end=&quot;1238&quot; data-start=&quot;1202&quot; data-ke-size=&quot;size16&quot;&gt;SQL을 &lt;b&gt;프로그래밍 언어 안에 포함해서 사용하는 방식&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;1241&quot; data-start=&quot;1240&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1295&quot; data-start=&quot;1243&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1256&quot; data-start=&quot;1243&quot; data-section-id=&quot;1v0fej4&quot;&gt;Java + JDBC&lt;/li&gt;
&lt;li data-end=&quot;1276&quot; data-start=&quot;1257&quot; data-section-id=&quot;15rfqx0&quot;&gt;Python + psycopg2&lt;/li&gt;
&lt;li data-end=&quot;1295&quot; data-start=&quot;1277&quot; data-section-id=&quot;ysyk80&quot;&gt;C + Embedded SQL&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1325&quot; data-start=&quot;1297&quot; data-ke-size=&quot;size16&quot;&gt;즉 SQL을 프로그램 로직 안에서 실행하는 구조다.&lt;/p&gt;
&lt;hr data-end=&quot;1330&quot; data-start=&quot;1327&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;1350&quot; data-start=&quot;1332&quot; data-section-id=&quot;befx7x&quot;&gt;2. SQL 기본 데이터 타입&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;425&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdOOs8/dJMcagdFiA0/5GCb6ujZ0K4a1bDVQBY65K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdOOs8/dJMcagdFiA0/5GCb6ujZ0K4a1bDVQBY65K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdOOs8/dJMcagdFiA0/5GCb6ujZ0K4a1bDVQBY65K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdOOs8%2FdJMcagdFiA0%2F5GCb6ujZ0K4a1bDVQBY65K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;425&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;425&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1376&quot; data-start=&quot;1352&quot; data-ke-size=&quot;size16&quot;&gt;SQL에서는 다양한 데이터 타입을 제공한다.&lt;/p&gt;
&lt;h2 data-end=&quot;1387&quot; data-start=&quot;1378&quot; data-section-id=&quot;uyj6fw&quot; data-ke-size=&quot;size26&quot;&gt;문자열 타입&lt;/h2&gt;
&lt;h3 data-end=&quot;1400&quot; data-start=&quot;1389&quot; data-section-id=&quot;k21ipd&quot; data-ke-size=&quot;size23&quot;&gt;char(n)&lt;/h3&gt;
&lt;p data-end=&quot;1414&quot; data-start=&quot;1402&quot; data-ke-size=&quot;size16&quot;&gt;고정 길이 문자열이다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;char&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1468&quot; data-start=&quot;1437&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1453&quot; data-start=&quot;1437&quot; data-section-id=&quot;osixex&quot;&gt;항상 10자리 공간을 사용&lt;/li&gt;
&lt;li data-end=&quot;1468&quot; data-start=&quot;1454&quot; data-section-id=&quot;o4jsl&quot;&gt;부족하면 공백으로 채움&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1473&quot; data-start=&quot;1470&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1489&quot; data-start=&quot;1475&quot; data-section-id=&quot;1d6mwk4&quot; data-ke-size=&quot;size23&quot;&gt;varchar(n)&lt;/h3&gt;
&lt;p data-end=&quot;1503&quot; data-start=&quot;1491&quot; data-ke-size=&quot;size16&quot;&gt;가변 길이 문자열이다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1558&quot; data-start=&quot;1529&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1537&quot; data-start=&quot;1529&quot; data-section-id=&quot;rysi86&quot;&gt;최대 20자&lt;/li&gt;
&lt;li data-end=&quot;1558&quot; data-start=&quot;1538&quot; data-section-id=&quot;1yirq25&quot;&gt;실제 저장 길이는 문자열 길이만큼&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1591&quot; data-start=&quot;1560&quot; data-ke-size=&quot;size16&quot;&gt;실무에서는 대부분 &lt;b&gt;varchar&lt;/b&gt;를 많이 사용한다.&lt;/p&gt;
&lt;hr data-end=&quot;1596&quot; data-start=&quot;1593&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1606&quot; data-start=&quot;1598&quot; data-section-id=&quot;heolgb&quot; data-ke-size=&quot;size26&quot;&gt;숫자 타입&lt;/h2&gt;
&lt;h3 data-end=&quot;1615&quot; data-start=&quot;1608&quot; data-section-id=&quot;1xxg88l&quot; data-ke-size=&quot;size23&quot;&gt;int&lt;/h3&gt;
&lt;p data-end=&quot;1622&quot; data-start=&quot;1617&quot; data-ke-size=&quot;size16&quot;&gt;정수 타입&lt;/p&gt;
&lt;p data-end=&quot;1625&quot; data-start=&quot;1624&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;age &lt;/span&gt;&lt;span&gt;INT&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1650&quot; data-start=&quot;1647&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1664&quot; data-start=&quot;1652&quot; data-section-id=&quot;1qcfiq2&quot; data-ke-size=&quot;size23&quot;&gt;smallint&lt;/h3&gt;
&lt;p data-end=&quot;1674&quot; data-start=&quot;1666&quot; data-ke-size=&quot;size16&quot;&gt;작은 범위 정수&lt;/p&gt;
&lt;p data-end=&quot;1695&quot; data-start=&quot;1676&quot; data-ke-size=&quot;size16&quot;&gt;메모리를 조금 더 절약할 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;1700&quot; data-start=&quot;1697&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1719&quot; data-start=&quot;1702&quot; data-section-id=&quot;qu36qc&quot; data-ke-size=&quot;size23&quot;&gt;numeric(p, d)&lt;/h3&gt;
&lt;p data-end=&quot;1732&quot; data-start=&quot;1721&quot; data-ke-size=&quot;size16&quot;&gt;고정 소수점 숫자다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1760&quot; data-start=&quot;1734&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1746&quot; data-start=&quot;1734&quot; data-section-id=&quot;2vpm91&quot;&gt;p : 전체 자릿수&lt;/li&gt;
&lt;li data-end=&quot;1760&quot; data-start=&quot;1747&quot; data-section-id=&quot;e93xs5&quot;&gt;d : 소수점 자릿수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1763&quot; data-start=&quot;1762&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;numeric&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1794&quot; data-start=&quot;1790&quot; data-ke-size=&quot;size16&quot;&gt;가능 값&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;123456.78&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1848&quot; data-start=&quot;1815&quot; data-ke-size=&quot;size16&quot;&gt;금액 계산 같은 &lt;b&gt;정확한 계산&lt;/b&gt;이 필요한 경우 사용한다.&lt;/p&gt;
&lt;hr data-end=&quot;1853&quot; data-start=&quot;1850&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1863&quot; data-start=&quot;1855&quot; data-section-id=&quot;yocfuk&quot; data-ke-size=&quot;size23&quot;&gt;real&lt;/h3&gt;
&lt;p data-end=&quot;1873&quot; data-start=&quot;1865&quot; data-ke-size=&quot;size16&quot;&gt;부동소수점 숫자&lt;/p&gt;
&lt;p data-end=&quot;1888&quot; data-start=&quot;1875&quot; data-ke-size=&quot;size16&quot;&gt;약 &lt;b&gt;7자리 정밀도&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;1893&quot; data-start=&quot;1890&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1915&quot; data-start=&quot;1895&quot; data-section-id=&quot;5mmkp1&quot; data-ke-size=&quot;size23&quot;&gt;double precision&lt;/h3&gt;
&lt;p data-end=&quot;1931&quot; data-start=&quot;1917&quot; data-ke-size=&quot;size16&quot;&gt;더 높은 정밀도 부동소수점&lt;/p&gt;
&lt;p data-end=&quot;1946&quot; data-start=&quot;1933&quot; data-ke-size=&quot;size16&quot;&gt;약 &lt;b&gt;15~17자리&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1963&quot; data-start=&quot;1948&quot; data-ke-size=&quot;size16&quot;&gt;과학 계산 등에서 사용한다.&lt;/p&gt;
&lt;hr data-end=&quot;1968&quot; data-start=&quot;1965&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1982&quot; data-start=&quot;1970&quot; data-section-id=&quot;7yht2x&quot; data-ke-size=&quot;size23&quot;&gt;float(n)&lt;/h3&gt;
&lt;p data-end=&quot;2005&quot; data-start=&quot;1984&quot; data-ke-size=&quot;size16&quot;&gt;정밀도를 지정하는 부동소수점 타입이다.&lt;/p&gt;
&lt;hr data-end=&quot;2063&quot; data-start=&quot;2060&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;2102&quot; data-start=&quot;2065&quot; data-section-id=&quot;exh2kg&quot;&gt;3. SQL 제약조건 (Integrity Constraints)&lt;/h1&gt;
&lt;p data-end=&quot;2137&quot; data-start=&quot;2104&quot; data-ke-size=&quot;size16&quot;&gt;제약조건은 데이터의 &lt;b&gt;무결성&lt;/b&gt;을 보장하기 위해 사용한다.&lt;/p&gt;
&lt;p data-end=&quot;2157&quot; data-start=&quot;2139&quot; data-ke-size=&quot;size16&quot;&gt;대표적인 제약조건은 다음과 같다.&lt;/p&gt;
&lt;hr data-end=&quot;2162&quot; data-start=&quot;2159&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2175&quot; data-start=&quot;2164&quot; data-section-id=&quot;18kb16z&quot; data-ke-size=&quot;size26&quot;&gt;NOT NULL&lt;/h2&gt;
&lt;p data-end=&quot;2203&quot; data-start=&quot;2177&quot; data-ke-size=&quot;size16&quot;&gt;속성이 NULL 값을 가질 수 없도록 제한한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;name &lt;/span&gt;&lt;span&gt;varchar&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;NOT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NULL&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2246&quot; data-start=&quot;2243&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2256&quot; data-start=&quot;2248&quot; data-section-id=&quot;xbmh9f&quot; data-ke-size=&quot;size26&quot;&gt;CHECK&lt;/h2&gt;
&lt;p data-end=&quot;2273&quot; data-start=&quot;2258&quot; data-ke-size=&quot;size16&quot;&gt;속성 값의 범위를 제한한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;CHECK&lt;/span&gt;&lt;span&gt; (salary &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2307&quot; data-start=&quot;2306&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;age &lt;/span&gt;&lt;span&gt;INT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CHECK&lt;/span&gt;&lt;span&gt;(age &lt;/span&gt;&lt;span&gt;&amp;gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2348&quot; data-start=&quot;2345&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2364&quot; data-start=&quot;2350&quot; data-section-id=&quot;hsvq2m&quot; data-ke-size=&quot;size26&quot;&gt;PRIMARY KEY&lt;/h2&gt;
&lt;p data-end=&quot;2386&quot; data-start=&quot;2366&quot; data-ke-size=&quot;size16&quot;&gt;테이블의 &lt;b&gt;기본 키&lt;/b&gt;를 정의한다.&lt;/p&gt;
&lt;p data-end=&quot;2390&quot; data-start=&quot;2388&quot; data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2409&quot; data-start=&quot;2392&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2399&quot; data-start=&quot;2392&quot; data-section-id=&quot;1rfpts4&quot;&gt;중복 불가&lt;/li&gt;
&lt;li data-end=&quot;2409&quot; data-start=&quot;2400&quot; data-section-id=&quot;17gvzi3&quot;&gt;NULL 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2412&quot; data-start=&quot;2411&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;PRIMARY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KEY&lt;/span&gt;&lt;span&gt;(id)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2453&quot; data-start=&quot;2442&quot; data-ke-size=&quot;size16&quot;&gt;복합 키도 가능하다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;PRIMARY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KEY&lt;/span&gt;&lt;span&gt;(student_id, course_id)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2505&quot; data-start=&quot;2502&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2521&quot; data-start=&quot;2507&quot; data-section-id=&quot;xnimcs&quot; data-ke-size=&quot;size26&quot;&gt;FOREIGN KEY&lt;/h2&gt;
&lt;p data-end=&quot;2542&quot; data-start=&quot;2523&quot; data-ke-size=&quot;size16&quot;&gt;다른 테이블의 기본 키를 참조한다.&lt;/p&gt;
&lt;p data-end=&quot;2545&quot; data-start=&quot;2544&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;FOREIGN&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;KEY&lt;/span&gt;&lt;span&gt;(dept_name)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;REFERENCES&lt;/span&gt;&lt;span&gt; department&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2608&quot; data-start=&quot;2604&quot; data-ke-size=&quot;size16&quot;&gt;외래키는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2627&quot; data-start=&quot;2610&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2619&quot; data-start=&quot;2610&quot; data-section-id=&quot;17h0pqu&quot;&gt;NULL 가능&lt;/li&gt;
&lt;li data-end=&quot;2627&quot; data-start=&quot;2620&quot; data-section-id=&quot;1rfo3l5&quot;&gt;중복 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2662&quot; data-start=&quot;2629&quot; data-ke-size=&quot;size16&quot;&gt;하지만 반드시 &lt;b&gt;참조 테이블에 존재하는 값이어야 한다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;2667&quot; data-start=&quot;2664&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;2690&quot; data-start=&quot;2669&quot; data-section-id=&quot;15kloov&quot;&gt;4. 하위 질의 (Subquery)&lt;/h1&gt;
&lt;p data-end=&quot;2730&quot; data-start=&quot;2692&quot; data-ke-size=&quot;size16&quot;&gt;하위 질의는 &lt;b&gt;SQL 안에 들어가는 또 다른 SELECT문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;2733&quot; data-start=&quot;2732&quot; data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT 안에 SELECT&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2772&quot; data-start=&quot;2761&quot; data-ke-size=&quot;size16&quot;&gt;구조라고 보면 된다.&lt;/p&gt;
&lt;p data-end=&quot;2775&quot; data-start=&quot;2774&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; salary &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; avg(salary) &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2881&quot; data-start=&quot;2876&quot; data-ke-size=&quot;size16&quot;&gt;이 쿼리는&lt;/p&gt;
&lt;p data-end=&quot;2906&quot; data-start=&quot;2883&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;평균 급여보다 높은 교수&lt;/b&gt;를 찾는다.&lt;/p&gt;
&lt;hr data-end=&quot;2911&quot; data-start=&quot;2908&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2934&quot; data-start=&quot;2913&quot; data-section-id=&quot;160ig95&quot; data-ke-size=&quot;size26&quot;&gt;하위 질의가 들어갈 수 있는 위치&lt;/h2&gt;
&lt;h3 data-end=&quot;2946&quot; data-start=&quot;2936&quot; data-section-id=&quot;plbc0e&quot; data-ke-size=&quot;size23&quot;&gt;SELECT&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; name,&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; avg(salary) &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3037&quot; data-start=&quot;3034&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3047&quot; data-start=&quot;3039&quot; data-section-id=&quot;yn9kds&quot; data-ke-size=&quot;size23&quot;&gt;FROM&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; dept_name, avg(salary)&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GROUP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;BY&lt;/span&gt;&lt;span&gt; dept_name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; dept_avg;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3175&quot; data-start=&quot;3172&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3186&quot; data-start=&quot;3177&quot; data-section-id=&quot;75r13f&quot; data-ke-size=&quot;size23&quot;&gt;WHERE&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FR&lt;/span&gt;&lt;span&gt;OM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;IN&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (SE&lt;/span&gt;&lt;span&gt;LECT&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; department);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3292&quot; data-start=&quot;3289&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;3310&quot; data-start=&quot;3294&quot; data-section-id=&quot;1nvwdwn&quot;&gt;5. IN / NOT IN&lt;/h1&gt;
&lt;h2 data-end=&quot;3317&quot; data-start=&quot;3312&quot; data-section-id=&quot;1mnyo2&quot; data-ke-size=&quot;size26&quot;&gt;IN&lt;/h2&gt;
&lt;p data-end=&quot;3341&quot; data-start=&quot;3319&quot; data-ke-size=&quot;size16&quot;&gt;값이 &lt;b&gt;집합에 존재하는지 확인&lt;/b&gt;한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;IN&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; department);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3447&quot; data-start=&quot;3444&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3458&quot; data-start=&quot;3449&quot; data-section-id=&quot;117wn7b&quot; data-ke-size=&quot;size26&quot;&gt;NOT IN&lt;/h2&gt;
&lt;p data-end=&quot;3483&quot; data-start=&quot;3460&quot; data-ke-size=&quot;size16&quot;&gt;집합에 &lt;b&gt;존재하지 않는 값&lt;/b&gt;을 찾는다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;NOT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;IN&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; dept_name &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; department);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3567&quot; data-start=&quot;3562&quot; data-ke-size=&quot;size16&quot;&gt;주의할 점&lt;/p&gt;
&lt;p data-end=&quot;3614&quot; data-start=&quot;3569&quot; data-ke-size=&quot;size16&quot;&gt;NOT IN은 &lt;b&gt;NULL이 포함되면 결과가 예상과 다르게 나올 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;3628&quot; data-start=&quot;3616&quot; data-ke-size=&quot;size16&quot;&gt;그래서 실무에서는 보통&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;NOT EXISTS&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3662&quot; data-start=&quot;3650&quot; data-ke-size=&quot;size16&quot;&gt;를 더 많이 사용한다.&lt;/p&gt;
&lt;hr data-end=&quot;3667&quot; data-start=&quot;3664&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;3693&quot; data-start=&quot;3669&quot; data-section-id=&quot;1h0qvxl&quot;&gt;6. ANY / SOME / ALL 비교&lt;/h1&gt;
&lt;p data-end=&quot;3718&quot; data-start=&quot;3695&quot; data-ke-size=&quot;size16&quot;&gt;이 부분은 노트에 조금 부족해서 보완한다.&lt;/p&gt;
&lt;hr data-end=&quot;3723&quot; data-start=&quot;3720&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3738&quot; data-start=&quot;3725&quot; data-section-id=&quot;110b7ew&quot; data-ke-size=&quot;size26&quot;&gt;SOME / ANY&lt;/h2&gt;
&lt;p data-end=&quot;3750&quot; data-start=&quot;3740&quot; data-ke-size=&quot;size16&quot;&gt;둘은 같은 의미다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;= SOME&lt;/span&gt;&lt;br /&gt;&lt;span&gt;= ANY&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3775&quot; data-start=&quot;3774&quot; data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;p data-end=&quot;3801&quot; data-start=&quot;3777&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;집합 안에 하나라도 만족하면 TRUE&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;3804&quot; data-start=&quot;3803&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;salary &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ANY&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; salary &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3872&quot; data-start=&quot;3869&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3880&quot; data-start=&quot;3874&quot; data-section-id=&quot;1hrylms&quot; data-ke-size=&quot;size26&quot;&gt;ALL&lt;/h2&gt;
&lt;p data-end=&quot;3901&quot; data-start=&quot;3882&quot; data-ke-size=&quot;size16&quot;&gt;집합의 &lt;b&gt;모든 값과 비교&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-end=&quot;3904&quot; data-start=&quot;3903&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;salary &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ALL&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; salary &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;3971&quot; data-start=&quot;3969&quot; data-ke-size=&quot;size16&quot;&gt;의미&lt;/p&gt;
&lt;p data-end=&quot;3994&quot; data-start=&quot;3973&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모든 교수보다 급여가 높은 사람&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;3999&quot; data-start=&quot;3996&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4011&quot; data-start=&quot;4001&quot; data-section-id=&quot;1ilzxmz&quot; data-ke-size=&quot;size23&quot;&gt;중요한 비교&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;표현의미
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;4072&quot; data-start=&quot;4013&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;4072&quot; data-start=&quot;4035&quot;&gt;
&lt;tr data-end=&quot;4051&quot; data-start=&quot;4035&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4045&quot; data-start=&quot;4035&quot;&gt;= ANY&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4051&quot; data-start=&quot;4045&quot;&gt;IN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;4072&quot; data-start=&quot;4052&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4062&quot; data-start=&quot;4052&quot;&gt;&amp;ne; ALL&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4072&quot; data-start=&quot;4062&quot;&gt;NOT IN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;4077&quot; data-start=&quot;4074&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;4103&quot; data-start=&quot;4079&quot; data-section-id=&quot;1m5bi11&quot;&gt;7. EXISTS / NOT EXISTS&lt;/h1&gt;
&lt;h2 data-end=&quot;4114&quot; data-start=&quot;4105&quot; data-section-id=&quot;x4s1ut&quot; data-ke-size=&quot;size26&quot;&gt;EXISTS&lt;/h2&gt;
&lt;p data-end=&quot;4143&quot; data-start=&quot;4116&quot; data-ke-size=&quot;size16&quot;&gt;하위 질의 결과가 &lt;b&gt;비어있지 않으면 TRUE&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;4146&quot; data-start=&quot;4145&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor i&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXISTS&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; teaches t&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; i.id &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; t.id&lt;/span&gt;&lt;br /&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4269&quot; data-start=&quot;4268&quot; data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;p data-end=&quot;4288&quot; data-start=&quot;4271&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;강의를 하는 교수만 조회&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;4293&quot; data-start=&quot;4290&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4308&quot; data-start=&quot;4295&quot; data-section-id=&quot;d4tkk0&quot; data-ke-size=&quot;size26&quot;&gt;NOT EXISTS&lt;/h2&gt;
&lt;p data-end=&quot;4335&quot; data-start=&quot;4310&quot; data-ke-size=&quot;size16&quot;&gt;하위 질의 결과가 &lt;b&gt;비어 있으면 TRUE&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;4338&quot; data-start=&quot;4337&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor i&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;NOT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EXISTS&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; teaches t&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; i.id &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; t.id&lt;/span&gt;&lt;br /&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4465&quot; data-start=&quot;4464&quot; data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;p data-end=&quot;4483&quot; data-start=&quot;4467&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;강의를 하지 않는 교수&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;4488&quot; data-start=&quot;4485&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;4507&quot; data-start=&quot;4490&quot; data-section-id=&quot;1kkrddu&quot;&gt;8. FROM 절 하위 질의&lt;/h1&gt;
&lt;p data-end=&quot;4552&quot; data-start=&quot;4509&quot; data-ke-size=&quot;size16&quot;&gt;FROM 절에서 하위 질의를 쓰면&lt;br /&gt;&lt;b&gt;임시 테이블처럼 사용&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;4555&quot; data-start=&quot;4554&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; dept_name, avg(salary)&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GROUP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;BY&lt;/span&gt;&lt;span&gt; dept_name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; dept_avg;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4709&quot; data-start=&quot;4680&quot; data-ke-size=&quot;size16&quot;&gt;이때 반드시 &lt;b&gt;별칭(alias)&lt;/b&gt; 을 줘야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;4714&quot; data-start=&quot;4711&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;4733&quot; data-start=&quot;4716&quot; data-section-id=&quot;gzu65k&quot;&gt;9. WITH 절 (CTE)&lt;/h1&gt;
&lt;p data-end=&quot;4766&quot; data-start=&quot;4735&quot; data-ke-size=&quot;size16&quot;&gt;WITH 절은 &lt;b&gt;임시 릴레이션을 정의하는 기능&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;4799&quot; data-start=&quot;4768&quot; data-ke-size=&quot;size16&quot;&gt;Common Table Expression이라고 부른다.&lt;/p&gt;
&lt;p data-end=&quot;4802&quot; data-start=&quot;4801&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;WITH&lt;/span&gt;&lt;span&gt; dept_avg &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; dept_name, avg(salary) &lt;/span&gt;&lt;span&gt;AS&lt;/span&gt;&lt;span&gt; avg_salary&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;GROUP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;BY&lt;/span&gt;&lt;span&gt; dept_name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; dept_avg&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; avg_salary &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;50000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;4982&quot; data-start=&quot;4980&quot; data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5023&quot; data-start=&quot;4984&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4999&quot; data-start=&quot;4984&quot; data-section-id=&quot;6r4ro8&quot;&gt;복잡한 쿼리 가독성 개선&lt;/li&gt;
&lt;li data-end=&quot;5008&quot; data-start=&quot;5000&quot; data-section-id=&quot;1dmfwpw&quot;&gt;재사용 가능&lt;/li&gt;
&lt;li data-end=&quot;5023&quot; data-start=&quot;5009&quot; data-section-id=&quot;19e6cs0&quot;&gt;단계적 질의 작성 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;5057&quot; data-start=&quot;5025&quot; data-ke-size=&quot;size16&quot;&gt;요즘 SQL에서는 &lt;b&gt;WITH 절 사용이 매우 흔하다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;5062&quot; data-start=&quot;5059&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;5079&quot; data-start=&quot;5064&quot; data-section-id=&quot;13mzidd&quot;&gt;10. 스칼라 하위 질의&lt;/h1&gt;
&lt;p data-end=&quot;5114&quot; data-start=&quot;5081&quot; data-ke-size=&quot;size16&quot;&gt;스칼라 하위 질의는 &lt;b&gt;단일 값만 반환하는 하위 질의&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;5117&quot; data-start=&quot;5116&quot; data-ke-size=&quot;size16&quot;&gt;즉&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5136&quot; data-start=&quot;5119&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5127&quot; data-start=&quot;5119&quot; data-section-id=&quot;jb90qo&quot;&gt;하나의 튜플&lt;/li&gt;
&lt;li data-end=&quot;5136&quot; data-start=&quot;5128&quot; data-section-id=&quot;jbb5zg&quot;&gt;하나의 속성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;5148&quot; data-start=&quot;5138&quot; data-ke-size=&quot;size16&quot;&gt;만 반환해야 한다.&lt;/p&gt;
&lt;p data-end=&quot;5151&quot; data-start=&quot;5150&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; salary &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; avg(salary) &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5255&quot; data-start=&quot;5252&quot; data-ke-size=&quot;size16&quot;&gt;여기서&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;avg(salary)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5290&quot; data-start=&quot;5278&quot; data-ke-size=&quot;size16&quot;&gt;결과는 하나의 값이다.&lt;/p&gt;
&lt;p data-end=&quot;5310&quot; data-start=&quot;5292&quot; data-ke-size=&quot;size16&quot;&gt;그래서 스칼라 하위 질의가 된다.&lt;/p&gt;
&lt;hr data-end=&quot;5315&quot; data-start=&quot;5312&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5323&quot; data-start=&quot;5317&quot; data-section-id=&quot;1hrq9cy&quot; data-ke-size=&quot;size23&quot;&gt;주의&lt;/h3&gt;
&lt;p data-end=&quot;5357&quot; data-start=&quot;5325&quot; data-ke-size=&quot;size16&quot;&gt;하위 질의가 &lt;b&gt;여러 튜플을 반환하면 오류가 발생한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;5360&quot; data-start=&quot;5359&quot; data-ke-size=&quot;size16&quot;&gt;예&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;br /&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor&lt;/span&gt;&lt;br /&gt;&lt;span&gt;WHERE&lt;/span&gt;&lt;span&gt; salary &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;br /&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;SELECT&lt;/span&gt;&lt;span&gt; salary &lt;/span&gt;&lt;span&gt;FROM&lt;/span&gt;&lt;span&gt; instructor);&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;5493&quot; data-start=&quot;5456&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 여러 결과가 나오기 때문에 &lt;b&gt;런타임 오류&lt;/b&gt;가 발생한다.&lt;/p&gt;
&lt;hr data-end=&quot;5498&quot; data-start=&quot;5495&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 data-end=&quot;5507&quot; data-start=&quot;5500&quot; data-section-id=&quot;10ogz9i&quot;&gt;핵심 정리&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5865&quot; data-start=&quot;5526&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5563&quot; data-start=&quot;5526&quot; data-section-id=&quot;p495d5&quot;&gt;SQL은 &lt;b&gt;DDL, DML, 트랜잭션 제어&lt;/b&gt; 기능을 가진다.&lt;/li&gt;
&lt;li data-end=&quot;5605&quot; data-start=&quot;5564&quot; data-section-id=&quot;9j50u8&quot;&gt;SQL에는 &lt;b&gt;문자열, 정수, 부동소수점, 고정소수점&lt;/b&gt; 타입이 있다.&lt;/li&gt;
&lt;li data-end=&quot;5666&quot; data-start=&quot;5606&quot; data-section-id=&quot;317pto&quot;&gt;제약조건은 &lt;b&gt;NOT NULL, CHECK, PRIMARY KEY, FOREIGN KEY&lt;/b&gt; 등이 있다.&lt;/li&gt;
&lt;li data-end=&quot;5705&quot; data-start=&quot;5667&quot; data-section-id=&quot;1xo9uwe&quot;&gt;&lt;b&gt;Subquery&lt;/b&gt;는 SQL 안에 들어가는 SELECT문이다.&lt;/li&gt;
&lt;li data-end=&quot;5750&quot; data-start=&quot;5706&quot; data-section-id=&quot;1rblush&quot;&gt;하위 질의는 SELECT, FROM, WHERE에 사용 가능하다.&lt;/li&gt;
&lt;li data-end=&quot;5794&quot; data-start=&quot;5751&quot; data-section-id=&quot;kz1qo6&quot;&gt;IN, EXISTS, ANY, ALL은 집합 비교 연산이다.&lt;/li&gt;
&lt;li data-end=&quot;5829&quot; data-start=&quot;5795&quot; data-section-id=&quot;1lxoj3i&quot;&gt;WITH 절은 임시 테이블(CTE)을 만드는 기능이다.&lt;/li&gt;
&lt;li data-end=&quot;5865&quot; data-start=&quot;5830&quot; data-section-id=&quot;uiwgvs&quot;&gt;&lt;b&gt;스칼라 하위 질의는 반드시 단일 값만 반환해야 한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;5880&quot; data-start=&quot;5872&quot; data-section-id=&quot;1xyzdl5&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-end=&quot;5956&quot; data-start=&quot;5882&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS STUDY/DB</category>
      <category>EXISTS</category>
      <category>SQL기초</category>
      <category>sql서브쿼리</category>
      <category>sql제약조건</category>
      <category>SQL타입</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/21</guid>
      <comments>https://pixxlix.tistory.com/21#entry21comment</comments>
      <pubDate>Thu, 12 Mar 2026 02:29:33 +0900</pubDate>
    </item>
    <item>
      <title>[데이터베이스 시스템 7판 2장] 관계형 데이터베이스 구조 정리 | 릴레이션, 키, 관계대수, 무결성까지 한 번에 이해하기</title>
      <link>https://pixxlix.tistory.com/20</link>
      <description>&lt;h2 data-end=&quot;383&quot; data-start=&quot;360&quot; data-section-id=&quot;gqkzer&quot; data-ke-size=&quot;size26&quot;&gt;1. 관계형 데이터베이스의 기본 구조&lt;/h2&gt;
&lt;p data-end=&quot;462&quot; data-start=&quot;385&quot; data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스는 데이터를 &lt;b&gt;테이블 형태&lt;/b&gt;로 저장하는 방식이다.&lt;br /&gt;우리가 흔히 보는 행과 열 구조가 바로 관계형 모델의 핵심이다.&lt;/p&gt;
&lt;p data-end=&quot;503&quot; data-start=&quot;464&quot; data-ke-size=&quot;size16&quot;&gt;여기서 중요한 용어 몇 개를 먼저 정리하고 가면 이해가 훨씬 쉬워진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;662&quot; data-start=&quot;505&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;534&quot; data-start=&quot;505&quot; data-section-id=&quot;pnegpd&quot;&gt;&lt;b&gt;릴레이션(Relation)&lt;/b&gt;: 튜플들의 집합&lt;/li&gt;
&lt;li data-end=&quot;574&quot; data-start=&quot;535&quot; data-section-id=&quot;1kau5la&quot;&gt;&lt;b&gt;튜플(Tuple)&lt;/b&gt;: 하나의 행(row), 즉 속성값들의 집합&lt;/li&gt;
&lt;li data-end=&quot;619&quot; data-start=&quot;575&quot; data-section-id=&quot;xoppj9&quot;&gt;&lt;b&gt;속성(Attribute)&lt;/b&gt;: 열(column), 각 데이터 항목의 이름&lt;/li&gt;
&lt;li data-end=&quot;662&quot; data-start=&quot;620&quot; data-section-id=&quot;1uellap&quot;&gt;&lt;b&gt;도메인(Domain)&lt;/b&gt;: 각 속성이 가질 수 있는 허용된 값의 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;686&quot; data-start=&quot;664&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생 테이블이 있다고 해보자.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;학번&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;이름&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 학과
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;769&quot; data-start=&quot;688&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;769&quot; data-start=&quot;719&quot;&gt;
&lt;tr data-end=&quot;744&quot; data-start=&quot;719&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;729&quot; data-start=&quot;719&quot;&gt;2024001&lt;/td&gt;
&lt;td data-end=&quot;735&quot; data-start=&quot;729&quot; data-col-size=&quot;sm&quot;&gt;김민수&lt;/td&gt;
&lt;td data-end=&quot;744&quot; data-start=&quot;735&quot; data-col-size=&quot;sm&quot;&gt;컴퓨터학과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;769&quot; data-start=&quot;745&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;755&quot; data-start=&quot;745&quot;&gt;2024002&lt;/td&gt;
&lt;td data-end=&quot;761&quot; data-start=&quot;755&quot; data-col-size=&quot;sm&quot;&gt;이서연&lt;/td&gt;
&lt;td data-end=&quot;769&quot; data-start=&quot;761&quot; data-col-size=&quot;sm&quot;&gt;경영학과&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;774&quot; data-start=&quot;771&quot; data-ke-size=&quot;size16&quot;&gt;여기서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;863&quot; data-start=&quot;776&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;794&quot; data-start=&quot;776&quot; data-section-id=&quot;54pdww&quot;&gt;테이블 전체는 &lt;b&gt;릴레이션&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;808&quot; data-start=&quot;795&quot; data-section-id=&quot;1fgnd68&quot;&gt;각 행은 &lt;b&gt;튜플&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;829&quot; data-start=&quot;809&quot; data-section-id=&quot;xiuzr8&quot;&gt;학번, 이름, 학과는 &lt;b&gt;속성&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;863&quot; data-start=&quot;830&quot; data-section-id=&quot;17unb9b&quot;&gt;학과 속성에 들어갈 수 있는 값들의 범위는 &lt;b&gt;도메인&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;875&quot; data-start=&quot;865&quot; data-ke-size=&quot;size16&quot;&gt;이라고 보면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;880&quot; data-start=&quot;877&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;907&quot; data-start=&quot;882&quot; data-section-id=&quot;965b0d&quot; data-ke-size=&quot;size26&quot;&gt;2. 릴레이션 스키마와 릴레이션 인스턴스&lt;/h2&gt;
&lt;p data-end=&quot;949&quot; data-start=&quot;909&quot; data-ke-size=&quot;size16&quot;&gt;이 부분은 ch.1의 &lt;b&gt;스키마 / 인스턴스&lt;/b&gt; 개념이 그대로 이어진다.&lt;/p&gt;
&lt;h3 data-end=&quot;980&quot; data-start=&quot;951&quot; data-section-id=&quot;10ws37s&quot; data-ke-size=&quot;size23&quot;&gt;릴레이션 스키마(Relation Schema)&lt;/h3&gt;
&lt;p data-end=&quot;1067&quot; data-start=&quot;981&quot; data-ke-size=&quot;size16&quot;&gt;릴레이션의 구조를 정의하는 틀이다.&lt;br /&gt;즉, &lt;b&gt;테이블 이름&lt;/b&gt;, &lt;b&gt;속성 이름&lt;/b&gt;, &lt;b&gt;타입&lt;/b&gt;, &lt;b&gt;도메인&lt;/b&gt; 등을 정의한 설계도라고 보면 된다.&lt;/p&gt;
&lt;p data-end=&quot;1071&quot; data-start=&quot;1069&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;student(s_id, name, dept_name)&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1160&quot; data-start=&quot;1116&quot; data-ke-size=&quot;size16&quot;&gt;이건 student 테이블이 어떤 속성들로 구성되는지 보여주는 &lt;b&gt;스키마&lt;/b&gt;다.&lt;/p&gt;
&lt;hr data-end=&quot;1165&quot; data-start=&quot;1162&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1199&quot; data-start=&quot;1167&quot; data-section-id=&quot;zzguok&quot; data-ke-size=&quot;size23&quot;&gt;릴레이션 인스턴스(Relation Instance)&lt;/h3&gt;
&lt;p data-end=&quot;1238&quot; data-start=&quot;1200&quot; data-ke-size=&quot;size16&quot;&gt;어느 시점에 실제로 테이블 안에 들어 있는 &lt;b&gt;튜플들의 집합&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;1242&quot; data-start=&quot;1240&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1270&quot; data-start=&quot;1244&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1254&quot; data-start=&quot;1244&quot; data-section-id=&quot;rvagip&quot;&gt;스키마 = 구조&lt;/li&gt;
&lt;li data-end=&quot;1270&quot; data-start=&quot;1255&quot; data-section-id=&quot;1jmtjdh&quot;&gt;인스턴스 = 실제 데이터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1284&quot; data-start=&quot;1272&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 구분하면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;1289&quot; data-start=&quot;1286&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1314&quot; data-start=&quot;1291&quot; data-section-id=&quot;1jfr87d&quot; data-ke-size=&quot;size26&quot;&gt;3. 속성, 튜플, 도메인 개념 정리&lt;/h2&gt;
&lt;h3 data-end=&quot;1333&quot; data-start=&quot;1316&quot; data-section-id=&quot;wn0oqv&quot; data-ke-size=&quot;size23&quot;&gt;속성(Attribute)&lt;/h3&gt;
&lt;p data-end=&quot;1370&quot; data-start=&quot;1334&quot; data-ke-size=&quot;size16&quot;&gt;테이블의 열(column)이다.&lt;br /&gt;데이터의 한 항목을 의미한다.&lt;/p&gt;
&lt;p data-end=&quot;1374&quot; data-start=&quot;1372&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1397&quot; data-start=&quot;1375&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1379&quot; data-start=&quot;1375&quot; data-section-id=&quot;yiiqop&quot;&gt;학번&lt;/li&gt;
&lt;li data-end=&quot;1384&quot; data-start=&quot;1380&quot; data-section-id=&quot;yil42g&quot;&gt;이름&lt;/li&gt;
&lt;li data-end=&quot;1391&quot; data-start=&quot;1385&quot; data-section-id=&quot;cezc94&quot;&gt;전화번호&lt;/li&gt;
&lt;li data-end=&quot;1397&quot; data-start=&quot;1392&quot; data-section-id=&quot;2ha2o8&quot;&gt;학과명&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1430&quot; data-start=&quot;1399&quot; data-ke-size=&quot;size16&quot;&gt;속성의 개수를 &lt;b&gt;degree(차수)&lt;/b&gt; 라고도 부른다.&lt;/p&gt;
&lt;p data-end=&quot;1461&quot; data-start=&quot;1432&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 속성이 4개면 그 릴레이션의 차수는 4다.&lt;/p&gt;
&lt;hr data-end=&quot;1466&quot; data-start=&quot;1463&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1481&quot; data-start=&quot;1468&quot; data-section-id=&quot;kko3u7&quot; data-ke-size=&quot;size23&quot;&gt;튜플(Tuple)&lt;/h3&gt;
&lt;p data-end=&quot;1523&quot; data-start=&quot;1482&quot; data-ke-size=&quot;size16&quot;&gt;테이블의 한 행(row)이다.&lt;br /&gt;하나의 데이터 레코드라고 생각하면 된다.&lt;/p&gt;
&lt;p data-end=&quot;1557&quot; data-start=&quot;1525&quot; data-ke-size=&quot;size16&quot;&gt;튜플 수는 &lt;b&gt;cardinality(기수)&lt;/b&gt; 라고 한다.&lt;/p&gt;
&lt;p data-end=&quot;1561&quot; data-start=&quot;1559&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1615&quot; data-start=&quot;1563&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1586&quot; data-start=&quot;1563&quot; data-section-id=&quot;1niey3k&quot;&gt;&lt;b&gt;차수(degree)&lt;/b&gt; = 열 개수&lt;/li&gt;
&lt;li data-end=&quot;1615&quot; data-start=&quot;1587&quot; data-section-id=&quot;py98yz&quot;&gt;&lt;b&gt;기수(cardinality)&lt;/b&gt; = 행 개수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1642&quot; data-start=&quot;1617&quot; data-ke-size=&quot;size16&quot;&gt;이 둘은 자주 헷갈리니까 구분해두는 게 좋다.&lt;/p&gt;
&lt;hr data-end=&quot;1647&quot; data-start=&quot;1644&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1664&quot; data-start=&quot;1649&quot; data-section-id=&quot;1tw3mof&quot; data-ke-size=&quot;size23&quot;&gt;도메인(Domain)&lt;/h3&gt;
&lt;p data-end=&quot;1690&quot; data-start=&quot;1665&quot; data-ke-size=&quot;size16&quot;&gt;각 속성이 가질 수 있는 허용된 값의 범위다.&lt;/p&gt;
&lt;p data-end=&quot;1757&quot; data-start=&quot;1692&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 나이 속성의 도메인은 정수일 수 있고,&lt;br /&gt;학년 속성의 도메인은 1, 2, 3, 4처럼 제한될 수도 있다.&lt;/p&gt;
&lt;p data-end=&quot;1842&quot; data-start=&quot;1759&quot; data-ke-size=&quot;size16&quot;&gt;관계형 모델에서는 일반적으로 각 속성의 값이 &lt;b&gt;원자적(atomic)&lt;/b&gt; 이어야 한다.&lt;br /&gt;즉, 더 이상 쪼갤 수 없는 단일 값이어야 한다는 뜻이다.&lt;/p&gt;
&lt;p data-end=&quot;1899&quot; data-start=&quot;1844&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 전화번호 속성에 여러 개 번호를 한 번에 넣는 건 관계형 모델 관점에서는 적절하지 않다.&lt;/p&gt;
&lt;hr data-end=&quot;1904&quot; data-start=&quot;1901&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1916&quot; data-start=&quot;1906&quot; data-section-id=&quot;hobs33&quot; data-ke-size=&quot;size23&quot;&gt;NULL 값&lt;/h3&gt;
&lt;p data-end=&quot;1935&quot; data-start=&quot;1917&quot; data-ke-size=&quot;size16&quot;&gt;NULL은 그냥 빈 값이 아니라,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1967&quot; data-start=&quot;1937&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1951&quot; data-start=&quot;1937&quot; data-section-id=&quot;1rjkos7&quot;&gt;&lt;b&gt;아직 모르는 값&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;1967&quot; data-start=&quot;1952&quot; data-section-id=&quot;6nxfqc&quot;&gt;&lt;b&gt;존재하지 않는 값&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1984&quot; data-start=&quot;1969&quot; data-ke-size=&quot;size16&quot;&gt;을 나타내는 특별한 값이다.&lt;/p&gt;
&lt;p data-end=&quot;2029&quot; data-start=&quot;1986&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생의 연락처가 아직 등록되지 않았다면 NULL이 들어갈 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;2034&quot; data-start=&quot;2031&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2050&quot; data-start=&quot;2036&quot; data-section-id=&quot;jkdi87&quot; data-ke-size=&quot;size26&quot;&gt;4. 키의 종류 정리&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;433&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqhNCe/dJMcah4HHCZ/PPQvVQ7HfbeUo1QFz6GeL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqhNCe/dJMcah4HHCZ/PPQvVQ7HfbeUo1QFz6GeL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqhNCe/dJMcah4HHCZ/PPQvVQ7HfbeUo1QFz6GeL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqhNCe%2FdJMcah4HHCZ%2FPPQvVQ7HfbeUo1QFz6GeL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;433&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;433&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;2118&quot; data-start=&quot;2052&quot; data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스에서는 각 튜플을 구분하고, 테이블 사이 관계를 맺기 위해 &lt;b&gt;키(Key)&lt;/b&gt; 개념이 매우 중요하다.&lt;/p&gt;
&lt;hr data-end=&quot;2123&quot; data-start=&quot;2120&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2146&quot; data-start=&quot;2125&quot; data-section-id=&quot;1n9axeh&quot; data-ke-size=&quot;size23&quot;&gt;1) 슈퍼키(Super Key)&lt;/h3&gt;
&lt;p data-end=&quot;2180&quot; data-start=&quot;2148&quot; data-ke-size=&quot;size16&quot;&gt;튜플을 &lt;b&gt;유일하게 식별할 수 있는 속성들의 집합&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-end=&quot;2239&quot; data-start=&quot;2182&quot; data-ke-size=&quot;size16&quot;&gt;단, 꼭 최소일 필요는 없다.&lt;br /&gt;즉, 필요 없는 속성이 더 들어가 있어도 유일성만 만족하면 슈퍼키다.&lt;/p&gt;
&lt;p data-end=&quot;2274&quot; data-start=&quot;2241&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생 테이블이 있고 속성이 다음과 같다고 해보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2292&quot; data-start=&quot;2276&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2280&quot; data-start=&quot;2276&quot; data-section-id=&quot;yiiqop&quot;&gt;학번&lt;/li&gt;
&lt;li data-end=&quot;2285&quot; data-start=&quot;2281&quot; data-section-id=&quot;yil42g&quot;&gt;이름&lt;/li&gt;
&lt;li data-end=&quot;2292&quot; data-start=&quot;2286&quot; data-section-id=&quot;cezc94&quot;&gt;전화번호&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2309&quot; data-start=&quot;2294&quot; data-ke-size=&quot;size16&quot;&gt;이때 가능한 슈퍼키 예시는:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2366&quot; data-start=&quot;2311&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2319&quot; data-start=&quot;2311&quot; data-section-id=&quot;1lrzben&quot;&gt;{학번}&lt;/li&gt;
&lt;li data-end=&quot;2332&quot; data-start=&quot;2320&quot; data-section-id=&quot;9lmr&quot;&gt;{학번, 이름}&lt;/li&gt;
&lt;li data-end=&quot;2347&quot; data-start=&quot;2333&quot; data-section-id=&quot;1d65eqb&quot;&gt;{학번, 전화번호}&lt;/li&gt;
&lt;li data-end=&quot;2366&quot; data-start=&quot;2348&quot; data-section-id=&quot;1l0vkof&quot;&gt;{학번, 이름, 전화번호}&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2389&quot; data-start=&quot;2368&quot; data-ke-size=&quot;size16&quot;&gt;이처럼 유일성만 만족하면 다 슈퍼키다.&lt;/p&gt;
&lt;hr data-end=&quot;2394&quot; data-start=&quot;2391&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2421&quot; data-start=&quot;2396&quot; data-section-id=&quot;10w7uqy&quot; data-ke-size=&quot;size23&quot;&gt;2) 후보키(Candidate Key)&lt;/h3&gt;
&lt;p data-end=&quot;2449&quot; data-start=&quot;2423&quot; data-ke-size=&quot;size16&quot;&gt;슈퍼키 중에서 &lt;b&gt;최소성&lt;/b&gt;까지 만족하는 키다.&lt;/p&gt;
&lt;p data-end=&quot;2453&quot; data-start=&quot;2451&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2481&quot; data-start=&quot;2454&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2463&quot; data-start=&quot;2454&quot; data-section-id=&quot;r5iusk&quot;&gt;유일해야 하고&lt;/li&gt;
&lt;li data-end=&quot;2481&quot; data-start=&quot;2464&quot; data-section-id=&quot;6dgvma&quot;&gt;불필요한 속성이 없어야 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2497&quot; data-start=&quot;2483&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생 테이블에서&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2518&quot; data-start=&quot;2499&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2507&quot; data-start=&quot;2499&quot; data-section-id=&quot;1lrzben&quot;&gt;{학번}&lt;/li&gt;
&lt;li data-end=&quot;2518&quot; data-start=&quot;2508&quot; data-section-id=&quot;j824q6&quot;&gt;{전화번호}&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2561&quot; data-start=&quot;2520&quot; data-ke-size=&quot;size16&quot;&gt;둘 다 각 학생을 유일하게 구분할 수 있다면 둘 다 후보키가 될 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;2566&quot; data-start=&quot;2563&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2591&quot; data-start=&quot;2568&quot; data-section-id=&quot;6ujw6i&quot; data-ke-size=&quot;size23&quot;&gt;3) 기본키(Primary Key)&lt;/h3&gt;
&lt;p data-end=&quot;2616&quot; data-start=&quot;2593&quot; data-ke-size=&quot;size16&quot;&gt;후보키 중에서 &lt;b&gt;대표로 선택된 키&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;2638&quot; data-start=&quot;2618&quot; data-ke-size=&quot;size16&quot;&gt;기본키는 다음 조건을 만족해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2665&quot; data-start=&quot;2640&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2651&quot; data-start=&quot;2640&quot; data-section-id=&quot;2fqhvo&quot;&gt;&lt;b&gt;중복 불가&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2665&quot; data-start=&quot;2652&quot; data-section-id=&quot;1kpc22j&quot;&gt;&lt;b&gt;NULL 불가&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2707&quot; data-start=&quot;2667&quot; data-ke-size=&quot;size16&quot;&gt;그래서 보통 NOT NULL 과 UNIQUE 성격을 함께 가진다.&lt;/p&gt;
&lt;p data-end=&quot;2735&quot; data-start=&quot;2709&quot; data-ke-size=&quot;size16&quot;&gt;기본키를 정할 때는 보통 이런 기준을 고려한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2772&quot; data-start=&quot;2737&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2751&quot; data-start=&quot;2737&quot; data-section-id=&quot;c22iwg&quot;&gt;값이 잘 바뀌지 않는가&lt;/li&gt;
&lt;li data-end=&quot;2758&quot; data-start=&quot;2752&quot; data-section-id=&quot;1ukb7nk&quot;&gt;단순한가&lt;/li&gt;
&lt;li data-end=&quot;2772&quot; data-start=&quot;2759&quot; data-section-id=&quot;8avn77&quot;&gt;짧고 관리하기 쉬운가&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2777&quot; data-start=&quot;2774&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2802&quot; data-start=&quot;2779&quot; data-section-id=&quot;d0hh1z&quot; data-ke-size=&quot;size23&quot;&gt;4) 외래키(Foreign Key)&lt;/h3&gt;
&lt;p data-end=&quot;2833&quot; data-start=&quot;2804&quot; data-ke-size=&quot;size16&quot;&gt;한 테이블에서 다른 테이블의 키를 참조하는 속성이다.&lt;/p&gt;
&lt;p data-end=&quot;2872&quot; data-start=&quot;2835&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 &lt;b&gt;테이블과 테이블을 연결하는 다리&lt;/b&gt; 같은 역할이다.&lt;/p&gt;
&lt;p data-end=&quot;2956&quot; data-start=&quot;2874&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생 테이블에 학과명이 있고,&lt;br /&gt;학과 테이블에 학과명이 기본키라면 학생 테이블의 학과명은 학과 테이블을 참조하는 외래키가 될 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;2971&quot; data-start=&quot;2958&quot; data-ke-size=&quot;size16&quot;&gt;외래키의 특징은 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3031&quot; data-start=&quot;2973&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2984&quot; data-start=&quot;2973&quot; data-section-id=&quot;8l4mh&quot;&gt;&lt;b&gt;중복 가능&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2998&quot; data-start=&quot;2985&quot; data-section-id=&quot;1igh87q&quot;&gt;&lt;b&gt;NULL 가능&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3031&quot; data-start=&quot;2999&quot; data-section-id=&quot;15sancn&quot;&gt;반드시 &lt;b&gt;참조 대상 테이블에 존재하는 값&lt;/b&gt;이어야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3073&quot; data-start=&quot;3033&quot; data-ke-size=&quot;size16&quot;&gt;즉, 부모 테이블에 없는 값을 자식 테이블에서 마음대로 넣으면 안 된다.&lt;/p&gt;
&lt;hr data-end=&quot;3078&quot; data-start=&quot;3075&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3105&quot; data-start=&quot;3080&quot; data-section-id=&quot;1jfeor0&quot; data-ke-size=&quot;size23&quot;&gt;5) 대체키(Alternate Key)&lt;/h3&gt;
&lt;p data-end=&quot;3143&quot; data-start=&quot;3107&quot; data-ke-size=&quot;size16&quot;&gt;후보키 중에서 &lt;b&gt;기본키로 선택되지 않은 나머지 키&lt;/b&gt;를 말한다.&lt;/p&gt;
&lt;p data-end=&quot;3219&quot; data-start=&quot;3145&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 후보키가 {학번}, {전화번호} 두 개이고,&lt;br /&gt;기본키를 {학번}으로 골랐다면 {전화번호}는 대체키가 된다.&lt;/p&gt;
&lt;hr data-end=&quot;3224&quot; data-start=&quot;3221&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3382&quot; data-start=&quot;3350&quot; data-section-id=&quot;1o8l0nd&quot; data-ke-size=&quot;size26&quot;&gt;5. 관계대수(Relational Algebra)란?&lt;/h2&gt;
&lt;p data-end=&quot;3428&quot; data-start=&quot;3384&quot; data-ke-size=&quot;size16&quot;&gt;관계대수는 관계형 데이터베이스에서 데이터를 질의하는 &lt;b&gt;형식적 연산 체계&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-end=&quot;3482&quot; data-start=&quot;3430&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말하면,&lt;br /&gt;&amp;ldquo;테이블에서 원하는 데이터를 어떻게 뽑아낼지&amp;rdquo;를 수학적으로 표현한 방식이다.&lt;/p&gt;
&lt;p data-end=&quot;3548&quot; data-start=&quot;3484&quot; data-ke-size=&quot;size16&quot;&gt;관계대수는 &lt;b&gt;절차적 질의어&lt;/b&gt;에 가깝다.&lt;br /&gt;즉, 어떤 데이터를 얻을지뿐 아니라 &lt;b&gt;어떻게 얻는지도 표현&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-end=&quot;3634&quot; data-start=&quot;3550&quot; data-ke-size=&quot;size16&quot;&gt;SQL을 배우기 전에 관계대수를 배우는 이유는,&lt;br /&gt;관계형 데이터베이스가 내부적으로 어떤 방식으로 연산을 생각하는지 이해하는 데 도움이 되기 때문이다.&lt;/p&gt;
&lt;hr data-end=&quot;3639&quot; data-start=&quot;3636&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3656&quot; data-start=&quot;3641&quot; data-section-id=&quot;azj41t&quot; data-ke-size=&quot;size26&quot;&gt;6. 기본 관계 연산자&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tUw8x/dJMcagraYiy/CYvqGw9K9PeWq1gH81HLb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tUw8x/dJMcagraYiy/CYvqGw9K9PeWq1gH81HLb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tUw8x/dJMcagraYiy/CYvqGw9K9PeWq1gH81HLb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtUw8x%2FdJMcagraYiy%2FCYvqGw9K9PeWq1gH81HLb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;1080&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;3702&quot; data-start=&quot;3658&quot; data-ke-size=&quot;size16&quot;&gt;관계대수의 기본 연산자는 크게 &lt;b&gt;단항 연산&lt;/b&gt;과 &lt;b&gt;이항 연산&lt;/b&gt;으로 나뉜다.&lt;/p&gt;
&lt;hr data-end=&quot;3707&quot; data-start=&quot;3704&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;3718&quot; data-start=&quot;3709&quot; data-section-id=&quot;t8wts3&quot; data-ke-size=&quot;size23&quot;&gt;단항 연산&lt;/h3&gt;
&lt;p data-end=&quot;3743&quot; data-start=&quot;3720&quot; data-ke-size=&quot;size16&quot;&gt;하나의 릴레이션에 대해 수행하는 연산이다.&lt;/p&gt;
&lt;h4 data-end=&quot;3766&quot; data-start=&quot;3745&quot; data-ke-size=&quot;size20&quot;&gt;1) 선택(Select, &amp;sigma;)&lt;/h4&gt;
&lt;p data-end=&quot;3807&quot; data-start=&quot;3767&quot; data-ke-size=&quot;size16&quot;&gt;조건을 만족하는 튜플만 골라낸다.&lt;br /&gt;SQL의 WHERE와 비슷하다.&lt;/p&gt;
&lt;p data-end=&quot;3811&quot; data-start=&quot;3809&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3834&quot; data-start=&quot;3812&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3834&quot; data-start=&quot;3812&quot; data-section-id=&quot;kpf9os&quot;&gt;급여가 50000 이상인 교수만 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3864&quot; data-start=&quot;3836&quot; data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;행을 고르는 연산&lt;/b&gt;이라고 생각하면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;3869&quot; data-start=&quot;3866&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;3893&quot; data-start=&quot;3871&quot; data-ke-size=&quot;size20&quot;&gt;2) 추출(Project, &amp;pi;)&lt;/h4&gt;
&lt;p data-end=&quot;3934&quot; data-start=&quot;3894&quot; data-ke-size=&quot;size16&quot;&gt;원하는 속성만 뽑아낸다.&lt;br /&gt;SQL의 SELECT 컬럼명과 비슷하다.&lt;/p&gt;
&lt;p data-end=&quot;3938&quot; data-start=&quot;3936&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3960&quot; data-start=&quot;3939&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3960&quot; data-start=&quot;3939&quot; data-section-id=&quot;1ojgj65&quot;&gt;교수 테이블에서 이름과 학과만 추출&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3981&quot; data-start=&quot;3962&quot; data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;열을 고르는 연산&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-end=&quot;3986&quot; data-start=&quot;3983&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;4010&quot; data-start=&quot;3988&quot; data-ke-size=&quot;size20&quot;&gt;3) 재명명(Rename, &amp;rho;)&lt;/h4&gt;
&lt;p data-end=&quot;4033&quot; data-start=&quot;4011&quot; data-ke-size=&quot;size16&quot;&gt;릴레이션이나 속성 이름을 임시로 바꾼다.&lt;/p&gt;
&lt;p data-end=&quot;4097&quot; data-start=&quot;4035&quot; data-ke-size=&quot;size16&quot;&gt;이 연산은 특히 &lt;b&gt;같은 테이블을 두 번 비교할 때&lt;/b&gt; 중요하다.&lt;br /&gt;즉, 셀프 조인 같은 상황에서 많이 쓴다.&lt;/p&gt;
&lt;hr data-end=&quot;4102&quot; data-start=&quot;4099&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4136&quot; data-start=&quot;4104&quot; data-section-id=&quot;cxfacp&quot; data-ke-size=&quot;size23&quot;&gt;같은 테이블끼리 비교하는 이유: 셀프 조인 아이디어&lt;/h3&gt;
&lt;p data-end=&quot;4174&quot; data-start=&quot;4138&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;가장 높은 연봉을 받는 교수&amp;rdquo;를 찾고 싶다고 해보자.&lt;/p&gt;
&lt;p data-end=&quot;4257&quot; data-start=&quot;4176&quot; data-ke-size=&quot;size16&quot;&gt;이럴 때 한 테이블 안의 데이터를 그 테이블 자체와 비교해야 한다.&lt;br /&gt;그래서 같은 릴레이션을 하나 더 복사한 것처럼 &lt;b&gt;재명명&lt;/b&gt;해서 사용한다.&lt;/p&gt;
&lt;p data-end=&quot;4275&quot; data-start=&quot;4259&quot; data-ke-size=&quot;size16&quot;&gt;과정은 이렇게 이해하면 된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;4491&quot; data-start=&quot;4277&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;4316&quot; data-start=&quot;4277&quot; data-section-id=&quot;1isqp1c&quot;&gt;instructor를 하나 더 만들어 d라고 이름 붙인다.&lt;/li&gt;
&lt;li data-end=&quot;4356&quot; data-start=&quot;4317&quot; data-section-id=&quot;1iiihlx&quot;&gt;instructor &amp;times; d 로 모든 교수-교수 조합을 만든다.&lt;/li&gt;
&lt;li data-end=&quot;4408&quot; data-start=&quot;4357&quot; data-section-id=&quot;1w3w8ct&quot;&gt;instructor.salary &amp;lt; d.salary 조건을 만족하는 경우를 찾는다.&lt;/li&gt;
&lt;li data-end=&quot;4450&quot; data-start=&quot;4409&quot; data-section-id=&quot;1cyq8m3&quot;&gt;그러면 &amp;ldquo;자신보다 더 높은 연봉을 가진 사람이 있는 교수&amp;rdquo;가 나온다.&lt;/li&gt;
&lt;li data-end=&quot;4491&quot; data-start=&quot;4451&quot; data-section-id=&quot;1b3o0k9&quot;&gt;이들을 전체 교수 목록에서 빼면, 최종적으로 최고 연봉자만 남는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;4550&quot; data-start=&quot;4493&quot; data-ke-size=&quot;size16&quot;&gt;이 예시는 관계대수에서 &lt;b&gt;카티션 곱 + 선택 + 차집합&lt;/b&gt;이 어떻게 결합되는지 보여줘서 꽤 중요하다.&lt;/p&gt;
&lt;hr data-end=&quot;4555&quot; data-start=&quot;4552&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4566&quot; data-start=&quot;4557&quot; data-section-id=&quot;cm9en3&quot; data-ke-size=&quot;size23&quot;&gt;이항 연산&lt;/h3&gt;
&lt;p data-end=&quot;4594&quot; data-start=&quot;4568&quot; data-ke-size=&quot;size16&quot;&gt;두 개의 릴레이션을 대상으로 수행하는 연산이다.&lt;/p&gt;
&lt;h4 data-end=&quot;4617&quot; data-start=&quot;4596&quot; data-ke-size=&quot;size20&quot;&gt;1) 합집합(Union, &amp;cup;)&lt;/h4&gt;
&lt;p data-end=&quot;4634&quot; data-start=&quot;4618&quot; data-ke-size=&quot;size16&quot;&gt;두 릴레이션의 결과를 합친다.&lt;/p&gt;
&lt;p data-end=&quot;4693&quot; data-start=&quot;4636&quot; data-ke-size=&quot;size16&quot;&gt;단, 합집합을 하려면 두 릴레이션의 구조가 호환되어야 한다.&lt;br /&gt;즉, 속성 개수와 타입이 맞아야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;4698&quot; data-start=&quot;4695&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;4730&quot; data-start=&quot;4700&quot; data-ke-size=&quot;size20&quot;&gt;2) 차집합(Set Difference, -)&lt;/h4&gt;
&lt;p data-end=&quot;4764&quot; data-start=&quot;4731&quot; data-ke-size=&quot;size16&quot;&gt;한 릴레이션에는 있고 다른 릴레이션에는 없는 튜플만 남긴다.&lt;/p&gt;
&lt;hr data-end=&quot;4769&quot; data-start=&quot;4766&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-end=&quot;4806&quot; data-start=&quot;4771&quot; data-ke-size=&quot;size20&quot;&gt;3) 카티션 곱(Cartesian Product, &amp;times;)&lt;/h4&gt;
&lt;p data-end=&quot;4829&quot; data-start=&quot;4807&quot; data-ke-size=&quot;size16&quot;&gt;두 릴레이션의 모든 튜플 조합을 만든다.&lt;/p&gt;
&lt;p data-end=&quot;4881&quot; data-start=&quot;4831&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 A에 3개 행, B에 4개 행이 있으면&lt;br /&gt;A &amp;times; B 결과는 12개 행이 된다.&lt;/p&gt;
&lt;p data-end=&quot;4942&quot; data-start=&quot;4883&quot; data-ke-size=&quot;size16&quot;&gt;카티션 곱 자체만 단독으로 쓰기보다는,&lt;br /&gt;보통 이후에 선택 조건을 걸어서 조인처럼 활용하는 경우가 많다.&lt;/p&gt;
&lt;hr data-end=&quot;5105&quot; data-start=&quot;5102&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;5126&quot; data-start=&quot;5107&quot; data-section-id=&quot;13k4kb4&quot; data-ke-size=&quot;size26&quot;&gt;7. 확장 관계 연산자와 조인&lt;/h2&gt;
&lt;p data-end=&quot;5154&quot; data-start=&quot;5128&quot; data-ke-size=&quot;size16&quot;&gt;기본 연산 외에도 자주 쓰는 확장 연산이 있다.&lt;/p&gt;
&lt;h3 data-end=&quot;5180&quot; data-start=&quot;5156&quot; data-section-id=&quot;1sj5jg5&quot; data-ke-size=&quot;size23&quot;&gt;1) 배정(Assignment, &amp;larr;)&lt;/h3&gt;
&lt;p data-end=&quot;5214&quot; data-start=&quot;5181&quot; data-ke-size=&quot;size16&quot;&gt;중간 결과를 변수처럼 저장해서 다음 연산에 활용할 때 쓴다.&lt;/p&gt;
&lt;p data-end=&quot;5244&quot; data-start=&quot;5216&quot; data-ke-size=&quot;size16&quot;&gt;복잡한 관계대수 식을 단계별로 표현할 때 유용하다.&lt;/p&gt;
&lt;hr data-end=&quot;5249&quot; data-start=&quot;5246&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5278&quot; data-start=&quot;5251&quot; data-section-id=&quot;wmo8f0&quot; data-ke-size=&quot;size23&quot;&gt;2) 교집합(Intersection, &amp;cap;)&lt;/h3&gt;
&lt;p data-end=&quot;5304&quot; data-start=&quot;5279&quot; data-ke-size=&quot;size16&quot;&gt;두 릴레이션에 공통으로 포함된 튜플만 남긴다.&lt;/p&gt;
&lt;hr data-end=&quot;5309&quot; data-start=&quot;5306&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5326&quot; data-start=&quot;5311&quot; data-section-id=&quot;1f9bjrr&quot; data-ke-size=&quot;size23&quot;&gt;3) 조인(Join)&lt;/h3&gt;
&lt;p data-end=&quot;5392&quot; data-start=&quot;5327&quot; data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스에서 제일 중요한 연산 중 하나다.&lt;br /&gt;서로 관련 있는 두 테이블을 연결해서 새로운 결과를 만든다.&lt;/p&gt;
&lt;hr data-end=&quot;5397&quot; data-start=&quot;5394&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5425&quot; data-start=&quot;5399&quot; data-section-id=&quot;wmguod&quot; data-ke-size=&quot;size23&quot;&gt;자연 조인(Natural Join, ⨝)&lt;/h3&gt;
&lt;p data-end=&quot;5452&quot; data-start=&quot;5426&quot; data-ke-size=&quot;size16&quot;&gt;이름이 같은 속성을 기준으로 자동으로 조인한다.&lt;/p&gt;
&lt;p data-end=&quot;5512&quot; data-start=&quot;5454&quot; data-ke-size=&quot;size16&quot;&gt;장점은 간단하다는 거지만,&lt;br /&gt;원하지 않는 같은 이름의 속성까지 기준으로 잡힐 수 있어서 조심해야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;5517&quot; data-start=&quot;5514&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5544&quot; data-start=&quot;5519&quot; data-section-id=&quot;owsms0&quot; data-ke-size=&quot;size23&quot;&gt;세타 조인(Theta Join, ⨝&amp;theta;)&lt;/h3&gt;
&lt;p data-end=&quot;5564&quot; data-start=&quot;5545&quot; data-ke-size=&quot;size16&quot;&gt;조건을 명시해서 조인하는 방식이다.&lt;/p&gt;
&lt;p data-end=&quot;5571&quot; data-start=&quot;5566&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5635&quot; data-start=&quot;5572&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5635&quot; data-start=&quot;5572&quot; data-section-id=&quot;ci3zc1&quot;&gt;student.dept_name = department.dept_name&lt;br /&gt;같은 조건을 직접 줘서 조인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;5688&quot; data-start=&quot;5637&quot; data-ke-size=&quot;size16&quot;&gt;세타 조인에서 조건이 = 인 경우를 &lt;b&gt;동등 조인(Equal Join)&lt;/b&gt; 이라고 한다.&lt;/p&gt;
&lt;hr data-end=&quot;5693&quot; data-start=&quot;5690&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5716&quot; data-start=&quot;5695&quot; data-section-id=&quot;1ckfn70&quot; data-ke-size=&quot;size23&quot;&gt;외부 조인(Outer Join)&lt;/h3&gt;
&lt;p data-end=&quot;5756&quot; data-start=&quot;5717&quot; data-ke-size=&quot;size16&quot;&gt;일치하는 값이 없어도 한쪽 테이블의 튜플을 유지하고 싶을 때 사용한다.&lt;/p&gt;
&lt;p data-end=&quot;5769&quot; data-start=&quot;5758&quot; data-ke-size=&quot;size16&quot;&gt;종류는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;5946&quot; data-start=&quot;5771&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;5827&quot; data-start=&quot;5771&quot; data-section-id=&quot;wncvyy&quot;&gt;&lt;b&gt;왼쪽 외부 조인(Left Outer Join, ⟕)&lt;/b&gt;&lt;br /&gt;왼쪽 릴레이션의 모든 튜플 유지&lt;/li&gt;
&lt;li data-end=&quot;5888&quot; data-start=&quot;5829&quot; data-section-id=&quot;1ofubb6&quot;&gt;&lt;b&gt;오른쪽 외부 조인(Right Outer Join, ⟖)&lt;/b&gt;&lt;br /&gt;오른쪽 릴레이션의 모든 튜플 유지&lt;/li&gt;
&lt;li data-end=&quot;5946&quot; data-start=&quot;5890&quot; data-section-id=&quot;14bomrw&quot;&gt;&lt;b&gt;전체 외부 조인(Full Outer Join, ⟗)&lt;/b&gt;&lt;br /&gt;양쪽 릴레이션의 모든 튜플 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;6027&quot; data-start=&quot;5948&quot; data-ke-size=&quot;size16&quot;&gt;이건 SQL의 LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN과 연결해서 이해하면 된다.&lt;/p&gt;
&lt;hr data-end=&quot;6032&quot; data-start=&quot;6029&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;6066&quot; data-start=&quot;6034&quot; data-section-id=&quot;jvsjuu&quot; data-ke-size=&quot;size26&quot;&gt;8. 관계 해석(Relational Calculus)&lt;/h2&gt;
&lt;p data-end=&quot;6101&quot; data-start=&quot;6068&quot; data-ke-size=&quot;size16&quot;&gt;노트에 짧게 정리된 부분인데, 개념만 같이 잡고 가면 좋다.&lt;/p&gt;
&lt;p data-end=&quot;6166&quot; data-start=&quot;6103&quot; data-ke-size=&quot;size16&quot;&gt;관계 해석은 관계대수와 달리 &lt;b&gt;선언형 질의어&lt;/b&gt;다.&lt;br /&gt;즉, 결과로 &lt;b&gt;무엇을 원하는지&lt;/b&gt;를 표현하는 방식이다.&lt;/p&gt;
&lt;p data-end=&quot;6178&quot; data-start=&quot;6168&quot; data-ke-size=&quot;size16&quot;&gt;종류는 두 가지다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6210&quot; data-start=&quot;6180&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6194&quot; data-start=&quot;6180&quot; data-section-id=&quot;1fpnf6d&quot;&gt;&lt;b&gt;튜플 관계 해석&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;6210&quot; data-start=&quot;6195&quot; data-section-id=&quot;49uact&quot;&gt;&lt;b&gt;도메인 관계 해석&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;6271&quot; data-start=&quot;6212&quot; data-ke-size=&quot;size16&quot;&gt;관계대수는 &amp;ldquo;어떻게 구할지&amp;rdquo;에 초점이 있고,&lt;br /&gt;관계 해석은 &amp;ldquo;무엇을 구할지&amp;rdquo;에 초점이 있다고 보면 된다.&lt;/p&gt;
&lt;p data-end=&quot;6332&quot; data-start=&quot;6273&quot; data-ke-size=&quot;size16&quot;&gt;SQL은 완전히 관계대수도 아니고 완전히 관계해석도 아니지만,&lt;br /&gt;전체적으로는 선언형 질의어에 더 가깝다.&lt;/p&gt;
&lt;hr data-end=&quot;6337&quot; data-start=&quot;6334&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;6354&quot; data-start=&quot;6339&quot; data-section-id=&quot;5ipn7i&quot; data-ke-size=&quot;size26&quot;&gt;9. 무결성과 제약조건&lt;/h2&gt;
&lt;p data-end=&quot;6405&quot; data-start=&quot;6356&quot; data-ke-size=&quot;size16&quot;&gt;데이터베이스에서 무결성은&lt;br /&gt;&lt;b&gt;데이터가 정확하고 일관되게 유지되는 성질&lt;/b&gt;을 의미한다.&lt;/p&gt;
&lt;p data-end=&quot;6466&quot; data-start=&quot;6407&quot; data-ke-size=&quot;size16&quot;&gt;아무 값이나 막 들어가면 데이터베이스는 금방 망가진다.&lt;br /&gt;그래서 제약조건을 통해 데이터를 보호해야 한다.&lt;/p&gt;
&lt;hr data-end=&quot;6471&quot; data-start=&quot;6468&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6504&quot; data-start=&quot;6473&quot; data-section-id=&quot;3tp2bi&quot; data-ke-size=&quot;size23&quot;&gt;1) 개체 무결성(Entity Integrity)&lt;/h3&gt;
&lt;p data-end=&quot;6528&quot; data-start=&quot;6505&quot; data-ke-size=&quot;size16&quot;&gt;기본키는 각 행을 유일하게 식별해야 하므로&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6563&quot; data-start=&quot;6530&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6545&quot; data-start=&quot;6530&quot; data-section-id=&quot;1fgtyeo&quot;&gt;&lt;b&gt;중복되면 안 되고&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;6563&quot; data-start=&quot;6546&quot; data-section-id=&quot;11xnlsz&quot;&gt;&lt;b&gt;NULL이면 안 된다&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;6588&quot; data-start=&quot;6565&quot; data-ke-size=&quot;size16&quot;&gt;즉, 기본키의 무결성을 보장하는 규칙이다.&lt;/p&gt;
&lt;hr data-end=&quot;6593&quot; data-start=&quot;6590&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6631&quot; data-start=&quot;6595&quot; data-section-id=&quot;1lt4mqj&quot; data-ke-size=&quot;size23&quot;&gt;2) 참조 무결성(Referential Integrity)&lt;/h3&gt;
&lt;p data-end=&quot;6667&quot; data-start=&quot;6632&quot; data-ke-size=&quot;size16&quot;&gt;외래키는 반드시 참조 대상 테이블에 존재하는 값을 가져야 한다.&lt;/p&gt;
&lt;p data-end=&quot;6700&quot; data-start=&quot;6669&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 학생 테이블에 없는 학과를 참조하면 안 된다.&lt;/p&gt;
&lt;p data-end=&quot;6704&quot; data-start=&quot;6702&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6754&quot; data-start=&quot;6705&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6729&quot; data-start=&quot;6705&quot; data-section-id=&quot;1wyo8y&quot;&gt;존재하지 않는 부모 키를 참조하면 안 됨&lt;/li&gt;
&lt;li data-end=&quot;6754&quot; data-start=&quot;6730&quot; data-section-id=&quot;gaiuz8&quot;&gt;테이블 간 관계가 일관되게 유지되어야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;6759&quot; data-start=&quot;6756&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6793&quot; data-start=&quot;6761&quot; data-section-id=&quot;1v84x2f&quot; data-ke-size=&quot;size23&quot;&gt;3) 도메인 무결성(Domain Integrity)&lt;/h3&gt;
&lt;p data-end=&quot;6822&quot; data-start=&quot;6794&quot; data-ke-size=&quot;size16&quot;&gt;각 속성은 자신의 도메인에 맞는 값만 가져야 한다.&lt;/p&gt;
&lt;p data-end=&quot;6830&quot; data-start=&quot;6824&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;6887&quot; data-start=&quot;6831&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;6847&quot; data-start=&quot;6831&quot; data-section-id=&quot;uilzci&quot;&gt;학점에 문자 넣으면 안 됨&lt;/li&gt;
&lt;li data-end=&quot;6870&quot; data-start=&quot;6848&quot; data-section-id=&quot;1hz21m4&quot;&gt;주민번호 자리에 알파벳 넣으면 안 됨&lt;/li&gt;
&lt;li data-end=&quot;6887&quot; data-start=&quot;6871&quot; data-section-id=&quot;ytpvgo&quot;&gt;나이는 음수가 되면 안 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;6924&quot; data-start=&quot;6889&quot; data-ke-size=&quot;size16&quot;&gt;이런 규칙은 CHECK, 타입 제한 등으로 보장할 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;6929&quot; data-start=&quot;6926&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6951&quot; data-start=&quot;6931&quot; data-section-id=&quot;3d0ozm&quot; data-ke-size=&quot;size23&quot;&gt;외래키 제약조건에서 중요한 점&lt;/h3&gt;
&lt;p data-end=&quot;7014&quot; data-start=&quot;6953&quot; data-ke-size=&quot;size16&quot;&gt;외래키는 단순히 &amp;ldquo;참조한다&amp;rdquo;로 끝나는 게 아니라,&lt;br /&gt;부모 테이블과 자식 테이블의 변화에 따라 제약이 생긴다.&lt;/p&gt;
&lt;h4 data-end=&quot;7034&quot; data-start=&quot;7016&quot; data-ke-size=&quot;size20&quot;&gt;1) 삽입 / 수정 제한&lt;/h4&gt;
&lt;p data-end=&quot;7062&quot; data-start=&quot;7035&quot; data-ke-size=&quot;size16&quot;&gt;부모 테이블에 없는 값을 외래키로 넣을 수 없다.&lt;/p&gt;
&lt;h4 data-end=&quot;7077&quot; data-start=&quot;7064&quot; data-ke-size=&quot;size20&quot;&gt;2) 삭제 제한&lt;/h4&gt;
&lt;p data-end=&quot;7133&quot; data-start=&quot;7078&quot; data-ke-size=&quot;size16&quot;&gt;부모 테이블의 행을 삭제하려면&lt;br /&gt;자식 테이블에서 그 값을 참조하고 있는 행도 함께 고려해야 한다.&lt;/p&gt;
&lt;h4 data-end=&quot;7167&quot; data-start=&quot;7135&quot; data-ke-size=&quot;size20&quot;&gt;3) ON DELETE / ON UPDATE 옵션&lt;/h4&gt;
&lt;p data-end=&quot;7209&quot; data-start=&quot;7168&quot; data-ke-size=&quot;size16&quot;&gt;부모 값이 삭제되거나 수정될 때 자식 쪽을 어떻게 처리할지 정할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;7213&quot; data-start=&quot;7211&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7251&quot; data-start=&quot;7214&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7225&quot; data-start=&quot;7214&quot; data-section-id=&quot;14p2dtm&quot;&gt;CASCADE&lt;/li&gt;
&lt;li data-end=&quot;7238&quot; data-start=&quot;7226&quot; data-section-id=&quot;1bmmwwh&quot;&gt;SET NULL&lt;/li&gt;
&lt;li data-end=&quot;7251&quot; data-start=&quot;7239&quot; data-section-id=&quot;lwfg8k&quot;&gt;RESTRICT&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7292&quot; data-start=&quot;7253&quot; data-ke-size=&quot;size16&quot;&gt;이 부분은 실제 SQL에서 외래키 제약조건 설정할 때 굉장히 중요하다.&lt;/p&gt;
&lt;hr data-end=&quot;7426&quot; data-start=&quot;7423&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7451&quot; data-start=&quot;7428&quot; data-section-id=&quot;1eqrbwf&quot; data-ke-size=&quot;size26&quot;&gt;10. 문제로 다시 보는 핵심 포인트&lt;/h2&gt;
&lt;h3 data-end=&quot;7478&quot; data-start=&quot;7453&quot; data-section-id=&quot;c5qx2f&quot; data-ke-size=&quot;size23&quot;&gt;릴레이션 인스턴스 vs 릴레이션 스키마&lt;/h3&gt;
&lt;p data-end=&quot;7494&quot; data-start=&quot;7479&quot; data-ke-size=&quot;size16&quot;&gt;이 둘은 꼭 구분해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7565&quot; data-start=&quot;7496&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7534&quot; data-start=&quot;7496&quot; data-section-id=&quot;1qpodeq&quot;&gt;&lt;b&gt;릴레이션 스키마&lt;/b&gt;: 속성 이름, 타입, 도메인을 정의한 구조&lt;/li&gt;
&lt;li data-end=&quot;7565&quot; data-start=&quot;7535&quot; data-section-id=&quot;q1iupa&quot;&gt;&lt;b&gt;릴레이션 인스턴스&lt;/b&gt;: 실제 시점의 데이터 집합&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7587&quot; data-start=&quot;7567&quot; data-ke-size=&quot;size16&quot;&gt;즉, 설계도와 실제 데이터의 차이다.&lt;/p&gt;
&lt;hr data-end=&quot;7592&quot; data-start=&quot;7589&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7610&quot; data-start=&quot;7594&quot; data-section-id=&quot;1tvct5c&quot; data-ke-size=&quot;size23&quot;&gt;주 키는 왜 중요한가?&lt;/h3&gt;
&lt;p data-end=&quot;7656&quot; data-start=&quot;7611&quot; data-ke-size=&quot;size16&quot;&gt;주 키는 각 튜플을 유일하게 식별해야 하므로&lt;br /&gt;중복되거나 NULL이면 안 된다.&lt;/p&gt;
&lt;p data-end=&quot;7681&quot; data-start=&quot;7658&quot; data-ke-size=&quot;size16&quot;&gt;그래서 다음 같은 경우는 허용되지 않는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;7723&quot; data-start=&quot;7683&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;7699&quot; data-start=&quot;7683&quot; data-section-id=&quot;1hmg1f7&quot;&gt;주 키 값이 중복되는 경우&lt;/li&gt;
&lt;li data-end=&quot;7723&quot; data-start=&quot;7700&quot; data-section-id=&quot;147sxf5&quot;&gt;속성 도메인에 없는 값이 들어가는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;7728&quot; data-start=&quot;7725&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;7749&quot; data-start=&quot;7730&quot; data-section-id=&quot;aa3bg6&quot; data-ke-size=&quot;size23&quot;&gt;advisor 릴레이션 예시&lt;/h3&gt;
&lt;p data-end=&quot;7793&quot; data-start=&quot;7750&quot; data-ke-size=&quot;size16&quot;&gt;s_id만으로 advisor 릴레이션의 기본키가 될 수 있는지 생각해보면,&lt;/p&gt;
&lt;p data-end=&quot;7807&quot; data-start=&quot;7795&quot; data-ke-size=&quot;size16&quot;&gt;답은 &lt;b&gt;안 된다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-end=&quot;7842&quot; data-start=&quot;7809&quot; data-ke-size=&quot;size16&quot;&gt;왜냐하면 한 학생이 여러 지도교수를 가질 수 있기 때문이다.&lt;/p&gt;
&lt;p data-end=&quot;7846&quot; data-start=&quot;7844&quot; data-ke-size=&quot;size16&quot;&gt;예:&lt;/p&gt;
&lt;div&gt;s_id&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;i_id
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;7904&quot; data-start=&quot;7847&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;7904&quot; data-start=&quot;7873&quot;&gt;
&lt;tr data-end=&quot;7888&quot; data-start=&quot;7873&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7880&quot; data-start=&quot;7873&quot;&gt;1001&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7888&quot; data-start=&quot;7880&quot;&gt;2001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;7904&quot; data-start=&quot;7889&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7896&quot; data-start=&quot;7889&quot;&gt;1001&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;7904&quot; data-start=&quot;7896&quot;&gt;2002&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;7984&quot; data-start=&quot;7906&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 s_id만으로는 유일성을 보장하지 못한다.&lt;br /&gt;그래서 기본키는 보통 &lt;b&gt;{s_id, i_id}&lt;/b&gt; 같은 &lt;b&gt;복합키&lt;/b&gt;가 된다.&lt;/p&gt;
&lt;p data-end=&quot;8022&quot; data-start=&quot;7986&quot; data-ke-size=&quot;size16&quot;&gt;이 예시는 &amp;ldquo;유일성&amp;rdquo;이 실제로 어떤 뜻인지 이해하는 데 꽤 좋다.&lt;/p&gt;
&lt;hr data-end=&quot;8027&quot; data-start=&quot;8024&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;8041&quot; data-start=&quot;8029&quot; data-section-id=&quot;1gctv3v&quot; data-ke-size=&quot;size26&quot;&gt;11. 핵심 요약&lt;/h2&gt;
&lt;p data-end=&quot;8065&quot; data-start=&quot;8043&quot; data-ke-size=&quot;size16&quot;&gt;이번 챕터 핵심만 다시 정리하면 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8430&quot; data-start=&quot;8067&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8105&quot; data-start=&quot;8067&quot; data-section-id=&quot;1nxc7an&quot;&gt;&lt;b&gt;릴레이션&lt;/b&gt;은 튜플의 집합이고, &lt;b&gt;튜플&lt;/b&gt;은 하나의 행이다.&lt;/li&gt;
&lt;li data-end=&quot;8145&quot; data-start=&quot;8106&quot; data-section-id=&quot;r9hq2r&quot;&gt;&lt;b&gt;속성&lt;/b&gt;은 열이고, 각 속성은 자신만의 &lt;b&gt;도메인&lt;/b&gt;을 가진다.&lt;/li&gt;
&lt;li data-end=&quot;8189&quot; data-start=&quot;8146&quot; data-section-id=&quot;1tx1yje&quot;&gt;&lt;b&gt;릴레이션 스키마&lt;/b&gt;는 구조, &lt;b&gt;릴레이션 인스턴스&lt;/b&gt;는 실제 데이터다.&lt;/li&gt;
&lt;li data-end=&quot;8236&quot; data-start=&quot;8190&quot; data-section-id=&quot;1ddnz3&quot;&gt;&lt;b&gt;슈퍼키&lt;/b&gt;는 유일성만 만족, &lt;b&gt;후보키&lt;/b&gt;는 유일성과 최소성 모두 만족한다.&lt;/li&gt;
&lt;li data-end=&quot;8285&quot; data-start=&quot;8237&quot; data-section-id=&quot;rj9w33&quot;&gt;&lt;b&gt;기본키&lt;/b&gt;는 후보키 중 대표 키, &lt;b&gt;외래키&lt;/b&gt;는 다른 테이블을 참조하는 키다.&lt;/li&gt;
&lt;li data-end=&quot;8337&quot; data-start=&quot;8286&quot; data-section-id=&quot;1jdvmg8&quot;&gt;관계대수의 기본 연산은 &lt;b&gt;선택, 추출, 재명명, 합집합, 차집합, 카티션 곱&lt;/b&gt;이다.&lt;/li&gt;
&lt;li data-end=&quot;8379&quot; data-start=&quot;8338&quot; data-section-id=&quot;16ziuwm&quot;&gt;조인은 관계형 데이터베이스에서 테이블 간 관계를 다루는 핵심 연산이다.&lt;/li&gt;
&lt;li data-end=&quot;8430&quot; data-start=&quot;8380&quot; data-section-id=&quot;1f9j0lp&quot;&gt;무결성은 &lt;b&gt;개체 무결성, 참조 무결성, 도메인 무결성&lt;/b&gt;으로 구분해 이해하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;8435&quot; data-start=&quot;8432&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;8443&quot; data-start=&quot;8437&quot; data-section-id=&quot;1h9nj85&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-end=&quot;8499&quot; data-start=&quot;8445&quot; data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스 2장 정리: &amp;ldquo;테이블 구조를 어떻게 이해할 것인가&amp;rdquo;에 대한 챕터 &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-end=&quot;8617&quot; data-start=&quot;8501&quot; data-ke-size=&quot;size16&quot;&gt;1장에서 데이터베이스가 왜 필요한지 배웠다면, 2장에서는 이제 그 데이터가 &lt;b&gt;어떤 구조로 저장되고&lt;/b&gt;, 그 구조 안에서 &lt;b&gt;키와 관계, 제약조건이 어떤 역할을 하는지&lt;/b&gt; 본격적으로 배운다.&lt;/p&gt;
&lt;p data-end=&quot;8629&quot; data-start=&quot;8619&quot; data-ke-size=&quot;size16&quot;&gt;특히 여기서 나오는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;8664&quot; data-start=&quot;8631&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;8637&quot; data-start=&quot;8631&quot; data-section-id=&quot;1thjdx4&quot;&gt;릴레이션&lt;/li&gt;
&lt;li data-end=&quot;8642&quot; data-start=&quot;8638&quot; data-section-id=&quot;yijbg8&quot;&gt;튜플&lt;/li&gt;
&lt;li data-end=&quot;8647&quot; data-start=&quot;8643&quot; data-section-id=&quot;yimbxw&quot;&gt;속성&lt;/li&gt;
&lt;li data-end=&quot;8651&quot; data-start=&quot;8648&quot; data-section-id=&quot;3751qk&quot;&gt;키&lt;/li&gt;
&lt;li data-end=&quot;8658&quot; data-start=&quot;8652&quot; data-section-id=&quot;1xbgdis&quot;&gt;관계대수&lt;/li&gt;
&lt;li data-end=&quot;8664&quot; data-start=&quot;8659&quot; data-section-id=&quot;1z2of1&quot;&gt;무결성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;8745&quot; data-start=&quot;8666&quot; data-ke-size=&quot;size16&quot;&gt;내용은 뒤에서 SQL, 정규화, ERD, 스키마 설계 배울 때 계속 나온다!! 개념을 정확히 잡아두자.&lt;/p&gt;
&lt;p data-end=&quot;8821&quot; data-start=&quot;8761&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CS STUDY/DB</category>
      <category>db기초</category>
      <category>PK</category>
      <category>SQL</category>
      <category>관계대수</category>
      <category>관계형데이터베이스</category>
      <category>데이터베이스정리</category>
      <category>릴레이션</category>
      <category>무결성</category>
      <category>외래키</category>
      <category>후보키</category>
      <author>데구르르..</author>
      <guid isPermaLink="true">https://pixxlix.tistory.com/20</guid>
      <comments>https://pixxlix.tistory.com/20#entry20comment</comments>
      <pubDate>Thu, 12 Mar 2026 02:21:37 +0900</pubDate>
    </item>
  </channel>
</rss>