<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>코딩공학</title>
    <link>https://zeus2141.tistory.com/</link>
    <description>화학공학과 개발자</description>
    <language>ko</language>
    <pubDate>Tue, 7 Jul 2026 13:34:57 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Churnobyl</managingEditor>
    <image>
      <title>코딩공학</title>
      <url>https://tistory1.daumcdn.net/tistory/6125350/attach/24fc4206424a40b0aa352582d85d587a</url>
      <link>https://zeus2141.tistory.com</link>
    </image>
    <item>
      <title>[TypeScript] Type + JavaScript - (2) Interface</title>
      <link>https://zeus2141.tistory.com/145</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2cafe/btsIQpIQ07a/Ic9dGv0pL3LXUHMOEKuYTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2cafe/btsIQpIQ07a/Ic9dGv0pL3LXUHMOEKuYTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2cafe/btsIQpIQ07a/Ic9dGv0pL3LXUHMOEKuYTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2cafe%2FbtsIQpIQ07a%2FIc9dGv0pL3LXUHMOEKuYTk%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;492&quot; height=&quot;277&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 타입 호환성 (Type Compatibility, Type Equivalence)&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;타입 호환성&lt;/b&gt;&lt;/span&gt;은 프로그래밍 언어론(Programming Language Theory)에서 언급되는 내용으로 &lt;b&gt;어떤 맥락에서 타입 A를 가진 객체가 타입 B와 동등한지(취급해도 되는지)를 판단하는 것&lt;/b&gt;이다. 만약 A타입을 가진 객체를 B로 취급해도 된다면 A를 B로 할당 가능하다고 하며, 타입 A는 타입 B의 서브타입이라고 한다. 그리고 프로그래밍 언어마다 이러한 타입 호환성의 정의 범위는 다르다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;예컨데 C#이나 자바 등의 언어에서 '사람'이라는 클래스를 확장한 '학생'이라는 클래스가 있을 경우 '학생'클래스는 '사람' 클래스로 취급할 수 있으며 '학생'클래스는 '사람'클래스의 서브타입이다. 하지만 '강아지'라는 클래스가 '사람' 클래스를 확장하지 않았다면 '강아지'클래스는 '사람'클래스로 취급할 수 없다. 이렇게 '&lt;b&gt;학생&lt;/b&gt;클래스는 &lt;b&gt;사람&lt;/b&gt;클래스를 확장한 클래스다!' 라고 타입을 구체적으로 명시해 타입 호환성을 판단하는 것을 &lt;b&gt;명목적 타이핑 체계&lt;/b&gt;라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 명목적 타이핑 vs 구조적 타이핑&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFtTCx/btsIKDgFJoo/Vvx6erbTPogs28duqhgfu0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFtTCx/btsIKDgFJoo/Vvx6erbTPogs28duqhgfu0/img.jpg&quot; data-alt=&quot;출처 : https://speakerdeck.com/takasek/what-is-a-nominal-type&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFtTCx/btsIKDgFJoo/Vvx6erbTPogs28duqhgfu0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFtTCx%2FbtsIKDgFJoo%2FVvx6erbTPogs28duqhgfu0%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;599&quot; height=&quot;449&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 : https://speakerdeck.com/takasek/what-is-a-nominal-type&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;위에서 언급한 것처럼 명목적 타이핑 체계를 사용하는 언어는 C#이나 자바같은 언어다. 타입 호환성 체계에는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Nominal_type_system&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;명목적 타이핑 체계&lt;/b&gt;&lt;/a&gt;(Nominal Type System)과 &lt;a href=&quot;https://en.wikipedia.org/wiki/Structural_type_system&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;구조적 타이핑 체계&lt;/b&gt;&lt;/a&gt;(Structural Type System)으로 구분할 수 있다. 명목적 타이핑은 위에서 알아봤고 구조적 서브타이핑에 대해 알아보자. &lt;b&gt;구조적 서브타이핑&lt;/b&gt;은 타입의 관계를 명시적으로 정의하는 명목적 타이핑 체계와는 다르게 A타입과 B타입이 &lt;b&gt;같은 형태를 가지고 있는지 아닌지&lt;/b&gt;가 판단의 중점이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;800&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eaZ4rj/btsIJV9OnCK/O4ZTgiUm6HgJCNjkpvKl01/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eaZ4rj/btsIJV9OnCK/O4ZTgiUm6HgJCNjkpvKl01/img.jpg&quot; data-alt=&quot;구조적 타이핑 체계 (덕 타이핑)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eaZ4rj/btsIJV9OnCK/O4ZTgiUm6HgJCNjkpvKl01/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeaZ4rj%2FbtsIJV9OnCK%2FO4ZTgiUm6HgJCNjkpvKl01%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;654&quot; height=&quot;368&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;구조적 타이핑 체계 (덕 타이핑)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;즉, '오리 같이 생김', '수영함', '꽥꽥거림'라는 속성을 가진 물체가 존재한다면 우리는 확신할 수는 없지만 이 물체를 오리라고 판단할 수 있는 것과 같은 맥락이다. 만약 명목적 타이핑 체계를 사용한다면 '오리'라는 물체는 '오리같이 생겼고 수영을 하며 꽥꽥거린다'라고 딱 규정을 해놓고 여기에 부합하는 물체만 오리라고 판단할 것이다. 결론적으로 구조적으로 같은 속성을 가지고 있다면 호환 가능하다고 판단하는 유연한 체계를 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;구조적 타이핑 체계라고 하며 다른 말로는&amp;nbsp;&lt;/span&gt;&lt;b&gt;덕 타이핑&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;(Duck Typing)이라고도 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;3. 인터페이스 (Interface)&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; 타입스크립트로 돌아와서 타입스크립트의 핵심 원칙 중 하나는 타입 검사가 &lt;b&gt;값의 형태&lt;/b&gt;에 초점을 맞춘다는 것이다. 이는 위에서 설명한&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;구조적 서브타이핑&lt;/b&gt;&lt;/span&gt;(Structural Subtyping) 혹은 덕 타이핑이다. 타입의 엄격한 검사를 통해 높은 코드 품질을 지향하는 타입스크립트지만, 함수 표현식이나 객체 리터럴같은 익명 객체를 광범위하게 사용하는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;자바스크립트 코드의 작성 방식을 자연스럽게 표현하기 위해서는 구조적 타이핑 시스템을 사용하는 것이 좋다고 판단한 것으로 보인다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&amp;nbsp;타입스크립트에서는 속성들을 모아놓은 타입들의 이름을 짓기 위해서 &lt;span style=&quot;color: #000000;&quot;&gt;인터페이스&lt;/span&gt;를 사용한다. 인터페이스는 앞서 공부한 원시타입처럼 변수, 함수, 클래스에 전부 사용할 수 있으며, 함수가 리턴하는 값에 대해서도 기술할 수 있다. &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;인터페이스&lt;/b&gt;&lt;/span&gt;는 쉽게 생각해서 &lt;b&gt;원시타입 하나로 표현할 수 없는 복합적인 속성들로 이루어진 타입의 이름을 지어주는 것이다.&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721866667346&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// id, content, completed 속성으로 이루어진 타입을 Todo라고 할 것이다.
interface Todo {
  id: number;
  content: string;
  completed: boolean;
}

// todos 변수는 Todo의 배열이다.
let todos: Todo[];

// todos 변수는 Todo의 배열로 정의되어 있으니까 요구사항에 맞게 넣어줘야지
todos = [
  {
    id: 1,
    content: &quot;타입스크립트 공부하기&quot;,
    completed: false,
  },
  {
    id: 2,
    content: &quot;Todo앱 만들기&quot;,
    // Error : Property 'completed' is missing in type '{ id: number; content: string; }' but required in type 'Todo'.ts(2741)
  },
  {
    id: 3,
    content: &quot;리액트 프로젝트 만들기&quot;,
    completed: false,
    expireDate: &quot;5월 5일&quot;,
    // Error: Object literal may only specify known properties, and 'expireDate' does not exist in type 'Todo'.ts(2353)
  },
];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;위의 코드에서 Todo 인터페이스를 선언해 주었다. Todo 인터페이스는 number타입 id, string타입 content, boolean타입 completed로 이루어져 있다. 그 다음으로 todos변수에 Todo[] 인터페이스를 타입으로 선언했으므로 이제 todos변수는 해당 인터페이스를 준수해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;그리고 타입 검사는 속성들의 순서를 지킬 필요가 없다. 단지 인터페이스가 요구하는 속성들이 존재하는지, 속성들이 요구하는 타입을 가졌는지만 판단한다.&lt;/span&gt;&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;선택적 프로퍼티 (Optional Properties)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;인터페이스의 모든 속성이 항상 전부 필요한 것이 아니라면 선택적 프로퍼티를 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721882893473&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Todo {
  id: number;
  content: string;
  completed: boolean;
  expireDate?: Date;
}

let todos: Todo[];

todos = [
  {
    id: 1,
    content: &quot;타입스크립트 공부하기&quot;,
    completed: false,
  },
  {
    id: 2,
    content: &quot;Todo앱 만들기&quot;,
    completed: false,
    expireDateeeee: new Date(),
    // Error: Object literal may only specify known properties, but 'expireDateeeee' does not exist in type 'Todo'. Did you mean to write 'expireDate'?ts(2561)
  },
  {
    id: 3,
    content: &quot;리액트 프로젝트 만들기&quot;,
    completed: false,
    expireDate: new Date(),
    // OK
  },
  {
    id: 4,
    content: &quot;개인 프로젝트 완성하기&quot;,
    completed: false,
    // OK
  },
];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Todo 인터페이스의 expireDate 속성 끝에 ? 를 붙여 표시하면 선택적 프로퍼티가 된다. 선택적 프로퍼티의 이점은 id:2 객체의 expireDateeeee처럼 인터페이스에 속하지 않는 프로퍼티의 사용을 방지하면서, 사용 가능한 속성을 기술하는 것이다. 따라서 id:3, id:4의 객체 둘 다 Todo 인터페이스로 취급된다.&lt;/span&gt;&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;읽기전용 프로퍼티 (Readonly Properties)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;어떤 속성은 객체가 처음 생성될 때만 수정 가능하고 이후에는 변경되는 것을 방지해야 한다. 이러한 경우 프로퍼티 이름 앞에 &lt;b&gt;readonly&lt;/b&gt;를 붙여서 읽기전용 프로퍼티를 지정할 수 있다. 그럼 읽기전용 프로퍼티로 지정된 프로퍼티는 처음 할당 후 바꿀 수 없는 const처럼 행동한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721883447059&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface CustomSquare {
  readonly X: number;
  readonly Y: number;
  edge: number;
}

let square: CustomSquare;

square = {
  X: 5,
  Y: 2,
  edge: 10,
};

console.log(
  &quot;x : &quot;,
  square.X,
  &quot; y : &quot;,
  square.Y,
  &quot; area: &quot;,
  square.edge * square.edge
);
// x :  5  y :  2  area:  100

square.X = 100;
// Error: Cannot assign to 'x' because it is a read-only property.

square.edge = 50;
// OK&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;초과 프로퍼티 검사 (Excess Property Checks)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;JavaScript에서는 어떤 함수에 객체 리터럴이 잘못 전달되면 에러를 알려주지 않고 이상한 결과만을 뱉는다. 아래의 간단한 예제를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722055827378&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function add({ a, b, c }) {
  console.log(c);
  // undefined;
  return a + b + c;
}

console.log(add({ a: 5, b: 5, d: 5 }));
// NaN&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;자바스크립트에서 a, b, c로 이루어진 객체 타입을 파라미터로 받는 add함수에 a, b, d로 이루어진 객체 타입을 넘긴다면 c에 대해 어떠한 값도 전달되지 않았기 때문에 c는 undefined값을 갖는다. 결과적으로 a + b + c연산은 5 + 5+ undefined가 되기 때문에 &lt;b&gt;Not a Number&lt;/b&gt; 즉, 숫자가 아니다라는 결과를 얻는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 타입스크립트는 이러한 코드가 버그가 있을 수 있다고 생각한다. 타입스크립트에서 &lt;b&gt;객체 리터럴&lt;/b&gt; 즉 object 리터럴은 (1)&lt;b&gt;다른 변수에 할당될 때&lt;/b&gt;나 (2)&lt;b&gt;인수로 전달될 때&lt;/b&gt; &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;초과 프로퍼티 검사&lt;/b&gt;&lt;/span&gt;를 받는다. 만약 객체 리터럴이 대상 타입이 가지고 있지 않은 프로퍼티를 갖고 있다면 에러가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722056225733&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function add({ a, b, c }) {
  return a + b + c;
}

console.log(add({ a: 5, b: 5, d: 5 }));
// Error: Object literal may only specify known properties, and 'd' does not exist in type '{ a: any; b: any; c: any; }'.ts(2353)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;에러의 뜻은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체 리터럴은 알려진 프로퍼티들로만 지정할 수 있습니다. 그리고 'd'은 {a: any; b: any;c: any; }타입에 존재하지 않습니다. ts(2353)&lt;/span&gt;&lt;/blockquote&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;</description>
      <category>프로그래밍/Frontend</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/145</guid>
      <comments>https://zeus2141.tistory.com/145#entry145comment</comments>
      <pubDate>Sat, 27 Jul 2024 14:05:31 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] Type + JavaScript - (1) 기본 타입</title>
      <link>https://zeus2141.tistory.com/144</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BBPta/btsIHARyvQs/RbbjsD946kkNRMV0Vorar1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BBPta/btsIHARyvQs/RbbjsD946kkNRMV0Vorar1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BBPta/btsIHARyvQs/RbbjsD946kkNRMV0Vorar1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBBPta%2FbtsIHARyvQs%2FRbbjsD946kkNRMV0Vorar1%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;492&quot; height=&quot;277&quot; data-origin-width=&quot;1240&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;1. 기본 타입&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;자바스크립트의 원시 타입을 대부분 지원하며 추가적으로 Enum(열거)타입과 기타 여러 유용한 타입을 지원한다. 다만 원시값을 래핑하는 Boolean, Number, String 내장 객체타입도 지원하지만 매우 드물게 사용되므로 웬만해선 소문자로 시작하는 원시 타입을 사용하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;boolean(불리언)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;true/false값&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721608836419&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const a : boolean = false;
const b : boolean = 1;
// Error : Type 'number' is not assignable to type 'boolean'.ts(2322)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;number(숫자)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;JavaScript와 같이 TypeScript에서도 모든 숫자는 부동 소수점 값이다. 모든 부동 소수에는 number라는 타입이 붙여진다. TypeScript는 16,10, 2, 8진수 리터럴도 지원한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721610977553&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const decimal: number = 6;
// compiled: var decimal = 6;
const hex: number = 0xf00d;
// compiled: var hex = 0xf00d;
const binary: number = 0b1010;
// compiled: var binary = 10;
const octal: number = 0o744;
// compiled: var octal = 484;
const floatingPoint: number = 0.1;
// compiled: var floatingPoint = 0.1;
const square: number = 2 ^ 2;
// compiled: var square = 2 ^ 2;&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;string (문자열)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일반적인 문자열 데이터 타입&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721611774020&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let username: string = &quot;churnobyl&quot;;
username = &quot;bbb&quot;;
// OK
username = 5;
// Error : Type 'number' is not assignable to type 'string'.ts(2322)

const age: number = 20;
const sentence = `Hello, I'm ${age} years old`;
// compiled: var sentence = &quot;Hello, I'm &quot;.concat(age, &quot; years old&quot;);&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Array (배열)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;TypeScript에서 배열 타입은 두 가지 방법으로 쓸 수 있다. 타입 뒤에 []를 쓰거나, 제네릭 배열 타입을 쓰거나.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721611927826&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const list: number[] = [1, 5, 7];
const list2: Array&amp;lt;number&amp;gt; = [1, 5, 7];&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Tuple (튜플)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요소의 타입과 개수가 고정된 배열을 표현하기 위해서 튜플 타입을 사용할 수 있다. 다만 튜플 안의 요소의 타입이 모두 같을 필요는 없고 순서만 지켜주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721612315869&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x: [string, string, number];

x = [&quot;Hi&quot;, &quot;MyAgeIs&quot;, 5];
// OK

x = [&quot;Hi&quot;, &quot;MyAgeIs&quot;];
// Error: Type '[string, string]' is not assignable to type '[string, string, number]'.
//  Source has 2 element(s) but target requires 3.ts(2322)

console.log(x[0].toLowerCase());
// OK

console.log(x[2].toLowerCase());
// Error: Property 'toLowerCase' does not exist on type 'number'.ts(2339)

x[5] = &quot;New Element&quot;;
// Error: Type '&quot;New Element&quot;' is not assignable to type 'undefined'.ts(2322)&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Enum (열거)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바나 C#같이 TypeScript는 Enum 타입을 지원한다. 어떤 값의 집합에 이름을 부여해 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721612851754&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기본적으로 enum은 0부터 시작해 멤버들의 번호를 매김
enum ButtonType {
  Small,
  Medium,
  Big,
}

const submitButton: ButtonType = ButtonType.Big;

console.log(submitButton);
// 2

// 첫번째 멤버를 수동으로 바꾸면 해당 숫자부터 번호를 매김
enum Role {
  Common = 1,
  Admin,
}

console.log(Role[0]);
// undefined
console.log(Role[2]);
// Admin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;열거형은 JavaScript에는 존재하지 않는 개념이므로 이를 컴파일하면 자바스크립트의 객체로 변환한다. 예를 들어 ButtonType은 컴파일될 때, Small, Medium, Big 멤버가 추가되고 각각 0, 1, 2 값을 가진다. 다시 반대로도 접근할 수 있도록 0, 1, 2 멤버도 추가되고 각각 Small, Medium, Big 값을 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컴파일된 Enum&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1721613194528&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var ButtonType;
(function (ButtonType) {
  ButtonType[(ButtonType[&quot;Small&quot;] = 0)] = &quot;Small&quot;;
  ButtonType[(ButtonType[&quot;Medium&quot;] = 1)] = &quot;Medium&quot;;
  ButtonType[(ButtonType[&quot;Big&quot;] = 2)] = &quot;Big&quot;;
})(ButtonType || (ButtonType = {}));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dggFr6/btsIGZ5BwUC/qGwWn4DkcFt61ikfKIk5QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dggFr6/btsIGZ5BwUC/qGwWn4DkcFt61ikfKIk5QK/img.png&quot; data-alt=&quot;결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dggFr6/btsIGZ5BwUC/qGwWn4DkcFt61ikfKIk5QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdggFr6%2FbtsIGZ5BwUC%2FqGwWn4DkcFt61ikfKIk5QK%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;447&quot; height=&quot;134&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;컴파일된 결과를 보면 ButtonType 변수를 선언한 뒤, 괄호로 쌓인 블럭에 함수를 넣어 즉시 실행하는 &lt;b&gt;즉시실행함수(IIFE, Immediately Invoked Function Expression)&lt;/b&gt;을 사용해 안에 멤버들을 추가해준다.&lt;/span&gt;&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;any&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;애플리케이션을 만들 때, 알지 못하는 타입을 표현해야 할 수도 있다.이 때 any 타입을 사용할 수 있다. any타입을 사용하면 컴파일 시간에 검사를 하지 않고 통과한다. 즉, 자바스크립트처럼 작업할 수 있는 방법이지만 그럼 타입스크립트를 사용하는 이유가 없으므로 초기 개발시에만 해놓고 추후에 확실한 타입으로 대체해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721614853218&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Like JavaScript
let a: any = 4;
a = &quot;Gamja&quot;;
a = [1, 2, 3];
a = false;
// OK

const b: any = 4;
b.ifItExists();
b.toFixed();
// OK&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;void&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;어떤 타입도 존재할 수 없음을 나타낸다. any타입과 정반대의 역할을 한다. 변수에 이 타입을 지정했을 때, &lt;b&gt;undefined&lt;/b&gt; 혹은 strictNullChecks옵션이 false일 때 &lt;b&gt;null&lt;/b&gt;만 지정할 수 있으므로 그다지 유용하지 않다. 따라서 일반적으로 어떤 함수의 리턴값이 없을 때 void를 지정한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721615250130&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 변수에 타입을 지정했을 시 오직 undefined만 가능
let unusable: void = undefined;
// OK

// tsconfig.json에서 strictNullChecks옵션이 false일 때는 null도 가능 
unusable = null;
// OK&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;null &amp;amp; undefined&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;null, undefined도 역시 그 자체만으로는 유용하지 않으며, 다른 모든 타입의 하위타입이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721618537081&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 얘만 됨
let un: undefined = undefined;
let nu: null = null;

// 다른 모든 타입의 하위타입
let a: number = undefined;
let b:string = null;&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;never&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;절대 발생할 수 없는 타입을 나타낸다. never는 함수 표현식이나 화살표 함수 표현식에서 항상 오류를 발생시키거나 절대 반환하지 않는 반환타입으로 사용된다. never타입은 모든 타입에 할당 가능한 하위 타입이지만, 어떤 타입도 never에 할당할 수 없고 never의 하위 타입이 될 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721618648726&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// never를 반환하는 함수는 함수의 마지막에 도달할 수 없다.
function error(message: string): never {
    throw new Error(message);
}

// 반환 타입이 never로 추론된다.
function fail() {
    return error(&quot;Something failed&quot;);
}

// never를 반환하는 함수는 함수의 마지막에 도달할 수 없다.
function infiniteLoop(): never {
    while (true) {
    }
}&lt;/code&gt;&lt;/pre&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;object&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원시 타입이 아닌 타입을 나타낸다. 예를 들어, number, string, boolean, bigint, symbol, null, undefined가 아닌 나머지를 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721618897165&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;declare function create(o: object | null): void;

create({ prop: 0 }); // 성공
create(null); // 성공

create(42); // 오류
create(&quot;string&quot;); // 오류
create(false); // 오류
create(undefined); // 오류&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;타입 단언 (Type assertions)&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자가 타입스크립트보다 값에 대해서 더 잘 알고 있을 때, 해당 값을 확정지어줄 수 있다. 타입 단언도 두가지 형태로 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721619099434&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a: any = &quot;이건 문자열&quot;;

// angle-bracket 문법
let aLength: number = (&amp;lt;string&amp;gt;a).length;

let b: any = 5555;

// as 문법
let bToFixed = (b as number).toFixed();&lt;/code&gt;&lt;/pre&gt;</description>
      <category>프로그래밍/Frontend</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/144</guid>
      <comments>https://zeus2141.tistory.com/144#entry144comment</comments>
      <pubDate>Mon, 22 Jul 2024 12:32:21 +0900</pubDate>
    </item>
    <item>
      <title>[Redis] 인메모리 데이터베이스 Redis - (1) Introduction</title>
      <link>https://zeus2141.tistory.com/143</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2045&quot; data-origin-height=&quot;2048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y5ICX/btsIChZ3Am5/mc3hXO9PgAtoLpZN8jKeek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y5ICX/btsIChZ3Am5/mc3hXO9PgAtoLpZN8jKeek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y5ICX/btsIChZ3Am5/mc3hXO9PgAtoLpZN8jKeek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy5ICX%2FbtsIChZ3Am5%2Fmc3hXO9PgAtoLpZN8jKeek%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;398&quot; height=&quot;399&quot; data-origin-width=&quot;2045&quot; data-origin-height=&quot;2048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. What is Redis?&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;699&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E2iUs/btsIGaYYS2g/zFbB7YrItmiKVhYK88Sylk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E2iUs/btsIGaYYS2g/zFbB7YrItmiKVhYK88Sylk/img.png&quot; data-alt=&quot;2018년 가장 인기있는 데이터베이스 컨테이너 (출처 : Redis Youtube)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E2iUs/btsIGaYYS2g/zFbB7YrItmiKVhYK88Sylk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE2iUs%2FbtsIGaYYS2g%2FzFbB7YrItmiKVhYK88Sylk%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;677&quot; height=&quot;384&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;699&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2018년 가장 인기있는 데이터베이스 컨테이너 (출처 : Redis Youtube)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Redis&lt;span style=&quot;color: #000000;&quot;&gt;(REmote DIctionary Server)&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;는 &lt;b&gt;캐시 서버로 가장 많이 사용&lt;/b&gt;되는 &lt;b&gt;key-value기반&lt;/b&gt;&amp;nbsp;&lt;b&gt;인메모리 데이터베이스&lt;/b&gt;다. 프로세스에 할당된 메모리에 데이터를 올려놓고 사용하므로 하드디스크와 같은 대용량 저장장치를 사용하는 &lt;b&gt;RDBMS 같은 타 데이터베이스에 비해 굉장히 빠른 속도&lt;/b&gt;가 장점이다. 하지만 대용량 저장장치에 비하면 용량이 매우 작으므로 &lt;b&gt;메인 데이터베이스로 사용하기에는 한계&lt;/b&gt;가 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;또한 메모리는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Volatile_memory&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;volatile&lt;/b&gt;&lt;/a&gt;한 특성(휘발성)을 가지고 있으므로 프로세스가 종료되면 데이터가 유실될 수 있다. 그래서 Redis는 이러한 단점을 &lt;b&gt;AOF&lt;/b&gt;, &lt;b&gt;RDB&lt;/b&gt;라는 두가지 백업 방식을 이용해 커버한다. Redis의 백업 방식에 대한 자세한 내용은 뒤에 다시 언급하려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;추가적으로 Redis는 &lt;b&gt;key-value 자료 구조 기반&lt;/b&gt;으로 String뿐만 아니라 Lists, Sets, Hashes, HyperLogLog등의 &lt;b&gt;다양한 자료구조를 제공&lt;/b&gt;하므로 상황에 따라 적절한 자료구조를 사용해 효율성을 극대화할 수 있다. 예컨데, 집합의 카디널리티를 추정하는 확률적 데이터 구조인 HyperLogLog를 사용하면 해당 자료구조로 저장된 데이터를 다시는 확인할 수 없지만 몇 개의 데이터가 저장되어 있는지 확인할 수 있다. 또한 12kb의 고정적인 크기로 유지되어 용량을 매우 작게 차지하므로 대용량 데이터의 유니크한 값을 카운팅할 때 사용할 수 있다. 사용 예로 검색엔진에서 하루동안 검색된 검색어의 수나 한달동안 해당 사이트에 접속한 IP들의 유니크한 개수등을 알고 싶을 때 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;위의 설명을 토대로 Redis Github에서는 Redis를 &lt;b&gt;'&lt;a href=&quot;https://memcached.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;memcached&lt;/a&gt; +&amp;nbsp; 디스크 백업 + 다양한 자료구조'이 가능한 memcached의 확장버전&lt;/b&gt;으로 생각해도 된다고 언급한다. 두 프로젝트의 차이에 대해 알고 싶다면 aws에 &lt;a href=&quot;https://aws.amazon.com/ko/elasticache/redis-vs-memcached/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;두 오픈 소스 프로젝트를 비교해 정리한 페이지&lt;/a&gt;가 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Redis는 2009년 이탈리아의 &lt;a href=&quot;http://invece.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Salvatore Sanfilippo&lt;/a&gt;가 처음으로 개발했으며 2015년부터 Redis Lab에 합류해 수석 개발자로서 개발을 지속하고 있다.&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 레디스는 싱글 쓰레드 구조 (Single-Thread)&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Frame 1.png&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z7EuP/btsISXlfeez/Q1mZvUQIfEDa4qSZP5ahW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z7EuP/btsISXlfeez/Q1mZvUQIfEDa4qSZP5ahW1/img.png&quot; data-alt=&quot;레디스는 싱글 쓰레드로 동작한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z7EuP/btsISXlfeez/Q1mZvUQIfEDa4qSZP5ahW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz7EuP%2FbtsISXlfeez%2FQ1mZvUQIfEDa4qSZP5ahW1%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;448&quot; height=&quot;300&quot; data-filename=&quot;Frame 1.png&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;레디스는 싱글 쓰레드로 동작한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Redis는 지금까지 하나의 쓰레드만으로 작업을 처리하는 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;싱글 쓰레드 디자인&lt;/b&gt;&lt;/span&gt;을 채택했다. 그리고 싱글 쓰레드만으로 &lt;b&gt;초당 10만건 이상의 데이터 요청을 처리&lt;/b&gt;할 수 있었다. Redis 6.0을 발표하면서 멀티쓰레드를 지원하는 ThreadedIO 기능이 추가되었지만, 어쨌거나 Redis는 싱글쓰레드만으로 어떻게 빠른 속도를 달성할 수 있었을까? 그 부분은 아래 I/O Multiplexing에서 알아보기로 하고, 그전에 싱글 쓰레드 디자인이 &lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;멀티프로세스/쓰레드 방식과 구분되는&lt;/span&gt;&amp;nbsp;이점에 대해서 먼저 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;트랜잭션을 모른다면.&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;&amp;nbsp;먼저 데이터베이스 시스템의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Transaction&lt;/b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;(트랜잭션)을 알아야 한다. 데이터베이스 시스템에서 모든 작업은 트랜잭션 단위로 진행된다. 일반적으로 데이터베이스에 수행되는 작업은 단순히 하나의 데이터를 읽거나 쓰는 게 아니라 복수의 작업이 동시에 진행되어야 의미있는 경우가 대부분이다. 가령, 유저가 게시판에 글을 썼을 때 단순히 게시판 테이블에 INSERT문으로 레코드를 하나 추가하는 것에 그치지 않고 함께 작성한 태그나 이미지 관련 테이블에도 함께 INSERT를 해야할 경우가 있다. 이 중 하나의 작업만 성공해서는 아무 의미도 없고 그래서도 안된다(이미지를 올렸는데 이미지가 없는 글이 작성된다면..?). 따라서 하나의 트랜잭션은 모든 작업이 성공할 경우에만 실제로 데이터베이스에 반영될 수 있도록 commit을 하고 하나라도 실패했다면 rollback해 기존의 상태를 유지할 수 있도록 한다. &lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;이러한 트랜잭션의 특성을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #0070d1; text-align: start;&quot; href=&quot;https://ko.wikipedia.org/wiki/ACID&quot;&gt;&lt;b&gt;ACID&lt;/b&gt;&lt;/a&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;라고 줄여부르기도 하며 각각&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;A&lt;/b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;tomicity(원자성),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;C&lt;/b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;onsistency(일관성),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;I&lt;/b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;solation(고립성),&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;D&lt;/b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;urability(지속성)을 나타낸다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;멀티 쓰레드/프로세싱 방식에서는 다수의 쓰레드나 프로세스가 &lt;u&gt;동시에&lt;/u&gt; 단일 데이터베이스와 상호작용하려고 하는 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EA%B2%BD%EC%9F%81_%EC%83%81%ED%83%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;경쟁 상태&lt;/a&gt;(Race Condition)가 발생하므로 이러한 특성을 지키기 쉽지 않아 다양한 방식의 Lock을 이용해 해결해야 한다. 이는 설계가 복잡해질 수 밖에 없다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 Redis는 &lt;b&gt;단 하나의 쓰레드만 사용하므로 ACID를 만족하기 쉬울뿐더러 CPU 컨텍스트 스위칭 비용도 발생하지 않는다&lt;/b&gt;. 수많은 사람이 창고의 물건을 이곳저곳에 넣었다 빼기 위해서 넣는 순서나 빼는 순서 규칙을 지정하는 것보다, 단 한 사람이 창고를 관리하는 것이 물건의 위치를 더 잘 기억하며 관리하기 쉬운 것과 같다. 다만 그 한 사람이 수많은 사람들이 작업하는 속도만큼 빨라야 효율적이겠지만..&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;그리고 Redis는 창고지기 하나가 작업하는 속도를 비약적으로 끌어올렸다.&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Blocking I/O vs Non-Blocking I/O&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Redis가 빠른 속도를 달성할 수 있었던 기반인 I/O Multiplexing에 대해서 알아보기 위해서는 먼저 Blocking I/O와 Non-Blocking I/O의 차이에 대해서 알아보아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Blocking I/O&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsRHOF/btsISHK5KFb/NWTl5LyrBPSGmLdgwWMB1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsRHOF/btsISHK5KFb/NWTl5LyrBPSGmLdgwWMB1K/img.png&quot; data-alt=&quot;Blocking I/O&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsRHOF/btsISHK5KFb/NWTl5LyrBPSGmLdgwWMB1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsRHOF%2FbtsISHK5KFb%2FNWTl5LyrBPSGmLdgwWMB1K%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;538&quot; height=&quot;340&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Blocking I/O&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&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;Blocking I/O는 가장 일반적인 방식으로, 어떤 유저 프로세스(애플리케이션 프로세스)가 실행되다가 외부 파일이 필요하거나 외부로 파일을 내보내려고 할 경우 커널에 시스템 콜을 하고 I/O 작업이 진행되는 동안 자신의 작업을 중단하고 I/O가 끝날 때까지 대기하는 방식이다.&lt;/p&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;위 그림을 보면 애플리케이션 입장에서 I/O작업이 필요한 시점에 Read()를 호출해 커널에 시스템 콜을 하고, 커널이 필요한 I/O작업을 시작하고 Response가 올 때까지 하염없이 대기한다. 이 상태를 Block되었다고 하고 그동안 애플리케이션 프로세스는 다른 작업을 하지 못한다.&amp;nbsp;&lt;/p&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;h3 data-ke-size=&quot;size23&quot;&gt;Non-Blocking I/O&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNfP9c/btsISUQZoRI/qTuXKP2BgEhk77S4hcPVc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNfP9c/btsISUQZoRI/qTuXKP2BgEhk77S4hcPVc0/img.png&quot; data-alt=&quot;Non-Blocking I/O&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNfP9c/btsISUQZoRI/qTuXKP2BgEhk77S4hcPVc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNfP9c%2FbtsISUQZoRI%2FqTuXKP2BgEhk77S4hcPVc0%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;474&quot; height=&quot;340&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Non-Blocking I/O&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&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;Non-Blocking I/O는 유저 프로세스가 시스템 콜을 했을 때, I/O작업이 완료될 때까지 Blocking되지 않고 커널이 바로 리턴해 제어권을 유저 프로세스가 다시 받아 다른 작업을 계속 수행할 수 있는 방식이다. Read()를 호출해 시스템 콜을 하면 커널은 요청을 받은 즉시 작업의 완료 여부와는 무관하게 제어권을 다시 유저 프로세스에게 넘겨준다. 그리고 유저 프로세스는 다른 작업을 수행하는 중간중간에 시스템 콜을 보내서 앞선 작업의 완료 여부를 확인하고, 완료되었다면 그 작업의 나머지 부분을 수행한다.&lt;/p&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;위의 두 그림은 둘다 Synchronous 모델이다. Redis가 ACID 특히 원자성을 보장하기 위해서는 작업의 순서가 보장되어야 하니 Asynchronous 모델은 이번 글에서 제외했다. Sync, Async, Block, Non-block에 대해서 더 확실하게 알고 싶다면 네이버 클라우드 블로그를 참조하자. 아주 잘 정리되어 있다! &lt;a href=&quot;https://blog.naver.com/n_cloudplatform/222189669084&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. I/O Multiplexing&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Frame 2.png&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbvK8q/btsIQJPqT28/fEckP1E0T4SJ8E8wNslDXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbvK8q/btsIQJPqT28/fEckP1E0T4SJ8E8wNslDXk/img.png&quot; data-alt=&quot;I/O Multiplexing - Linux의 epoll()&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbvK8q/btsIQJPqT28/fEckP1E0T4SJ8E8wNslDXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbvK8q%2FbtsIQJPqT28%2FfEckP1E0T4SJ8E8wNslDXk%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;448&quot; height=&quot;300&quot; data-filename=&quot;Frame 2.png&quot; data-origin-width=&quot;448&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;I/O Multiplexing - Linux의 epoll()&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이제 Redis가 단 하나의 쓰레드만으로 어떻게 빠른 속도를 달성할 수 있었는지 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Redis는 메모리에 데이터를 적재하거나 읽는 것보다 외부에서 데이터 가져오고 외부로 내보내는(입출력) &lt;b&gt;네트워크 I/O가&amp;nbsp;훨씬 오래 걸린다&lt;/b&gt;는 사실에 주목했다. 데이터를 준비하는 과정이 더 오래걸리는 것이다. Redis는 이를 해결하기 위한 방법으로 OS의 &lt;a href=&quot;https://blog.naver.com/n_cloudplatform/222189669084&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;I/O Multiplexing&lt;/b&gt;&lt;/span&gt;&lt;/a&gt;을 이용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;I/O Multiplexing은 &lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; text-align: start;&quot;&gt;세부적으로 어떻게 구현되는지에 따라 달라지기 때문에 의견이 분분하지만&amp;nbsp;&lt;/span&gt; 일반적으로 Sync Non-blocking 모델로 분류된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;즉, 어떤 명령이 실행됐을 때 Redis는 해당 명령을 실행하기 위한 데이터가 전부 준비될 때까지 무작정 대기하지 않고&lt;b&gt; 작업할 배열에 추가만 해놓고&lt;/b&gt; 바로 다음 명령 수행을 위해 대기(&lt;b&gt;Event Loop System&lt;/b&gt;)한다. 데이터를 준비하는 일은 전부 OS에게 맡겨뒀다가 데이터가 전부 준비됐다고 OS가 알려주면 그제서야 해당 명령을 처리하는 것이다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이 방식을 사용하면 다수의 요청이 들어와도 처리할 준비가 완료된 명령부터 처리하면 되기 때문에 단일 쓰레드로도 높은 속도를 달성할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍/DataBase</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/143</guid>
      <comments>https://zeus2141.tistory.com/143#entry143comment</comments>
      <pubDate>Fri, 19 Jul 2024 14:37:25 +0900</pubDate>
    </item>
    <item>
      <title>[nginx] (2) nginx.conf</title>
      <link>https://zeus2141.tistory.com/142</link>
      <description>&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;880&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GNmoK/btsIiqhfszA/9MK5BnjlyCXbh9n0yIAy3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GNmoK/btsIiqhfszA/9MK5BnjlyCXbh9n0yIAy3K/img.png&quot; data-alt=&quot;nginx&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GNmoK/btsIiqhfszA/9MK5BnjlyCXbh9n0yIAy3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGNmoK%2FbtsIiqhfszA%2F9MK5BnjlyCXbh9n0yIAy3K%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;291&quot; height=&quot;200&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;880&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nginx&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. nginx의 구조&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; nginx는 C언어로 개발되었다. 그리고 nginx는 일반적으로 Linux나 Unix계열 OS에 얹어서 사용한다. Win32에서도 사용할 수 있다고 하지만, select()만 사용할 수 있어서 성능적으로 매우 비효율적이며, 앞으로의 확장성도 기대하기 어렵다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;OS에 따라 다른 설치법을 제공하니 &lt;a href=&quot;https://nginx.org/en/docs/install.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서&lt;/a&gt;를 확인하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;설치를 하고 나면 구성 파일이 기본적으로 세팅되어 있으므로 nginx명령어를 통해 바로 실행시킬 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719476752910&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;다음과 같이 실행하면 nginx는 구성파일인 nginx.conf를 읽고 구동을 시작한다. 이때 nginx.conf에 없는 필수 정보들은 기본값으로 세팅한다. 실행 후 ps -ef명령어를 통해 프로세스 목록을 확인하면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;993&quot; data-origin-height=&quot;515&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqxYR1/btsIel2jGFj/KC4uzP27tQBSUEu90ogE60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqxYR1/btsIel2jGFj/KC4uzP27tQBSUEu90ogE60/img.png&quot; data-alt=&quot;ps -ef&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqxYR1/btsIel2jGFj/KC4uzP27tQBSUEu90ogE60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqxYR1%2FbtsIel2jGFj%2FKC4uzP27tQBSUEu90ogE60%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;993&quot; height=&quot;515&quot; data-origin-width=&quot;993&quot; data-origin-height=&quot;515&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ps -ef&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;기본적으로 nginx를 설치하면 nginx.conf에서 Worker Process의 수는 auto로 설정되어 있다. auto 옵션은 일반적으로 논리 프로세서의 수로 설정이 된다. 본인 컴퓨터는 12-Core 24-Thread라 1개의 Master Process와 24개의 Worker Process가 생성된 것으로 보인다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;다음으로 nginx를 중지하거나 구성파일을 라이브로 리로드하기 위해서는 nginx -s [option]을 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;stop - 빠른 종료&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;quit&lt;/b&gt; - 우아한(Graceful) 종료 (Worker Process가 현재까지의 요청 처리를 완료할 때까지 프로세스를 대기했다가 종료)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;reload&lt;/b&gt; - 구성파일 다시 로드 (기존의 Worker Process들을 유지한 채로 새로운 구성파일을 이용해 Master Process가 새 Worker Process를 시작하고, 모든 과정이 성공적이라면 이전 Worker Process들을 Graceful하게 종료)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;reopen - 로그 파일 다시 열기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. nginx.conf의 구조&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx는 일반적으로 nginx.conf에 지시된 지시문으로 nginx를 제어한다. 따라서 nginx.conf를 잘 작성하면 nginx를 잘 다룰 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx의 지시문은 &lt;b&gt;단순 지시문&lt;/b&gt;과 &lt;b&gt;블록 지시문&lt;/b&gt;으로 구분된다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단순 지시문 - [지시문] [파라미터]; 형태.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;블록 지시문 - [지시문] [(파라미터)] {중괄호} 형태.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단순 지시문&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1719642842207&quot; class=&quot;conf abnf&quot; data-ke-language=&quot;conf&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;worker_processes auto;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;블록 지시문&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1719642862614&quot; class=&quot;conf nginx&quot; data-ke-language=&quot;conf&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;events {
	worker_connections 512;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;또 블록 지시문의 중괄호 안에 또 다른 지시문을 가질 수 있는 경우 이를 &lt;b&gt;컨텍스트&lt;/b&gt;라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;nginx.conf 안에서 지시문의 가장 큰 단위는 main 컨텍스트이며 그 안에 &lt;b&gt;다양한 트래픽 유형에 따라 지시어를 그룹화&lt;/b&gt;하는 몇 개의 컨텍스트가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;1046&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2UI4K/btsIhaGv3SS/Z0Kj9HqPdcHUdQNkkKv1N1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2UI4K/btsIhaGv3SS/Z0Kj9HqPdcHUdQNkkKv1N1/img.png&quot; data-alt=&quot;nginx.conf의 컨텍스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2UI4K/btsIhaGv3SS/Z0Kj9HqPdcHUdQNkkKv1N1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2UI4K%2FbtsIhaGv3SS%2FZ0Kj9HqPdcHUdQNkkKv1N1%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;1046&quot; height=&quot;732&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nginx.conf의 컨텍스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;main&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;events&lt;/b&gt; - 일반적인 연결 처리&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;http&lt;/b&gt; - HTTP 트래픽&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;mail - Mail 트래픽&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;stream - TCP/UDP 트래픽&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;웹 애플리케이션을 구동하는 등의 기초적인 상황에서는 events, https만 있어도 무방하다.&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. nginx.conf 작성하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx는 오픈소스 버전과 엔터프라이즈를 위한 유료버전인 nginx plus가 있다. 로드 밸런싱의 health check 기능 같은 건 유료버전에서만 지원하니 사용할 때 잘 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1719734596051&quot; class=&quot;conf properties&quot; data-ke-language=&quot;conf&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# main : 메인 컨텍스트

# user user [group];
# Worker Process가 어떤 user, group으로 실행되는지에 대한 설정
# user root root;로 할 경우 리눅스 파일 시스템의 모든 권한을 갖게 되므로 일반적으로 user nginx nginx;로 설정
# group을 생략할 경우 user와 같은 이름이 지정됨
# default : nobody
user  nginx nginx;

# worker_processes number | auto;
# Worker Process의 수
# CPU 코어 수, 하드디스크 수 등에 따라 최적의 수는 달라질 수 있으므로 일반적으로 CPU 코어의 수로 설정
# worker_processes auto;로 두는 게 일반적
# default : auto
worker_processes auto;

# worker_priority number(-20 ~ 20);
# Worker Process의 스케줄링 우선순위를 지정
# Linux에서 프로세스가 실행될 때 nice값에 따라 실행 우선순위가 주어짐
# worker_priority는 Worker Process의 이 값을 조정함
# 커널이 -5 정도의 우선순위로 실행되므로 이 값 이하의 값을 설정하는 것은 권장되지 않음
# default : 0
worker_priority -1;

# debug_points abort | stop;
# 디버깅을 위한 설정
# Worker Process 재시작 시 소켓의 리크 등과 같은 내부 오류가 발생했을 때
# abort(파일 생성)로 설정하면 시스템 디버거를 이용해 추가적인 코어 파일 생성
# default : abort
debug_points abort;

# error_log filePath [level];
# 로깅 구성
# 첫번째 파라미터 filePath에 로그의 위치를 지정할 수 있음
# 두번째 파라미터 level은 레벨에 따라 debug, info, notice, warn, error, crit, alert, emerg중에 선택
# 선택한 레벨 이상 수준의 로그만 기록됨
# main 컨텍스트뿐만 아니라 하위 어느 컨텍스트에도 재정의할 수 있음
# 하지만 재정의 했을 때 level를 적지 않으면 아예 로깅을 제외한다는 뜻이 되므로 주의
# 참고 : https://nginx.org/en/docs/ngx_core_module.html#error_log
# default : logs/error.log error
error_log logs/error.log error;

# load_module file;
# 다이나믹 모듈을 로드함
# nginx의 확장 기능이 필요한 경우, 서드파티 모듈을 추가할 수 있음
# 추가적으로 nginx -V 명령어에서 --with커맨드 부분을 확인하면 모듈을 확인할 수 있음
#load_module xxx;

# worker_rlimit_core number;
# Worker Process 당 코어 파일의 크기
worker_rlimit_core 100m;

# worker_rlimit_nofile number;
# Worker Process가 동시에 사용할 수 있는 파일의 최대 개수
worker_rlimit_nofile 2048;


# event : Connection 처리에 영향을 주는 지시문을 지정하는 컨텍스트
events {
	# worker_connections number;
	# 하나의 Worker Process에서 열 수 있는 최대 동시 연결 수
	# 이 숫자는 클라이언트와의 연결 뿐만 아니라 프록시 서버와의 연결을 포함한 모든 연결의 숫자임
	# 그리고 당연히 이 숫자는 worker_rlimit_nofile에 정의된 숫자를 넘을 수 없음
	# main 컨텍스트의 worker_processes가 4, 현재 worker_connections가 1024라면
	# worker_processes * worker_connections = 4096, 즉 최대 동시 접속이 4096 언저리까지 가능
	# default : 512
	worker_connections 1024;

	# use [method];
	# 연결 처리에 어떤 메서드를 사용할 것인지 결정
		# select - File Descriptor를 배열로 관리하며 배열을 순회하면서(O(n)) 이벤트가 발생했는지 체크하고 전체 File Descriptor가 매번 전달. OS 이식성 좋음
		# poll - select와 비슷하나 장단점이 있음. 접속량이 많아지면 오히려 select보다 성능 떨어짐. OS 이식성 나쁨
		# epoll - 리눅스에서 select의 단점을 보완한 방식.
		# kqueue - BSD계열에서의 epoll 방식
	# ※ nginx는 기본적으로 가장 효율적인 방법을 사용하기 때문에 굳이 적어줄 필요는 없다.
	use epoll;
}

# http : HTTP 서버에 대한 지시문을 지정하는 컨텍스트
http {
	# include file | mask;
	# 다른 file이나 mask와 매칭되는 파일들을 구성 파일로 임포트
	# 어느 컨텍스트에도 추가할 수 있음
	# 일반적으로 중복된 부분은 nginx.conf에 남기고
	# 상황 별로 다른 설정들은 upstream.conf, header.conf 등으로 분리한 뒤 임포트해서 씀
	include mime.types; # nginx내에서 처리할 MIME 타입 모음 임포트. nginx.conf와 동일 디렉토리에 mime.types 참고

	upstream backend_tomcat_server {
		server b1.churnobyl.com weight=5;
		server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;

		server backup1.churnobyl.com backup;
	}

	# server : 가상 서버 (Virtual Server)에 대한 지시문을 지정하는 컨텍스트
	server {
		# server_name name ...;
		# 가상 서버의 이름을 설정
		# 정규표현식(Regex), *(asterisk)도 사용가능
		# default &quot;&quot;
		server_name churnobyl.com *.churnobyl.com;

		listen 80;

		# location [ = | ~ | ~* | ^~ ] uri { . . . } or location @name uri { . . . }
		# uri 혹은 name 두 방식을 사용할 수 있음
		#
		location ~* / {
			proxy_pass https://backend_tomcat_server;

			health_check;
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;main컨텍스트와 events, http 컨텍스트만 포함하는 간단한 예제이며, 더 자세한 내용은 공식 문서의 &lt;a href=&quot;https://nginx.org/en/docs/ngx_core_module.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;핵심 기능&lt;/a&gt;, &lt;a href=&quot;https://nginx.org/en/docs/dirindex.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;알파벳 순 지시문&lt;/a&gt;을 살펴보자. 추가적으로 위의 코드는 개념을 위해 nginx.conf에 다 몰아 적었지만, 실제 사용할 때는 중복되는 내용만 nginx.conf에 적고 나머지는 용도에 따라 http.conf, stream.conf, http_dev.conf등으로 다른 파일로 뺀 뒤 import해서 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍/Infra</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/142</guid>
      <comments>https://zeus2141.tistory.com/142#entry142comment</comments>
      <pubDate>Sun, 30 Jun 2024 17:08:03 +0900</pubDate>
    </item>
    <item>
      <title>[nginx] (1) nginx 그리고 기본 역할</title>
      <link>https://zeus2141.tistory.com/141</link>
      <description>&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;880&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yA8go/btsH8UDyu8g/kEZ4Wshx06jaKl9RaI7Xm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yA8go/btsH8UDyu8g/kEZ4Wshx06jaKl9RaI7Xm1/img.png&quot; data-alt=&quot;nginx&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yA8go/btsH8UDyu8g/kEZ4Wshx06jaKl9RaI7Xm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyA8go%2FbtsH8UDyu8g%2FkEZ4Wshx06jaKl9RaI7Xm1%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;291&quot; height=&quot;200&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;880&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nginx&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. What is nginx?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;b&gt;nginx&lt;/b&gt;(발음: engine-x)는 &lt;b&gt;경량 웹 서버&lt;/b&gt;로 이고르 시쇼브(이하 이고르)가 직장에서 사용하던 웹서버인 &lt;b&gt;Apache HTTPd&lt;/b&gt;의 문제점을 해결하기 위해 2004년 처음 개발했다. 이 문제는 &lt;a href=&quot;https://en.wikipedia.org/wiki/C10k_problem&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;C10K Problem&lt;/a&gt;라는 별명으로 알려져 있는데, 10,000개의 클라이언트의 동시 연결을 단일 서버에서 처리할 시 하드웨어의 성능이 충분함에도 불구하고 당시 Apache HTTPd의 작동 방식으로 인해 성능이 저하되는 문제였다. 물론 C10K라는 이름은 Apache HTTPd에만 한정된 용어가 아니고, 그 시절 세계에서 가장 큰 서버가 처리하는 한계가 1만명 정도였기 때문에 이러한 이름이 붙었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;상황을 간략하게 설명하면, 초기&amp;nbsp;&lt;b&gt;Apache HTTPd&lt;/b&gt;는 &lt;b&gt;클라이언트로부터 요청을 받으면 요청을 처리하기 위한 프로세스나 쓰레드를 새로 만들고 소켓(Socket)을 열어 통신하는 구조&lt;/b&gt;였다. 이는 전통적이고 기본적인 통신 기법이었고 당시 그 시대엔 접속량이 많지 않았기 때문에 크게 문제가 되지 않았다. 하지만 세상이 점차 발전함에 따라 인터넷 세상은 더 활발해지고 동시에 특정 서버에 접속하려는 클라이언트의 수가 크게 늘어났다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;1112&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dLSr3x/btsH9Kz6IHl/l0ndxNEKpolNDB9KvKwuR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dLSr3x/btsH9Kz6IHl/l0ndxNEKpolNDB9KvKwuR1/img.png&quot; data-alt=&quot;당시 Apache HTTPd의 요청 처리 방식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dLSr3x/btsH9Kz6IHl/l0ndxNEKpolNDB9KvKwuR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdLSr3x%2FbtsH9Kz6IHl%2Fl0ndxNEKpolNDB9KvKwuR1%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;1112&quot; height=&quot;716&quot; data-origin-width=&quot;1112&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;당시 Apache HTTPd의 요청 처리 방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;앞서 말한 것처럼 당시 Apache HTTPd의 작동 방식은 클라이언트가 접속 요청을 할 때마다 새로운 프로세스나 쓰레드를 생성하는 것이다. 그리고 우리가 알다시피 이러한 운영은 &lt;b&gt;CPU와 메모리 자원의 낭비가 크다&lt;/b&gt;. thread() 명령을 통해 새로운 쓰레드를 생성할 때마다 각 쓰레드는 별도의 메모리 스택을 가지므로 한 개의 쓰레드가 1MB만 차지한다고 해도 10,000명의 요청을 동시에 처리하기 위해서는 최소 10GB의 메모리가 필요하다. 만약 돈을 빵빵하게 써서 메모리가 넉넉해, 1만개의 쓰레드를 생성해도 문제가 전혀 없다고 하더라도, 이제는&lt;b&gt; 경합 조건(Racing Condition)&lt;/b&gt;문제가 대두된다. 한정된 CPU 자원을 차지하기 위한 경쟁에 1만개의 쓰레드가 참여한다면 정신이 나가버릴 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Frame 8.png&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br9E72/btsIciiLduR/IQseTwBkH3E3qtjFeZ1kr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br9E72/btsIciiLduR/IQseTwBkH3E3qtjFeZ1kr1/img.png&quot; data-alt=&quot;nginx의 Event-Driven 방식 아키텍쳐&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br9E72/btsIciiLduR/IQseTwBkH3E3qtjFeZ1kr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr9E72%2FbtsIciiLduR%2FIQseTwBkH3E3qtjFeZ1kr1%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;1458&quot; height=&quot;566&quot; data-filename=&quot;Frame 8.png&quot; data-origin-width=&quot;1458&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nginx의 Event-Driven 방식 아키텍쳐&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이고르는 이 문제를 해결하기 위해 유닉스와 기타 고전적인 분산 시스템의 설계에 영감을 받아 &lt;b&gt;Event-Driven&lt;/b&gt;방식의 아키텍쳐를 개발했고 이것이 바로 nginx다. nginx가 해결한 방식은 위의 그림과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx의 가장 큰 특징은 &lt;b&gt;Master Process-Worker Process&lt;/b&gt;방식으로, &lt;b&gt;Master Process&lt;/b&gt;는 &lt;b&gt;구성 파일을 읽고 포트를 바인딩하는 등의 권한이 필요한 작업을 수행&lt;/b&gt;하고 &lt;b&gt;구성 파일이 지정한 수의 Worker Process를 생성&lt;/b&gt;한다. 그리고 &lt;b&gt;실제로 일을 하는 친구들&lt;/b&gt;이 바로 &lt;b&gt;Worker Process&lt;/b&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;b&gt;Worker Process&lt;/b&gt;는 생성될 때 Master Process로부터 &lt;b&gt;Listen Socket 세트를 함께 제공 받는다&lt;/b&gt;. Worker Process 왼쪽의 귀 모양이 &lt;b&gt;Listen Socket&lt;/b&gt;이며, &lt;b&gt;커널로부터 이벤트를 기다린다&lt;/b&gt;. nginx는 여러 Worker Process로 부하를 분산하기 위해 커널 특히, Unix 계열 OS 커널의 &lt;b&gt;SO_REUSERPORT&lt;/b&gt; 옵션을 사용하는데, 이 옵션을 이용한 nginx에서 &lt;b&gt;Socket Sharding&lt;/b&gt;라고 부르는 이 기술은 &lt;b&gt;같은 포트로 들어오는 워크로드를 여러 프로세스로 분산&lt;/b&gt;시킬 수 있다. 리눅스 커널의 도움을 받아 클라이언트로부터 들어오는 요청이 여러 Worker Process로 분산돼 들어오면 Worker Process의 Listen Socket은 각 클라이언트의 요청마다 &lt;b&gt;Connection Socket&lt;/b&gt;을 만든다. Connection Socket은 각각 Non-blocking I/O 방식으로 해당 클라이언트로부터 요청을 기다리고 Worker Process는 수많은 Connection Socket 중에서 요청이 온 클라이언트만 처리하면 된다. 따라서 특정 클라이언트의 요청이 올 때까지 기다릴 필요가 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Connection Socket들은 추가될 때마다 소량의 추가 메모리를 사용하므로 당시 HTTPd의 방식보다 리소스 측면에서도 유리하다. 물론 지금은 Apache 제품도 많이 발전해서 프로젝트의 성향에 따라 사용하면 된다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;337&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUbjkL/btsH9nT6U4X/fLEIKXs46cAnKGK8mLkkxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUbjkL/btsH9nT6U4X/fLEIKXs46cAnKGK8mLkkxk/img.png&quot; data-alt=&quot;출처 W3Techs.com&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUbjkL/btsH9nT6U4X/fLEIKXs46cAnKGK8mLkkxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUbjkL%2FbtsH9nT6U4X%2FfLEIKXs46cAnKGK8mLkkxk%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;337&quot; height=&quot;450&quot; data-origin-width=&quot;337&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 W3Techs.com&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx의 점유율이 계속해서 증가하더니 2023년에는 Apache를 제치고 1위를 달성했다고 한다. 앞으로도 이러한 추세는 계속될 듯하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. nginx의 역할&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Frame 7.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;1363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nHN6a/btsH9Z5Y02k/8SeohT36I9gJ9EPrAPuca0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nHN6a/btsH9Z5Y02k/8SeohT36I9gJ9EPrAPuca0/img.png&quot; data-alt=&quot;nginx의 다양한 역할&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nHN6a/btsH9Z5Y02k/8SeohT36I9gJ9EPrAPuca0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnHN6a%2FbtsH9Z5Y02k%2F8SeohT36I9gJ9EPrAPuca0%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;1610&quot; height=&quot;1363&quot; data-filename=&quot;Frame 7.png&quot; data-origin-width=&quot;1610&quot; data-origin-height=&quot;1363&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nginx의 다양한 역할&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx는 수많은 클라이언트와 서버의 연결을 원활하게 하는 역할 뿐만 아니라 추가적인 개발을 통해 서버단의 최전방에서 더 많은 역할을 수행한다. 위의 그림의 Usage를 요약하면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Usage1 : 포트 포워딩&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Usage2 : 로드 밸런싱&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Usage3 : 캐싱&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Usage4 : 정적 리소스 제공&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;그럼 각각의 역할에 대해서 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;포트 포워딩&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JVEku/btsH9ZyUwF2/YfKe4LJZHck99BBT9s38H1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JVEku/btsH9ZyUwF2/YfKe4LJZHck99BBT9s38H1/img.png&quot; data-alt=&quot;라우터의 포트 포워딩&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JVEku/btsH9ZyUwF2/YfKe4LJZHck99BBT9s38H1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJVEku%2FbtsH9ZyUwF2%2FYfKe4LJZHck99BBT9s38H1%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;865&quot; height=&quot;678&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;678&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;라우터의 포트 포워딩&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;일반적으로 포트 포워딩은 컴퓨터 네트워크에서 패킷이 라우터나 방화벽 같은 네트워크 게이트웨이를 통과하는 동안 네트워크 주소를 변환해 트래픽을 리디렉션하는 것을 의미한다. 즉 OSI 7계층 모델에서 세번째 계층인 네트워크 계층(Layer 3)에서 사용된다. 그리고 이러한 포트 포워딩은 외부 네트워크의&amp;nbsp; 사용자가 개인 네트워크의 서비스에 액세스할 수 있도록 하는 데 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx의 포트 포워딩은 라우터의 포트 포워딩과 다르게 애플리케이션 계층(Layer 7)에서 일반적으로 HTTP, HTTPS, Mail, Stream (TCP), uWSGI 등 상위 수준 프로토콜을 관리하는데 사용된다. nginx는 서버단에서 역방향 프록시 역할을 하며 들어오는 포트에 따라 다른 애플리케이션으로 라우팅시킨다.&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;로드 밸런싱&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx는 아주 간단한 설정만으로도 로드 밸런싱을 시킬 수 있다. 특별한 설정이 없는 경우 라운드 로빈 방식으로 부하가 분산되며 두 가지 옵션을 추가적으로 제공한다. 그리고 weight 옵션을 이용해 각 서버에 분산을 어떻게 할 것인지 지정할 수도 있으며, 주 서버가 모두 다운됐을 경우 backup서버로 트래픽을 이동시킬 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캐싱&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx는 콘텐츠 캐시를 가지고 있으며, 클라이언트와 Web Application Server 사이에 위치해 보이는 모든 콘텐츠의 복사본을 저장한다. 만약 클라이언트가 캐시에 저장된 컨텐츠를 요청하면 Web Application Server와 통신하는 대신 직접 복사본을 반환한다. nginx가 서버단의 최전방에 있기 때문에 성능 향상에 큰 도움이 될 수 있다.&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;정적 리소스 제공&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;nginx는 서버에 있는 정적 리소스를 제공해줄 수 있다. 현재 대부분의 백엔드 서버는 json을 통해 동적 데이터를 주고 받는 RESTful한 방식이라 현재는 크게 쓸 일은 없을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;만약 잘못된 경로로 접근했을 때&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;750&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtQ9l8/btsIa5E6S4k/rx7hhO1e8m9wqD0ePl2GO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtQ9l8/btsIa5E6S4k/rx7hhO1e8m9wqD0ePl2GO1/img.png&quot; data-alt=&quot;nginx 502 Bad Gateway&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtQ9l8/btsIa5E6S4k/rx7hhO1e8m9wqD0ePl2GO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtQ9l8%2FbtsIa5E6S4k%2Frx7hhO1e8m9wqD0ePl2GO1%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;750&quot; height=&quot;240&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;nginx 502 Bad Gateway&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이러한 화면 대신 특정한 html파일을 제공해줄 수도 있지 않을까 싶다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로그래밍/Infra</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/141</guid>
      <comments>https://zeus2141.tistory.com/141#entry141comment</comments>
      <pubDate>Wed, 26 Jun 2024 10:40:24 +0900</pubDate>
    </item>
    <item>
      <title>[정처기] 2024년 정기 기사 1회 정보처리기사 합격</title>
      <link>https://zeus2141.tistory.com/140</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pgOnp/btsH5Z4Rfn2/Zknvhkxj5MmZnN0iusajYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pgOnp/btsH5Z4Rfn2/Zknvhkxj5MmZnN0iusajYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pgOnp/btsH5Z4Rfn2/Zknvhkxj5MmZnN0iusajYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpgOnp%2FbtsH5Z4Rfn2%2FZknvhkxj5MmZnN0iusajYk%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;748&quot; height=&quot;206&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;정처기 실기까지 합격해서 최종적으로 정보처리기사를 땄다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;2020년에 화공기사를 딴 이후로 두번째 기사 자격증이다. 준비할 수 있는 기간이 길지 않았지만 짧은 시간 집중해서 딸 수 있었다. ✌&lt;/span&gt;&lt;/p&gt;</description>
      <category>정보처리기사</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/140</guid>
      <comments>https://zeus2141.tistory.com/140#entry140comment</comments>
      <pubDate>Wed, 19 Jun 2024 18:02:20 +0900</pubDate>
    </item>
    <item>
      <title>[Java DB] 2-2. MyBatis - 실제 사용하기 (활용편)</title>
      <link>https://zeus2141.tistory.com/139</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmrCbx/btsHZ6SpzRj/LSsDwp2HOJ5VIlxNVhApJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmrCbx/btsHZ6SpzRj/LSsDwp2HOJ5VIlxNVhApJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmrCbx/btsHZ6SpzRj/LSsDwp2HOJ5VIlxNVhApJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmrCbx%2FbtsHZ6SpzRj%2FLSsDwp2HOJ5VIlxNVhApJ0%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;10&quot; height=&quot;10&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b92JNS/btsHZ50ildD/qMq056mPU6cYb2rVjZHa91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b92JNS/btsHZ50ildD/qMq056mPU6cYb2rVjZHa91/img.png&quot; data-alt=&quot;Common Java Web Application&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b92JNS/btsHZ50ildD/qMq056mPU6cYb2rVjZHa91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb92JNS%2FbtsHZ50ildD%2FqMq056mPU6cYb2rVjZHa91%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;1186&quot; height=&quot;540&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Common Java Web Application&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. Spring-MyBatis&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;467&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k4F2s/btsHZKT7VhQ/aZENZqujrYmno6yi59A4BK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k4F2s/btsHZKT7VhQ/aZENZqujrYmno6yi59A4BK/img.png&quot; data-alt=&quot;MyBatis를 Spring boot에서 사용하기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k4F2s/btsHZKT7VhQ/aZENZqujrYmno6yi59A4BK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk4F2s%2FbtsHZKT7VhQ%2FaZENZqujrYmno6yi59A4BK%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;589&quot; height=&quot;467&quot; data-origin-width=&quot;589&quot; data-origin-height=&quot;467&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MyBatis를 Spring boot에서 사용하기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;앞선 글에서는 MyBatis를 이해하기 위해 jar파일로 임포트해서 사용하는 기초적인 방법을 사용했지만 실제 프로젝트에서는 대부분 스프링 부트에 얹어서 함께 사용한다. 스프링부트에서는 Mybatis starter를 지원한다. &lt;a href=&quot;https://start.spring.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://start.spring.io&lt;/a&gt; 나 IDE에서 스프링부트 프로젝트를 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;MyBatis가 스프링을 만나면 훨씬 더 강력해진다. 우선 첫번째. DB와 상호작용이 필요할 때 &lt;b&gt;SqlSessionFactory&lt;/b&gt;로부터 &lt;b&gt;SqlSession&lt;/b&gt;을 만드는 과정을 스프링이 대신해준다. 두번째. &lt;b&gt;SqlSession&lt;/b&gt;에서 &lt;b&gt;매핑된 SQL 쿼리를 가져와서 자바 인터페이스로 활용하는 과정&lt;/b&gt;도 스프링이 대신해준다. 즉, 자바 인터페이스가 스프링 빈으로 등록되어 있기 때문에 그냥 필요한 곳에 가져와서 쓰기만 하면 된다. 아래 그림을 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Frame 2.png&quot; data-origin-width=&quot;3275&quot; data-origin-height=&quot;2516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZjapP/btsHZ9uEzfb/UYN9rpNzLCxbGldKEr3Gkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZjapP/btsHZ9uEzfb/UYN9rpNzLCxbGldKEr3Gkk/img.png&quot; data-alt=&quot;Only use Mybatis&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZjapP/btsHZ9uEzfb/UYN9rpNzLCxbGldKEr3Gkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZjapP%2FbtsHZ9uEzfb%2FUYN9rpNzLCxbGldKEr3Gkk%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;3275&quot; height=&quot;2516&quot; data-filename=&quot;Frame 2.png&quot; data-origin-width=&quot;3275&quot; data-origin-height=&quot;2516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Only use Mybatis&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Frame 5.png&quot; data-origin-width=&quot;2188&quot; data-origin-height=&quot;2516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DvCjc/btsHZfVQl20/RoVSkD2MXTcoGJgRYw0cJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DvCjc/btsHZfVQl20/RoVSkD2MXTcoGJgRYw0cJk/img.png&quot; data-alt=&quot;MyBatis Spring&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DvCjc/btsHZfVQl20/RoVSkD2MXTcoGJgRYw0cJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDvCjc%2FbtsHZfVQl20%2FRoVSkD2MXTcoGJgRYw0cJk%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;2188&quot; height=&quot;2516&quot; data-filename=&quot;Frame 5.png&quot; data-origin-width=&quot;2188&quot; data-origin-height=&quot;2516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MyBatis Spring&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;MyBatis만 사용하는 그림과 비교해보면 SqlSession을 만드는 과정이 크게 압축되었다. 개발자가 적어주어야 했던 번거로운 과정의 대부분을 MyBatis-Spring이 대신해준다. 달라진 특징을 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Mybatis-Spring 라이브러리의 시작인&amp;nbsp;&lt;b&gt;SqlSessionFactoryBean&lt;/b&gt;은 기존의 SqlSessionFactoryBuilder를 대신해 SqlSessionFactory를 만들어 SqlSessionTemplate에 주입한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다음으로 &lt;b&gt;SqlSessionTemplate&lt;/b&gt;은 기존의 두 단계를 합친 SqlSessionFactory + SqlSession의 역할을 하며, 트랜잭션 관리, 자원 관리, 스레드 안전성, 간편한 DAO 구현을 가능하게 한다. 따라서 개발자는 세션 관리에 신경 쓸 필요없이 비즈니스 로직에 집중할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 사용하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Spring boot에서 의존성으로 &lt;b&gt;MyBatis Framework&lt;/b&gt;와 &lt;b&gt;H2 Database&lt;/b&gt;, &lt;b&gt;Lombok&lt;/b&gt;을 추가시켜 주었다. 전체적인 프로젝트 구조는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;393&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vU0c8/btsH0Ak2emf/nO1nkH3DswIEdggHzXFauk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vU0c8/btsH0Ak2emf/nO1nkH3DswIEdggHzXFauk/img.png&quot; data-alt=&quot;Spring Boot 프로젝트 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vU0c8/btsH0Ak2emf/nO1nkH3DswIEdggHzXFauk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvU0c8%2FbtsH0Ak2emf%2FnO1nkH3DswIEdggHzXFauk%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;393&quot; height=&quot;870&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Spring Boot 프로젝트 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스프링부트에서 MyBatis Framework를 추가하면, &lt;b&gt;mybatis-spring-boot-starter&lt;/b&gt;를 통해 필요한 추가적인 라이브러리를 추가한다. mybatis-spring-boot-starter는 다음과 같은 장점을 가지고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;DataSource&lt;/b&gt;를 자동으로 탐지&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;SqlSessionFactoryBean&lt;/b&gt;에 input으로 &lt;b&gt;DataSource&lt;/b&gt;를 넣어 &lt;b&gt;SqlSessionFactory 인스턴스&lt;/b&gt;를 만들고 스프링 빈으로 등록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;SqlSessionFactory&lt;/b&gt;로부터 &lt;b&gt;SqlSessionTemplate 인스턴스&lt;/b&gt;를 만들고 스프링 빈으로 등록&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;개발자가 만든 &lt;b&gt;mapper들을 자동으로 스캔&lt;/b&gt;하고 SqlSessionTemplate과 연결시킨 뒤 &lt;b&gt;스프링 컨텍스트&lt;/b&gt;에 등록&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;따라서 mapper들이 스프링 빈으로 주입될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;가장 많이 사용하는 일반적이고 효과적인 방법은 @MapperScan 어노테이션을 사용하는 것이다. 스프링 설정파일이 적절한 정보를 넣어주고 어노테이션을 사용하기만 하면 MyBatis가 알아서 설정 정보를 주입해서 Mapper를 바로 쓸 수 있도록 만들어 준다. 나는 프로젝트에 MyBatisConfig라는 이름의 클래스를 만들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718591426627&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.churnobyl.mybatisdemo.config;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan(basePackages = {&quot;com.churnobyl.mybatisdemo.model.dao&quot;})
public class MyBatisConfig {
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이렇게만 만들어주면 된다. 간단한 과정을 읊어보면, Spring이 시작될 때 @Configuration 어노테이션이 붙은 클래스들을 우선적으로 탐색해 필요한 정보들을 수집한다. 이때 Mybatis-spring의 @MapperScan 어노테이션이 있다면, basePackages의 경로 아래의 모든 interface를 탐색해 Mapper를 찾아 스프링 컨텍스트에 등록시켜 스프링이 빈으로 등록할 수 있도록 한다. 이때 DataSource는 매핑 정보는 스프링의 설정 파일에서 찾아간다. application.yml을 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718591828467&quot; class=&quot;yml yaml&quot; data-ke-language=&quot;yml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;spring:
  application:
    name: mybatisdemo
  datasource:
    dbcp2:
      driver: org.h2.Driver
    url: jdbc:h2:tcp://localhost/~/mybatisdb
    username: sa
    password:
  sql:
    init:
      mode: always
      schema-locations: classpath*:schema.sql
  h2:
    console:
      enabled: true
server:
  port: 8090

mybatis:
  mapper-locations: classpath:mappers/*.xml
  type-aliases-package: com.churnobyl.mybatisdemo.model.dto
  configuration:
    map-underscore-to-camel-case: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이런 식으로 DB와 연결하기 위한 DataSource나 mybatis에 관련한 정보를 넣어주면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&amp;nbsp;mybatis.mapper-locations&lt;/b&gt;은 인터페이스와 매핑될 xml파일이 위치한 폴더를 지정해주는 것으로, 매퍼 xml파일은 주로 하나 이상이므로 다음과 같이 asterisk(*)을 이용해 전체를 스캔할 수 있도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;b&gt;mybatis.type-aliases-package&lt;/b&gt;는 MyBatis가 사용자가 지정한 커스텀한 DTO를 이해하기 위해 필요한 패키지를 지정해주는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;마지막으로 &lt;b&gt;mybatis.configuration.map-underscore-to-camel-case&lt;/b&gt;는 자바와 데이터베이스 간의 차이를 해소시켜주기 위한 설정으로, 일반적으로 자바 변수명은 Camel Case를 사용하는 반면 데이터베이스의 칼럼은 언더스코어를 사용하는 Snake case를 사용한다. 위 설정을 적용하면, SQL문에서 언더스코어를 작성한 칼럼명이 MyBatis를 통해 리턴될 때 CamelCase로 자동으로 매핑시킨다. 예를 들어 SELECT member_name, member_age FROM member; 라는 SQL문이 있다면 실제로 리턴되는 이름은 memberName, memberAge이 되는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럼 만약 MapperScan을 사용하지 않는다면 어떻게 될까.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;당연히 MyBatis-Spring이 자동으로 해주는 동작을 직접 구현해주어야 한다. 다음은 MapperScan을 풀어 쓴 코드다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718592657851&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.churnobyl.mybatisdemo.config;

import com.churnobyl.mybatisdemo.model.dao.MemberDao;
import com.churnobyl.mybatisdemo.model.dao.TeamDao;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
public class MyBatisConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setTypeAliasesPackage(&quot;com.churnobyl.mybatisdemo.model.dto&quot;);
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(&quot;classpath:mappers/*.xml&quot;));
        return factoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    public MapperFactoryBean&amp;lt;MemberDao&amp;gt; memberDao(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean&amp;lt;MemberDao&amp;gt; factoryBean = new MapperFactoryBean&amp;lt;&amp;gt;(MemberDao.class);
        factoryBean.setSqlSessionFactory(sqlSessionFactory);
        return factoryBean;
    }

    @Bean
    public MapperFactoryBean&amp;lt;TeamDao&amp;gt; teamDao(SqlSessionFactory sqlSessionFactory) {
        MapperFactoryBean&amp;lt;TeamDao&amp;gt; factoryBean = new MapperFactoryBean&amp;lt;&amp;gt;(TeamDao.class);
        factoryBean.setSqlSessionFactory(sqlSessionFactory);
        return factoryBean;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;DataSource는 Spring이 자동으로 설정 정보를 Bean으로 등록해주지만 나머지는 수동으로 구현해야 한다. SqlSessionFactoryBean에 필요한 정보를 직접 넣어주고 SqlSessionFactory, SqlSessionTemplate, 그리고 각 Mapper를 수동으로 등록해주어야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;MapperScan을 적극 활용하자.&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바 인터페이스와 mapper.xml&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MemberDao&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718593039161&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.churnobyl.mybatisdemo.model.dao;

import com.churnobyl.mybatisdemo.model.dto.member.request.AddMemberRequestDto;
import com.churnobyl.mybatisdemo.model.dto.member.request.LoginMemberRequestDto;
import com.churnobyl.mybatisdemo.model.dto.member.response.MemberResponseDto;
import org.apache.ibatis.annotations.Delete;

import java.util.List;

public interface MemberDao {
    int signup(AddMemberRequestDto request);
    int login(LoginMemberRequestDto request);
    List&amp;lt;MemberResponseDto&amp;gt; getAll();

    @Delete(value = &quot;DELETE FROM member WHERE id = #{id}&quot;)
    int delete(Long id);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;memberMapper.xml&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718593063812&quot; class=&quot;xml&quot; data-ke-language=&quot;xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;!DOCTYPE mapper
        PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
        &quot;https://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&amp;gt;
&amp;lt;mapper namespace=&quot;com.churnobyl.mybatisdemo.model.dao.MemberDao&quot;&amp;gt;

    &amp;lt;!-- 유저 회원가입 --&amp;gt;
    &amp;lt;insert id=&quot;signup&quot; parameterType=&quot;addMemberRequestDto&quot;&amp;gt;
        INSERT INTO member
        (team_id, name, password)
        VALUES
        (#{teamId}, #{name}, #{password})
    &amp;lt;/insert&amp;gt;

    &amp;lt;!-- 유저 로그인 --&amp;gt;
    &amp;lt;select id=&quot;login&quot; parameterType=&quot;loginMemberRequestDto&quot; resultType=&quot;integer&quot;&amp;gt;
        SELECT count(id) FROM member
        WHERE name = #{name} and password = #{password}
    &amp;lt;/select&amp;gt;

    &amp;lt;!-- 유저 모두 가져오기 --&amp;gt;
    &amp;lt;select id=&quot;getAll&quot; resultType=&quot;memberResponseDto&quot;&amp;gt;
        SELECT
        m.id as id,
        t.name as teamName,
        m.name as name
        FROM member m
        LEFT JOIN team t
        ON m.team_id = t.id
    &amp;lt;/select&amp;gt;
&amp;lt;/mapper&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;SQL문을 자바의 메서드처럼 사용하기 위해서는 다음과 같이 코드를 작성하면 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;전체 프로젝트는 깃허브로 남겨두었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://github.com/Churnobyl/mybatisdemo-tistory.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/Churnobyl/mybatisdemo-tistory.git&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718593426692&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - Churnobyl/mybatisdemo-tistory&quot; data-og-description=&quot;Contribute to Churnobyl/mybatisdemo-tistory development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Churnobyl/mybatisdemo-tistory.git&quot; data-og-url=&quot;https://github.com/Churnobyl/mybatisdemo-tistory&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bTVXwa/hyWoDlW84k/Pf8TBi3kHEeQ9eR2H0c8O0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Churnobyl/mybatisdemo-tistory.git&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Churnobyl/mybatisdemo-tistory.git&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bTVXwa/hyWoDlW84k/Pf8TBi3kHEeQ9eR2H0c8O0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - Churnobyl/mybatisdemo-tistory&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to Churnobyl/mybatisdemo-tistory development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&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;</description>
      <category>프로그래밍/Java</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/139</guid>
      <comments>https://zeus2141.tistory.com/139#entry139comment</comments>
      <pubDate>Mon, 17 Jun 2024 12:04:00 +0900</pubDate>
    </item>
    <item>
      <title>[Java DB] 2-1. MyBatis - SQL 쿼리를 재사용하자 (개념편)</title>
      <link>https://zeus2141.tistory.com/138</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&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;476&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S9TKs/btsHXO3lhJG/EzyM92ex5p3wwyV39T1Ca1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S9TKs/btsHXO3lhJG/EzyM92ex5p3wwyV39T1Ca1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S9TKs/btsHXO3lhJG/EzyM92ex5p3wwyV39T1Ca1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS9TKs%2FbtsHXO3lhJG%2FEzyM92ex5p3wwyV39T1Ca1%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;10&quot; height=&quot;10&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTZZys/btsHUUpk7x6/NftoqvLMlxxuBTlo5CRG1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTZZys/btsHUUpk7x6/NftoqvLMlxxuBTlo5CRG1K/img.png&quot; data-alt=&quot;Common Java Web Application&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTZZys/btsHUUpk7x6/NftoqvLMlxxuBTlo5CRG1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTZZys%2FbtsHUUpk7x6%2FNftoqvLMlxxuBTlo5CRG1K%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;1186&quot; height=&quot;540&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Common Java Web Application&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;1. What is MyBatis&lt;/span&gt;&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/m2zsi/btsHU8OoZ8e/FJ7SzjUU797TCQpCdxAKKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m2zsi/btsHU8OoZ8e/FJ7SzjUU797TCQpCdxAKKk/img.png&quot; data-alt=&quot;MyBatis 로고&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m2zsi/btsHU8OoZ8e/FJ7SzjUU797TCQpCdxAKKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm2zsi%2FbtsHU8OoZ8e%2FFJ7SzjUU797TCQpCdxAKKk%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;636&quot; height=&quot;358&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MyBatis 로고&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이전 글에서 자바와 DB가 상호작용할 수 있는 밑거름인 JDBC에 대해 알아보았다. &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;JDBC&lt;/b&gt;&lt;/span&gt;는 DB와 가장 단순하게 연결하는 원시적인 방법이기 때문에 속도적인 측면에서 유리하나 &lt;b&gt;(1)&lt;/b&gt; &lt;b&gt;DB 접근이 필요한 코드마다 비슷한 SQL문을 반복해서 적어야 한다&lt;/b&gt;는 큰 단점이 존재한다. 또한 &lt;b&gt;(2)&lt;/b&gt; &lt;b&gt;SQL문이 자바 코드 안에 포함되기 때문에 유지 보수적인 측면에서도 불리하다&lt;/b&gt;. 쉽게 말해 휴먼 에러가 발생할 가능성이 높다. 추가적으로 &lt;b&gt;(3) JDBC에서 SELECT문의 결과는 ResultSet으로 리턴되며 우리가 원하는 객체로 사용하기 위해서는 한번 더 처리가 필요하다는 불편함&lt;/b&gt;도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&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;974&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYdCL9/btsHTGGhToB/5NRcJk0sflVyOhcuARcqpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYdCL9/btsHTGGhToB/5NRcJk0sflVyOhcuARcqpK/img.png&quot; data-alt=&quot;MyBatis를 사용하면 반복적인 SQL쿼리문을 상당량 줄일 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYdCL9/btsHTGGhToB/5NRcJk0sflVyOhcuARcqpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYdCL9%2FbtsHTGGhToB%2F5NRcJk0sflVyOhcuARcqpK%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;974&quot; height=&quot;725&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MyBatis를 사용하면 반복적인 SQL쿼리문을 상당량 줄일 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;MyBatis&lt;/b&gt;&lt;/span&gt;는 이러한 단점들을 커버할 수 있는 &lt;b&gt;강력한 SQL Mapper 라이브러리&lt;/b&gt;다. MyBatis는 &lt;b&gt;반복적인 SQL 쿼리문을 xml파일로 분리하고 인터페이스와 매핑해 필요한 코드에 재사용할 수 있도록&lt;/b&gt; 한다. 또한 DB와 상호작용의 결과를 ResultSet이 아닌 &lt;b&gt;지정한 객체나 컬렉션으로 리턴&lt;/b&gt;해주는 엄청난 기능도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;그럼 이제 MyBatis에 대해서 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. MyBatis 이해하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;우선 MyBatis 라이브러리를 프로젝트에 추가하는 방법은 여러가지가 있다. IDE와 함께 Maven이나 Gradle과 같은 의존성 관리 도구를 사용하면 간편하게 프로젝트에 추가할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Maven&lt;/span&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;lt;groupId&amp;gt;org.mybatis&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;lt;artifactId&amp;gt;mybatis&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;lt;version&amp;gt;x.x.x&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Gradle&lt;/span&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;implementation 'org.mybatis:mybatis:x.x.x'&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 이번 글의 목표는 &lt;b&gt;MyBatis 자체를 이해하는 것&lt;/b&gt;이므로 가장 원시적인 형태로 돌아가서 &lt;a href=&quot;https://github.com/mybatis/mybatis-3/releases&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;mybatis.jar&lt;/a&gt;파일을 프로젝트에 추가하는 방식을 사용하려고 한다. 추가적으로 데이터베이스는 H2를 사용할 것이므로 H2 데이터베이스의 JDBC Driver도 함께 추가해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;259&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eEVULs/btsHWyNnbCS/w75DdzoO0jJzFscUib7VI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eEVULs/btsHWyNnbCS/w75DdzoO0jJzFscUib7VI0/img.png&quot; data-alt=&quot;프로젝트 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eEVULs/btsHWyNnbCS/w75DdzoO0jJzFscUib7VI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeEVULs%2FbtsHWyNnbCS%2Fw75DdzoO0jJzFscUib7VI0%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;259&quot; height=&quot;483&quot; data-origin-width=&quot;259&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로젝트 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;다음과 같이 lib폴더에 &lt;b&gt;h2-2.2.224.jar JDBC Driver&lt;/b&gt;와 &lt;b&gt;mybatis-3.5.16.jar&lt;/b&gt;를 추가해 주었다. 이번 글에서는 위 프로젝트를 통해서 MyBatis를 알아볼 것이며 2편인 활용편에서 실제로 사용해볼 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;MyBatis를 뜯어보기 전에 한가지 짚고 넘어가야 할 게 있다. MyBatis는 xml파일에 SQL문을 저장하고 이를 Mapper나 DAO라는 이름의 자바 인터페이스와 매핑해 자바 애플리케이션에서 사용할 수 있도록 하는 것이 핵심이다. 따라서 위 프로젝트에서 MemberMapper.xml 파일과 MemberMapper 인터페이스를 어떻게 구현했는지 확인하고 넘어가자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;MemberMapper.xml&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718165602860&quot; class=&quot;xml&quot; data-ke-language=&quot;xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;!DOCTYPE mapper
        PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
        &quot;https://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&amp;gt;
&amp;lt;mapper namespace=&quot;org.churnobyl.mappers.MemberMapper&quot;&amp;gt;
    &amp;lt;select id=&quot;selectAll&quot; resultType=&quot;Member&quot;&amp;gt;
        SELECT * from member
    &amp;lt;/select&amp;gt;

    &amp;lt;insert id=&quot;insertMember&quot; parameterType=&quot;Member&quot;&amp;gt;
        INSERT INTO member
        (name, age)
        VALUES
        (#{name}, #{age})
    &amp;lt;/insert&amp;gt;
&amp;lt;/mapper&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MyBatis Docs에서 제공하는 기본 양식&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;MemberMapper.java&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718165625901&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.churnobyl.mappers;

import org.churnobyl.Member;
import java.util.List;

public interface MemberMapper {
    List&amp;lt;Member&amp;gt; selectAll();
    void insertMember(Member member);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;mapper.xml파일에서 &lt;b&gt;mapper&lt;/b&gt; element의 attribute인 &lt;b&gt;namespace&lt;/b&gt;는 해당 xml파일이 어느 자바 파일과 매핑될 것인지 지시해주고, 그 아래에는 select, insert같은 element안에 실제 SQL 쿼리를 넣어 준다. 그리고 각 element의 id를 매핑되는 자바 파일의 메서드 이름과 동일하게 설정해 MyBatis가 이해할 수 있도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이 과정을 통해 xml파일로 저장되어 있는 SQL 쿼리를 MyBatis를 통해 자바 메서드처럼 사용할 수 있다. 그리고 이 인터페이스의 메서드를 다음과 같이 호출해 자바 애플리케이션 안에서 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HelloService.java&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718166036396&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.churnobyl;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class HelloService {
    private final SqlSessionFactory sqlSessionFactory;

    public List&amp;lt;Member&amp;gt; getAll() {
        List&amp;lt;Member&amp;gt; members = null;

        try (SqlSession session = sqlSessionFactory.openSession()) {
            members = session.selectList(&quot;org.churnobyl.mappers.MemberMapper.selectAll&quot;);
            return members;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return members;
    }

    public boolean saveMember(String name, int age) {
        Member member = new Member(name, age);

        try (SqlSession session = sqlSessionFactory.openSession()) {
            session.insert(
                    &quot;org.churnobyl.mappers.MemberMapper.insertMember&quot;,
                    member
            );

            session.commit();

            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;실제 매핑된 메서드가 어떻게 사용되는지가 중요하기 때문에 SqlSessionFactory 의존성 주입 코드는 생략했다. 아래에서 더 자세히 설명할 예정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Service단에서 SqlSessionFactory를 주입한 뒤 각 메서드의 try-with-resources 안에서 openSession() 메서드를 호출해 새로운 SqlSession 자원을 받아온다. 그리고 나서 SqlSession의 메서드와 매핑된 이름인 &lt;b&gt;&quot;org.churnobyl.mappers.MemberMapper.insertMember&quot;&lt;/b&gt;등을 적절히 사용해 DB와의 상호작용을 수행할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;MyBatis가 작동하는 과정을 간단하게 알아보았고 이제 MyBatis를 뜯어보면서 내부 구조에 대해서 조금 더 깊게 알아보자. 우선 MyBatis가 수행하는 전체적인 과정을 그림으로 그려보았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Frame 2.png&quot; data-origin-width=&quot;3275&quot; data-origin-height=&quot;2516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dE1z21/btsHVHYb2vV/f1al62KsTKLGCE7fRbSwLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dE1z21/btsHVHYb2vV/f1al62KsTKLGCE7fRbSwLK/img.png&quot; data-alt=&quot;전체적인 MyBatis의 프로세스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dE1z21/btsHVHYb2vV/f1al62KsTKLGCE7fRbSwLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdE1z21%2FbtsHVHYb2vV%2Ff1al62KsTKLGCE7fRbSwLK%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;3275&quot; height=&quot;2516&quot; data-filename=&quot;Frame 2.png&quot; data-origin-width=&quot;3275&quot; data-origin-height=&quot;2516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;전체적인 MyBatis의 프로세스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;위 그림만 이해하면 MyBatis를 전반적으로 이해할 수 있다. 잘 안보이면 클릭해서 보면 된다. 핵심적인 맥락은 왼쪽의 초록색 화살표들과 보라색 화살표들의 흐름이지만, 우리는 먼저 MyBatis에서 가장 중요한 &lt;b&gt;SqlSession&lt;/b&gt;이 어떻게 만들어지는지부터 알아볼 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(1) SqlSessionFactoryBuilder&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Frame 3.png&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;898&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lnvoI/btsHWvxoSLO/kgac0F2S4jGyBHTd2XkdP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lnvoI/btsHWvxoSLO/kgac0F2S4jGyBHTd2XkdP0/img.png&quot; data-alt=&quot;SqlSessionFactoryBuilder와 SqlSessionFactory, 그리고 SqlSession&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lnvoI/btsHWvxoSLO/kgac0F2S4jGyBHTd2XkdP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlnvoI%2FbtsHWvxoSLO%2Fkgac0F2S4jGyBHTd2XkdP0%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;1904&quot; height=&quot;898&quot; data-filename=&quot;edited_Frame 3.png&quot; data-origin-width=&quot;1904&quot; data-origin-height=&quot;898&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SqlSessionFactoryBuilder와 SqlSessionFactory, 그리고 SqlSession&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;SqlSessionFactoryBuilder&lt;/b&gt;&lt;/span&gt;는 &lt;b&gt;Configuration&lt;/b&gt; 객체에 저장된 정보를 가지고 &lt;b&gt;SqlSessionFactory&lt;/b&gt;를 만들기 위해 사용한다. &lt;b&gt;SqlSessionFactory&lt;/b&gt;는 다시 &lt;b&gt;SqlSession&lt;/b&gt;을 얻기 위해 사용하며, 이 &lt;b&gt;SqlSession&lt;/b&gt;이 MyBatis에서 하나의 트랜잭션 단위가 된다. &lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;SqlSessionFactory&lt;/b&gt;&lt;/span&gt;을 중심으로 생각하면 이해하기 편하다. &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;SqlSessionFactory&lt;/b&gt;&lt;/span&gt;는&amp;nbsp; &lt;b&gt;자바와 데이터베이스가 상호작용하기 위한 모든 정보인 Configuration을 가지고 있으며 다시 Configuration을 주입한 SqlSession을 만들어 준다.&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;SqlSessionFactoryBuilder의 코드 일부를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718160323996&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.apache.ibatis.session;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

public class SqlSessionFactoryBuilder {

  (...생략...)
  
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
  
  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException(&quot;Error building SqlSession.&quot;, e);
    } finally {
      ErrorContext.instance().reset();
      try {
        if (inputStream != null) {
          inputStream.close();
        }
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}&lt;/code&gt;&lt;/pre&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;코드를 보면&amp;nbsp;&lt;b&gt;SqlSessionFactoryBuilder&lt;/b&gt; 클래스는 build()메서드를 통해 MyBatis 실행 정보를 담고 있는 &lt;b&gt;mybatis-config.xml&lt;/b&gt;를 읽어 들여 &lt;b&gt;Configuration&lt;/b&gt;로 만들고 이를 SqlSessionFactory 구현체인&amp;nbsp;&lt;b&gt;DefaultSqlSessionFactory&lt;/b&gt;에 담아 최종적으로 리턴해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;MyBatis가 데이터베이스에 접근하기 위해서는 mybatis-config.xml 파일을 읽어야 하기 때문에 build() 메서드에서 Reader나 InputStream 인터페이스를 파라미터로 받아야 한다. MyBatis는 이러한 정보를 쉽게 읽어들일 수 있도록 Resources 클래스를 자체적으로 지원한다. 이를 이용해 Builder를 세팅해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;mybatis-config.xml&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718168946894&quot; class=&quot;xml&quot; data-ke-language=&quot;xml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&amp;gt;
&amp;lt;!DOCTYPE configuration
        PUBLIC &quot;-//mybatis.org//DTD Config 3.0//EN&quot;
        &quot;https://mybatis.org/dtd/mybatis-3-config.dtd&quot;&amp;gt;
&amp;lt;configuration&amp;gt;
    &amp;lt;typeAliases&amp;gt;
        &amp;lt;typeAlias alias=&quot;Member&quot; type=&quot;org.churnobyl.Member&quot; /&amp;gt;
    &amp;lt;/typeAliases&amp;gt;
    &amp;lt;environments default=&quot;development&quot;&amp;gt;
        &amp;lt;environment id=&quot;development&quot;&amp;gt;
            &amp;lt;transactionManager type=&quot;JDBC&quot;/&amp;gt;
            &amp;lt;dataSource type=&quot;POOLED&quot;&amp;gt;
                &amp;lt;property name=&quot;driver&quot; value=&quot;org.h2.Driver&quot;/&amp;gt;
                &amp;lt;property name=&quot;url&quot; value=&quot;jdbc:h2:tcp://localhost/~/dbtest&quot;/&amp;gt;
                &amp;lt;property name=&quot;username&quot; value=&quot;sa&quot;/&amp;gt;
                &amp;lt;property name=&quot;password&quot; value=&quot;&quot;/&amp;gt;
            &amp;lt;/dataSource&amp;gt;
        &amp;lt;/environment&amp;gt;
    &amp;lt;/environments&amp;gt;
    &amp;lt;mappers&amp;gt;
        &amp;lt;mapper resource=&quot;org/churnobyl/mappers/MemberMapper.xml&quot;/&amp;gt;
    &amp;lt;/mappers&amp;gt;
&amp;lt;/configuration&amp;gt;&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1718161456238&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;String resource = &quot;properties/mybatis-config.xml&quot;;
InputStream inputStream = null;

try {
    inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
    throw new RuntimeException(e);
}

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; MyBatis 설정 파일인 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;mybatis-config.xml&lt;/b&gt;&lt;/span&gt;를 &lt;b&gt;Resources&lt;/b&gt; 클래스의 &lt;b&gt;getResourceAsStream()&lt;/b&gt; 메서드를 이용해 다음과 같이 &lt;b&gt;InputStream&lt;/b&gt;으로 읽어들여 &lt;b&gt;SqlSessionFactoryBuilder&lt;/b&gt;클래스의 &lt;b&gt;build()&lt;/b&gt; 메서드 인자로 넣으면 최종적으로&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;SqlSessionFactory&lt;/b&gt;&lt;/span&gt;를 얻을 수 있다. 한번 만든 SqlSessionFactory은 &lt;b&gt;애플리케이션이 실행하는 동안 존재해야만 하며&lt;/b&gt;, 삭제하거나 재생성하지 않는 편이 좋다. 그러므로 &lt;b&gt;SqlSessionFactory&lt;/b&gt;의 가장 좋은 스코프는 &lt;b&gt;애플리케이션 스코프&lt;/b&gt;다. Spring에서도 SqlSessionFactory의 일반적인 라이프사이클을 싱글턴 패턴으로 관리한다. &lt;b&gt;SqlSessionFactoryBuilder&lt;/b&gt;는 SqlSessionFactory를 만들고 나면 필요없으므로 &lt;b&gt;메서드 스코프&lt;/b&gt;로 사용하는 것을 권장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;다시 SqlSessionFactoryBuilder클래스의 구현 코드를 슬쩍 들여다보면 xml을 읽을 InputStream을 이용해 최종적으로 build(InputStream inputStream, String environment, Properties properties) 메서드에서 &lt;b&gt;XmlConfigBuilder&lt;/b&gt;를 통해 xml의 정보를 모두 파싱하고 &lt;b&gt;Configuration&lt;/b&gt;을 만든다. 이때 데이터베이스 정보나 특히 SQL 매핑 정보도 함께 파싱되어 Configuration 안에 저장된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(2) Configuration&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Frame 4.png&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;857&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPapf4/btsHXvQIXle/Hz1wqTWDk6mOK7C56bnbhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPapf4/btsHXvQIXle/Hz1wqTWDk6mOK7C56bnbhK/img.png&quot; data-alt=&quot;Configuration이 생성되는 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPapf4/btsHXvQIXle/Hz1wqTWDk6mOK7C56bnbhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPapf4%2FbtsHXvQIXle%2FHz1wqTWDk6mOK7C56bnbhK%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;1362&quot; height=&quot;857&quot; data-filename=&quot;Frame 4.png&quot; data-origin-width=&quot;1362&quot; data-origin-height=&quot;857&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Configuration이 생성되는 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Configuration 클래스는 해당 어플리케이션에서 MyBatis를 사용하기 위한 모든 정보를 담고 있는 아주 중요한 클래스다. 위 절에서 언급한 것처럼 XmlConfigBuilder가 파싱한 SQL 매핑 정보도 가지고 있다. 코드를 간단하게 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718165198273&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.apache.ibatis.session;

public class Configuration {

	(...생략...)

    protected final Map&amp;lt;String, MappedStatement&amp;gt; mappedStatements = new StrictMap&amp;lt;MappedStatement&amp;gt;(
      &quot;Mapped Statements collection&quot;)
          .conflictMessageProducer((savedValue, targetValue) -&amp;gt; &quot;. please check &quot; + savedValue.getResource() + &quot; and &quot;
              + targetValue.getResource());
              
    (...생략...)
              
    protected final Collection&amp;lt;XMLStatementBuilder&amp;gt; incompleteStatements = new LinkedList&amp;lt;&amp;gt;();
    
    (...생략...)
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;대부분의 코드는 생략하고 SQL문을 매핑하는 부분만 보자. Configuration 클래스는 최종적으로 매핑한 SQL문들을 &lt;b&gt;mappedStatements 맵&lt;/b&gt;에 &lt;b&gt;MappedStatement&lt;/b&gt; 객체로 저장한다. 앞서 SqlSession의 insert()나 selectList()메서드를 사용할 때 파라미터로 &quot;org.churnobyl.mappers.MemberMapper.insertMember&quot;와 같은 statement가 들어갔었는데, 바로 이 statement가 mappedStatements 맵의 key로 저장되고, insertMember()와 매핑된 MemberMapper.xml의 정보들은 MappedStatement 객체로 저장되는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;주목해야 할 점은 &lt;b&gt;incompleteStatements 맵&lt;/b&gt;인데, 이름 그대로 불완전한 SQL문이다. MyBatis는 애플리케이션이 시작되고 xml로부터 매핑 정보를 임시로 incompleteStatements에 먼저 저장한 뒤, 모든 참조와 종속성을 해결하고 나서 SQL문을 최종적으로 MappedStatement 객체에 저장하는 &lt;b&gt;전처리 과정&lt;/b&gt;이 존재한다. 이를 통해 모든 참조와 종속성이 올바르게 해결되도록 보장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(3) SqlSessionFactory&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;다음으로 SqlSessionFactory에 의해 Configuration이 주입되어 탄생하는 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;SqlSessionFactory&lt;/b&gt;&lt;/span&gt;다. 앞서 언급한 것처럼 SqlSessionFactory는 애플리케이션 전반에 걸쳐 하나만 존재해야 하므로 일반적으로 싱글턴 패턴으로 구현된다. SqlSessionFactory에서 &lt;b&gt;openSession()&lt;/b&gt;메서드를 사용하면 &lt;b&gt;Configuration&lt;/b&gt;과 &lt;b&gt;Executor&lt;/b&gt;를 주입해 SqlSession 인터페이스의 구현체인 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;DefaultSqlSession&lt;/b&gt;&lt;/span&gt; 객체를 리턴한다. 이 DefaultSqlSession이 실질적으로 DB와 상호작용을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;코드를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718173215879&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.apache.ibatis.session.defaults;

public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

  @Override
  public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
  }
  
  @Override
  public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, null, false);
  }
  
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
      boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException(&quot;Error opening session.  Cause: &quot; + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;b&gt;openSession()&lt;/b&gt; 메서드는 여러가지 파라미터를 받을 수 있도록 오버로딩되어 있다. 수행 결과를 자동으로 커밋할 것인지 여부에 따른 autoCommit을 불리언 타입으로 줄 수도 있고, Executor의 타입을 어떻게 할 것인지를 ExecutorType enum으로 줄 수도 있다. 어떤 것을 사용하든 최종적으로 &lt;b&gt;openSessionFromDataSource()&lt;/b&gt; 메서드를 거쳐 결과를 리턴한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;openSessionFromDataSource() 메서드는 SqlSession 인스턴스를 리턴하기 위해 필요한 Transaction이나 Executor등을 생성해 주입한다. 그중에서 Executor를 만드는&amp;nbsp;&lt;b&gt;configuration.newExecutor(tx, execType)&lt;/b&gt;에 주목하자. 이 메서드는 새로운 Executor 인스턴스를 리턴할 때 파라미터로 넘긴 execType에 따라 다양한 형태의 Executor 구현체를 리턴해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;ExecutorType enum을 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ExecutorType&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718174624844&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.apache.ibatis.session;

public enum ExecutorType {

  SIMPLE,

  REUSE,

  BATCH

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;Configuration.java&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718174959767&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.apache.ibatis.session;

public class Configuration {

  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    return (Executor) interceptorChain.pluginAll(executor);
  }
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;코드를 보면 알 수 있듯이 ExecutorType 파라미터가 없다면 기본적으로 SimpleExecutor를 생성하고, REUSE라면, ReuseExecutor, BATCH라면 BatchExecutor를 생성한다. 세가지 Executor의 차이점을 간략하게 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p 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;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;SimpleExecutor&lt;/b&gt;&lt;/span&gt;는 기본적인 작업만 진행하며, 한 세션 안에서 같은 작업이 일어나더라도 재사용하지 않고 새로운 Statement를 생성해 실행한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;ReuseExecutor&lt;/b&gt;&lt;/span&gt;는 ReuseExecutor 내에 statementMap 맵이 존재하며, 한 세션 안에서 일어나는 작업을 맵에 저장했다가 만약 다음 실행할 작업이 맵 안에 존재한다면 해당 Statement를 재사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;BatchExecutor&lt;/b&gt;&lt;/span&gt;는 BatchExecutor 내에 statementList가 존재하며, 가장 마지막에 실행한 Statement와 SQL문이 다음 실행할 작업과 같다면 해당 Statement를 재사용한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이를 적절히 이용하면 각 작업마다 상황에 맞는 Executor를 이용해 최적화할 여지가 있다.&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(4) SqlSession&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;SqlSession&lt;/b&gt;&lt;/span&gt;은 MyBatis에서 DB와 CRUD를 수행하는 실질적인 인터페이스다. 기본적인 구현체로는 DefaultSqlSession이 있으나 Thread-safe하지 않다. 단일쓰레드 환경에서는 상관없지만, 웹 애플리케이션 같은 멀티쓰레딩 환경에서는 여러 쓰레드가 하나의 DefaultSqlSession을 동시에 사용하려고 시도할 때 충돌이 발생할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;그래서 MyBatis는 SqlSession의 또 다른 구현체인 SqlSessionManager를 제공한다. &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;SqlSessionManager&lt;/b&gt;&lt;/span&gt;는 클래스내에&amp;nbsp;&lt;b&gt;ThreadLocal&lt;/b&gt; 클래스를 사용해 각 쓰레드가 독립적인 SqlSession 인스턴스를 보유하는 것을 보장한다. 추가적으로 Spring에서 사용하는 MyBatis-Spring에서도 Thread-safe한 &lt;b&gt;SqlSessionTemplate&lt;/b&gt; 클래스를 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;지금은 가장 일반적인 Mybatis를 공부하고 있으므로 DefaultSqlSession을 보자. 대부분을 덜어내고 SELECT문을 수행해 List형태로 반환하는 selectList()메서드와 commit(), rollback() 메서드만 남겨두었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718177016449&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.apache.ibatis.session.defaults;

public class DefaultSqlSession implements SqlSession {

  private final Configuration configuration;
  private final Executor executor;

  private final boolean autoCommit;
  private boolean dirty;

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

  @Override
  public &amp;lt;E&amp;gt; List&amp;lt;E&amp;gt; selectList(String statement) {
    return this.selectList(statement, null);
  }

  @Override
  public &amp;lt;E&amp;gt; List&amp;lt;E&amp;gt; selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  @Override
  public &amp;lt;E&amp;gt; List&amp;lt;E&amp;gt; selectList(String statement, Object parameter, RowBounds rowBounds) {
    return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
  }

  private &amp;lt;E&amp;gt; List&amp;lt;E&amp;gt; selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      dirty |= ms.isDirtySelect();
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException(&quot;Error querying database.  Cause: &quot; + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  @Override
  public void commit() {
    commit(false);
  }

  @Override
  public void commit(boolean force) {
    try {
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException(&quot;Error committing transaction.  Cause: &quot; + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  @Override
  public void rollback() {
    rollback(false);
  }

  @Override
  public void rollback(boolean force) {
    try {
      executor.rollback(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException(&quot;Error rolling back transaction.  Cause: &quot; + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;DefaultSqlSession는 CRUD와 관련한 다양한 메서드를 구현해 작업을 수행할 수 있도록 되어 있다. 여기는 selectList()메서드를 포함해 일부만 적어놓았지만 실제 구현체를 찾아보면 delete(), update(), selectOne() 등 다양한 메서드를 구현해놓은 것을 알 수 있다. 모든 메서드들은 실행할 statement를 파라미터로 받아 Configuration으로부터 해당 statement와 매핑된 MappedStatement객체를 찾고 Executor를 이용해 결과를 받아온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;다시 HelloService 서비스을 보면 조금 더 이해하기 쉬울 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;HelloService.java&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1718186968272&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.churnobyl;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class HelloService {

    private final SqlSessionFactory sqlSessionFactory;
    
    private HelloService() {
        String resource = &quot;properties/mybatis-config.xml&quot;;
        InputStream inputStream = null;

        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    public List&amp;lt;Member&amp;gt; getAll() {
        List&amp;lt;Member&amp;gt; members = null;

        try (SqlSession session = sqlSessionFactory.openSession()) {
            members = session.selectList(&quot;org.churnobyl.mappers.MemberMapper.selectAll&quot;);
            return members;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return members;
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;맨 위에 잠깐 나온 HelloService 코드와 다른 점은 생성자에서 sqlSessionFactory에 인스턴스를 주입하도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;getAll() 메서드에 주목하자. 만약 Controller가 모든 멤버 정보를 알고 싶어 HelloService의 getAll() 메서드를 호출한다고 해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp;이제 HelloService의 getAll() 메서드에서는 DB로부터 정보를 가져오기 위해 sqlSessionFactory의 openSession()메서드를 호출해 새로운 SqlSession을 요청한다. 새로운 session이 생성되면 우리가 만든 매퍼인 MemberMapper 인터페이스에서 모든 멤버 정보를 가져오는 &lt;b&gt;selectAll()&lt;/b&gt;을 호출하기 위해 파라미터로 &lt;b&gt;&quot;org.churnobyl.mappers.MemberMapper.selectAll&quot;&lt;/b&gt; 을 넣을 수 있다. 이 파라미터를 이용해 SqlSession은 Configuration의 mappedStatements 맵을 검색한 뒤 MappedStatement객체를 찾고 다시 이 정보를 Executor에게 넘겨 결과를 받아올 수 있는 것이다.&lt;/span&gt;&lt;/p&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;</description>
      <category>프로그래밍/Java</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/138</guid>
      <comments>https://zeus2141.tistory.com/138#entry138comment</comments>
      <pubDate>Wed, 12 Jun 2024 19:42:00 +0900</pubDate>
    </item>
    <item>
      <title>[Java DB] 1. JDBC - Java와 DB 상호작용의 기초</title>
      <link>https://zeus2141.tistory.com/137</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oBTpZ/btsHTSrkrPM/zicwmSLBhVRFKuBhXcbKw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oBTpZ/btsHTSrkrPM/zicwmSLBhVRFKuBhXcbKw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oBTpZ/btsHTSrkrPM/zicwmSLBhVRFKuBhXcbKw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoBTpZ%2FbtsHTSrkrPM%2FzicwmSLBhVRFKuBhXcbKw0%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;10&quot; height=&quot;10&quot; data-origin-width=&quot;476&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0lfbl/btsHS9aq0TH/rnLJfCantynJFO0RpQDxH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0lfbl/btsHS9aq0TH/rnLJfCantynJFO0RpQDxH1/img.png&quot; data-alt=&quot;Common Java Web Application&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0lfbl/btsHS9aq0TH/rnLJfCantynJFO0RpQDxH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0lfbl%2FbtsHS9aq0TH%2FrnLJfCantynJFO0RpQDxH1%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;1186&quot; height=&quot;540&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Common Java Web Application&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;0. 들어가기 전에.. - 웹 어플리케이션 요청 응답 프로세스&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vBpx9/btsHUcDaW1X/CYQs50z0oN3KskqWc6MVck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vBpx9/btsHUcDaW1X/CYQs50z0oN3KskqWc6MVck/img.png&quot; data-alt=&quot;웹 어플리케이션의 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vBpx9/btsHUcDaW1X/CYQs50z0oN3KskqWc6MVck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvBpx9%2FbtsHUcDaW1X%2FCYQs50z0oN3KskqWc6MVck%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;701&quot; height=&quot;230&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;웹 어플리케이션의 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;자바를 베이스로 한 웹 어플리케이션을 포함한 모든 언어의 웹 어플리케이션의 대략적인 구조는 다음과 같다. 먼저 유저가 웹사이트에 접근해 정보를 &lt;b&gt;요청&lt;/b&gt;하면 웹 서버에 탑재된 WAS(Web Application Server)는 &lt;b&gt;응답&lt;/b&gt;을 주는데, 이 때 해당 요청이 DB 데이터를 필요로 하거나 데이터를 저장해야 한다면 DB에 접근한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;자바에 한정해 좀 더 구체화해보자. 예를 들어 Web Application은 &lt;b&gt;Spring&lt;/b&gt;, WAS은 &lt;b&gt;Tomcat&lt;/b&gt;, DB는 &lt;b&gt;MySQL&lt;/b&gt;을 사용한다고 가정해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;932&quot; data-origin-height=&quot;567&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3DdZb/btsHThrBxLg/hjaoIv2HiiVUWGZVdnrmL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3DdZb/btsHThrBxLg/hjaoIv2HiiVUWGZVdnrmL1/img.png&quot; data-alt=&quot;웹 어플리케이션 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3DdZb/btsHThrBxLg/hjaoIv2HiiVUWGZVdnrmL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3DdZb%2FbtsHThrBxLg%2FhjaoIv2HiiVUWGZVdnrmL1%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;932&quot; height=&quot;567&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;웹 어플리케이션 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;&lt;b&gt;(1)&lt;/b&gt; Tomcat이 열려있는 웹서버 포트에 유저의 요청이 도착하면 &lt;b&gt;(2)&lt;/b&gt; Tomcat은 &lt;b&gt;HttpServletRequest&lt;/b&gt;와 &lt;b&gt;HttpServletResponse&lt;/b&gt;객체를 생성하고 &lt;b&gt;(3)&lt;/b&gt; Spring의 DispatcherServlet에 요청을 전달한다. &lt;b&gt;(4)&lt;/b&gt; DispatcherServlet은 요청을 적절한 컨트롤러로 전달하고 &lt;b&gt;(5)&lt;/b&gt; 요청을 전달받은 컨트롤러는 요청을 처리하기 위해 Service 계층과 상호 작용해 비지니스 로직을 수행하고 반환할 모델 데이터를 준비한다. &lt;b&gt;(6)&lt;/b&gt; 이때 MySQL과 상호 작용이 필요하다면 MySQL이 열려있는 포트에 접속을 요청하고 작업을 수행한다. &lt;b&gt;(7)&lt;/b&gt; 그리고 최종적인 결과를 다시 DispacherServlet이 적절한 형식으로 변환해 HttpServletResponse객체에 기록한 뒤 &lt;b&gt;(8)&lt;/b&gt; Tomcat이 최종적으로 응답을 반환한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;우리는 이중 여섯번째 프로세스인 DB와의 상호작용에 주목할 것이다.&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;1. Java와 Database의 상호작용 - Socket&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpVzSY/btsHR8JzXr4/xQH09IlmJ32eMN09zwZIV1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpVzSY/btsHR8JzXr4/xQH09IlmJ32eMN09zwZIV1/img.jpg&quot; data-alt=&quot;파이프 소켓(Pipe Socket)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpVzSY/btsHR8JzXr4/xQH09IlmJ32eMN09zwZIV1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpVzSY%2FbtsHR8JzXr4%2FxQH09IlmJ32eMN09zwZIV1%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;313&quot; height=&quot;313&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파이프 소켓(Pipe Socket)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;자바 프로그램과 DB와의 상호작용을 더 디테일하게 이해하기 위해 본질적인 부분부터 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;자바 프로그램이나 데이터베이스와 같은 &lt;b&gt;프로세스&lt;/b&gt;가 데이터를 프로세스 바깥으로 내보내거나 받기 위해서는 &lt;b&gt;소켓&lt;/b&gt;(Socket)이 필요하다.&amp;nbsp; &lt;b&gt;소켓&lt;/b&gt;이란 일반적인 정의로 A파이프의 끝과 B파이프의 끝을 연결하기 위한 이음새를 말한다. 쉽게 말해, 어떤 다른 두 물체를 연결하기 위한 접점을 말한다. 이 정의를 네트워크 분야에서의 의미로 확장해보자. &lt;b&gt;네트워크 소켓&lt;/b&gt;(Network socket)은 컴퓨터 네트워크를 경유하는 프로세스 간 통신의 종착점이다. 즉 A 프로세스와 B 프로세스가 통신하기 위해서는 네트워크(파이프)를 거쳐야 하는데, 이 때 A, B프로세스가 네트워크에 연결하기 위한 각각의 통신 접점이 &lt;b&gt;소켓&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;643&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHH7RK/btsHTVBYUBM/BjKC7lT6jatn3o4Xea0ge0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHH7RK/btsHTVBYUBM/BjKC7lT6jatn3o4Xea0ge0/img.png&quot; data-alt=&quot;소켓&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHH7RK/btsHTVBYUBM/BjKC7lT6jatn3o4Xea0ge0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHH7RK%2FbtsHTVBYUBM%2FBjKC7lT6jatn3o4Xea0ge0%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;444&quot; height=&quot;409&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;592&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;소켓&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;결론적으로 모든 데이터베이스는 데이터를 받아들이고 보내는 소켓이 있다. 데이터베이스는 특정 포트(MySQL 기본 포트 : 3306, H2 기본 포트 : 8080 등..)에 바인딩하고 클라이언트로부터 들어오는 연결 요청을 기다린다. 그리고 연결이 수락되면 새로운 Socket 인스턴스를 만들어 클라이언트와의 통신을 수행한다. 이는 기본적인 서버-클라이언트 통신 방법이며, 간단한 자바 코드로 예제를 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;Java - DB 연결 예제&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1717997005175&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class SimpleExampleDB {

    private static final int port = 9990;

    public static void main(String[] args) {

        // 특정 포트에 바인딩
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println(&quot;서버 Listening Port = &quot; + port);


            // 클라이언트 요청 대기
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println(&quot;클라이언트 연결됨&quot;);
                new ClientHandler(clientSocket).start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 클라이언트 요청 수신 시 새로운 소켓 스레드 생성 클래스
     */
    private static class ClientHandler extends Thread {
        private final Socket clientSocket;

        public ClientHandler(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }

        // 스레드가 동작하기 위해 run 메서드 오버라이드
        @Override
        public void run() {
            try (InputStream in = clientSocket.getInputStream();
                 OutputStream out = clientSocket.getOutputStream();
                 BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                 PrintWriter writer = new PrintWriter(out, true)) {

                String message;
                while ((message = reader.readLine()) != null) {
                    System.out.println(&quot;클라이언트로부터 수신받은 메시지: &quot; + message);
                    writer.println(&quot;저장완료&quot;);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1717997017924&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.net.Socket;

public class SimpleExampleJavaProgram {
    static final String serverAddress = &quot;localhost&quot;;
    static final int port = 9990;

    public static void main(String[] args) {
        try (Socket socket = new Socket(serverAddress, port);
             InputStream in = socket.getInputStream();
             OutputStream out = socket.getOutputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(in));
             PrintWriter writer = new PrintWriter(out, true)) {

            System.out.println(&quot;DB와 연결됨&quot;);

            // DB로 메시지 보내기
            String message = &quot;DB야! memberA를 저장해줘&quot;;
            writer.println(message);
            
            // DB로부터 수신받은 메시지
            String response = reader.readLine();
            System.out.println(&quot;DB로부터 받은 메시지: &quot; + response);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;1264&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIL4y5/btsHSp5vT2m/69JMBAHaSwIz8XxlIsmNs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIL4y5/btsHSp5vT2m/69JMBAHaSwIz8XxlIsmNs1/img.png&quot; data-alt=&quot;결과 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIL4y5/btsHSp5vT2m/69JMBAHaSwIz8XxlIsmNs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIL4y5%2FbtsHSp5vT2m%2F69JMBAHaSwIz8XxlIsmNs1%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;1264&quot; height=&quot;171&quot; data-origin-width=&quot;1264&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;결과 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;로직은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;651&quot; data-origin-height=&quot;781&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rUpFH/btsHSVXez0L/9YNRWdF4vAWF32BCNtYgAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rUpFH/btsHSVXez0L/9YNRWdF4vAWF32BCNtYgAk/img.png&quot; data-alt=&quot;예제의 개략적인 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rUpFH/btsHSVXez0L/9YNRWdF4vAWF32BCNtYgAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrUpFH%2FbtsHSVXez0L%2F9YNRWdF4vAWF32BCNtYgAk%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;501&quot; height=&quot;601&quot; data-origin-width=&quot;651&quot; data-origin-height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예제의 개략적인 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;(1) 서버를 모사한 &lt;b&gt;SimpleExampleDB&lt;/b&gt;는 데이터를 수신할 ServerSocket을 만들고 9990 포트에 바인딩한다. (2) 이제 클라이언트를 모사한 &lt;b&gt;SimpleExampleJavaProgram&lt;/b&gt;은 Socket을 만들고 두 프로세스 간의 논리적인 접속을 성립(establish)하기 위해 9990포트의 프로세스와 3-Way Handshake를 수행한다. (3) 서로 간의 논리적인 접속이 확인됐다면 SimpleExampleDB는 해당 클라이언트와 연결을 유지하기 위해 새로운 쓰레드인 &lt;b&gt;ClientHandler를&lt;/b&gt; 생성하고 클라이언트로부터 수신을 기다린다. (4) 클라이언트인 &lt;b&gt;SimpleExampleJavaProgram&lt;/b&gt;은 &lt;b&gt;DB&lt;/b&gt;를 향해 전하고자 하는 메시지(여기서는 'DB야! memberA를 저장해줘')를 던지고, &lt;b&gt;DB&lt;/b&gt;는 해당 메시지를 수신한 뒤 적절한 로직을 수행하고 다시 클라이언트에게 메시지를 전달한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;간략한 예제지만 이는 자바와 데이터베이스가 통신하는 과정을 전체적으로 나타낸다. 메시지가 &lt;span style=&quot;text-align: start;&quot;&gt;'DB야! memberA를 저장해줘'가 아니라 DB가 이해할 수 있는 메시지라면 DB는 적절한 로직을 수행해 자바 프로그램에게 적절한 결과를 전달해 줄 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;2. JDBC의 도입&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJa936/btsHSYNb3Oy/KYUTTCDPOFxkLMwI1pvG50/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJa936/btsHSYNb3Oy/KYUTTCDPOFxkLMwI1pvG50/img.jpg&quot; data-alt=&quot;다양한 데이터베이스 벤더&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJa936/btsHSYNb3Oy/KYUTTCDPOFxkLMwI1pvG50/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJa936%2FbtsHSYNb3Oy%2FKYUTTCDPOFxkLMwI1pvG50%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;593&quot; height=&quot;230&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다양한 데이터베이스 벤더&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;자바와 데이터 베이스 간의 통신은 위 절과 같은 방법으로 수행할 수 있다. 하지만 큰 문제점들이 있다. 위의 그림과 같이 &lt;b&gt;데이터베이스 벤더는 너무 다양하다&lt;/b&gt;. 각 데이터베이스 벤더의 작동방식은 모두 제각각이고 보안 방식도 다르다. 따라서 자바 프로그램에서 특정 데이터베이스를 사용하기 위해서는 해당 데이터베이스가 요구하는 규격에 맞춰서 코드를 작성해야 했다. 그런데 만약 MySQL에서 PostgreSQL로 마이그레이션을 해야한다면..?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;자바 진영은 이러한 문제를 고민했고, 1997년 JDK 1.1에서 Microsoft ODBC 방식을 차용한 JDBC를 소개했다. &lt;b&gt;JDBC&lt;/b&gt;는 &lt;b&gt;각 데이터베이스 벤더들이 통일된 양식을 따르도록 DB 연결 과정을 표준화한 API 규격&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;602&quot; data-origin-height=&quot;803&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fr1UD/btsHUdvXsEj/eqRrmJgS3IWCx0nCRayD50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fr1UD/btsHUdvXsEj/eqRrmJgS3IWCx0nCRayD50/img.png&quot; data-alt=&quot;JDK 1.1 출시 (97.2월)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fr1UD/btsHUdvXsEj/eqRrmJgS3IWCx0nCRayD50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFr1UD%2FbtsHUdvXsEj%2FeqRrmJgS3IWCx0nCRayD50%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;602&quot; height=&quot;803&quot; data-origin-width=&quot;602&quot; data-origin-height=&quot;803&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JDK 1.1 출시 (97.2월)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp; Java가 JDBC를 이용해 문제를 해결한 방식은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&lt;i&gt;'야 너네 다 다른 거 알겠어. 그니까 내가 기준만 줄게. 여기에 맞춰서 &lt;u&gt;같은 방식으로 실행 가능하도록&lt;/u&gt; 만들어.'&lt;/i&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt; &amp;nbsp;이때부터 각 데이터베이스 벤더들은 Java의 기준인 JDBC 인터페이스를 자기들의 제품에 맞게 구현한 &lt;b&gt;JDBC Driver&lt;/b&gt;를 만들었다. 이로써 각 벤더의 JDBC Driver 구현체는 전부 다르게 생겼지만 연결 방법이 통일되어 개발자들은 해당 제품에서 제공하는 JDBC Driver를 자바 코드에 추가만 해도 같은 코드로 데이터베이스와 연결할 수 있게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;1291&quot; data-origin-height=&quot;709&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uiXug/btsHUIhXpOc/qVR5V3o2uPGzOolhKkoEkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uiXug/btsHUIhXpOc/qVR5V3o2uPGzOolhKkoEkK/img.png&quot; data-alt=&quot;JDBC API&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uiXug/btsHUIhXpOc/qVR5V3o2uPGzOolhKkoEkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuiXug%2FbtsHUIhXpOc%2FqVR5V3o2uPGzOolhKkoEkK%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;587&quot; height=&quot;322&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;709&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JDBC API&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;JDBC Driver는 데이터베이스 벤더에서 대부분 지원한다. 예를 들면 MySQL은 &lt;b&gt;Connector/J&lt;/b&gt;라는 이름으로 지원하고, Oracle은 제품 다운로드 시 &lt;b&gt;/oraclexe/app/oracle/product/버전/server/jdbc/lib&lt;/b&gt; 안에 &lt;b&gt;ojdbc버전.jar&lt;/b&gt;라는 이름으로 지원한다. H2도 &lt;b&gt;/H2/bin&lt;/b&gt; 안에 &lt;b&gt;h2-버전이름.jar&lt;/b&gt;가 있고, PostgreSQL도 &lt;b&gt;pgJDBC&lt;/b&gt;라는 이름으로 JDBC를 지원한다. 그 밖의 데이터베이스도 데이터베이스 + JDBC라고 검색하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;이제 JDBC의 동작 흐름을 살펴보자.&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;3. JDBC API&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DPgTP/btsHSyBlvO3/vMFj3Zg9w3KpVAXaBy6qM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DPgTP/btsHSyBlvO3/vMFj3Zg9w3KpVAXaBy6qM1/img.png&quot; data-alt=&quot;JDBC 동작 흐름&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DPgTP/btsHSyBlvO3/vMFj3Zg9w3KpVAXaBy6qM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDPgTP%2FbtsHSyBlvO3%2FvMFj3Zg9w3KpVAXaBy6qM1%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;645&quot; height=&quot;367&quot; data-origin-width=&quot;645&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;JDBC 동작 흐름&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;JDBC API의 전체적인 동작 흐름은 다음과 같다. 앞서 말한 것처럼 JDBC API는 인터페이스이다. 실제로는 각 인터페이스를 구현한 각 벤더의 JDBC Driver 구현체를 통해 각 벤더의 데이터베이스에 일관적으로 접근할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;위의 그림에서 보이는 인터페이스들을 간단히 설명하면, &lt;b&gt;DriverManager&lt;/b&gt;는 각 벤더의 &lt;b&gt;Driver&lt;/b&gt; &lt;b&gt;구현체&lt;/b&gt;를 등록하고 Driver로부터 &lt;b&gt;DB와 전체적인 연결을 유지&lt;/b&gt;하는 &lt;b&gt;Connection&lt;/b&gt; 인터페이스를 획득한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;DB와 연결을 유지하는 &lt;b&gt;Connection&lt;/b&gt; 인터페이스는 내부적으로 &lt;b&gt;세션(Session)&lt;/b&gt;을 생성한다. &lt;b&gt;세션&lt;/b&gt;은 논리적인 개념으로 한 유저의 로그인 상태를 의미하며, 보통 Connection과 1:1 대응된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;추후에 더 자세히 설명하겠지만, Connection이 단 하나만 존재한다고 가정하자. 이 때 하나의 유저가 DB와 상호작용 하기 위해 Connection을 차지하고 있다면, 다른 유저는 기존 유저가 작업을 끝낼 때까지 대기해야 한다. 이러한 문제점을 해결하기 위해 애플리케이션은 일반적으로 Connection을 다수 만들어 놓고 상호작용이 필요할 때마다 유저가 비어있는 Connection을 차지해 사용하는 &lt;b&gt;커넥션 풀(Connection Pool)&lt;/b&gt;을 만들어 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;다음으로 Connection 인터페이스로부터 &lt;b&gt;SQL 쿼리를 실행할 수 있는&lt;/b&gt; &lt;b&gt;Statement&lt;/b&gt; 인터페이스를 얻는다. Statement 인터페이스를 통해 SQL 쿼리문을 DB에 전달해 &lt;b&gt;결과들의 컬렉션&lt;/b&gt;인 &lt;b&gt;ResultSet&lt;/b&gt; 인터페이스를 얻거나 INSERT, UPDATE 등의 쿼리를 실행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;이제 주요 클래스인 DriverManager에 대해 좀 더 자세히 알아보고 실제 데이터베이스와 연결하는 예제를 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;4. JDBC 주요 클래스 - DriverManager&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp; 각 벤더의 JDBC Driver를 열어보면 &lt;b&gt;META-INF\services\java.sql.Driver&lt;/b&gt;가 있다. 이를 열어보면 java.sql.Driver 인터페이스를 해당 벤더가 구현한 Driver 클래스의 위치가 기록되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;437&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cP6eHW/btsHTjjeByi/ZKIMjlqo5ZJXlCE7ngdQeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cP6eHW/btsHTjjeByi/ZKIMjlqo5ZJXlCE7ngdQeK/img.png&quot; data-alt=&quot;java.sql.Driver&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cP6eHW/btsHTjjeByi/ZKIMjlqo5ZJXlCE7ngdQeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcP6eHW%2FbtsHTjjeByi%2FZKIMjlqo5ZJXlCE7ngdQeK%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;437&quot; height=&quot;155&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;java.sql.Driver&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;Driver의 구현 방법은 벤더마다 다르지만 공통적으로 클래스 로딩 시에 &lt;b&gt;DriverManager&lt;/b&gt;의 &lt;b&gt;registerDriver &lt;/b&gt;메서드를 호출하도록 되어있다. 이는 JDBC의 규칙에 따른 것으로 애플리케이션 시작 시에 각 Driver가 registerDriver()를 호출하면 해당 Driver객체가 DriverManager의 registeredDrivers 리스트에 추가된다. 아래의 DriverManager 클래스 일부를 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718006735736&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package java.sql;

public class DriverManager {

    // List of registered JDBC drivers
    private static final CopyOnWriteArrayList&amp;lt;DriverInfo&amp;gt; registeredDrivers = new CopyOnWriteArrayList&amp;lt;&amp;gt;();
    
    /**
     * Registers the given driver with the {@code DriverManager}.
     * A newly-loaded driver class should call
     * the method {@code registerDriver} to make itself
     * known to the {@code DriverManager}. If the driver is currently
     * registered, no action is taken.
     *
     * @param driver the new JDBC Driver that is to be registered with the
     *               {@code DriverManager}
     * @param da     the {@code DriverAction} implementation to be used when
     *               {@code DriverManager#deregisterDriver} is called
     * @throws SQLException if a database access error occurs
     * @throws NullPointerException if {@code driver} is null
     * @since 1.8
     */
    public static void registerDriver(java.sql.Driver driver,
            DriverAction da)
        throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if (driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println(&quot;registerDriver: &quot; + driver);

    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;registerDriver 메서드 주석을 살펴보면 다음과 같은 설명이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;Registers the given driver with the DriverManager. A newly-loaded driver class should call the method registerDriver to make itself known to the DriverManager.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;즉, 해당 Driver를 DriverManager에 등록하는 메서드이며, &lt;b&gt;새롭게 로드되는 Driver 클래스는 DriverManager에게 자신을 알리기 위해 registerDriver()메서드를 호출해야 한다&lt;/b&gt;고 친절하게 설명해주고 있다. DriverInfo는 wrapper 클래스로 Driver.equals() 메서드의 노출을 막기 위해 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;그럼 H2 데이터베이스의 Driver 구현체는 어떻게 구현되어 있을까.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718007867507&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.h2;

public class Driver implements java.sql.Driver, JdbcDriverBackwardsCompat {
	
    private static final Driver INSTANCE = new Driver();
    
    private static boolean registered;
    
    static {
        load();
    }
    
    public static synchronized Driver load() {
        try {
            if (!registered) {
                registered = true;
                DriverManager.registerDriver(INSTANCE);
            }
        } catch (SQLException e) {
            DbException.traceThrowable(e);
        }
        return INSTANCE;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;보다시피 Driver 클래스 로딩 시에 static 키워드를 통해 load() 메서드를 실행하고, 다시 load()메서드에서 DriverManager.registerDriver(INSTANCE) 메서드를 실행하는 것을 알 수 있다. MySQL 데이터베이스는 어떻게 구현해놨을까?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1718008031572&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package com.mysql.cj.jdbc;

import java.sql.SQLException;

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
	static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException(&quot;Can't register driver!&quot;);
        }
    }
    
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance().
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;구현방식은 조금 다르지만 어쨌든 java.sql.DriverManager.registerDriver(new Driver())를 호출해 규칙을 만족하고 있다.&lt;/span&gt;&lt;/p&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;5. JDBC 예제&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1718009361819&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.sql.*;

public class DBConnectionExample {
    public static void main(String[] args) {
        // tcp프로토콜을 통해 h2의 dbtest 데이터베이스에 접근
        String jdbcUrl = &quot;jdbc:h2:tcp://localhost/~/dbtest&quot;;

        // 유저 이름
        String username = &quot;sa&quot;;

        // 비밀번호
        String password = &quot;&quot;;


        // Connection 객체 및 Statement 객체 획득
        try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
             Statement stmt = conn.createStatement()) {

            System.out.println(&quot;conn = &quot; + conn.getClass());

            // member 테이블 존재한다면 드랍
            String dropTableSQL = &quot;DROP TABLE IF EXISTS member&quot;;
            stmt.execute(dropTableSQL);

            // member 테이블 생성
            String createTableSQL = &quot;CREATE TABLE member (id INT PRIMARY KEY, name VARCHAR(255))&quot;;
            stmt.execute(createTableSQL);
            System.out.println(&quot;Table 'member' created.&quot;);


            // member 넣기
            for (int i = 1; i &amp;lt; 11; i++) {
                String insertSQL = &quot;INSERT INTO member (id, name) VALUES (&quot; + i + &quot;, 'member&quot; + i + &quot;')&quot;;
                stmt.executeUpdate(insertSQL);
            }

            // PreparedStatement 객체 생성 및 미리 컴파일
            PreparedStatement pstmt = conn.prepareStatement(&quot;SELECT * FROM member WHERE name LIKE ?&quot;);
            
            // 동적으로 sql문을 완성
            pstmt.setString(1, &quot;%member%&quot;);

            ResultSet resultSet = pstmt.executeQuery();

            while (resultSet.next()) {
                System.out.println(&quot;id = &quot; + resultSet.getInt(&quot;id&quot;) +
                        &quot; name = &quot; +
                        resultSet.getString(&quot;name&quot;));
            }

            resultSet.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;conn = class org.h2.jdbc.JdbcConnection&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;Table 'member' created.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;id = 1 name = member1&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;id = 2 name = member2&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;id = 3 name = member3&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;id = 4 name = member4&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;id = 5 name = member5&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt; id = 6 name = member6&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt; id = 7 name = member7&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt; id = 8 name = member8&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt; id = 9 name = member9&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt; id = 10 name = member10&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;간단한 예제를 만들었다. 특징적인 것만 간단히 언급하면, &lt;b&gt;DriverManager.getConnection()&lt;/b&gt;메서드에 jdbcUrl과 username, password를 파라미터로 넘겨 Connection 객체를 획득하고, Connection 객체로부터 실질적으로 SQL 쿼리문을 실행할 Statement객체를 획득한다. &lt;b&gt;Statement&lt;/b&gt;객체의 &lt;b&gt;execute()&lt;/b&gt;메서드를 사용해 SQL 쿼리문을 실행할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;또 Connection 객체로부터 &lt;b&gt;PreparedStatement&lt;/b&gt; 객체를 얻을 수 있는데, 이 객체는 SQL 쿼리문을 미리 컴파일 해두고 ?부분을 나중에 동적으로 채워넣을 수 있는 객체다. SQL Injection같은 공격을 차단할 수도 있고, 필요한 부분을 나중에 채워넣을 수 있으므로 여러모로 Statement보다 쓸모있는 객체다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;ResultSet객체는 조회한 결과 값을 순차적으로 접근할 수 있는 객체다. 물론 조회한 데이터를 전부 가지고 있는 게 아니라 인덱스 정보만을 가지고 있어서 next()메서드와 while문 조합을 통해 차례로 호출해 데이터를 뽑아내야 한다. 또한 위의 코드의 while문 안에서 next()메서드가 먼저 1회 호출되는 것을 볼 수 있다. 즉 next()메서드를 1회 호출하기 전까지는 최초 데이터의 위치에 자리 잡고 있지 않기 때문에 주의해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&amp;nbsp;사용하면 할수록 고대의 방식은 복잡하기 그지없다. 이제 조금 더 최신의 방식을 알아보도록 하자.&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로그래밍/Java</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/137</guid>
      <comments>https://zeus2141.tistory.com/137#entry137comment</comments>
      <pubDate>Mon, 10 Jun 2024 18:19:30 +0900</pubDate>
    </item>
    <item>
      <title>[정처기 실기] 소프트웨어 설계 - (2) 요구사항 확인2</title>
      <link>https://zeus2141.tistory.com/136</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bk44lc/btsGVhr1X44/JbRc7T9KwPNZd9gqyVGC1k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bk44lc/btsGVhr1X44/JbRc7T9KwPNZd9gqyVGC1k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bk44lc/btsGVhr1X44/JbRc7T9KwPNZd9gqyVGC1k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbk44lc%2FbtsGVhr1X44%2FJbRc7T9KwPNZd9gqyVGC1k%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;380&quot; height=&quot;452&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Part1. 소프트웨어 설계&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;소프트웨어 설계 혹은 소프트웨어 디자인은 소프트웨어 문제 해결과 계획 과정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 요구사항 확인 2&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style2&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;039. 유스케이스 다이어그램 (Use Case Diagram)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m7KRX/btsGQ1Lg27J/rp3JlfAbnLBk8wDn0D6CP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m7KRX/btsGQ1Lg27J/rp3JlfAbnLBk8wDn0D6CP1/img.png&quot; data-alt=&quot;유스케이스 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m7KRX/btsGQ1Lg27J/rp3JlfAbnLBk8wDn0D6CP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm7KRX%2FbtsGQ1Lg27J%2Frp3JlfAbnLBk8wDn0D6CP1%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;596&quot; height=&quot;435&quot; data-origin-width=&quot;813&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;유스케이스 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;유스케이스 다이어그램&lt;/b&gt;&lt;/span&gt;은 사용자와 다른 외부 시스템들이 &lt;b&gt;개발될 시스템을 이용해 수행할 수 있는 기능을 &lt;span style=&quot;color: #f89009;&quot;&gt;사용자 관점에서&lt;/span&gt; 표현한 것&lt;/b&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구성 요소&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;시스템&lt;/b&gt;(System)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템 내부의 유스케이스들을 사각형으로 묶어 시스템의 범위를 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;액터&lt;/b&gt;&lt;/span&gt;(Actor)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;시스템과 상호작용을 하는 모든 외부 요소&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주로 사람이나 외부 시스템을 의미함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사람 모양이나 Actor라는 이름을 붙여서 표현함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;주액터&lt;/b&gt; - 시스템을 사용함으로써 이득을 얻는 대상으로 주로 사람에 해당됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;부액터&lt;/b&gt; - 주액터의 목적 달성을 위해 시스템에 서비스를 제공하는 외부 시스템으로 조직이나 기관이 될 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;유스케이스&lt;/b&gt;(Use Case)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사용자가 보는 관점에서 시스템이 액터에게 제공하는 서비스나 기능을 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;타원 안에 글씨로 나타냄&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;관계&lt;/b&gt;(Relationship)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;유스케이스 다이어그램에서 관계는 액터와 유스케이스, 유스케이스와 유스케이스 사이에서 나타날 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;유스케이스에서 나타날 수 있는 관계&lt;/b&gt; : &lt;b&gt;포함&lt;/b&gt;(Include) 관계, &lt;b&gt;확장&lt;/b&gt;(Extends) 관계, &lt;b&gt;일반화&lt;/b&gt;(Generalization) 관계&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;040. 유스케이스에서 나타날 수 있는 관계&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;370&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckjas4/btsGShT9ZM5/5Lijys5kPBAs1ijxzNPNW0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckjas4/btsGShT9ZM5/5Lijys5kPBAs1ijxzNPNW0/img.jpg&quot; data-alt=&quot;유스케이스의 관계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckjas4/btsGShT9ZM5/5Lijys5kPBAs1ijxzNPNW0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fckjas4%2FbtsGShT9ZM5%2F5Lijys5kPBAs1ijxzNPNW0%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;370&quot; height=&quot;85&quot; data-origin-width=&quot;370&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;유스케이스의 관계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;포함&lt;/b&gt;&lt;/span&gt;(Include) 관계&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;두 개 이상의 유스케이스에 공통적으로 적용되는 기능을 별도로 분리하여 새로운 유스케이스로 만든 경우&lt;/b&gt;, 원래의 유스케이스와 새롭게 분리된 유스케이스와의 관계를 포함 관계라고 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;원래의 유스케이스에서 새롭게 만든 포함되는 유스케이스 쪽으로 점선 화살표를 연결한 후 화살표 위에 ≪include≫라고 표기함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ex&amp;gt; 상품 주문, 배송 조회, 리뷰 작성 ----- ≪include≫------&amp;gt; 로그인&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;확장&lt;/b&gt;&lt;/span&gt;(Extend) 관계&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;유스케이스가 특정 조건에 부합되어 유스케이스의 기능이 확장될 때&lt;/b&gt; 원래의 유스케이스와 확장된 유스케이스와의 관계를 확장 관계라고 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;확장될 유스케이스에서 원래의 유스케이스 쪽으로 점선 화살표를 연결한 후 화살표 위에 ≪extend≫라고 표기함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ex&amp;gt; 사진 업로드 ----- ≪extend≫ -----&amp;gt; 리뷰 작성&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;041. 활동 다이어그램 (Activity Diagram)&lt;/span&gt;&lt;/h4&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;713&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DkLjj/btsGSPiNlqk/9tHX3GUhOLQ0lm3PuewKKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DkLjj/btsGSPiNlqk/9tHX3GUhOLQ0lm3PuewKKk/img.png&quot; data-alt=&quot;활동 다이어그램 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DkLjj/btsGSPiNlqk/9tHX3GUhOLQ0lm3PuewKKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDkLjj%2FbtsGSPiNlqk%2F9tHX3GUhOLQ0lm3PuewKKk%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;431&quot; height=&quot;384&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;713&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;활동 다이어그램 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;활동 다이어그램&lt;/b&gt;&lt;/span&gt;은 &lt;b&gt;사용자의 관점에서 시스템이 수행하는 기능을 처리 흐름에 따라 순서대로 표현한 것&lt;/b&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구성 요소&amp;nbsp;&lt;/span&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czDSWX/btsGThMD3IL/gkHxcVYG6It4qslunaUzU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czDSWX/btsGThMD3IL/gkHxcVYG6It4qslunaUzU1/img.png&quot; data-alt=&quot;활동 다이어그램 구성 요소&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czDSWX/btsGThMD3IL/gkHxcVYG6It4qslunaUzU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczDSWX%2FbtsGThMD3IL%2FgkHxcVYG6It4qslunaUzU1%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;391&quot; height=&quot;425&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;670&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;활동 다이어그램 구성 요소&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;액션(Action) / 액티비티(Activity)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;액션&lt;/b&gt; - 더 이상 분해할 수 없는 단일 작업&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;액티비티&lt;/b&gt; - 몇 개의 액션으로 분리될 수 있는 작업&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시작 노드 (Start Node)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;액션이나 액티비티가 시작됨을 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;종료 노드 (End Node)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;액티비티 안의 모든 흐름이 종료됨을 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조건(판단) 노드&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조건에 따라 제어의 흐름이 분리됨을 화살표가 나가는 마름모꼴로 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;들어오는 제어 흐름은 한 개이고 나가는 제어 흐름은 여러 개임&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;병합 노드&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;여러 경로의 흐름이 하나로 합쳐짐을 화살표가 들어오는 마름모꼴로 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;들어오는 제어 흐름은 여러 개이고 나가는 제어 흐름은 한 개임&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;포크(Fork) 노드&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;액티비티의 흐름이 분리되어 수행됨을 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;들어오는 액티비티 흐름은 한 개이고 나가는 액티비티 흐름은 여러 개임&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조인(Join) 노드&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;분리되어 수행되던 액티비티의 흐름이 다시 합쳐짐을 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;들어오는 액티비티 흐름은 여러 개이고 나가는 액티비티 흐름은 한 개임&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스윔레인(Swim Lane)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;액티비티 수행을 담당하는 주체를 구분하는 선&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;가로 또는 세로 실선을 그어 구분함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;042. 클래스 다이어그램 (Class Diagram)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nlux2/btsGT0Kpycd/4AUGB9ZhBCoZREIn5ptpak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nlux2/btsGT0Kpycd/4AUGB9ZhBCoZREIn5ptpak/img.png&quot; data-alt=&quot;클래스 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nlux2/btsGT0Kpycd/4AUGB9ZhBCoZREIn5ptpak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnlux2%2FbtsGT0Kpycd%2F4AUGB9ZhBCoZREIn5ptpak%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;545&quot; height=&quot;506&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1301&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;클래스 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;클래스 다이어그램&lt;/b&gt;&lt;/span&gt;은 클래스와 클래스가 가지는 속성, 클래스 사이의 관계를 표현한 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구성 요소&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;클래스&lt;/b&gt;&lt;/span&gt;(Class)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;각각의 객체들이 갖는 속성과 오퍼레이션(동작)을 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일반적으로 &lt;b&gt;3개의 구획(Compartment)로 나눠 클래스 이름, 속성, 오퍼레이션을 표기&lt;/b&gt;함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;속성&lt;/b&gt;&lt;/span&gt;(Attribute) - 클래스의 상태나 정보를 표현&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;오퍼레이션&lt;/b&gt;&lt;/span&gt;(Operation) - 클래스가 수행할 수 있는 동작으로, 함수(메소드)라고도 함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;제약조건&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;속성에 입력될 값에 대한 제약조건이나 오퍼레이션 수행 전후에 지정해야 할 조건이 있다면 이를 쪽지 형태의 사각형에 적음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스 안에 제약조건을 기술할 때는 중괄호 { }를 이용함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;관계&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;관계는 클래스와 클래스 사이의 연관성을 표현함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스 다이어그램에 표현하는 관계에는 연관 관계, 집합 관계, 포함 관계, 일반화 관계, 의존 관계가 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;043. 연관 클래스&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;405&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tXEHw/btsGTyN7ZvA/1Jz1xPvFs3kZexHMMmqHPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tXEHw/btsGTyN7ZvA/1Jz1xPvFs3kZexHMMmqHPk/img.png&quot; data-alt=&quot;연관 클래스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tXEHw/btsGTyN7ZvA/1Jz1xPvFs3kZexHMMmqHPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtXEHw%2FbtsGTyN7ZvA%2F1Jz1xPvFs3kZexHMMmqHPk%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;405&quot; height=&quot;203&quot; data-origin-width=&quot;405&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;연관 클래스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;연관 클래스&lt;/b&gt;&lt;/span&gt;는 &lt;b&gt;연관 관계에 있는 두 클래스에 추가적으로 표현해야 할 속성이나 오퍼레이션이 있는 경우 생성하는 클래스&lt;/b&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;두 클래스의 연관 관계를 나타내는 &lt;b&gt;선의 가운데로부터 점선을 연관 클래스로 이어 표시&lt;/b&gt;한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;연관 클래스의 이름은 연관 관계의 이름을 이용해 지정한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;044. 시퀀스 다이어그램 (Sequence Diagram)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;771&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y4oza/btsGTiShMAw/s7vayoTfu0O5LuwewS92Bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y4oza/btsGTiShMAw/s7vayoTfu0O5LuwewS92Bk/img.png&quot; data-alt=&quot;시퀀스 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y4oza/btsGTiShMAw/s7vayoTfu0O5LuwewS92Bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY4oza%2FbtsGTiShMAw%2Fs7vayoTfu0O5LuwewS92Bk%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;478&quot; height=&quot;427&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;771&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;시퀀스 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;시퀀스 다이어그램&lt;/b&gt;&lt;/span&gt;은 &lt;b&gt;시스템이나 객체들이 메시지를 주고받으며 상호작용하는 과정을 그림으로 표현한 것&lt;/b&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구성 요소&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;액터 (Actor)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템으로부터 서비스를 요청하는 외부 요소로, 사람이나 외부 시스템을 의미함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;사람 형태&lt;/b&gt; 아래에 이름을 적어 나타냄&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체 (Object)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시지를 주고받는 주체&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;네모 박스&lt;/b&gt; 안에 이름을 적어 나타냄&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;생명선 (Lifeline)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체가 메모리에 존재하는 기간으로, &lt;b&gt;객체 아래쪽에 점선을 그어 표현&lt;/b&gt;함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체 소멸(X 표시)이 표시된 기간까지 존재함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실행 상자 (Active Box, 활성 상자)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체가 메시지를 주고받으며 구동되고 있음을 표현하며, &lt;b&gt;긴 막대기&lt;/b&gt; 형태로 나타냄&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시지 (Message)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체가 상호 작용을 위해 주고받는 메시지로, &lt;b&gt;화살표와 함께 설명&lt;/b&gt;을 적어 나타냄&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체 소멸&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해당 객체가 더 이상 메모리에 존재하지 않음을 표현한 것으로, &lt;b&gt;X표시&lt;/b&gt;로 나타냄&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프레임 (Frame)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다이어그램의 전체 또는 일부를 묶어 표현한 것으로, 여러 요소를 감싸는 &lt;b&gt;네모 박스 왼쪽 위에 해당 프레임의 이름&lt;/b&gt;을 적음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;045. 커뮤니케이션 다이어그램 (Communication Diagram)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWGdIR/btsGRommfbo/dJN5Ylsxj745OMfk6A12wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWGdIR/btsGRommfbo/dJN5Ylsxj745OMfk6A12wK/img.png&quot; data-alt=&quot;커뮤니케이션 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWGdIR/btsGRommfbo/dJN5Ylsxj745OMfk6A12wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWGdIR%2FbtsGRommfbo%2FdJN5Ylsxj745OMfk6A12wK%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;403&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;커뮤니케이션 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;커뮤니케이션 다이어그램&lt;/b&gt;&lt;/span&gt;은 &lt;b&gt;시스템이나 객체들이 메시지를 주고받으며 상호작용하는 과정과 &lt;span style=&quot;color: #f89009;&quot;&gt;객체들 간의 연관&lt;/span&gt;을 그림으로 표현한 것&lt;/b&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구성 요소&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;액터 (Actor)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시스템으로부터 서비스를 요청하는 외부 요소로, 사람이나 외부 시스템을 의미함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체 (Object)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시지를 주고받는 주체&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;링크 (Link)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체들 간의 관계를 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;액터와 객체, 객체와 객체 간에 실선을 그어 표현함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시지 (Message)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체가 상호 작용을 위해 주고받는 내용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;화살표의 방향을 메시지를 받는 쪽으로 향하게 표현함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일정한 순서에 의해 처리되는 메시지의 경우 숫자로 순서를 표시함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;046. 상태 다이어그램 (State Diagram)&lt;/span&gt;&lt;/h4&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;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9GuxE/btsGSSAo8AW/7ewL9kooUJeaV8Rlb0WXak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9GuxE/btsGSSAo8AW/7ewL9kooUJeaV8Rlb0WXak/img.png&quot; data-alt=&quot;상태 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9GuxE/btsGSSAo8AW/7ewL9kooUJeaV8Rlb0WXak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9GuxE%2FbtsGSSAo8AW%2F7ewL9kooUJeaV8Rlb0WXak%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;502&quot; height=&quot;347&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;884&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상태 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;상태 다이어그램&lt;/b&gt;&lt;/span&gt;은 &lt;b&gt;객체들 사이에 발생하는 이벤트에 의한 객체들의 상태 변화를 그림으로 표현한 것&lt;/b&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구성 요소&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;상태&lt;/b&gt; (State)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체의 상태를 표현한 것으로 네모 박스에 상태를 표현함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;시작 상태&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상태의 시작을 표현한 것으로 검정 동그라미로 표현&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;종료 상태&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상태의 종료를 표현한 것으로 검정 동그라미에 동그라미 하나를 덧씌워 표현&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상태 전환&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상태 사이의 흐름, 변화를 화살표로 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트 (Event)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상태에 변화를 주는 현상&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이벤트에는 조건, 외부 신호, 시간의 흐름 등이 있음&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프레임 (Frame)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상태 다이어그램의 범위를 표현한 것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;047. 패키지 다이어그램&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRMdpe/btsGTjEf5HP/EggIgSdd7bA0TdYME9B1GK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRMdpe/btsGTjEf5HP/EggIgSdd7bA0TdYME9B1GK/img.png&quot; data-alt=&quot;패키지 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRMdpe/btsGTjEf5HP/EggIgSdd7bA0TdYME9B1GK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRMdpe%2FbtsGTjEf5HP%2FEggIgSdd7bA0TdYME9B1GK%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;564&quot; height=&quot;281&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;패키지 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;패키지 다이어그램&lt;/b&gt;&lt;/span&gt;은 &lt;b&gt;유스케이스나 클래스 등의 요소들을 그룹화한 패키지 간의 의존 관계를 표현한 것&lt;/b&gt;이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구성 요소&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;패키지&lt;/b&gt; (Package)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;객체들을 그룹화&lt;/b&gt;한 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;단순 표기법 - 패키지 안에 패키지 이름만 표현&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;확장 표기법 - 패키지 안에 요소까지 표현&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체 (Object)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;유스케이스, 클래스, 인터페이스, 테이블 등 패키지에 포함될 수 있는 다양한 요소들&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;의존 관계 (Dependency)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;패키지와 패키지, 패키지와 객체 간을 점선 화살표로 연결하여 표현함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;스테레오 타입을 이용해 의존 관계를 구체적으로 표현할 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;의존 관계의 표현 형태는 사용자가 임의로 작성할 수 있으며, 대표적으로 import와 access가 사용됨&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;import - 패키지에 포함된 객체들을 직접 가져와서 이용하는 관계&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;access - 인터페이스를 통해 패키지 내의 객체에 접근하여 이용하는 관계&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;048. 구조적 방법론&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;-- (생략) --&lt;/span&gt;&lt;/p&gt;</description>
      <category>정보처리기사</category>
      <author>Churnobyl</author>
      <guid isPermaLink="true">https://zeus2141.tistory.com/136</guid>
      <comments>https://zeus2141.tistory.com/136#entry136comment</comments>
      <pubDate>Wed, 24 Apr 2024 15:48:41 +0900</pubDate>
    </item>
  </channel>
</rss>