<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Jacob's Development Blog</title>
    <link>https://code-masterjung.tistory.com/</link>
    <description>제이콥의 개발 블로그</description>
    <language>ko</language>
    <pubDate>Sat, 25 Jul 2026 22:06:53 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>제이콥(JACOB)</managingEditor>
    <image>
      <title>Jacob's Development Blog</title>
      <url>https://tistory1.daumcdn.net/tistory/3047861/attach/0cc50573be5342788909391ea4f201c5</url>
      <link>https://code-masterjung.tistory.com</link>
    </image>
    <item>
      <title>[Javascript] 프로토타입(Prototype)</title>
      <link>https://code-masterjung.tistory.com/137</link>
      <description>&lt;h1 id=&quot;prototype&quot;&gt;Prototype&lt;/h1&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;프로토타입은 직접 참조 당하지 않고 프로토타입 링크(__proto__)를 통해 부모의 프로토타입 링크만 참조한다.&lt;/li&gt;
&lt;li&gt;프로토 타입 속성(property's)의 값은 객체이며, 프로토타입 체인 아래에 있는 객체에 상속되기를 원하는 속성과 메서드를 저장하기 위한 공간인 객체이다.&lt;/li&gt;
&lt;li&gt;그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;Object.is(),&lt;span&gt;&amp;nbsp;&lt;/span&gt;Object.keys()같은 메서드들은 프로토타입 객체 안에 정의되지 않았기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;Object.prototype에서 상속되는 객체 인스턴스나 객체 유형(object type)에 의해 상속되지 않는다.&lt;/li&gt;
&lt;li&gt;프로토타입 객체에는 속성과 메서드를 추가할 수 있으며, 원하는 객체로 완전히 교체할 수도 있다.&lt;/li&gt;
&lt;li&gt;함수를 생성자로 사용하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;new&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드와 함께 객체를 생성할 때, 객체는 생성자의 프로토타입을 가리키는 프로토타입 링크를 얻고 프로토타입 속성에 접근할 수 있다.&lt;/li&gt;
&lt;li&gt;객체 고유의 속성은 이름이 같은 프로토타입 속성보다 우선한다.&lt;/li&gt;
&lt;li&gt;hasOwnProperty()&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드를 통해 객체의 고유한 속성과&lt;span&gt;&amp;nbsp;&lt;/span&gt;prototype&lt;span&gt;&amp;nbsp;&lt;/span&gt;속성을 구분할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85-%EB%A7%81%ED%81%AC__proto__&quot; data-ke-size=&quot;size26&quot;&gt;프로토타입 링크(__proto__)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;프로토타입 링크를&lt;span&gt;&amp;nbsp;&lt;/span&gt;__proto__라고 편의상 적어놓긴 했지만 정식 명칭은&lt;span&gt;&amp;nbsp;&lt;/span&gt;[[prototype]]이고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;__proto__은 크롬을 포함한 최신 자바스크립트 환경에서 사용하는 이름이다.&lt;/p&gt;
&lt;h3 id=&quot;%EA%B7%B8%EB%9F%B0%EB%8D%B0-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85-%EB%A7%81%ED%81%AC%EB%A5%BC-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94%EA%B1%B8%EA%B9%8C&quot; data-ke-size=&quot;size23&quot;&gt;그런데 프로토타입 링크를 언제 사용되는걸까?&lt;/h3&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 class=&quot;angelscript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;const arr = [1, 2, 3];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;arr의&lt;span&gt;&amp;nbsp;&lt;/span&gt;prototype을 확인해 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;undefined이고 위 코드에서 확인할 수 있다시피 따로 프로토타입에 만들어 놓은 메서드는 없다. 하지만&lt;span&gt;&amp;nbsp;&lt;/span&gt;map이나&lt;span&gt;&amp;nbsp;&lt;/span&gt;filter등 내가 정의하지 않는 메서드를 사용할 수 있는데, 이 이유가 프로토타입 링크에 의한 탐색 때문인 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0dDWw/btrpkuvE0jm/5O7K8JiHkLEZo0IShOPAN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0dDWw/btrpkuvE0jm/5O7K8JiHkLEZo0IShOPAN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0dDWw/btrpkuvE0jm/5O7K8JiHkLEZo0IShOPAN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0dDWw%2FbtrpkuvE0jm%2F5O7K8JiHkLEZo0IShOPAN0%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;532&quot; height=&quot;288&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;288&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;pre class=&quot;angelscript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;const arr = [1, 2, 3];
arr.__proto__.map((v) =&amp;gt; console.log(v));
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;만약 위 코드와 같이 프로토타입 링크를 생략하지 않고 사용하면 자바스크립트의&lt;span&gt;&amp;nbsp;&lt;/span&gt;this가 점(.) 앞에 객체를 바라보도록 설계되었기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;this바인딩 문제가 발생한다.&lt;/p&gt;
&lt;h2 id=&quot;%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85-%EC%B2%B4%EC%9D%B4%EB%8B%9D&quot; data-ke-size=&quot;size26&quot;&gt;프로토타입 체이닝&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;프로토타입 링크(__proto__)를 따라 탐색하는 것을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;객체의 프로퍼티나 메서드를 사용할때, 해당 객체에 프로퍼티가 존재하지 않는다면 현재 객체의&lt;span&gt;&amp;nbsp;&lt;/span&gt;__proto__&lt;span&gt;&amp;nbsp;&lt;/span&gt;프로퍼티가 있는 체크하고 없다면 부모의&lt;span&gt;&amp;nbsp;&lt;/span&gt;__proto__&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체를 탐색하며, 최종&lt;span&gt;&amp;nbsp;&lt;/span&gt;Objects.prototype&lt;span&gt;&amp;nbsp;&lt;/span&gt;객체까지 없으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;undefined를 반환한다.&lt;/p&gt;
&lt;h2 id=&quot;%ED%99%94%EC%82%B4%ED%91%9C-%ED%95%A8%EC%88%98%EC%99%80-%EC%9D%BC%EB%B0%98-%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C%EC%9D%98-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85&quot; data-ke-size=&quot;size26&quot;&gt;화살표 함수와 일반 함수에서의 프로토타입&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 함수 선언문의 함수(function)은 프로토타입을 가지고 있으며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;constructor는 자기 자신이고&lt;span&gt;&amp;nbsp;&lt;/span&gt;__proto__&lt;span&gt;&amp;nbsp;&lt;/span&gt;속성으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;Object를 상속 받고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;261&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T4fDb/btrpcP2qcxp/KNVdytJw8GgEXXLEWMl29k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T4fDb/btrpcP2qcxp/KNVdytJw8GgEXXLEWMl29k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T4fDb/btrpcP2qcxp/KNVdytJw8GgEXXLEWMl29k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT4fDb%2FbtrpcP2qcxp%2FKNVdytJw8GgEXXLEWMl29k%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;261&quot; height=&quot;107&quot; data-origin-width=&quot;261&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&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;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;66&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k0WQR/btrpcP2qcw2/fxxUwTOEFgBIf3YcZ23HPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k0WQR/btrpcP2qcw2/fxxUwTOEFgBIf3YcZ23HPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k0WQR/btrpcP2qcw2/fxxUwTOEFgBIf3YcZ23HPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk0WQR%2FbtrpcP2qcw2%2FfxxUwTOEFgBIf3YcZ23HPk%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;275&quot; height=&quot;66&quot; data-origin-width=&quot;275&quot; data-origin-height=&quot;66&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;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/javascript/prototype&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ajdkfl6445.gitbook.io/study/javascript/prototype&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1640804019175&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;prototype - 콥 노트&quot; data-og-description=&quot;객체의 프로퍼티나 메서드를 사용할때, 해당 객체에 프로퍼티가 존재하지 않는다면 현재 객체의 __proto__ 프로퍼티가 있는 체크하고 없다면 부모의 __proto__ 객체를 탐색하며, 최종 Objects.prototype &quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/javascript/prototype&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/javascript/prototype&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/javascript/prototype&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/javascript/prototype&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;prototype - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;객체의 프로퍼티나 메서드를 사용할때, 해당 객체에 프로퍼티가 존재하지 않는다면 현재 객체의 __proto__ 프로퍼티가 있는 체크하고 없다면 부모의 __proto__ 객체를 탐색하며, 최종 Objects.prototype&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Javascript</category>
      <category>JavaScript</category>
      <category>js</category>
      <category>prototype</category>
      <category>자바스크립트</category>
      <category>프로토타입</category>
      <category>프로토타입 체이닝</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/137</guid>
      <comments>https://code-masterjung.tistory.com/137#entry137comment</comments>
      <pubDate>Sat, 1 Jan 2022 04:53:56 +0900</pubDate>
    </item>
    <item>
      <title>this, arrow-function</title>
      <link>https://code-masterjung.tistory.com/136</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;gift-g3089fc15e_1280.png&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HLkyI/btrpku3tRfy/Nzym5hreFb9qgiq3xqFoP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HLkyI/btrpku3tRfy/Nzym5hreFb9qgiq3xqFoP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HLkyI/btrpku3tRfy/Nzym5hreFb9qgiq3xqFoP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHLkyI%2Fbtrpku3tRfy%2FNzym5hreFb9qgiq3xqFoP0%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;247&quot; height=&quot;283&quot; data-filename=&quot;gift-g3089fc15e_1280.png&quot; data-origin-width=&quot;1115&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h1 id=&quot;this&quot;&gt;this&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바스크립트의 함수에는 메서드가 호출된 개체를 참조하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;this라는 특수한 변수가 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;this의 값은 함수 호출에 따라 동적으로 변하며, 이 때문에 작은 문제 상황에 직면하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;함수는 정적 범위(static/lexical scope)와 동적 범위의 두 가지 범위(scope)에서 실행된다. '정적 범위'는 함수를 둘러싸는 범위이고, '동적 범위'는 함수를 호출한 범위이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바스크립트에서 함수는 생성자, 메서드(객체의 일부), 함수(서브 루틴, subroutines)의 역할을 하는데, 함수가 서브루틴(이름이 겹쳐 아래부터는 서브루틴으로 명명)의 역할을 할 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;this가 동적으로 변해 문제가 생긴다. 서브루틴이 객체에 대해 호출되지 않기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;this의 값은&lt;span&gt;&amp;nbsp;&lt;/span&gt;strict&lt;span&gt;&amp;nbsp;&lt;/span&gt;모드에서 정의되지 않고 전역 범위로 설정된다. 이때문에 콜백함수를 사용하는게 어렵게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제를 보자.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;const person = {
  default: &quot;Hello&quot;,
  greet: function (names) {
    names.forEach(function (name) {
      console.log(`${this.default} ${name}`);
    });
  },
};

person.greet([&quot;world&quot;, &quot;heaven&quot;]);
// undefined world
// undefined heaven
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드를 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;names배열에&lt;span&gt;&amp;nbsp;&lt;/span&gt;forEach&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수에 서브 루틴을 전달하고 있다. 이때의 서브 루틴의&lt;span&gt;&amp;nbsp;&lt;/span&gt;this값은&lt;span&gt;&amp;nbsp;&lt;/span&gt;undefined이며, 외부 메서드인&lt;span&gt;&amp;nbsp;&lt;/span&gt;greet의&lt;span&gt;&amp;nbsp;&lt;/span&gt;this에는 접근할 수 없다.&lt;br /&gt;&amp;nbsp;그래서 이 문제를 해결하기 위해서는 이 서브루틴에 정적(lexical)&lt;span&gt;&amp;nbsp;&lt;/span&gt;this가 필요하며, 변수에&lt;span&gt;&amp;nbsp;&lt;/span&gt;this를 할당한 다음 클로저를 통해 서브루틴에 접근하는 방식을 사용해야 한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;const person = {
  default: &quot;Hello&quot;,
  greet: function (names) {
    const lexcicalThis = this;
    names.forEach(function (name) {
      console.log(`${lexcicalThis.default} ${name}`);
    });
  },
};

person.greet([&quot;world&quot;, &quot;heaven&quot;]);
// Hello world
// Hello heaven
&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;h2 id=&quot;%ED%99%94%EC%82%B4%ED%91%9C-%ED%95%A8%EC%88%98%EC%99%80-this&quot; data-ke-size=&quot;size26&quot;&gt;화살표 함수와 this&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;화살표 함수는 그 자체의 사용법을 매우 간단하다(&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions&quot;&gt;Arrow function - MDN&lt;/a&gt;). 다만&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;화살표 함수는 화살표 함수 자신의&lt;span&gt;&amp;nbsp;&lt;/span&gt;this&lt;span&gt;&amp;nbsp;&lt;/span&gt;값을 가질 수 없으며,&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;대신&lt;span&gt;&amp;nbsp;&lt;/span&gt;this는 화살표 함수를 둘러싸는 범위(정적 컨텍스트)의&lt;span&gt;&amp;nbsp;&lt;/span&gt;this를 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위의 예제를 화살표 함수를 이용하여 수정하면 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;const person = {
  default: &quot;Hello&quot;,
  greet: function (names) {
    names.forEach((name) =&amp;gt; {
      console.log(`${this.default} ${name}`);
    });
  },
};

person.greet([&quot;world&quot;, &quot;heaven&quot;]);
// Hello world
// Hello heaven
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;this%EC%9D%98-%EB%B0%94%EC%9D%B8%EB%94%A9%EC%97%90-%EB%94%B0%EB%A5%B8-%EB%B6%84%EB%A5%98&quot;&gt;this의 바인딩에 따른 분류&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 글은&lt;span&gt;&amp;nbsp;&lt;/span&gt;this를 좀 더 이해하기 쉽게 풀어쓴 내용이다. 조금 더 정적인(?) 내용을 원한다면 아래의 내용을 참고하자.&lt;/p&gt;
&lt;h2 id=&quot;%EA%B8%B0%EB%B3%B8-%EB%B0%94%EC%9D%B8%EB%94%A9default-binding&quot; data-ke-size=&quot;size26&quot;&gt;기본 바인딩(Default binding)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전역 공간에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;this는 전역 객체를 참조한다.&lt;/li&gt;
&lt;li&gt;[브라우저]에서 함수가 단독 호출되면, &quot;비엄격 모드&quot;에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;window가 바인딩되고, &quot;엄격 모드&quot;에서는 전역 객체는 바인딩 대상에서 제외되기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;undefined가 바인딩 된다.&lt;/li&gt;
&lt;li&gt;[Node]에서 함수가 단독 호출되면 this는 마찬가지로 전역 객체에 바인딩이 되는데, 노드 환경에서는 전역객체인&lt;span&gt;&amp;nbsp;&lt;/span&gt;global에 바인딩이 되고, 전역 코드에서의&lt;span&gt;&amp;nbsp;&lt;/span&gt;this는&lt;span&gt;&amp;nbsp;&lt;/span&gt;exports에 바인딩이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;%EC%95%94%EC%8B%9C%EC%A0%81-%EB%B0%94%EC%9D%B8%EB%94%A9implicity-binding&quot; data-ke-size=&quot;size26&quot;&gt;암시적 바인딩(Implicity binding)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수를 메서드로서 호출한 경우 this 는 메서드 호출 주체(메서드명 앞에 객체)를 참조한다.&lt;/li&gt;
&lt;li&gt;함수를 함수로써 호출한 경우 this 는 전역 객체를 참조한다. (메서드의 내부 함수도 동일)&lt;/li&gt;
&lt;li&gt;콜백 함수 내부에서의 this 는 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며, 정의 하지 않은 경우는 전역 객체를 참조한다.&lt;/li&gt;
&lt;li&gt;생성자 함수에서의 this 는 생성될 인스턴스를 참조한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;%EB%AA%85%EC%8B%9C%EC%A0%81-%EB%B0%94%EC%9D%B8%EB%94%A9explicit-binding&quot; data-ke-size=&quot;size26&quot;&gt;명시적 바인딩(Explicit binding)&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;call,&lt;span&gt;&amp;nbsp;&lt;/span&gt;apply&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;this&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 명시적으로 지정하면서 함수 또는 메서드를 호출한다.&lt;/li&gt;
&lt;li&gt;bind&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드는&lt;span&gt;&amp;nbsp;&lt;/span&gt;this&lt;span&gt;&amp;nbsp;&lt;/span&gt;및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만든다.&lt;/li&gt;
&lt;li&gt;요소를 순회하면서 콜백 함수를 반복 호출하는 내용의 일부 메서드는 별도의 인자로 this 를 받기도 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;%EC%B0%B8%EA%B3%A0-%EB%8D%94%EA%B8%80%EB%9F%AC%EC%8A%A4-%ED%81%AC%EB%9D%BD%ED%8F%AC%EB%93%9C%EA%B0%80-%EC%83%9D%EA%B0%81%ED%95%9C-this&quot; data-ke-size=&quot;size26&quot;&gt;참고) 더글러스 크락포드가 생각한 this&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;더글러스 크락포드는 그의 저서&lt;span&gt;&amp;nbsp;&lt;/span&gt;[How JavaScript Works]에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;this를 완전히 사용하지 않을 것을 권장한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;this가 없이 프로그램을 만드는 것은 생각보다 쉬우며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;this를 언어에서 제거해도 언어는 여전히 튜링 완전(Turing Complete)하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this는 대명사이다. 언어에&lt;span&gt;&amp;nbsp;&lt;/span&gt;this가 있는 것만으로도, 그 언어에 대해서 말하는 것이 얼마나 힘들지 모른다.&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;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/javascript/arrow-function-and-this&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ajdkfl6445.gitbook.io/study/javascript/arrow-function-and-this&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1640795440906&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;this, arrow-function - 콥 노트&quot; data-og-description=&quot;자바스크립트에서 함수는 생성자, 메서드(객체의 일부), 함수(서브 루틴, subroutines)의 역할을 하는데, 함수가 서브루틴(이름이 겹쳐 아래부터는 서브루틴으로 명명)의 역할을 할 때 this가 동적으&quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/javascript/arrow-function-and-this&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/javascript/arrow-function-and-this&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/javascript/arrow-function-and-this&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/javascript/arrow-function-and-this&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;this, arrow-function - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 함수는 생성자, 메서드(객체의 일부), 함수(서브 루틴, subroutines)의 역할을 하는데, 함수가 서브루틴(이름이 겹쳐 아래부터는 서브루틴으로 명명)의 역할을 할 때 this가 동적으&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Javascript</category>
      <category>this</category>
      <category>자바스크립트</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/136</guid>
      <comments>https://code-masterjung.tistory.com/136#entry136comment</comments>
      <pubDate>Fri, 31 Dec 2021 01:00:20 +0900</pubDate>
    </item>
    <item>
      <title>호이스팅(hoisting)</title>
      <link>https://code-masterjung.tistory.com/135</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;construction-gd708bc122_1280.png&quot; data-origin-width=&quot;1089&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgsIQR/btrpcM5fGZR/yXx6813BXMrI63VotNPNE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgsIQR/btrpcM5fGZR/yXx6813BXMrI63VotNPNE0/img.png&quot; data-alt=&quot;https://pixabay.com/vectors/construction-crane-hoist-hoisting-2027213/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgsIQR/btrpcM5fGZR/yXx6813BXMrI63VotNPNE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgsIQR%2FbtrpcM5fGZR%2FyXx6813BXMrI63VotNPNE0%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;330&quot; height=&quot;387&quot; data-filename=&quot;construction-gd708bc122_1280.png&quot; data-origin-width=&quot;1089&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://pixabay.com/vectors/construction-crane-hoist-hoisting-2027213/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;호이스팅(hoisting)&lt;/h2&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;좀 더 자세하게 말하자면 자바스크립트는 코드의 라인 순서와 관계없이 함수 선언식과 변수를 위한 메모리 공간을 먼저 확보한다. 따라서 함수와 변수(var)는 코드의 최상단으로 끌어 올려진 것(hoisted)처럼 보이게 되는데 이를 호이스팅이라고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수 전체가 호이스팅 된다.&lt;/li&gt;
&lt;li&gt;var로 변수 선언시, 선언은 호이스팅되지만, 할당을 호이스팅 되지 않는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변수 스코프는 함수&lt;/b&gt;이며, 함수만이 새 스코프를 도입할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%82%AC%EC%A0%84-%EC%A7%80%EC%8B%9D%EB%93%A4&quot; data-ke-size=&quot;size26&quot;&gt;호이스팅을 이해하기 위한 사전 지식들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;호이스팅을 이해하기 위해 조금 축약하여 개념만 쉽게 이해해 보자.&lt;/p&gt;
&lt;h3 id=&quot;%EC%8A%A4%EC%BD%94%ED%94%84scope&quot; data-ke-size=&quot;size23&quot;&gt;스코프(scope)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;스코프란 그 변수나 함수에 접근할 수 있는 위치를 말한다. 아래와 같은 예제가 있다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;x의 스코프는 함수&lt;span&gt;&amp;nbsp;&lt;/span&gt;foo()이다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;function foo() {
  var x;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;%EC%96%B4%ED%9C%98%EC%A0%81-%EC%8A%A4%EC%BD%94%ED%94%84&quot; data-ke-size=&quot;size23&quot;&gt;어휘적 스코프&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;어휘적 스코프는 정적 스코프(Static scope), 렉시컬 스코프(Lexical scope)라고도 불리며, 프로그램을 실행하지 않고 소스 코드에 존재하는대로 해석한 스코프이다.&lt;/p&gt;
&lt;h3 id=&quot;%EB%B3%80%EC%88%98-%EC%8A%A4%EC%BD%94%ED%94%84&quot; data-ke-size=&quot;size23&quot;&gt;변수 스코프&lt;/h3&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&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;변수 스코프는 함수&lt;/b&gt;이며, 함수만이 새 스코프를 도입할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;%EC%A4%91%EC%B2%A9-%EC%8A%A4%EC%BD%94%ED%94%84&quot; data-ke-size=&quot;size23&quot;&gt;중첩 스코프&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;스코프가 변수의 스코프 안에 중첩되어 있으면 그 변수는 해당 스코프 전체에서 접근할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;function foo(arg) {
  function bar() {
    console.log(`Hi, ${arg}`);
  }
  bar();
}

foo(&quot;jacob&quot;); // Hi, jacob
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;shadowing&quot; data-ke-size=&quot;size23&quot;&gt;Shadowing&lt;/h3&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 class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;var x = &quot;global&quot;;
function f() {
  var x = &quot;local&quot;;
  console.log(x); // local
}
f(); // local
console.log(x); // global
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;var%EB%A1%9C-%EB%B3%80%EC%88%98-%EC%84%A0%EC%96%B8%EC%8B%9C-%EC%84%A0%EC%96%B8%EC%9D%80-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85%EB%90%98%EC%A7%80%EB%A7%8C-%ED%95%A0%EB%8B%B9%EC%9D%84-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85-%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; data-ke-size=&quot;size26&quot;&gt;var로 변수 선언시, 선언은 호이스팅되지만, 할당을 호이스팅 되지 않는다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;자바스크립트는 변수 선언을 모드 끌어올려 직접적 스코프 맨 앞으로 옮기며, 선언하기전 변수에 접근할 수 있게 된다.&lt;br /&gt;&amp;nbsp;물론 할당은 호이스팅되지 않기 때문에 초기화 전에 변수에 접근했다면 값은&lt;span&gt;&amp;nbsp;&lt;/span&gt;undefined이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;function f() {
  console.log(a); // undefined
  var a = 1;
  console.log(a); // 1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 자바스크립트 엔진은 아래와 같이 실행한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;function f() {
  var a; // 호이스팅!
  console.log(a); // undefined
  a = 1;
  console.log(a); // 1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%EA%B0%80%EB%B3%80-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85&quot; data-ke-size=&quot;size26&quot;&gt;가변 호이스팅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제를 예측해 보자.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;var a = 123;

function f() {
  alert(a);
  var a = 1;
  alert(a);
}

f();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 결과가 나올까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;대부분 첫번째&lt;span&gt;&amp;nbsp;&lt;/span&gt;alert창에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;123이 나오고 두번째&lt;span&gt;&amp;nbsp;&lt;/span&gt;alert창에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;1이 나올거라고 예측할 수 있겠지만, 위에서 배운 내용을 토대로 살펴본다면 첫번째는&lt;span&gt;&amp;nbsp;&lt;/span&gt;undeinfed, 두번째는&lt;span&gt;&amp;nbsp;&lt;/span&gt;1이 나오게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 변수의 스코프는 함수이기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;var a = 1는 호이스팅되어,&lt;span&gt;&amp;nbsp;&lt;/span&gt;var a의 선언은 해당 함수(f())의 최 상단으로 이동하게 된다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;var a = 123;

function f() {
  var a;
  alert(a);
  a = 1;
  alert(a);
}

f();
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;외부 변수인&lt;span&gt;&amp;nbsp;&lt;/span&gt;var a = 123은 쉐도잉이 되어, 내부 변수인(f()의 지역변수)&lt;span&gt;&amp;nbsp;&lt;/span&gt;var a;가&lt;span&gt;&amp;nbsp;&lt;/span&gt;var a = 123;보다 우선되어, 첫번째&lt;span&gt;&amp;nbsp;&lt;/span&gt;alert()에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;undefined가 나오며, 두번째&lt;span&gt;&amp;nbsp;&lt;/span&gt;alert()에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;a=1로 초기화가 된 이후이기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;1이 나오게 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;const-let&quot;&gt;const, let&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ES5를 아직 사용하고 있다면 위의 호이스팅의 동작을 주의 깊게 봐야한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;ES6에서는 변수를 선언하는 동안 추가 범위가 제공된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;const,&lt;span&gt;&amp;nbsp;&lt;/span&gt;let은 블록 범위(block-scoped)이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;%EB%B8%94%EB%A1%9D%EC%9D%B4-%EB%AD%94%EB%8D%B0%EC%9A%94&quot; data-ke-size=&quot;size26&quot;&gt;블록이 뭔데요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;블록 범위라는 말은 많이 들어봤지만, java나 c, c++에 대한 배경 지식이 없다면 블록이라는게 생소할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;var a = 1;
{
  let a = 2;
  console.log(a); // 2
}
console.log(a); //1
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드를 보면 줄괄호를 넣어놨는데, 저 중괄호 사이의 범위가 바로 블록이다. 위에서 말한 언어에서 범위를 정의하기 위해 블록을 도입했고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Javascript에서도 연결된 범위가 없기 때문에 관용적으로 블록을 도입하고 싶었다.&lt;br /&gt;&amp;nbsp;그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;ES6에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;const나&lt;span&gt;&amp;nbsp;&lt;/span&gt;let&lt;span&gt;&amp;nbsp;&lt;/span&gt;키워드를 통해 블록 범위 변수를 생성할 수 있도록 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 예를 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;a블록 내부에 생성된 변수는 블록 내에서 사용할 수 있다. 일반적으로 블록 범위 변수를 선언할 때는 블록 상단에 선언을 추가하는 것이 좋다.&lt;/p&gt;
&lt;h3 id=&quot;%EC%B0%B8%EA%B3%A0-tdz&quot; data-ke-size=&quot;size23&quot;&gt;참고) TDZ&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일시적 사각지대(TDZ,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Temporal Dead Zone)는 블록 시작부터 초기화가 완료될 때까지의 위치를 말한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;{
  // TDZ starts at beginning of scope
  console.log(bar); // undefined
  console.log(foo); // ReferenceError
  var bar = 1;
  let foo = 2; // End of TDZ (for foo)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;TDZ는 코드가 작성된 위치가 아닌 실행 순서에 따라 영역이 달라진다는 것이 중요하다.&lt;/p&gt;
&lt;pre class=&quot;go&quot; data-role=&quot;codeBlock&quot; data-info=&quot;javascript&quot;&gt;&lt;code&gt;{
  // TDZ starts at beginning of scope
  const func = () =&amp;gt; console.log(letVar); /

  // Within the TDZ letVar access throws `ReferenceError`

  let letVar = 3; // End of TDZ (for letVar)
  func(); // Called outside TDZ!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 코드와 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;let&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수가 선언되기 전에 해당 변수를 사용하는 함수를 작성했더라도,&lt;span&gt;&amp;nbsp;&lt;/span&gt;TDZ&lt;span&gt;&amp;nbsp;&lt;/span&gt;외부에서 해당 함수가 호출되었기 때문에 잘 동작하는 것을 볼 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;const-let-1&quot; data-ke-size=&quot;size26&quot;&gt;const, let&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ES6에서 해당 키워드로 선언된 변수는 블록 범위로 호이스트 되지만, 선언 전에 변수를 참조하는 것은 오류로 된다.&lt;/li&gt;
&lt;li&gt;const의 경우 값에 대한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;읽기 전용 참조&lt;/b&gt;를 만든다. 이것은 참조가 보유한 값이 변경 불가능하다는 것을 의미 하진 않으며, 하지만 변수 식별자를 재할당 할 수는 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;%EC%B0%B8%EA%B3%A0-const%EB%82%98-let%EC%9D%80-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85-%EB%90%98%EB%8A%94%EB%8D%B0-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0-%EC%88%98-%EC%97%86%EC%A7%80&quot; data-ke-size=&quot;size23&quot;&gt;참고) const나 let은 호이스팅 되는데 왜 사용할 수 없지?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 이 부분은 궁금해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;v8의 코드를 조금 확인해 봤다. 물론&lt;span&gt;&amp;nbsp;&lt;/span&gt;c++을 모르지만 테스트 코드를 통해서 해당 내용을 추측해 보자면&lt;span&gt;&amp;nbsp;&lt;/span&gt;variableMode가&lt;span&gt;&amp;nbsp;&lt;/span&gt;var라면 메모리에 할당시키지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;variableProxy에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;variableMode가&lt;span&gt;&amp;nbsp;&lt;/span&gt;let이나&lt;span&gt;&amp;nbsp;&lt;/span&gt;const라면 해당 위치를 초기화 해버려서 선언했어도 사용을 못하게 막아져 있었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/javascript/hoisting&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ajdkfl6445.gitbook.io/study/javascript/hoisting&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1640794700575&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;호이스팅 - 콥 노트&quot; data-og-description=&quot;외부 변수인 var a = 123은 쉐도잉이 되어, 내부 변수인(f()의 지역변수) var a;가 var a = 123;보다 우선되어, 첫번째 alert()에서는 undefined가 나오며, 두번째 alert()에서는 a=1로 초기화가 된 이후이기 때문&quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/javascript/hoisting&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/javascript/hoisting&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/javascript/hoisting&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/javascript/hoisting&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;호이스팅 - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;외부 변수인 var a = 123은 쉐도잉이 되어, 내부 변수인(f()의 지역변수) var a;가 var a = 123;보다 우선되어, 첫번째 alert()에서는 undefined가 나오며, 두번째 alert()에서는 a=1로 초기화가 된 이후이기 때문&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Javascript</category>
      <category>hositing</category>
      <category>JavaScript</category>
      <category>자바스크립트</category>
      <category>호이스팅</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/135</guid>
      <comments>https://code-masterjung.tistory.com/135#entry135comment</comments>
      <pubDate>Thu, 30 Dec 2021 01:22:48 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 도커 입문하기 5 - 이미지 업데이트</title>
      <link>https://code-masterjung.tistory.com/134</link>
      <description>&lt;h1 id=&quot;%EB%8F%84%EC%BB%A4-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%ED%95%98%EA%B8%B0&quot;&gt;도커 이미지 업데이트 하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 위에서 말한 고민에는 이런 문제점 들이 포함된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지금은 애플리케이션이 작아서 그나마 문제가 없지만, 변경 범위나 내용에 상관없이 모든 종속성을 다시 설치해야 한다.&lt;/li&gt;
&lt;li&gt;다시 빌드하고 다시 실행하는 과정이 너무 번거롭다.&lt;/li&gt;
&lt;li&gt;[2]번의 과정만 사실 있는게 아니라, 이전 컨테이너가 아직 실행 중이기 때문에 이전 컨테이너가 3000번 포트를 사용하고 있다. 따라서 수정한 컨테이너를 실행하려면 이전 컨테이너를 중지해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 어떻게 이 부분을 효율적으로 개선할 수 있을까?&lt;/p&gt;
&lt;h2 id=&quot;1-%ED%95%AD%EC%83%81-%EC%A0%84%EC%B2%B4-%EC%A2%85%EC%86%8D%EC%84%B1-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EA%B0%9C%EC%84%A0-dockerfile-%EC%88%98%EC%A0%95%ED%95%98%EC%9E%90&quot; data-ke-size=&quot;size26&quot;&gt;1. 항상 전체 종속성 다운로드 개선 - Dockerfile 수정하자!&lt;/h2&gt;
&lt;pre class=&quot;dockerfile&quot; data-role=&quot;codeBlock&quot; data-info=&quot;Dockerfile&quot;&gt;&lt;code&gt;# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
WORKDIR /app
COPY . .
RUN yarn install --production
CMD [&quot;node&quot;, &quot;src/index.js&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 내용은 기존에 만들었던 파일이다. 조금 수정해 보자.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot; data-role=&quot;codeBlock&quot; data-info=&quot;Dockerfile&quot;&gt;&lt;code&gt;# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python2 g++ make
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --production
COPY . .
CMD [&quot;node&quot;, &quot;src/index.js&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;COPY package.json yarn.lock ./&lt;span&gt;&amp;nbsp;&lt;/span&gt;단계에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;변화가 있을 경우에만 다음 단계에서 종속성을 다시 받게되고, 아무런 변화가 없다면 캐시(cache)를 이용해 이 과정이 생략&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정하고 다시 실행해 보자.&lt;/p&gt;
&lt;h3 id=&quot;%EC%B5%9C%EC%B4%88-%EB%B9%8C%EB%93%9C%EC%8B%9C-%EC%A2%85%EC%86%8D%EC%84%B1-%EC%84%A4%EC%B9%98%EA%B3%BC%EC%A0%95%EC%97%90%EC%84%9C-696s-%EC%86%8C%EC%9A%94&quot; data-ke-size=&quot;size23&quot;&gt;최초 빌드시 종속성 설치과정에서 69.6s 소요&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;968&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hh0Fo/btronIO5iqi/wVEpViAR98luZIYteRmwn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hh0Fo/btronIO5iqi/wVEpViAR98luZIYteRmwn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hh0Fo/btronIO5iqi/wVEpViAR98luZIYteRmwn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHh0Fo%2FbtronIO5iqi%2FwVEpViAR98luZIYteRmwn1%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;1190&quot; height=&quot;968&quot; data-origin-width=&quot;1190&quot; data-origin-height=&quot;968&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;%EC%A2%85%EC%86%8D%EC%84%B1%EC%97%90-%EB%B3%80%ED%99%94-%EC%97%86%EB%8A%94-%EB%82%B4%EC%9A%A9%EB%A7%8C-%EC%88%98%EC%A0%95%EC%8B%9C-0s-%EC%86%8C%EC%9A%94&quot; data-ke-size=&quot;size23&quot;&gt;종속성에 변화 없는 내용만 수정시 0s 소요&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce4jAR/btroi5EhEX6/diWDNAUvxJaTPPjFXTcSo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce4jAR/btroi5EhEX6/diWDNAUvxJaTPPjFXTcSo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce4jAR/btroi5EhEX6/diWDNAUvxJaTPPjFXTcSo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce4jAR%2Fbtroi5EhEX6%2FdiWDNAUvxJaTPPjFXTcSo0%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;1202&quot; height=&quot;964&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;964&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%9D%B4%EB%B2%88%EC%97%90%EB%8A%94-%EC%B6%94%EA%B0%80-%EC%A2%85%EC%86%8D%EC%84%B1%EC%9D%84-%EC%84%A4%EC%B9%98%ED%95%98%EC%97%AC-%EB%B3%80%ED%99%94%EB%A5%BC-%EC%A4%AC%EC%9D%84-%EA%B2%BD%EC%9A%B0&quot; data-ke-size=&quot;size23&quot;&gt;그리고 이번에는 추가 종속성을 설치하여, 변화를 줬을 경우&lt;/h3&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;871&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ca2gbF/btromRyvU1F/JZk3GwZpZ1oNsWSDGmosm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ca2gbF/btromRyvU1F/JZk3GwZpZ1oNsWSDGmosm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ca2gbF/btromRyvU1F/JZk3GwZpZ1oNsWSDGmosm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fca2gbF%2FbtromRyvU1F%2FJZk3GwZpZ1oNsWSDGmosm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;871&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;871&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;%EC%B0%B8%EA%B3%A0-%EC%97%AC%EB%9F%AC-%ED%8C%8C%EC%9D%BC-copy%ED%95%98%EB%8A%94-%EB%B2%95&quot; data-ke-size=&quot;size23&quot;&gt;참고 - 여러 파일 COPY하는 법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방법은 여러개가 있는걸로 알지만 내가 사용하는 방법은 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot; data-role=&quot;codeBlock&quot; data-info=&quot;Dockerfile&quot;&gt;&lt;code&gt;# ...
COPY package.json yarn.lock ./
#...
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 쭈욱 나열해 주고, 기존에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;.만 사용했지만 멀티 파일의 경우 뒤에 역슬러시를 하나 더 붙여 줘야 한다.&lt;/p&gt;
&lt;h2 id=&quot;2-%EB%B9%8C%EB%93%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EA%B0%9C%EC%84%A0%ED%95%98%EC%9E%90%EC%88%98%EC%A0%95-%EB%8B%A4%EC%8B%9C-%EB%B9%8C%EB%93%9C-%EB%8B%A4%EC%8B%9C-%EC%8B%A4%ED%96%89-docker-volume&quot; data-ke-size=&quot;size26&quot;&gt;2. 빌드 프로세스를 개선하자(수정 -&amp;gt; 다시 빌드 -&amp;gt; 다시 실행) - Docker Volume&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 1번 과정을 통해 비효율적인 종속성 설치 문제를 해결했지만, 여전히 수정 사항이 있을때마다 이미지를 다시 빌드하고, 컨테이너를 다시 실행해야만 변경된 내용이 반영이 되고 있다.&lt;br /&gt;&amp;nbsp;이러한 작업은 시간을 오래 소요하면서도 이미지를 자주 빌드해야 해서 리소스도 많이 소모된다.&lt;/p&gt;
&lt;h3 id=&quot;docker-volume%EC%9D%B4%EB%9E%80&quot; data-ke-size=&quot;size23&quot;&gt;Docker Volume이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;도커 볼륨이란 도커 컨테이너에서 호스트 디렉토리에 있는 파일들을 참조해서 사용하는 방식이다. (&lt;a href=&quot;https://docs.docker.com/storage/volumes/&quot;&gt;공식 문서&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;542&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJi8jp/btroj7Iosgt/6nHsExY8qUA9leP9JxOTW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJi8jp/btroj7Iosgt/6nHsExY8qUA9leP9JxOTW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJi8jp/btroj7Iosgt/6nHsExY8qUA9leP9JxOTW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJi8jp%2Fbtroj7Iosgt%2F6nHsExY8qUA9leP9JxOTW0%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;1036&quot; height=&quot;542&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;542&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;도커 볼륨을 이용하면 기존에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;COPY&lt;span&gt;&amp;nbsp;&lt;/span&gt;지시자를 통해 호스트의 디렉토리에 있는 파일을 그대로 도커 컨테이너로 복사한 것을, 복사하지 않고 도커 컨테이너에서 호스트의 디렉토리에 있는 파일을 계속 참조해서 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;계속 참조하고 있으므로 소스 코드를 변경해도 다시&lt;span&gt;&amp;nbsp;&lt;/span&gt;COPY를 사용해 이미지를 빌드할 필요가 없다.&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&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.docker.com/storage/bind-mounts/&quot;&gt;bind-mounts&lt;/a&gt;라는 방법을 사용했으나, 도커 볼륨에 비해 제한적이고 맥이나 윈도우에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;bind-mounts보다 더 나은 성능을 내기 때문에 도커 볼륨을 많이 사용한다.&lt;/li&gt;
&lt;li&gt;bind-mounts는 호스트의 디렉토리 구조와&lt;span&gt;&amp;nbsp;&lt;/span&gt;OS에 의존하지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;volume의 경우 완전히&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker에 의해 관리된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;%EB%AA%85%EB%A0%B9%EC%96%B4-v-or-mount&quot; data-ke-size=&quot;size23&quot;&gt;명령어&lt;span&gt;&amp;nbsp;&lt;/span&gt;-v&lt;span&gt;&amp;nbsp;&lt;/span&gt;or&lt;span&gt;&amp;nbsp;&lt;/span&gt;--mount&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;볼륨을 생성할 때는 두 가지 옵션을 이용하여 볼륨을 생성할 수 있다. 결론적으로 볼륨을 생성한다는 것은 같으나, 사용 방식에 있어 차이점이 있어 둘 다 알아만 놓으면 될거 같다.(&lt;a href=&quot;https://docs.docker.com/storage/volumes/#choose-the--v-or---mount-flag&quot;&gt;공식 문서&lt;/a&gt;)&lt;/p&gt;
&lt;h3 id=&quot;%EB%8F%84%EC%BB%A4-%EB%B3%BC%EB%A5%A8%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%8B%A4%ED%96%89&quot; data-ke-size=&quot;size23&quot;&gt;도커 볼륨을 사용해 애플리케이션 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 나왔던 공식 예제를 도커 볼륨을 통해 실행해 보자.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ docker run -dp 3000:3000 -v /app/node_modules -v $(pwd):/app getting-started
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 컨테이너가 제대로 시작된 것을 확인할 수 있다.&lt;/p&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;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce8QYk/btrofGke3ha/eZt9QTDIDK6kSxEvVvWqL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce8QYk/btrofGke3ha/eZt9QTDIDK6kSxEvVvWqL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce8QYk/btrofGke3ha/eZt9QTDIDK6kSxEvVvWqL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce8QYk%2FbtrofGke3ha%2FeZt9QTDIDK6kSxEvVvWqL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;152&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매핑한 주소로 이동해도 화면도 잘 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQbqmt/btronIO5ipc/a1TKm0nrncXqW5zZsEPdo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQbqmt/btronIO5ipc/a1TKm0nrncXqW5zZsEPdo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQbqmt/btronIO5ipc/a1TKm0nrncXqW5zZsEPdo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQbqmt%2FbtronIO5ipc%2Fa1TKm0nrncXqW5zZsEPdo1%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;558&quot; height=&quot;172&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;172&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;h3 id=&quot;%EC%B0%B8%EA%B3%A0-docker-run-dp-30003000-v-appnode_modules-v-pwdapp-getting-started-%EC%99%9C-%EC%9D%B4%EB%A0%87%EA%B2%8C-%EB%AA%85%EB%A0%B9%ED%96%88%EB%82%98&quot; data-ke-size=&quot;size23&quot;&gt;참고) docker run -dp 3000:3000 -v /app/node_modules -v $(pwd):/app getting-started 왜 이렇게 명령했나?&lt;/h3&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;1280&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SYpRx/btroj6CJ9Nh/3ra0NBLuGpHHSZk1t346eK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SYpRx/btroj6CJ9Nh/3ra0NBLuGpHHSZk1t346eK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SYpRx/btroj6CJ9Nh/3ra0NBLuGpHHSZk1t346eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSYpRx%2Fbtroj6CJ9Nh%2F3ra0NBLuGpHHSZk1t346eK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;571&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;%EC%9E%AC%EB%B9%8C%EB%93%9C-%EC%97%86%EC%9D%B4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%9E%AC%EC%8B%9C%EC%9E%91%EC%9C%BC%EB%A1%9C-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%EB%90%98%EB%8A%94%EC%A7%80-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot; data-ke-size=&quot;size23&quot;&gt;재빌드 없이 컨테이너 재시작으로 업데이트 되는지 확인하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;You have no todo items yet! Add one above!&lt;span&gt;&amp;nbsp;&lt;/span&gt;텍스트를 수정 후 컨테이너를 재시작 해보자.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 동작중인 컨테이너 중지&lt;/li&gt;
&lt;li&gt;변경할 내용 코드에 반영 후 변경 사항 저장&lt;/li&gt;
&lt;li&gt;도커 볼륨 재실행&lt;/li&gt;
&lt;li&gt;기존 페이지 새로고침&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;1104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvS8yv/btrocCiRaxK/P2IKkRuk2YKsLL7cYRvOcK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvS8yv/btrocCiRaxK/P2IKkRuk2YKsLL7cYRvOcK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvS8yv/btrocCiRaxK/P2IKkRuk2YKsLL7cYRvOcK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bvS8yv/btrocCiRaxK/P2IKkRuk2YKsLL7cYRvOcK/img.gif&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;1090&quot; height=&quot;1104&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;1104&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;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/update-image&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ajdkfl6445.gitbook.io/study/devops/docker/update-image&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1639932961426&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;도커 이미지 업데이트 - 콥 노트&quot; data-og-description=&quot;위 1번 과정을 통해 비효율적인 종속성 설치 문제를 해결했지만, 여전히 수정 사항이 있을때마다 이미지를 다시 빌드하고, 컨테이너를 다시 실행해야만 변경된 내용이 반영이 되고 있다. 이러한&quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/update-image&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/update-image&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/update-image&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/update-image&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;도커 이미지 업데이트 - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;위 1번 과정을 통해 비효율적인 종속성 설치 문제를 해결했지만, 여전히 수정 사항이 있을때마다 이미지를 다시 빌드하고, 컨테이너를 다시 실행해야만 변경된 내용이 반영이 되고 있다. 이러한&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Docker</category>
      <category>docker</category>
      <category>dockervolume</category>
      <category>image</category>
      <category>update</category>
      <category>도커볼륨</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/134</guid>
      <comments>https://code-masterjung.tistory.com/134#entry134comment</comments>
      <pubDate>Mon, 20 Dec 2021 01:57:10 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 도커 입문하기 4 - 도커 이미지 만들기</title>
      <link>https://code-masterjung.tistory.com/133</link>
      <description>&lt;h1 id=&quot;%EB%8F%84%EC%BB%A4-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0&quot;&gt;도커 이미지 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;도커 컨테이너는&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker create [이미지 이름]&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령을 통해 만들 수 있다. 아래에서는 도커 이미지를 만드는 방법 먼저 배워보자.&lt;br /&gt;예제 코드는 공식 홈페이지에 있는 것을 사용할 예정이다. [&lt;a href=&quot;https://github.com/docker/getting-started/tree/master/app&quot;&gt;getting-started&lt;/a&gt;]&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUXmhc/btrnR2gdx22/nAmaeF4jvngdshGhFca7KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUXmhc/btrnR2gdx22/nAmaeF4jvngdshGhFca7KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUXmhc/btrnR2gdx22/nAmaeF4jvngdshGhFca7KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUXmhc%2FbtrnR2gdx22%2FnAmaeF4jvngdshGhFca7KK%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;799&quot; height=&quot;808&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;1-%EB%8F%84%EC%BB%A4-%ED%8C%8C%EC%9D%BCdokerfile-%EB%A7%8C%EB%93%A4%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;1. 도커 파일(Dokerfile) 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 파일은 컨테이너 이미지를 만드는데 사용되는 단순한 텍스트 기반 지침 스크립트이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;Dockerfile이라는 이름의 파일을 만들자.&lt;br /&gt;만약&lt;span&gt;&amp;nbsp;&lt;/span&gt;vscode를 사용중이라면&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker extenstion을 설치하면 자동 완성 기능도 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot; data-role=&quot;codeBlock&quot; data-info=&quot;Dockerfile&quot;&gt;&lt;code&gt;# syntax=docker/dockerfile:1
FROM node:12-alpine
RUN apk add --no-cache python3 g++ make
WORKDIR /app
COPY . .
RUN yarn install --production
CMD [&quot;node&quot;, &quot;src/index.js&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 명령어는 하나의 레이어를 생성한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FROM: 베이스 이미지이다. 간단하게 말해&lt;span&gt;&amp;nbsp;&lt;/span&gt;OS라고 생각하면 되며, 참고로&lt;span&gt;&amp;nbsp;&lt;/span&gt;Alpine은 매우 작은&lt;span&gt;&amp;nbsp;&lt;/span&gt;Linux&lt;span&gt;&amp;nbsp;&lt;/span&gt;배포판인&lt;span&gt;&amp;nbsp;&lt;/span&gt;Alpine Linux를 기반으로 하는 기본 이미지이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;node:12-alpine의 의미는 노드 12에 설치된 알파인 기반 이미지를 의미하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;node:alpine&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용시 최신 버전의 알파인을 사용할 수 있다. (지금은 테스트이기 때문에 무거운 베이스 이미지 대신 알파인을 사용한 것이다.)&lt;/li&gt;
&lt;li&gt;RUN: 도커 이미지가 생성되기 전에 수행할 쉘 명령어이다.&lt;/li&gt;
&lt;li&gt;COPY:&lt;span&gt;&amp;nbsp;&lt;/span&gt;Docker클라이언트의 현재 디렉토리에서 파일을 추가한다. (COPY (source) (dist))&lt;/li&gt;
&lt;li&gt;CMD: 컨테이너 내에서 실행할 명령을 지정하며, 도커 파일 내에서 한 번만 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 실행하고 컨테이너를 생성할 때 베이스 이미지(레이어) 위에 쓰기 가능한 새 레이어(&quot;컨테이너 레이어&quot;)를 추가한다.&lt;/p&gt;
&lt;h2 id=&quot;2-docker-build-%EB%AA%85%EB%A0%B9%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%EB%B9%8C%EB%93%9C%ED%95%98%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;2. docker build 명령을 사용하여 컨테이너 이미지를 빌드하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령을 통해 도커파일을 사용하여 새 컨테이너 이미지를 빌드할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ docker build -t getteing-started .
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-t: 이 플래그는 이미지에 태그를 지정해준다. 사람이 읽을 수 있는 최종 이미지의 이름으로 생각하면 되고, 이미지 이름을 지정했기 때문에 컨테이너를 실행할 때 해당 이미지를 참조할 수 있다.&lt;/li&gt;
&lt;li&gt;.: 현재 디렉터리에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;Dockerfile을 찾아야 한다고 알려준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령을 실행하면 이미지가 빌드 된다.&lt;/p&gt;
&lt;h3 id=&quot;%EC%B0%B8%EA%B3%A0-error-cant-find-python-executable-python-you-can-set-the-python-env-variable&quot; data-ke-size=&quot;size23&quot;&gt;참고) Error: Can't find Python executable &quot;python&quot;, you can set the PYTHON env variable&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위와 같은 에러가 발생했을 수도 있다. 일단 검색을 해본 결과 나와 같은 mac M1 프로세스 노트북에서 나타나는거 같다. 해결 방법은 내가 에러를 잡기 위해 노력한 시간보다는 차암 쉽다..&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 Dockerfile 스크립트에서 python3 -&amp;gt; python2로 바꿔주자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/docker/getting-started/issues/222#issuecomment-979057033&quot;&gt;https://github.com/docker/getting-started/issues/222#issuecomment-979057033&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;3-docker-run-%EB%A7%8C%EB%93%A0-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A1%9C-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;3. docker run - 만든 이미지로 애플리케이션 실행하기.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어를 통해 컨테이너를 시작한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ docker run -dp 3000:3000 getting-started
$ docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-dp: 이 플래그는 새 컨테이너를 분리된 모드(백그라운드)에서 실행하고 호스트의 포트&lt;span&gt;&amp;nbsp;&lt;/span&gt;3000과 컨테이너의 포트&lt;span&gt;&amp;nbsp;&lt;/span&gt;3000&lt;span&gt;&amp;nbsp;&lt;/span&gt;간에 매핑을 생성하고 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;포트 매핑이 없으면 애플리케이션에 접근할 수 없다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQl60P/btrnOQVhdJb/AjD6A3qufkhfSDyVNhPJT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQl60P/btrnOQVhdJb/AjD6A3qufkhfSDyVNhPJT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQl60P/btrnOQVhdJb/AjD6A3qufkhfSDyVNhPJT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQl60P%2FbtrnOQVhdJb%2FAjD6A3qufkhfSDyVNhPJT0%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;980&quot; height=&quot;108&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;status가&lt;span&gt;&amp;nbsp;&lt;/span&gt;Up으로 잘 실행되고 있음을 알 수 있다. 이제 그럼 브라우저에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;http://localhost:3000을 열어 확인해 보자.&lt;/p&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;199&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kNzO5/btrnPk920x5/57e9TROi5UbRzxdwKCMtP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kNzO5/btrnPk920x5/57e9TROi5UbRzxdwKCMtP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kNzO5/btrnPk920x5/57e9TROi5UbRzxdwKCMtP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkNzO5%2FbtrnPk920x5%2F57e9TROi5UbRzxdwKCMtP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;199&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;199&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 대시보드에서도 잘 실행되고 있음을 한번 더 확인할 수 있따.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GVje2/btrnHSmsJeM/OihTE2U6TEp8E23XTzbyXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GVje2/btrnHSmsJeM/OihTE2U6TEp8E23XTzbyXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GVje2/btrnHSmsJeM/OihTE2U6TEp8E23XTzbyXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGVje2%2FbtrnHSmsJeM%2FOihTE2U6TEp8E23XTzbyXk%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;1270&quot; height=&quot;720&quot; data-origin-width=&quot;1270&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에 있는 예제를 통해 기본 예제에 대한 실습을 해봤다. 이제 다음 글에서 좀 더 도커에 대해 알아보자.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/make-image&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ajdkfl6445.gitbook.io/study/devops/docker/make-image&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1639420749404&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;도커 이미지 생성하기 - 콥 노트&quot; data-og-description=&quot;FROM: 베이스 이미지이다. 간단하게 말해 OS라고 생각하면 되며, 참고로 Alpine은 매우 작은 Linux 배포판인 Alpine Linux를 기반으로 하는 기본 이미지이다. node:12-alpine의 의미는 노드 12에 설치된 알파인&quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/make-image&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/make-image&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/make-image&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/make-image&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;도커 이미지 생성하기 - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;FROM: 베이스 이미지이다. 간단하게 말해 OS라고 생각하면 되며, 참고로 Alpine은 매우 작은 Linux 배포판인 Alpine Linux를 기반으로 하는 기본 이미지이다. node:12-alpine의 의미는 노드 12에 설치된 알파인&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Docker</category>
      <category>docker</category>
      <category>docker image</category>
      <category>도커</category>
      <category>도커이미지</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/133</guid>
      <comments>https://code-masterjung.tistory.com/133#entry133comment</comments>
      <pubDate>Tue, 14 Dec 2021 03:39:48 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 도커 입문하기 3 - 기본 명령어 및 실습</title>
      <link>https://code-masterjung.tistory.com/132</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/docker/&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;h3 id=&quot;docker-run&quot; data-ke-size=&quot;size23&quot;&gt;docker run&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이미지를 생성 및 실행해 주는 명령어이다. 원래는 분리된 생성(docker create)와 실행(docker start)을 한번에 실행시키는 명령어이다.&lt;/p&gt;
&lt;pre class=&quot;css&quot; data-role=&quot;codeBlock&quot; data-info=&quot;&quot;&gt;&lt;code&gt;docker run [이미지 이름] [실행할 명령어]
&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;1280&quot; data-origin-height=&quot;1101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhQQV1/btrnJdcrt4X/DgoppOkYseEvhbPD6ywdi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhQQV1/btrnJdcrt4X/DgoppOkYseEvhbPD6ywdi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhQQV1/btrnJdcrt4X/DgoppOkYseEvhbPD6ywdi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdhQQV1%2FbtrnJdcrt4X%2FDgoppOkYseEvhbPD6ywdi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;1101&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 그림은&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker run hello-wolrd를 실행했을 때 나오는 결과이다. 중간에 메시지에 나와 있는 것을 보면 처음에 봤던&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://file+.vscode-resource.vscode-webview.net/Users/cob/Desktop/gitbook/devops/docker/!%5Bimage%5D(https:/user-images.githubusercontent.com/42884032/145722649-43de55df-343c-4db9-8703-4d044c21263b.png)&quot;&gt;도커 아키텍처&lt;/a&gt;가 자연스레 생각날 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;참고로 [실행할 명령어]의 경우 옵셔널한 값이다. 기본적으로는 원래 이미지에 설정된 기본 실행 명령어가 실행되지만, [실행할 명령어] 자리에 명령어를 별도로 넣어주게 되면, 기존에 설정된 값을 무시하고 내가 입력한 명령어를 실행한다. 당연히 [실행할 명령어]는 도커 이미지에 설정된 명령어야 하며, 아닐 경우 에러가 발생한다.&lt;/p&gt;
&lt;h3 id=&quot;docker-ps&quot; data-ke-size=&quot;size23&quot;&gt;docker ps&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커의 현재 실행중인 프로세스 상태를 보여준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqjroi/btrnQk9tqoe/VCLIIktK5raKLTUhmlOMzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqjroi/btrnQk9tqoe/VCLIIktK5raKLTUhmlOMzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqjroi/btrnQk9tqoe/VCLIIktK5raKLTUhmlOMzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcqjroi%2FbtrnQk9tqoe%2FVCLIIktK5raKLTUhmlOMzk%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;883&quot; height=&quot;189&quot; data-origin-width=&quot;883&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분은 위의 설명으로 알 수 있으나&lt;span&gt;&amp;nbsp;&lt;/span&gt;status에 대해서만 추가적으로 작성하자면, 아래와 같은 상태값을 지닌다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Up: 실행중&lt;/li&gt;
&lt;li&gt;Exited: 종료&lt;/li&gt;
&lt;li&gt;Pause: 일시 정지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 실행 중인 컨테이너 뿐만 아니라 다른 컨테이너도 다 보고 싶다면 옵션을 명령어에 추가해 주면된다.&lt;/p&gt;
&lt;pre class=&quot;elixir&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ docker ps -a
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;docker-stop-vs-docker-kill&quot; data-ke-size=&quot;size23&quot;&gt;docker stop vs docker kill&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 중지 명령어 이며, 명령어 뒤에는 [중지할 container ID 또는 이름]을 입력한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker stop: 진행 중이던 작업을 모두 마치고 컨테이너를 중지한다. (docker stop&lt;span&gt;&amp;nbsp;&lt;/span&gt;-&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;SIGTERM&lt;span&gt;&amp;nbsp;&lt;/span&gt;- 진행중인 프로세스 정리(Grace Period) -&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;SIGKILL)&lt;/li&gt;
&lt;li&gt;docker kill: 즉시 컨테이너를 중지한다. (docker kill&lt;span&gt;&amp;nbsp;&lt;/span&gt;-&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;SIGKILL)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;docker-rm&quot; data-ke-size=&quot;size23&quot;&gt;docker rm&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 삭제 명령이며, 명령어 뒤에는 [중지할 container ID 또는 이름]을 입력한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker rm -f redis: 실행 중인 컨테이너를 강제로 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;docker-rmi&quot; data-ke-size=&quot;size23&quot;&gt;docker rmi&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 컨테이너 뿐만 아니라 이미지를 삭제하고 싶을때 사용하는 명령어 이며, 이 명령어 뒤에는 [이미지 ID]를 입력한다.&lt;br /&gt;참고로&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker images&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 통해 도커 이미지 목록을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H20EE/btrnJcEAADt/q6ulYfwX7nNb4ZULbk5kf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H20EE/btrnJcEAADt/q6ulYfwX7nNb4ZULbk5kf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H20EE/btrnJcEAADt/q6ulYfwX7nNb4ZULbk5kf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH20EE%2FbtrnJcEAADt%2Fq6ulYfwX7nNb4ZULbk5kf0%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;566&quot; height=&quot;122&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 명령어도 쉽지만, 처음엔 조금 헷갈릴 수가 있는게 이미지 삭제를 바로 하면 에러 메시지가 나올 것이다.&lt;br /&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;1159&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1jm29/btrnPkvmprr/GO6aeKIBquc6KEacqHD8e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1jm29/btrnPkvmprr/GO6aeKIBquc6KEacqHD8e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1jm29/btrnPkvmprr/GO6aeKIBquc6KEacqHD8e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1jm29%2FbtrnPkvmprr%2FGO6aeKIBquc6KEacqHD8e0%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;1159&quot; height=&quot;586&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림은&lt;span&gt;&amp;nbsp;&lt;/span&gt;hello-world&lt;span&gt;&amp;nbsp;&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;docker rm -f $(docker ps -aq --filter ancestor=hello-world)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nnLoF/btrnL89M7IZ/vlUP7K5DzQJSF8GqBzzYwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nnLoF/btrnL89M7IZ/vlUP7K5DzQJSF8GqBzzYwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nnLoF/btrnL89M7IZ/vlUP7K5DzQJSF8GqBzzYwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnnLoF%2FbtrnL89M7IZ%2FvlUP7K5DzQJSF8GqBzzYwk%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;941&quot; height=&quot;284&quot; data-origin-width=&quot;941&quot; data-origin-height=&quot;284&quot;/&gt;&lt;/span&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;docker image prune:&lt;span&gt;&amp;nbsp;&lt;/span&gt;prune은 불필요한 것을 제거한다는 의미로, 사용하지 않는 이미지만 삭제할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;docker-rm-docker-ps-a-q&quot; data-ke-size=&quot;size23&quot;&gt;docker rm 'docker ps -a -q'&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 중지된 모든 컨테이너를 삭제한다.&lt;/p&gt;
&lt;h3 id=&quot;docker-exec&quot; data-ke-size=&quot;size23&quot;&gt;docker exec&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행중인 컨테이너에게 명령를 전달한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/exec/#examples&quot;&gt;공식 문서 예제&lt;/a&gt;를 참고하자.&lt;/p&gt;
&lt;h3 id=&quot;docker-system-command&quot; data-ke-size=&quot;size23&quot;&gt;docker system [command]&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker system df: 도커 디스크 사용량을 볼 수 있다.&lt;/li&gt;
&lt;li&gt;docker system events: 서버로 부터 실시간 이벤트를 받는다.&lt;/li&gt;
&lt;li&gt;docker system info: 시스템 전체 정보를 보여준다.&lt;/li&gt;
&lt;li&gt;docker system prune: 사용하지 않는 데이터를 삭제한다. 여기에는 중지된 모든 컨테이너와 네트워크, 이미지 등이 삭제되며 삭제한 목록과 확보한 공간의 크기를 알려준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;%EB%A0%88%EB%94%94%EC%8A%A4-%EC%84%A4%EC%B9%98%ED%95%B4%EB%B3%B4%EA%B8%B0&quot;&gt;레디스 설치해보기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 사용하는 레디스를 도커 환경에서 실행해 보자.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ docker run redis
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제&lt;span&gt;&amp;nbsp;&lt;/span&gt;redis-cli&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 쓰기 위해서는 당연히&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker&lt;span&gt;&amp;nbsp;&lt;/span&gt;컨테이너 안에서 실행시켜 주면된다.&lt;br /&gt;&amp;nbsp;이때 위에서 언급한 실행 중인 컨테이너에게 명령을 전달하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker exec를 이용하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VpNEy/btrnQ5c8Vsi/yCcSksg9RBIkhcLUiIKUB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VpNEy/btrnQ5c8Vsi/yCcSksg9RBIkhcLUiIKUB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VpNEy/btrnQ5c8Vsi/yCcSksg9RBIkhcLUiIKUB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVpNEy%2FbtrnQ5c8Vsi%2FyCcSksg9RBIkhcLUiIKUB0%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;881&quot; height=&quot;167&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;redis-cli-%EC%8B%A4%ED%96%89-%ED%95%98%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;redis-cli 실행 하기&lt;/h2&gt;
&lt;pre class=&quot;shell&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;# $ docker exec -it [컨테이너 ID] 명령어
$ docker exec -it f2cdcd1244a2 redis-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-it&lt;span&gt;&amp;nbsp;&lt;/span&gt;옵션 없이 사용할 경우 아래 그림과 같이 레디스 클라이언트가 실행되자마자 종료되서 명령어를 입력할 수 없게 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ThHet/btrnQjCJhJJ/Y1zBgUohO7mzKkZKOlwbk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ThHet/btrnQjCJhJJ/Y1zBgUohO7mzKkZKOlwbk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ThHet/btrnQjCJhJJ/Y1zBgUohO7mzKkZKOlwbk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FThHet%2FbtrnQjCJhJJ%2FY1zBgUohO7mzKkZKOlwbk1%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;524&quot; height=&quot;74&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;-i: interactive(대화형),&lt;span&gt;&amp;nbsp;&lt;/span&gt;-t: terminal(터미널) 옵션을 추가하여 레디스 클라이언트가 유지되면서 터미널로 메시지를 보낼 수 있게 해주었다.&lt;/p&gt;
&lt;h2 id=&quot;%EC%8B%A4%ED%96%89-%EC%A4%91%EC%9D%B8-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%9D%98-%EC%89%98-%ED%99%98%EA%B2%BD%EC%97%90-%EC%A0%91%EC%86%8D%ED%95%98%EB%8A%94-%EB%B2%95&quot; data-ke-size=&quot;size26&quot;&gt;실행 중인 컨테이너의 쉘 환경에 접속하는 법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 약간만 수정해 주면된다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ docker exec -it [컨테이너 ID] sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 쉘 환경을 나오는 단축키는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Control + D&lt;span&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;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/basic-command&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ajdkfl6445.gitbook.io/study/devops/docker/basic-command&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1639412151042&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;도커 기본 명령어 및 실습 - 콥 노트&quot; data-og-description=&quot;참고로 [실행할 명령어]의 경우 옵셔널한 값이다. 기본적으로는 원래 이미지에 설정된 기본 실행 명령어가 실행되지만, [실행할 명령어] 자리에 명령어를 별도로 넣어주게 되면, 기존에 설정된 &quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/basic-command&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/basic-command&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/basic-command&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/basic-command&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;도커 기본 명령어 및 실습 - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;참고로 [실행할 명령어]의 경우 옵셔널한 값이다. 기본적으로는 원래 이미지에 설정된 기본 실행 명령어가 실행되지만, [실행할 명령어] 자리에 명령어를 별도로 넣어주게 되면, 기존에 설정된&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Docker</category>
      <category>command</category>
      <category>docker</category>
      <category>Redis</category>
      <category>도커기본명령어</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/132</guid>
      <comments>https://code-masterjung.tistory.com/132#entry132comment</comments>
      <pubDate>Tue, 14 Dec 2021 01:16:56 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 도커 입문하기 2 - 배경 지식 쌓기</title>
      <link>https://code-masterjung.tistory.com/131</link>
      <description>&lt;h1&gt;넋두리&lt;/h1&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;h1 id=&quot;%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C&quot;&gt;컨테이너란 무엇일까?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;컨테이너를 처음들은 사람도 많을 거고, 사실 도커나 쿠버네티스 먼저 떠오르는 사람이 많을 것이다. 이 관점에서만 본다면 컨테이너를 저렴한 가상 머신(VM)으로만 생각할 수도 있다.&lt;br /&gt;&amp;nbsp;컨테이너(container)란, 공통 기능 세트를 충족하는 단일 호스트에서 실행되는 격리된 프로세스 그룹이며, 이런 기능 중 일부는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Linux&lt;span&gt;&amp;nbsp;&lt;/span&gt;커널에서 찾을 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%84%EC%BB%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%A5%BC-%EA%B2%A9%EB%A6%AC%EC%8B%9C%ED%82%A4%EB%8A%94-%EA%B2%83%EC%9D%BC%EA%B9%8C&quot; data-ke-size=&quot;size26&quot;&gt;어떻게 도커 컨테이너를 격리시키는 것일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이를 위해서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Linux의&lt;span&gt;&amp;nbsp;&lt;/span&gt;cgroup(control groups)과 네임스페이스(namespaces)에 대해 알아야 한다. 물론 이 기술도 처음부터 나온건 아니고 과거에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;chroot와&lt;span&gt;&amp;nbsp;&lt;/span&gt;pivot_root(2) 등으로 격리 문제를 해결할려고 했으며, 여기에 대한 내용은 일단 생략한다.&lt;/p&gt;
&lt;h2 id=&quot;linux-namespacesns&quot; data-ke-size=&quot;size26&quot;&gt;Linux Namespaces(ns)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;네임스페이스는 2002년 리눅스 2.4.19와 함께 도입된 리눅스 커널 기능이다. (도커 역시 리눅스 커널 기능을 이용해 컨테이너를 격리한다.) 이 기능은 추상화 계층에서 특정 전역 시스템 리소스를 랭핑하는 것이다. 이렇게 하면 네임스페이스 내의 프로세스에 리소스의 자체 격리 인스턴스가 있는것처럼 보이게 할 수 있으며, 쉽게 말해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;네임스페이스의 주요 기능은 프로세스를 서로 격리한다는 것인거다&lt;/b&gt;.&lt;br /&gt;&amp;nbsp;개발자는 컨테이너를 사용하면 완전한 VM처럼 보이고 느껴지는 격리된 환경을 제공하지만, 사실 VM이 아니라 어딘가에 있는 서버에서 실행 중인 프로세스이며, 두 개의 컨테이너를 시작하면 어딘가의 단일 서버에서 두 개의 프로세스가 실행되고 있지만 서로 격리되어 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;현재 사용 가능한 네임스페이스는 여러 개의 고유한 네임스페이스(mnt,&lt;span&gt;&amp;nbsp;&lt;/span&gt;pid,&lt;span&gt;&amp;nbsp;&lt;/span&gt;net,&lt;span&gt;&amp;nbsp;&lt;/span&gt;ipc,&lt;span&gt;&amp;nbsp;&lt;/span&gt;uts,&lt;span&gt;&amp;nbsp;&lt;/span&gt;user&lt;span&gt;&amp;nbsp;&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;user: root가 같은 사용자 공간 내에서 권할을 가질 수 있게 된다.&lt;/li&gt;
&lt;li&gt;pid: 다른 네임스페이스의 pid세트로부터 독립적인 프로세스의 pip세트를 할당한다.&lt;/li&gt;
&lt;li&gt;net: 독립적인 네트워크 스택을 가지고 자신의 개인 라우팅 ip 주소 세트 테이블, 소켓 연결 테이블, 방화벽, 기타 네트워크 관련 자원 추적 목록을 가질 수 있다.&lt;/li&gt;
&lt;li&gt;mnt: 호스트 파일 시스템에 영향을 주지 않고 마운트 네임스페이스에서 파일 시스템을 마운트 및 언마운트 할 수 있다.&lt;/li&gt;
&lt;li&gt;ipc: 자신의 ipc자원이 아닌 posix 메시지 큐이다.&lt;/li&gt;
&lt;li&gt;uts: 하나의 시스템이 다른 프로세스에 다른 호스트 및 도메인 이름을 가지고 있는 것처럼 보일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cgroup&quot; data-ke-size=&quot;size26&quot;&gt;cgroup&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;제어 그룹(cgroup)은 프로세스 모음의 리소스 사용량(CPU,&lt;span&gt;&amp;nbsp;&lt;/span&gt;메모리, 디스크 I/O, 네트워크 등)을 제한, 설명 및 격리하는 리눅스 커널 기능이며, 아래와 같은 기능이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리소스 제한: 프로세스가 사용할 수 있는 특정 리소스의 양을 제한하도록 cgroup을 구성할 수 있다.&lt;/li&gt;
&lt;li&gt;우선 순위: 리소스 경합이 있을때 다른 cgroup의 프로세스와 비교하여 프로세스가 사용할 수 있는 리소스의 양을 제어할 수 있다.&lt;/li&gt;
&lt;li&gt;회계: 리소스 제한은 cgroup 수준에서 모니터링 되고 보고된다.&lt;/li&gt;
&lt;li&gt;제어: 단일 명령으로 cgroup에 있는 모든 프로세스의 상태(고정, 중지, 다시시작)를 변경할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;쿠버네티스 환경에서 cgroup을 사용하여 포드 수준에서 리소스 요청 및 제한, 해당&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/&quot;&gt;QoS 클래스&lt;/a&gt;를 구현할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;%EA%B7%B8%EB%9F%BC-linux-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88lxc%EC%99%80-docker-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; data-ke-size=&quot;size26&quot;&gt;그럼 Linux 컨테이너(LXC)와 Docker 컨테이너의 차이점은 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 내용만 봐서는 도커는 리눅스의&lt;span&gt;&amp;nbsp;&lt;/span&gt;ns와&lt;span&gt;&amp;nbsp;&lt;/span&gt;cgroup을 사용해서 구축했기 때문에 큰 차이가 없을거 같지만 목적이 다르다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;LXC는 OS 컨테이너화에 중점을 두는 반면&lt;span&gt;&amp;nbsp;&lt;/span&gt;Docker&lt;span&gt;&amp;nbsp;&lt;/span&gt;컨테이너는 애플리케이션 컨테이너화에 중점을 둔다. 또한 LXC는 다목적 운영체제의 가상화이며, 도커는 단일 목적 애플리에키션의 가상화이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;LXC는 단일 제어 호스트에서 여러 개의 가상환경(VE)을 생성하고 실행할 수 있는 OS 수준의 가상화 기술을 제공하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;VM에 비해 더 저렴하고 빠르게 구축할 수 있는 방법이다. 하지만 추가적인 리눅스 지식과 경험이 필요하다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker는&lt;span&gt;&amp;nbsp;&lt;/span&gt;dotCloud라고 불렸던 사이드 프로젝트에서 시작되었으며, 이후 오픈소스로 공개되었다. 도커는 사실&lt;span&gt;&amp;nbsp;&lt;/span&gt;LXC&lt;span&gt;&amp;nbsp;&lt;/span&gt;기능의 확장이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Docker는&lt;span&gt;&amp;nbsp;&lt;/span&gt;go&lt;span&gt;&amp;nbsp;&lt;/span&gt;언어로 개발되었으며&lt;span&gt;&amp;nbsp;&lt;/span&gt;LXC,&lt;span&gt;&amp;nbsp;&lt;/span&gt;cgroup&lt;span&gt;&amp;nbsp;&lt;/span&gt;및 리눅스 커널 자체를 활용한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;LXC를 기반으로 하기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;Docker&lt;span&gt;&amp;nbsp;&lt;/span&gt;컨테이너에는 별도의 운영 체제가 포함되어 있지 않으며, 대신 기본 인프라에서 제공하는 운영 체제의 고유 기능에 의존한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;LXC는 오래되었지만 시스템 관리자에 중점을 두며, 일반적으로 파일 시스템에 다른 루트를 제공하고 서로 격리되고 상태를 공유할 수 없는 환경을 생성하여 전체 시스템에 대한 OS 컨테이너를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 반면&lt;span&gt;&amp;nbsp;&lt;/span&gt;Docker는 OS수준을 넘어 애플리케이션 자체의 보다 세분화된 세계로 컨테이너를 가져왔다. 초반에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;LXC를 기반으로 구축되기 시작했지만, 나중에는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/opencontainers/runc/tree/master/libcontainer&quot;&gt;libcontainer&lt;/a&gt;라는 자체 실행 환경으로 옮겼다. 각 컨테이너에 대해 운영체제를 초기화를 시작하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;LXC와 달리&lt;span&gt;&amp;nbsp;&lt;/span&gt;Docker는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Docker Engine에서 제공하는 하나의 OS환경을 제공한다. 따라서 개발자가&lt;span&gt;&amp;nbsp;&lt;/span&gt;Docker&lt;span&gt;&amp;nbsp;&lt;/span&gt;이미지로 지정된 자체 애플리케이션 환경에 상주하는 애플리케이션을 쉽게 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 도커의 경우 이미지 빌드를 위한 명령 시퀀스를 자동화하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;dockerfile을 사용하여 개발자간 공유할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%82%B4-%EC%BB%B4%ED%93%A8%ED%84%B0%EC%97%90%EC%84%9C-cgroup-ns%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A0-%EC%88%98-%EC%9E%88%EC%A7%80&quot; data-ke-size=&quot;size26&quot;&gt;어떻게 내 컴퓨터에서 cgroup, ns를 사용할 수 있지?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;난 리눅스가 아닌데, 내 컴퓨터에서 cgroup, ns를 이용하여 컨테이너가 격리될 수가 있지?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것에 대한 이유는 도커를 설치한 후 간단한 명령어를 통해 추측해 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ docker version
&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;597&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/640nv/btrnL72UlOc/KHkzsbrvs2v7pzo65ELZSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/640nv/btrnL72UlOc/KHkzsbrvs2v7pzo65ELZSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/640nv/btrnL72UlOc/KHkzsbrvs2v7pzo65ELZSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F640nv%2FbtrnL72UlOc%2FKHkzsbrvs2v7pzo65ELZSK%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;597&quot; height=&quot;604&quot; data-origin-width=&quot;597&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림과 같이 서버 정보중&lt;span&gt;&amp;nbsp;&lt;/span&gt;OS/Arch&lt;span&gt;&amp;nbsp;&lt;/span&gt;정보를 보면&lt;span&gt;&amp;nbsp;&lt;/span&gt;linux/arm64라고 적혀있는 것을 확인할 수 있다. 나같은 경우는&lt;span&gt;&amp;nbsp;&lt;/span&gt;macOS&lt;span&gt;&amp;nbsp;&lt;/span&gt;환경임에도 불구하고&lt;span&gt;&amp;nbsp;&lt;/span&gt;linux라고 적혀있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 바로 내가 사용하는 컴퓨터 환경과 상관없이 도커 서버가 리눅스 VM 환경에서 실행되고 있기 때문이다. 따라서 모든 컨테이너는 리눅스 가상 머신 위에서 생성되며, 리눅스 커널이 컨테이너를 격리시키고 리소스를 나눠주게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/background-knowledge&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ajdkfl6445.gitbook.io/study/devops/docker/background-knowledge&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1639325261507&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;배경지식 - 콥 노트&quot; data-og-description=&quot;제어 그룹(cgroup)은 프로세스 모음의 리소스 사용량(CPU, 메모리, 디스크 I/O, 네트워크 등)을 제한, 설명 및 격리하는 리눅스 커널 기능이며, 아래와 같은 기능이 있다.&quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/background-knowledge&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/background-knowledge&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/background-knowledge&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker/background-knowledge&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;배경지식 - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;제어 그룹(cgroup)은 프로세스 모음의 리소스 사용량(CPU, 메모리, 디스크 I/O, 네트워크 등)을 제한, 설명 및 격리하는 리눅스 커널 기능이며, 아래와 같은 기능이 있다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Docker</category>
      <category>cgroup</category>
      <category>docker</category>
      <category>linux</category>
      <category>namespaces</category>
      <category>도커</category>
      <category>리눅스</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/131</guid>
      <comments>https://code-masterjung.tistory.com/131#entry131comment</comments>
      <pubDate>Mon, 13 Dec 2021 01:04:31 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] 도커 입문하기 1 - 맛보기 및 설치</title>
      <link>https://code-masterjung.tistory.com/130</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;넋두리&lt;/h2&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;h1 id=&quot;docker&quot;&gt;Docker&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O6Tij/btrnB83SEo5/NFuiBpUjTykNGXgHayEPB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O6Tij/btrnB83SEo5/NFuiBpUjTykNGXgHayEPB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O6Tij/btrnB83SEo5/NFuiBpUjTykNGXgHayEPB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO6Tij%2FbtrnB83SEo5%2FNFuiBpUjTykNGXgHayEPB0%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;1202&quot; height=&quot;309&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;%EC%99%9C-%EB%8F%84%EC%BB%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80&quot; data-ke-size=&quot;size26&quot;&gt;왜 도커를 사용하지?&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커(Docker)는 컨테이너라고 하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;느슨하게 격리된 환경&lt;/b&gt;에서 애플리케이션을 패키징하고 실행할 수 있는 기능을 제공하며 격리 보안을 통해 주어진 호스트에서 많은 컨테이너를 동시에 실행시킬 수도 있다.&lt;br /&gt;&lt;b&gt;컨테이너는 가볍고 애플리케이션을 실행하는데 필요한 모든 것을 포함하므로 현재 호스트에 설치된 것에 의존할 필요가 없다&lt;/b&gt;. 작업하는 동안 컨테이너를 쉽게 공유할 수 있으며 공유하는 모든 사람이 동일한 방식으로 작동하는 동일한 컨테이너를 갖게 된다. - docker 공식문서 -&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker는 애플리케이션을 신속하게 구축, 테스트 및 배포할 수 있는 소프트웨어 플랫폼입니다. Docker는 소프트웨어를 컨테이너라는 표준화된 유닛으로 패키징하며, 이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;컨테이너에는 라이브러리, 시스템 도구, 코드, 런타임 등 소프트웨어를 실행하는 데 필요한 모든 것이 포함&lt;/b&gt;되어 있습니다. Docker를 사용하면 환경에 구애받지 않고 애플리케이션을 신속하게 배포 및 확장할 수 있으며 코드가 문제없이 실행될 것임을 확신할 수 있습니다. - AWS에서 소개하는 Docker의 정의 -&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이 내용만 봐서는 환경에 구애받지 않고 동일한 환경으로 가볍게 애플리케이션을 실행할 수 있으니 좋을거 같다는 생각이 들지만 사실 아직 감이 확오진 않는다. 한 가지 간단한 예를 들어보자. 우리가 애플리케이션을 실행한다고 하면, 아래와 같은 과정이 필요하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스톨러를 내려받고, 인스톨러를 실행하여, 프로그램 설치를 완료&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;환경에 구애받지 않는다는 것은 바로 이런거다. 위 과정에서 내가 윈도우건, 리눅스건, macos이건 상관없고 서버가 어떤거건 패키지 버전이 어떻건 다 상관없이 환경에 구애받지 않고 동일한 환경으로 실행시켜준다는 것이다. 이것을 도커 컨테이너를 통해 가능하게 해준다는 것이다.&lt;/p&gt;
&lt;h2 id=&quot;docker%EA%B0%80-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EB%9D%BC%EB%8A%94-%EB%A7%90%EC%9D%B8%EA%B0%80&quot; data-ke-size=&quot;size26&quot;&gt;Docker가 컨테이너라는 말인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 말이 아니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;Docker는&lt;span&gt;&amp;nbsp;&lt;/span&gt;컨테이너를 위한 운영체제이며, 각 서버에 설치되어 컨테이너를 구축, 시작, 중단하는데 사용할 수 있는 명령어를 제공해 준다.(도커는 코드를 실행하는 표준 방식을 제공해 주는 것이다.) 우리는 도커를 더 잘 이해하기 위해서 컨테이너에 대해 명확히 알아야 하며, 컨테이너를 알기 위해서는 도커 이미지에 대해 알아야 한다.&lt;/p&gt;
&lt;h2 id=&quot;%EA%B0%91%EC%9E%90%EA%B8%B0-%EB%8F%84%EC%BB%A4-%EC%9D%B4%EB%AF%B8%EC%A7%80&quot; data-ke-size=&quot;size26&quot;&gt;갑자기 도커 이미지?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 이미지란 컨테이너를 정의하는 읽기 전용 템플릿이다. 이 이미지에는 코드에 필요한 라이브러리, 종속성에 대한 정의, 실행되는 코드가 포함되어 있다. 이런 도커 이미지가 인스턴스화된(실행되는) 것이 바로 도커 컨테이너이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아하, 도커 컨테이너는 도커 이미지의 인스턴스구나.&lt;/li&gt;
&lt;li&gt;도커 컨테이너를 실행하면, 그 건테이너 안에서 실행하고자 하는 애플리케이션을 실행할 수 있겠네!&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;%EB%8F%84%EC%BB%A4%EC%9D%98-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98&quot; data-ke-size=&quot;size26&quot;&gt;도커의 아키텍처&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 설명을 보고 이제&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.docker.com/get-started/overview/&quot;&gt;도커 공식문서&lt;/a&gt;에 나온 아키텍처를 보면 아주 조금! 알까 말까한 느낌이 들면서도 더 알고 싶은 학습욕이 솟구치게 된다(?). 가즈아!&lt;/p&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;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tuV0y/btrnCAskO0s/zpTA2ZOQ071KXxUgPRIhn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tuV0y/btrnCAskO0s/zpTA2ZOQ071KXxUgPRIhn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tuV0y/btrnCAskO0s/zpTA2ZOQ071KXxUgPRIhn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtuV0y%2FbtrnCAskO0s%2FzpTA2ZOQ071KXxUgPRIhn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;872&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;docker-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;Docker 설치하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 설치는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://docs.docker.com/get-docker/&quot;&gt;공식 문서에 제공하는 다운로드&lt;/a&gt;를 통해 진행하면 된다. 참고로 내가 사용하는 환경은&lt;span&gt;&amp;nbsp;&lt;/span&gt;apple silicon(M1 Mackbook pro)이다.&lt;/p&gt;
&lt;h3 id=&quot;aplle-silicon-mac%EC%9D%B4%EB%9D%BC%EB%A9%B4&quot; data-ke-size=&quot;size23&quot;&gt;Aplle silicon mac이라면...&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rosetta2로 업데이트 해준다.&lt;a href=&quot;https://docs.docker.com/desktop/mac/install/#mac-with-apple-silicon&quot;&gt;참고 링크&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;ada&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ softwareupdate --install-rosetta
&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;273&quot; data-origin-height=&quot;237&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HfL9a/btrnJBwvOjo/EaVhtcd3jvlY1biQ5ebSpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HfL9a/btrnJBwvOjo/EaVhtcd3jvlY1biQ5ebSpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HfL9a/btrnJBwvOjo/EaVhtcd3jvlY1biQ5ebSpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHfL9a%2FbtrnJBwvOjo%2FEaVhtcd3jvlY1biQ5ebSpk%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;273&quot; height=&quot;237&quot; data-origin-width=&quot;273&quot; data-origin-height=&quot;237&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되면 위와 같은 아이콘을 눌러 도커를 실행하자.&lt;/p&gt;
&lt;h3 id=&quot;docker-desktop-needs-privileged-access&quot; data-ke-size=&quot;size23&quot;&gt;&quot;Docker Desktop needs privileged access.&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ok누르고 본인&lt;span&gt;&amp;nbsp;&lt;/span&gt;mac의 암호를 입력 하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbFC9e/btrnE9ngc2A/riaOoOx8AiSq06sCTuB3sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbFC9e/btrnE9ngc2A/riaOoOx8AiSq06sCTuB3sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbFC9e/btrnE9ngc2A/riaOoOx8AiSq06sCTuB3sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbFC9e%2FbtrnE9ngc2A%2FriaOoOx8AiSq06sCTuB3sk%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;598&quot; height=&quot;196&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&quot;%EB%8F%84%EC%BB%A4-%EC%84%A4%EC%B9%98-%ED%99%95%EC%9D%B8&quot; data-ke-size=&quot;size23&quot;&gt;도커 설치 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널(bash, zsh 등)에&lt;span&gt;&amp;nbsp;&lt;/span&gt;docker version이라고 명령어를 입력하면 도커 클라이언트와 서버의 정보가 잘 나온다면 성공한 것이다.&lt;/p&gt;
&lt;h3 id=&quot;%ED%8A%9C%EB%A1%9C%EB%A6%AC%EC%96%BC&quot; data-ke-size=&quot;size23&quot;&gt;튜로리얼?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필수는 아니지만, 해볼거라면 단순히 클릭만 하지말고, 위에 있는 도커 아키텍처를 보면서 흐름을 느껴보자.&lt;/p&gt;
&lt;h3 id=&quot;id-pw&quot; data-ke-size=&quot;size23&quot;&gt;id, pw?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 홈페이지에서 회원 가입을 한 후 아이디와 암호를 입력하면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;%EC%84%A4%EC%B9%98-%EC%99%84%EB%A3%8C-%ED%9B%84&quot; data-ke-size=&quot;size26&quot;&gt;설치 완료 후&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 데스크탑과 도커 허브를 보며 눌러보고 실행해보고 정지도 해보자. 그리고 계속 도커 아키텍처 이미지를 다시 보면 이해하는데 도움이 많이 된다.&lt;/p&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;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bq4APH/btrnLG5n4Rh/tBK3MQ7RXRqkhxexuE5hp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bq4APH/btrnLG5n4Rh/tBK3MQ7RXRqkhxexuE5hp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bq4APH/btrnLG5n4Rh/tBK3MQ7RXRqkhxexuE5hp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbq4APH%2FbtrnLG5n4Rh%2FtBK3MQ7RXRqkhxexuE5hp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;728&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 다음 내용은 다음 글에서 이어서 진행해 보겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ajdkfl6445.gitbook.io/study/devops/docker&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1639325212755&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Docker - 콥 노트&quot; data-og-description=&quot;Docker는 애플리케이션을 신속하게 구축, 테스트 및 배포할 수 있는 소프트웨어 플랫폼입니다. Docker는 소프트웨어를 컨테이너라는 표준화된 유닛으로 패키징하며, 이 컨테이너에는 라이브러리, &quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/devops/docker&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Docker - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Docker는 애플리케이션을 신속하게 구축, 테스트 및 배포할 수 있는 소프트웨어 플랫폼입니다. Docker는 소프트웨어를 컨테이너라는 표준화된 유닛으로 패키징하며, 이 컨테이너에는 라이브러리,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Docker</category>
      <category>docker</category>
      <category>도커</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/130</guid>
      <comments>https://code-masterjung.tistory.com/130#entry130comment</comments>
      <pubDate>Mon, 13 Dec 2021 00:57:27 +0900</pubDate>
    </item>
    <item>
      <title>React Native에서 상태 관리</title>
      <link>https://code-masterjung.tistory.com/129</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;상태 관리라는 주제는 RN에 국한된 내용은 아니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;typescript를 기반으로 여러 방법을 복습겸 테스트 해보자. 더 나아가 아직 사용해보지 않는&lt;span&gt;&amp;nbsp;&lt;/span&gt;recoil&lt;span&gt;&amp;nbsp;&lt;/span&gt;라이브러리도 경험해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;a href=&quot;https://ajdkfl6445.gitbook.io/study/mobile/react-native/install&quot;&gt;설치 및 할일 만들기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/mobile/react-native/hooks&quot;&gt;내비게이션, Hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/JungKyuHyun/React-Native-Study-Diary-App&quot;&gt;다이어리 앱 만들기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/JungKyuHyun/React-Native-Study-Typescript&quot;&gt;React Native With Typescript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;getting-start&quot; data-ke-size=&quot;size26&quot;&gt;Getting Start&lt;/h2&gt;
&lt;pre class=&quot;kotlin&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ npx react-native init StateManagementInReactNative --template react-native-template-typescript
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;1-redux-toolkit-rtk&quot;&gt;1. Redux Toolkit (RTK)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typescript에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;redux를 사용하려고 하니, 너무 많은 타이핑과 구조체들이 필요해서 실제로 프로덕션 환경에서 도입하여 사용해본 반가운 라이브러리다. (참고:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://redux-toolkit.js.org/introduction/getting-started&quot;&gt;공식문서&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랫만에 다시 보자&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow&quot;&gt;리덕스의 개념 및 데이터 흐름&lt;/a&gt;!&lt;/p&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;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n4QTT/btrns39bfrb/JlETNl3NP35dyXpyBx6QEk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n4QTT/btrns39bfrb/JlETNl3NP35dyXpyBx6QEk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n4QTT/btrns39bfrb/JlETNl3NP35dyXpyBx6QEk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/n4QTT/btrns39bfrb/JlETNl3NP35dyXpyBx6QEk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;960&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;%EC%84%A4%EC%B9%98&quot; data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;pre class=&quot;dockerfile&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ yarn add redux react-redux @reduxjs/toolkit
$ yarn add -D @types/react-redux
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%EA%B8%B0%EB%B3%B8-%EA%B5%AC%ED%98%84%EC%B2%B4&quot; data-ke-size=&quot;size26&quot;&gt;기본 구현체&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 인증 상태를 관리할&lt;span&gt;&amp;nbsp;&lt;/span&gt;authSlice&lt;span&gt;&amp;nbsp;&lt;/span&gt;생성.&lt;/p&gt;
&lt;pre class=&quot;pf&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;// slices/auth.ts
import {createSlice, PayloadAction} from '@reduxjs/toolkit';

export interface User {
  id: number;
  username: string;
  displayName: string;
}

interface AuthState {
  user: User | null;
}

const initialState: AuthState = {
  user: null,
};

const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    authorize(state, action: PayloadAction&amp;lt;User&amp;gt;) {
      state.user = action.payload;
    },
    logout(state) {
      state.user = null;
    },
  },
});

export default authSlice.reducer;
export const {authorize, logout} = authSlice.actions;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rootReducer&lt;span&gt;&amp;nbsp;&lt;/span&gt;생성&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;// slices/index.ts
import {combineReducers} from 'redux';
import auth from './auth';

const rootReducer = combineReducers({
  auth,
});

export type RootState = ReturnType&amp;lt;typeof rootReducer&amp;gt;;

export default rootReducer;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스토어 추가&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import React from 'react';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import rootReducer from './slices';

const store = createStore(rootReducer);

function App() {
  return &amp;lt;Provider store={store}&amp;gt;{/* ... */}&amp;lt;/Provider&amp;gt;;
}

export default App;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%EC%83%81%ED%83%9C-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;상태 가져오기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useSelector&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용&lt;/p&gt;
&lt;pre class=&quot;pf&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;function AuthStatus() {
  const user = useSelector((state: RootState) =&amp;gt; state.auth.user);

  return (
    &amp;lt;View style={styles.status}&amp;gt;
      &amp;lt;Text style={styles.text}&amp;gt;
        {user != null ? user.displayName : '로그인하세요'}
      &amp;lt;/Text&amp;gt;
    &amp;lt;/View&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%EC%95%A1%EC%85%98-%ED%95%A8%EC%88%98-%EC%A0%84%EB%8B%AC%ED%95%98%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;액션 함수 전달하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useDispatch&lt;span&gt;&amp;nbsp;&lt;/span&gt;훅으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;dispatch&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 얻고 필요할때&lt;span&gt;&amp;nbsp;&lt;/span&gt;dispatch&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수로 액션 함수 전달. (다시 한번 더 읽기&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow#core-concepts-and-principles&quot;&gt;리덕스의 핵심 개념 및 원리&lt;/a&gt;!)&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;function AuthButtons() {
  const dispatch = useDispatch();
  const onPressLogin = () =&amp;gt; {
    dispatch(
      authorize({
        id: 1,
        username: 'JungKyuHyun',
        displayName: 'Jacob',
      }),
    );
  };
  const onPressLogout = () =&amp;gt; {
    dispatch(logout());
  };

  return (
    &amp;lt;View&amp;gt;
      &amp;lt;Button title=&quot;로그인&quot; onPress={onPressLogin} /&amp;gt;
      &amp;lt;Button title=&quot;로그아웃&quot; onPress={onPressLogout} /&amp;gt;
    &amp;lt;/View&amp;gt;
  );
}
&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;1060&quot; data-origin-height=&quot;962&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TqwRW/btrnv2IcG26/6tduvGkBZ1CoZ6Ru1bN8DK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TqwRW/btrnv2IcG26/6tduvGkBZ1CoZ6Ru1bN8DK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TqwRW/btrnv2IcG26/6tduvGkBZ1CoZ6Ru1bN8DK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/TqwRW/btrnv2IcG26/6tduvGkBZ1CoZ6Ru1bN8DK/img.gif&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;1060&quot; height=&quot;962&quot; data-origin-width=&quot;1060&quot; data-origin-height=&quot;962&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;useselector-%EC%82%AC%EC%9A%A9%EC%8B%9C-rootstate-%EC%97%86%EC%9D%B4-%ED%83%80%EC%9E%85-%EC%B6%94%EB%A1%A0%EB%90%98%EA%B2%8C-%ED%95%98%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;useSelector 사용시 RootState 없이 타입 추론되게 하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 공부하면서 발견한 내용이다. 매번&lt;span&gt;&amp;nbsp;&lt;/span&gt;RootState&lt;span&gt;&amp;nbsp;&lt;/span&gt;타입을 가져왔었는데 이런 방법이 존재했네;;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;// slices/index.ts
// ...
export type RootState = ReturnType&amp;lt;typeof rootReducer&amp;gt;;

// &amp;lt;-- here! ---&amp;gt;
declare module 'react-redux' {
  interface DefaultRootState extends RootState {}
}

export default rootReducer;
&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;958&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhd59e/btrnv2IcG2e/uZETkkTMGQ5k6Dn6bhUub0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhd59e/btrnv2IcG2e/uZETkkTMGQ5k6Dn6bhUub0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhd59e/btrnv2IcG2e/uZETkkTMGQ5k6Dn6bhUub0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdhd59e%2Fbtrnv2IcG2e%2FuZETkkTMGQ5k6Dn6bhUub0%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;958&quot; height=&quot;88&quot; data-origin-width=&quot;958&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&quot;%EB%A6%AC%EB%8D%95%EC%8A%A4%EC%99%80-%EC%97%B0%EB%8F%99%ED%95%98%EB%8A%94-%EB%A1%9C%EC%A7%81%EC%9D%84-hook%EC%9C%BC%EB%A1%9C-%EB%B6%84%EB%A6%AC&quot; data-ke-size=&quot;size26&quot;&gt;리덕스와 연동하는 로직을 Hook으로 분리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리덕스의 상태를 다루는 로직과 컴포넌트의 UI로직을 분리하자. (나중에 상태 관리 라이브러리 마이그레이션할 때도 편하며, 커스텀 훅으로 만들면 재사용도 가능해 진다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저&lt;span&gt;&amp;nbsp;&lt;/span&gt;AuthStatus.tsx에서 사용했던 스토어로부터 상태를 가져오던 부분을 커스텀 훅으로 만들었다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;// hooks/useUser.ts
export default function useUser() {
  return useSelector(state =&amp;gt; state.auth.user);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AuthButtons.tsx에서 사용하던 리덕스 부분을 분리했다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;// hooks/useAuthActions.ts
// 1번 방법
export default function useAuthActions() {
  const dispatch = useDispatch();
  return {
    authorize: (user: User) =&amp;gt; dispatch(authorize(user)),
    logout: () =&amp;gt; dispatch(logout()),
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번 방법은 직접 타이핑 해보면 알겠지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;dispatch하기 위해 한번 더 랩핑하는 과정이 귀찮다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;// hooks/useAuthActions.ts
// 2번 방법
export default function useAuthActions() {
  const dispatch = useDispatch();
  return bindActionCreators({authorize, logout}, dispatch);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 방법의 경우 제공되는 유틸함수 이용(bindActionCreators)를 이용했다. 훨씬도 간결하고 깔끔해 졌다.&lt;br /&gt;또한 각 액션 생성 함수들의 파라미터 타입을 따로 알지 않아도 되기 때문에 편하다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;// hooks/useAuthActions.ts
// 3번 방법
export default function useAuthActions() {
  const dispatch = useDispatch();
  return useMemo(
    () =&amp;gt; bindActionCreators({authorize, logout}, dispatch),
    [dispatch],
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번 방법은 2번 방법을 최적화한 것이다.&lt;br /&gt;이 훅에서는 컴포넌트가 새로 렌더링될 때마다&lt;span&gt;&amp;nbsp;&lt;/span&gt;bindActionCreators가 호출되어 각 함수들이 새로 선언되게 되는데, 그 함수가&lt;span&gt;&amp;nbsp;&lt;/span&gt;useEffect에서 사용하게 되면 의도치 않은 버그가 생길 수 있기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;useMemo를 사용해 준다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 id=&quot;%EB%A6%AC%EB%8D%95%EC%8A%A4%EB%A1%9C-%ED%95%A0%EC%9D%BC-%EB%AA%A9%EB%A1%9D-%EB%A7%8C%EB%93%A4%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;리덕스로 할일 목록 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redux Toolkit을 사용할 때는 불변성을 유지하지 않아도 자동으로 관리되기 때문에 push, slice 등의 함수를 사용해도 괜찮다.&lt;br /&gt;&lt;b&gt;다만 리듀서에서 반환하는 값이 없을 때는 라이브러리에서 불변성 유지를 자동으로 해주지만, 값을 반환한다면 불변성 자동 관리가 생략된다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import {createSlice, nanoid, PayloadAction} from '@reduxjs/toolkit';

export interface Todo {
  id: string;
  text: string;
  done: boolean;
}

const initialState: Todo[] = [
  {id: '1', text: 'RN 배우기', done: true},
  {id: '2', text: 'React 배우기', done: false},
  {id: '3', text: 'React Redux 배우기', done: false},
];

const todoSlice = createSlice({
  name: 'todos',
  initialState,
  reducers: {
    add: {
      // @see https://redux-toolkit.js.org/api/createSlice#customizing-generated-action-creators
      prepare(text: string) {
        return {payload: {id: nanoid(), text}};
      },
      reducer(state, action: PayloadAction&amp;lt;{id: string; text: string}&amp;gt;) {
        state.push({
          ...action.payload,
          done: false,
        });
      },
    },
    remove(state, action: PayloadAction&amp;lt;string&amp;gt;) {
      return state.filter(todo =&amp;gt; todo.id !== action.payload);
    },
    toggle(state, action: PayloadAction&amp;lt;string&amp;gt;) {
      const selected = state.find(todo =&amp;gt; todo.id === action.payload);
      if (selected === undefined) {
        return;
      }
      selected.done = !selected.done;
    },
  },
});

export const {add, remove, toggle} = todoSlice.actions;
export default todoSlice.reducer;
&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;1040&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbFZAZ/btrnwzMwoZj/nrdq4HAdq04fmO20QSvVnK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbFZAZ/btrnwzMwoZj/nrdq4HAdq04fmO20QSvVnK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbFZAZ/btrnwzMwoZj/nrdq4HAdq04fmO20QSvVnK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bbFZAZ/btrnwzMwoZj/nrdq4HAdq04fmO20QSvVnK/img.gif&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;1040&quot; height=&quot;956&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9E%91%EC%97%85&quot;&gt;비동기 작업&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지가 존재하지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;redux-thunk를 통해 만들어 보자.&lt;br /&gt;redux-thunk의 경우 함수를 기반으로 작동하며&lt;span&gt;&amp;nbsp;&lt;/span&gt;Redux Toolkit에 내장되어 있다. 나는 사실 이 방법이 리덕스에서 사용하는 가장 기본적인 비동기 처리지만 이상하게 이것만 직접 사용해 본적은 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리덕스를 사용할 때는&lt;span&gt;&amp;nbsp;&lt;/span&gt;redux-sage,&lt;span&gt;&amp;nbsp;&lt;/span&gt;redux-observable으로만 비동기 처리를 했었는데 지금은 로딩, 오류 등의 상태관리가 너무 귀찮기도 하고 리덕스의 타이핑이 줄어도 많기도 하여,&lt;span&gt;&amp;nbsp;&lt;/span&gt;react-query를 많이 사용하다 보니 더더욱 접할일이 없었다. 왜 안사용했는지도 지금은 조금 더 명확하게 알 수 있을거 같아 공부할겸 지금 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출할 때는 많이 사용하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Axios&lt;span&gt;&amp;nbsp;&lt;/span&gt;라이브러리를 사용해 보겠다.&lt;/p&gt;
&lt;h2 id=&quot;%EC%84%A4%EC%B9%98-1&quot; data-ke-size=&quot;size26&quot;&gt;설치&lt;/h2&gt;
&lt;pre class=&quot;dockerfile&quot; data-role=&quot;codeBlock&quot; data-info=&quot;bash&quot;&gt;&lt;code&gt;$ yarn add axios
$ yarn add -D @types/axios
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4-%EC%A0%81%EC%9A%A9&quot; data-ke-size=&quot;size26&quot;&gt;미들웨어 적용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존&lt;span&gt;&amp;nbsp;&lt;/span&gt;createStore에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;configureStore로 변경한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import {configureStore} from '@reduxjs/toolkit';
// ...

// const store = createStore(rootReducer);
const store = configureStore({reducer: rootReducer}); // &amp;lt;-- here!

function App() {
  return (
    &amp;lt;Provider store={store}&amp;gt;
      &amp;lt;PostsApp /&amp;gt;
    &amp;lt;/Provider&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95&quot; data-ke-size=&quot;size26&quot;&gt;기본 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;createAsyncThunk&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수는&lt;span&gt;&amp;nbsp;&lt;/span&gt;Promise를 반환하는 함수를 기반으로 함수가 호출됐을 때, 성공하거나 실패했을 때 사용할 액션들을 제공한다.&lt;br /&gt;추후에&lt;span&gt;&amp;nbsp;&lt;/span&gt;dispatch(fetchPosts())하면 상황별로 이 액션들이&lt;span&gt;&amp;nbsp;&lt;/span&gt;dispatch된다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import {
  createAsyncThunk,
  createSlice,
  PayloadAction,
  SerializedError,
} from '@reduxjs/toolkit';
import {getPosts} from '../api/getPosts';
import {Post} from '../api/types';

export const fetchPosts = createAsyncThunk('posts/fetchUsers', getPosts);

interface PostsState {
  posts: {
    loading: boolean;
    data: Post[] | null;
    error: SerializedError | null;
  };
}

const initialState: PostsState = {
  posts: {
    loading: false,
    data: null,
    error: null,
  },
};

const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {},
  extraReducers: {
    [fetchPosts.pending.type]: state =&amp;gt; {
      state.posts = {
        loading: true,
        data: null,
        error: null,
      };
    },
    [fetchPosts.fulfilled.type]: (state, action: PayloadAction&amp;lt;Post[]&amp;gt;) =&amp;gt; {
      state.posts.data = action.payload;
      state.posts.loading = false;
    },
    [fetchPosts.rejected.type]: (
      state,
      action: ReturnType&amp;lt;typeof fetchPosts.rejected&amp;gt;,
    ) =&amp;gt; {
      state.posts.error = action.error;
      state.posts.loading = false;
    },
  },
});

export default postsSlice.reducer;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로&lt;span&gt;&amp;nbsp;&lt;/span&gt;createSlice할 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;fetchPosts를 통하여&lt;span&gt;&amp;nbsp;&lt;/span&gt;dispatch된 액션들을 처리하는 리듀서 함수들은&lt;span&gt;&amp;nbsp;&lt;/span&gt;extraReducers에 작성해야 한다. 왜냐하면 이 경우에 액션 생성함수와 리듀서를 동시에 만드는게 아니라 이미 정의된 액션들의 리듀서를 작성하는 것이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 만들 hook은 위에 훅과 달리 상태와 액션이 동시에 필요하여 한번에 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import {useCallback, useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {fetchPosts} from '../slices/posts';

interface Props {
  enabled: boolean;
}

export default function usePosts({enabled}: Props) {
  const posts = useSelector(state =&amp;gt; state.posts);
  const dispatch = useDispatch();
  const fetchData = useCallback(() =&amp;gt; {
    dispatch(fetchPosts());
  }, [dispatch]);

  useEffect(() =&amp;gt; {
    if (!enabled) {
      return;
    }
    fetchData();
  }, [enabled, fetchData]);

  return {
    ...posts,
    refetch: fetchData,
  };
}
&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;452&quot; data-origin-height=&quot;950&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJIq0u/btrnyICdhVU/AfhNJNbhFABu30uwwUc1G0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJIq0u/btrnyICdhVU/AfhNJNbhFABu30uwwUc1G0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJIq0u/btrnyICdhVU/AfhNJNbhFABu30uwwUc1G0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bJIq0u/btrnyICdhVU/AfhNJNbhFABu30uwwUc1G0/img.gif&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;452&quot; height=&quot;950&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;950&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;recoil&quot;&gt;Recoil&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;recoil에 대한 설명이 길어져&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/react/recoil&quot;&gt;Recoil 기초 개념 및 사용법&lt;/a&gt;으로 대체하겠다.&lt;/p&gt;
&lt;h2 id=&quot;userecoilcallbackcallback-deps%EB%A1%9C-%EC%B5%9C%EC%A0%81%ED%99%94-%ED%95%98%EA%B8%B0&quot; data-ke-size=&quot;size26&quot;&gt;useRecoilCallback(callback, deps)로 최적화 하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 기존에 작업된 코드를 보자.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import {useMemo} from 'react';
import {useRecoilValue, useSetRecoilState} from 'recoil';
import {nextTodoId, todosState} from '../atoms/todos';

export default function useTodosActions() {
  const set = useSetRecoilState(todosState);
  const nextId = useRecoilValue(nextTodoId);

  return useMemo(
    () =&amp;gt; ({
      add: (text: string) =&amp;gt;
        set(prev =&amp;gt; prev.concat({id: nextId.toString(), text, done: false})),
      remove: (id: string) =&amp;gt; set(prev =&amp;gt; prev.filter(todo =&amp;gt; todo.id !== id)),
      toggle: (id: string) =&amp;gt;
        set(prev =&amp;gt;
          prev.map(todo =&amp;gt;
            todo.id === id ? {...todo, done: !todo.done} : todo,
          ),
        ),
    }),
    [nextId, set],
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMemo가&lt;span&gt;&amp;nbsp;&lt;/span&gt;nextId에 의존하고 있어,&lt;span&gt;&amp;nbsp;&lt;/span&gt;nextId가 바뀔때마다&lt;span&gt;&amp;nbsp;&lt;/span&gt;useMemo에 등록한 함수가 한번 더 호출되면서 새로운 객체가 만들어 지고 있다.&lt;br /&gt;사실 성능적으로는 문제가 크게 없는 부분이긴 하지만, 최적화하는 법을 배울겸 수정해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[&lt;a href=&quot;https://recoiljs.org/docs/api-reference/core/useRecoilCallback/&quot;&gt;공식문서 - useRecoilCallback&lt;/a&gt;]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useRecoilCallback은&lt;span&gt;&amp;nbsp;&lt;/span&gt;useCallback가 유사하지만,&lt;span&gt;&amp;nbsp;&lt;/span&gt;recoil&lt;span&gt;&amp;nbsp;&lt;/span&gt;상태(state)에 동작하는 콜백용 api를 제공해준다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;type CallbackInterface = {
  snapshot: Snapshot,
  gotoSnapshot: Snapshot =&amp;gt; void,
  set: &amp;lt;T&amp;gt;(RecoilState&amp;lt;T&amp;gt;, (T =&amp;gt; T) | T) =&amp;gt; void,
  reset: &amp;lt;T&amp;gt;(RecoilState&amp;lt;T&amp;gt;) =&amp;gt; void,
  transact_UNSTABLE: ((TransactionInterface) =&amp;gt; void) =&amp;gt; void,
};

function useRecoilCallback&amp;lt;Args, ReturnValue&amp;gt;(
  callback: CallbackInterface =&amp;gt; (...Args) =&amp;gt; ReturnValue,
  deps?: $ReadOnlyArray&amp;lt;mixed&amp;gt;,
): (...Args) =&amp;gt; ReturnValue
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 보다시피 콜백 함수에는 여러 인터페이스가 제공되며, 위 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;snapshot을 통해 최적화 가능하다. 스냅샷의 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;recoil atom state에 대한 읽기 전용 보기를 제공하며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;Promise형태로 반환된다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import {useMemo} from 'react';
import {useRecoilCallback, useSetRecoilState} from 'recoil';
import {nextTodoId, todosState} from '../atoms/todos';

export default function useTodosActions() {
  const set = useSetRecoilState(todosState);
  const add = useRecoilCallback(
    ({snapshot}) =&amp;gt;
      async (text: string) =&amp;gt; {
        const nextId = await snapshot.getPromise(nextTodoId);
        set(prev =&amp;gt; prev.concat({id: nextId.toString(), text, done: false}));
      },
    [],
  );

  return useMemo(
    () =&amp;gt; ({
      add,
      remove: (id: string) =&amp;gt; set(prev =&amp;gt; prev.filter(todo =&amp;gt; todo.id !== id)),
      toggle: (id: string) =&amp;gt;
        set(prev =&amp;gt;
          prev.map(todo =&amp;gt;
            todo.id === id ? {...todo, done: !todo.done} : todo,
          ),
        ),
    }),
    [add, set],
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;nextTodoId가 바뀔 때마다 함수들이 새로 생성되지 않고&lt;span&gt;&amp;nbsp;&lt;/span&gt;add&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수가 호출될 때 그 내부에서 현재의&lt;span&gt;&amp;nbsp;&lt;/span&gt;nextTodoId를 조회한 뒤 해당 값을 사용해 새로운 항목을 등록하게 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;figure id=&quot;og_1639077423464&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;RN 상태관리 - 콥 노트&quot; data-og-description=&quot;리덕스를 사용할 때는 redux-sage, redux-observable으로만 비동기 처리를 했었는데 지금은 로딩, 오류 등의 상태관리가 너무 귀찮기도 하고 리덕스의 타이핑이 줄어도 많기도 하여, react-query를 많이 사&quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/mobile/react-native/rn&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/mobile/react-native/rn&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/mobile/react-native/rn&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/mobile/react-native/rn&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;RN 상태관리 - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;리덕스를 사용할 때는 redux-sage, redux-observable으로만 비동기 처리를 했었는데 지금은 로딩, 오류 등의 상태관리가 너무 귀찮기도 하고 리덕스의 타이핑이 줄어도 많기도 하여, react-query를 많이 사&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>React</category>
      <category>React</category>
      <category>react-natvie</category>
      <category>Recoil</category>
      <category>redux</category>
      <category>redux-toolkit</category>
      <category>RN</category>
      <category>RTK</category>
      <category>상태관리</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/129</guid>
      <comments>https://code-masterjung.tistory.com/129#entry129comment</comments>
      <pubDate>Fri, 10 Dec 2021 04:17:51 +0900</pubDate>
    </item>
    <item>
      <title>Recoil 기초 개념 및 사용법</title>
      <link>https://code-masterjung.tistory.com/128</link>
      <description>&lt;h1 id=&quot;recoil&quot;&gt;Recoil&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이스북에서 만든 상태관리 라이브러리로,&lt;span&gt;&amp;nbsp;&lt;/span&gt;useState를 사용하는 것만큼 사용이 간단하면서 상태 관리를 효과적으로 할 수 있게 도와준다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Recoil lets you create a data-flow graph that flows from atoms (shared state) through selectors (pure functions) and down into your React components.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 말이 리코일의 핵심 개념으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;Recoil은 원자(atom, 공유 상태)에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector(순수 함수)를 거쳐&lt;span&gt;&amp;nbsp;&lt;/span&gt;React Componenets로 흐르는 데이터 흐름 그래프를 만들 수 있게 해준다. 여기서 원자(atom)는 리액트 컴포넌트가 구독(subscribe)할 수 있는 상태 단위이며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector는 이 상태를 동기식 또는 비동기식으로 변환한다.&lt;/p&gt;
&lt;h2 id=&quot;%EA%B3%B5%EC%8B%9D-%EB%AC%B8%EC%84%9C&quot; data-ke-size=&quot;size26&quot;&gt;공식 문서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://recoiljs.org&quot;&gt;https://recoiljs.org&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95-1&quot; data-ke-size=&quot;size26&quot;&gt;기본 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 다루다보니 조금 공식 문서를 많이 참고하여 공부하자.&lt;/p&gt;
&lt;h3 id=&quot;recoilroot&quot; data-ke-size=&quot;size23&quot;&gt;RecoilRoot&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RecoilRoot는 리덕스의&lt;span&gt;&amp;nbsp;&lt;/span&gt;Provider와 비슷한 역할을 하여 여러 개의&lt;span&gt;&amp;nbsp;&lt;/span&gt;RecoilRoot가 공존할 수 있으며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;atom state의 독립적인 공급자/저장소를 나타낼 수 있다.&lt;br /&gt;컴포넌트에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;recoil과 연동할 때는 해당 컴포넌트와 가장 가까이 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;RecoilRoot를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import {RecoilRoot} from 'recoil';

function AppRoot() {
  return (
    &amp;lt;RecoilRoot&amp;gt;
      &amp;lt;ComponentThatUsesRecoil /&amp;gt;
    &amp;lt;/RecoilRoot&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;atom&quot; data-ke-size=&quot;size23&quot;&gt;atom&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;atom은&lt;span&gt;&amp;nbsp;&lt;/span&gt;recoil에서 상태를 정의하는 방법이라고 생각하면 된다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;const todoListState = atom({
  key: 'todoListState',
  default: [],
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태를 정의할 때는 고유값인 key를 설정하고, 기본값(default)을 설정하면 된다. 이렇게 정의한 atom은&lt;span&gt;&amp;nbsp;&lt;/span&gt;useRecoilValue,&lt;span&gt;&amp;nbsp;&lt;/span&gt;useSetRecoilState,&lt;span&gt;&amp;nbsp;&lt;/span&gt;useRecoilState의 훅으로 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;// 1.
const [todoList, setTodoList] = useRecoilState(todoListState);

// 2.
const todoList = useRecoilValue(todoListState);

// 3.
const setTodoList = useSetRecoilState(todoListState);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useRecoilState(todoListState)를 사용하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;useState()와 같이 배열의 첫 번째 원소가 상태, 두 번째 원소가 상태를 업데이트하는 함수를 반환하게 된다.&lt;/li&gt;
&lt;li&gt;useRecoilValue(todoListState)는 상태 값만 필요할 경우에 사용하면 된다.&lt;/li&gt;
&lt;li&gt;useSetRecoilState(todoListState)는 상태를 업데이트하는 함수만 필요한 경우 사용하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 상태를 업데이트하는 함수인&lt;span&gt;&amp;nbsp;&lt;/span&gt;setTodoList의 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;useState의 업데이트 함수처럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;setTodoList(todo)&lt;span&gt;&amp;nbsp;&lt;/span&gt;이런식으로 원하는 값을 바로 넣어 사용해도 되며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;setTodoList(prev =&amp;gt; [todo, ...prev])&lt;span&gt;&amp;nbsp;&lt;/span&gt;이런식으로 현재 값을 파라미터로 받아와서 구현할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;php&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;// https://recoiljs.org/docs/basic-tutorial/atoms
function TodoItem({item}) {
  const [todoList, setTodoList] = useRecoilState(todoListState);
  const index = todoList.findIndex(listItem =&amp;gt; listItem === item);

  const editItemText = ({target: {value}}) =&amp;gt; {
    const newList = replaceItemAtIndex(todoList, index, {
      ...item,
      text: value,
    });

    setTodoList(newList);
  };

  const toggleItemCompletion = () =&amp;gt; {
    const newList = replaceItemAtIndex(todoList, index, {
      ...item,
      isComplete: !item.isComplete,
    });

    setTodoList(newList);
  };

  const deleteItem = () =&amp;gt; {
    const newList = removeItemAtIndex(todoList, index);

    setTodoList(newList);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input type=&quot;text&quot; value={item.text} onChange={editItemText} /&amp;gt;
      &amp;lt;input
        type=&quot;checkbox&quot;
        checked={item.isComplete}
        onChange={toggleItemCompletion}
      /&amp;gt;
      &amp;lt;button onClick={deleteItem}&amp;gt;X&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function replaceItemAtIndex(arr, index, newValue) {
  return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)];
}

function removeItemAtIndex(arr, index) {
  return [...arr.slice(0, index), ...arr.slice(index + 1)];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;selector&quot; data-ke-size=&quot;size23&quot;&gt;selector&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A selector represents a piece of derived state. You can think of derived state as the output of passing state to a pure function that modifies the given state in some way.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector가 파생된 상태(derived state)의 조각를 나타낸다고 말하는데, 위에&lt;span&gt;&amp;nbsp;&lt;/span&gt;todo list의 예에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;필터링된 todo list,&lt;span&gt;&amp;nbsp;&lt;/span&gt;todo list의 통계값&lt;span&gt;&amp;nbsp;&lt;/span&gt;등을 파생된 상태라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector는&lt;span&gt;&amp;nbsp;&lt;/span&gt;recoil에서 관리하는 상태의 특정 부분만 선택하거나 상태를 사용하여 연산한 값을 조회할 때도 사용하면서,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다른 데이터에 의존하는 동적 데이터를 구축할 수 있게 해주는 것이다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[참고:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://recoiljs.org/docs/introduction/core-concepts/#selectors&quot;&gt;공식문서 - selector core concepts&lt;/a&gt;]&lt;/p&gt;
&lt;h2 id=&quot;%EC%9D%BD%EA%B8%B0%EC%A0%84%EC%9A%A9-selector&quot; data-ke-size=&quot;size26&quot;&gt;읽기전용 selector&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector는 속성 계산에 사용되는 함수인&lt;span&gt;&amp;nbsp;&lt;/span&gt;get만 쓰인 형태로, 상태(atom이나 다른&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector)를 조회할 수 있는 읽기 전용&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 읽기 전용이기 때문에 업데이트 함수를 주는&lt;span&gt;&amp;nbsp;&lt;/span&gt;useRecoilState와는 사용할 수 없으며&lt;span&gt;&amp;nbsp;&lt;/span&gt;useRecoilValue로만 상태를 조회해야 한다. (key는 내부적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector를 식별하는데 사용되는 고유한 문자열이다.)&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;const fontSizeLabelState = selector({
  key: 'fontSizeLabelState',
  get: ({get}) =&amp;gt; {
    const fontSize = get(fontSizeState);
    const unit = 'px';

    return `${fontSize}${unit}`;
  },
});

function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  const fontSizeLabel = useRecoilValue(fontSizeLabelState);

  return (
    &amp;lt;&amp;gt;
      &amp;lt;div&amp;gt;Current font size: {fontSizeLabel}&amp;lt;/div&amp;gt;

      &amp;lt;button onClick={() =&amp;gt; setFontSize(fontSize + 1)} style={{fontSize}}&amp;gt;
        Click to Enlarge
      &amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 전용&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector에서 아래와 같이 실제로 사용하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;atom이나&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector에 따라 동적으로 종속성이 결정되게 할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;const toggleState = atom({key: 'Toggle', default: false});

const mySelector = selector({
  key: 'MySelector',
  get: ({get}) =&amp;gt; {
    const toggle = get(toggleState);
    if (toggle) {
      return get(selectorA);
    } else {
      return get(selectorB);
    }
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%EC%96%91%EB%B0%A9%ED%96%A5-selector&quot; data-ke-size=&quot;size26&quot;&gt;양방향 selector&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽고 쓰기가 모두 가능한&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector이며, 값을 매개변수로 수신하고 이를 사용하여 데이터 흐름 그래프를 따라 업스트림으로 변경 사항을 전파할 수 있다. 또한 읽고 쓰기가 가능한&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector의 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;useRecoilState&lt;span&gt;&amp;nbsp;&lt;/span&gt;훅을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 추가 필드를 추가하기 위해 원자를 래핑하고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;set&lt;span&gt;&amp;nbsp;&lt;/span&gt;함수를 통해 업스트림&lt;span&gt;&amp;nbsp;&lt;/span&gt;atom으로 전달한다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;const proxySelector = selector({
  key: 'ProxySelector',
  get: ({get}) =&amp;gt; ({...get(myAtom), extraField: 'hi'}),
  set: ({set}, newValue) =&amp;gt; set(myAtom, newValue),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector는 데이터를 변환하므로 들어오는 값이 기본 값인지에 대한 체크를 하고 있다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;const transformSelector = selector({
  key: 'TransformSelector',
  get: ({get}) =&amp;gt; get(myAtom) * 100,
  set: ({set}, newValue) =&amp;gt;
    set(myAtom, newValue instanceof DefaultValue ? newValue : newValue / 100),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;set함수의 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;newValue&lt;span&gt;&amp;nbsp;&lt;/span&gt;값을 그대로 사용해서는 안되기 때문&lt;span&gt;&amp;nbsp;&lt;/span&gt;DefaultValue의 인스턴스인지 확인하는 과정이 추가되었다. 그 이유는 selector의 상태를 기본값으로 초기화하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;useResetRecoilState라는 훅을 대응하기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 아래 있는&lt;span&gt;&amp;nbsp;&lt;/span&gt;TempCelsius&lt;span&gt;&amp;nbsp;&lt;/span&gt;예제에서도 이를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 비동기&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector에 대한 간단한 예시이다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;const myQuery = selector({
  key: 'MyQuery',
  get: async ({get}) =&amp;gt; {
    return await myAsyncQuery(get(queryParamState));
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 동기를 다루는&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector에 대한 전쳬 예시이다. [&lt;a href=&quot;https://codesandbox.io/s/recoil-sync-selector-fdlyr?file=/src/App.js&quot;&gt;전체 코드&lt;/a&gt;]&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import {
  atom,
  selector,
  useRecoilState,
  DefaultValue,
  useResetRecoilState,
} from 'recoil';

const tempFahrenheit = atom({
  key: 'tempFahrenheit',
  default: 32,
});

const tempCelsius = selector({
  key: 'tempCelsius',
  get: ({get}) =&amp;gt; ((get(tempFahrenheit) - 32) * 5) / 9,
  set: ({set}, newValue) =&amp;gt;
    set(
      tempFahrenheit,
      newValue instanceof DefaultValue ? newValue : (newValue * 9) / 5 + 32,
    ),
});

function TempCelsius() {
  const [tempF, setTempF] = useRecoilState(tempFahrenheit);
  const [tempC, setTempC] = useRecoilState(tempCelsius);
  const resetTemp = useResetRecoilState(tempCelsius);

  const addTenCelsius = () =&amp;gt; setTempC(tempC + 10);
  const addTenFahrenheit = () =&amp;gt; setTempF(tempF + 10);
  const reset = () =&amp;gt; resetTemp();

  return (
    &amp;lt;div&amp;gt;
      Temp (Celsius): {tempC}
      &amp;lt;br /&amp;gt;
      Temp (Fahrenheit): {tempF}
      &amp;lt;br /&amp;gt;
      &amp;lt;button onClick={addTenCelsius}&amp;gt;Add 10 Celsius&amp;lt;/button&amp;gt;
      &amp;lt;br /&amp;gt;
      &amp;lt;button onClick={addTenFahrenheit}&amp;gt;Add 10 Fahrenheit&amp;lt;/button&amp;gt;
      &amp;lt;br /&amp;gt;
      &amp;lt;button onClick={reset}&amp;gt;Reset&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tsPRk/btrntc5Vw3e/AxWUuDycl9L0CpF60jgqRk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tsPRk/btrntc5Vw3e/AxWUuDycl9L0CpF60jgqRk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tsPRk/btrntc5Vw3e/AxWUuDycl9L0CpF60jgqRk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/tsPRk/btrntc5Vw3e/AxWUuDycl9L0CpF60jgqRk/img.gif&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;346&quot; height=&quot;178&quot; data-origin-width=&quot;346&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 비동기를 다루는&lt;span&gt;&amp;nbsp;&lt;/span&gt;selector에 대한 전체 예시이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-role=&quot;codeBlock&quot; data-info=&quot;typescript&quot;&gt;&lt;code&gt;import {selector, useRecoilValue} from 'recoil';

const myQuery = selector({
  key: 'MyDBQuery',
  get: async () =&amp;gt; {
    const response = await fetch(getMyRequestUrl());
    return response.json();
  },
});

function QueryResults() {
  const queryResults = useRecoilValue(myQuery);

  return &amp;lt;div&amp;gt;{queryResults.foo}&amp;lt;/div&amp;gt;;
}

function ResultsSection() {
  return (
    &amp;lt;React.Suspense fallback={&amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;}&amp;gt;
      &amp;lt;QueryResults /&amp;gt;
    &amp;lt;/React.Suspense&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;깃북으로 보기&lt;/h3&gt;
&lt;figure id=&quot;og_1639072309431&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Recoil 기초 개념 및 사용법 - 콥 노트&quot; data-og-description=&quot;상태를 정의할 때는 고유값인 key를 설정하고, 기본값(default)을 설정하면 된다. 이렇게 정의한 atom은 useRecoilValue, useSetRecoilState, useRecoilState의 훅으로 사용할 수 있다.&quot; data-og-host=&quot;ajdkfl6445.gitbook.io&quot; data-og-source-url=&quot;https://ajdkfl6445.gitbook.io/study/react/recoil&quot; data-og-url=&quot;https://ajdkfl6445.gitbook.io/study/react/recoil&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://ajdkfl6445.gitbook.io/study/react/recoil&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajdkfl6445.gitbook.io/study/react/recoil&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Recoil 기초 개념 및 사용법 - 콥 노트&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;상태를 정의할 때는 고유값인 key를 설정하고, 기본값(default)을 설정하면 된다. 이렇게 정의한 atom은 useRecoilValue, useSetRecoilState, useRecoilState의 훅으로 사용할 수 있다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajdkfl6445.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React</category>
      <category>React</category>
      <category>Recoil</category>
      <category>상태관리</category>
      <author>제이콥(JACOB)</author>
      <guid isPermaLink="true">https://code-masterjung.tistory.com/128</guid>
      <comments>https://code-masterjung.tistory.com/128#entry128comment</comments>
      <pubDate>Fri, 10 Dec 2021 02:52:12 +0900</pubDate>
    </item>
  </channel>
</rss>