<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[blog.rhostem.com]]></title><description><![CDATA[프론트엔드 웹 개발 기술 블로그]]></description><link>https://blog.rhostem.com/</link><image><url>https://blog.rhostem.com/images/rhostem-profile.jpeg</url><title>blog.rhostem.com</title><link>https://blog.rhostem.com/</link></image><generator>blog.rhostem.com</generator><lastBuildDate>Tue, 27 Dec 2022 15:39:19 GMT</lastBuildDate><atom:link href="https://blog.rhostem.com//rss.xml" rel="self" type="application/rss+xml"/><author><![CDATA[rhostem]]></author><copyright><![CDATA[Copyright © rhostem All rights reserved]]></copyright><item><title><![CDATA[[번역] 자바스크립트 프레임워크의 네 시대]]></title><description><![CDATA[이 글은  Four Eras of JavaScript Frameworks 를 번역한 글입니다.  개발자로 일하며 수많은 기술이 새로 등장하고 사라지는 모습을 반복적으로 보고 있으면 그런 변화와 자극에 조금씩 무심해지는 것 같습니다. 이 글은 지난 십수년간의 프론트엔드…]]></description><link>https://blog.rhostem.com//posts/2022-05-27-Four-Eras-of-JavaScript-Frameworks</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2022-05-27-Four-Eras-of-JavaScript-Frameworks</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Fri, 27 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://www.pzuraq.com/blog/four-eras-of-javascript-frameworks&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Four Eras of JavaScript Frameworks&lt;/a&gt;를 번역한 글입니다. &lt;/p&gt;
&lt;p&gt;개발자로 일하며 수많은 기술이 새로 등장하고 사라지는 모습을 반복적으로 보고 있으면 그런 변화와 자극에 조금씩 무심해지는 것 같습니다. 이 글은 지난 십수년간의 프론트엔드 웹 개발의 흐름을 되짚어보며 지금까지 어떤 일이 있었는지, 지금 어디에 와 있는지 진단하고 있습니다. 그런 거대한 흐름을 이해하고 나면 자신이 서 있는 위치와 함께 나 자신의 모습도 보이게 됩니다. 그리고 앞으로의 미래를 바라보며 내가 무엇을 할 수 있는지, 해야 할 일이 무엇인지 파악할 수 있게 되는 것 같습니다. 이래서 역사 공부가 필요한 것일까요? 🙂&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;나는 자바스크립트를 메인으로 한 개발을 지난 2012년에 시작했다. 나는 어떤 지역 사업체를 위한 웹사이트 개발에 초기부터 참여하여 PHP와 기본적인 CMS를 사용해 만들었다. 그리고 그 업체는 웹사이트를 리뉴얼하면서 많은 기능을 추가하기로 결정했다. 프로젝트 매니저는 나에게 개발에 .NET을 사용하길 원했다(왜냐하면 그가 잘 알고 있는 것이 .NET이었기 때문에). 하지만 페이지 새로고침도 없고, 액션 사이에 긴 딜레이도 없는 네이티브 어플리케이션처럼 보이길 바라기도 했다. 얼마간의 조사와 프로토타이핑 후, 나는 그가 원하는 것이 이제 막 나오기 시작한 최신의 자바스크립트 프레임워크로 웹에서 어플리케이션을 만드는 것과 같은 형태라고 확신했다.&lt;/p&gt;
&lt;p&gt;내가 첫 번째로 선택한 프레임워크는 Angular 1이었다. 나는 FuelPHP 백엔드와 함께 클라이언트 사이드도 상당히 많은 부분을 작업했는데, 개발 중에 오픈소스로 공개된 라우터 라이브러리를 사용하던 도중 이슈에 부딪혔다. 그 라이브러리를 사용하면 서브라우트/outlet을 렌더링할 때마다 화면이 깜빡였는데, 그런 모습은 웹사이트를 처음 설계하면서 기대했던 것이 아니었다. 해결책을 찾던 중 누군가가 Ruby on Rails와 Ember 조합을 추천했는데, 한번 시도해보니 깜빡임 현상 없이 기대한 대로 무척 잘 동작했다. 나는 그 프레임워크의 철학, 커뮤니티가 모두 마음에 들었고, 당시의 다른 대안과 비교해봤을 때 전반적으로 매우 생산적인 개발이 가능했다.&lt;/p&gt;
&lt;p&gt;그때 이후로 많은 것들이 변했다. 프레임워크들은 나타나고, 사라지고, 그리고 급격히 진화했다. 당신이 지금 자바스크립트로 브라우저에서 앱을 만드는 방식은 과거에는 어떤 주변 기술이었지만 지금은 표준 사례가 된 것이다. 그리고 우리가 사용하는 인프라는 완전히 바뀌어서 새로운 것들을 가능하게 해주고 있다.&lt;/p&gt;
&lt;p&gt;당시에는 물론 그 &lt;em&gt;방식&lt;/em&gt;에 대해 무척 많은 경쟁과 충돌이 있었다. 나는 프론트엔드 업계에 한동안 몸을 담아 온 대부분 사람이라면 다양한 논쟁에 참여했을 것이라 생각한다. 그 다양한 것들이란 아마도… 모든 것에 대해서다. 어떤 자바스크립트 프레임워크를 사용할 것인지, 어떻게 CSS를 작성할 것인지, 함수형 vs 객체 지향 프로그래밍, 어떻게 상태를 관리할 것인지, 어떤 빌드 시스템과 도구가 가장 유연하고 빠른 것인지 등등. 돌이켜 볼 때 우리가 얼마나 잘못된 것을 맞다고 주장하면서 더 큰 패턴을 놓치고 있었는지를 생각해보면 무척 웃기다. 물론 시간이 지나서 돌아보고 있으니 그렇게 생각할 수 있긴 하다.&lt;/p&gt;
&lt;p&gt;그래서 나는 회고를 해 보려고 한다. 지난 몇십 년간의 자바스크립트 개발을 뒤돌아보고 우리가 어디까지 왔는지를. 지난 시간은 다음의 네 시대로 나눌 수 있을 것이다.&lt;/p&gt;
&lt;ol&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;/p&gt;
&lt;p&gt;오늘날 논쟁은 더 격해졌다: ‘웹은 너무 덩치가 커져 버렸나?’,  ‘일반적인 웹사이트가 굳이 React로 작성될 필요가 있나?; 심지어 ‘우리는 자바스크립트를 사용해야 하나?’같은 것들이다. 나는 우리가 지금은 미래를 들여다볼 수 없지만 결국에는 다시 새로운 발견을 할 것이라 생각한다. 지금 우리는 서로 다른 이야기를 하면서 큰 그림을 놓치고 있다. 하지만 그런 과거로부터 얻은 작은 발견이 우리를 미래로 나아가게 만든다.&lt;/p&gt;
&lt;h2 id=&quot;예전-시대-the-before-times&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%A0%84-%EC%8B%9C%EB%8C%80-the-before-times&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예전 시대 (The Before Times)&lt;/h2&gt;
&lt;p&gt;자바스크립트는 1995년 처음 릴리즈되었다. 위에서 언급했듯 나는 그로부터 20년이 지난 후인 2012년부터 자바스크립트를 사용하기 시작했고, 당시의 프레임워크에 대해 설명하려고 하고 있다. 나는 이 시대의 많은 역사에 대해서 설명하려 하겠지만, 당신이 상상할 수 있듯이 이 시대는 또 지배적으로 사용되었던 패턴과 라이브러리, 빌드 툴 등에 의해 여러 개의 하위 시대로 나뉠 수 있다.&lt;/p&gt;
&lt;p&gt;다시 말해, 나는 내가 경험하지 않은 것들에 대해서는 얘기할 수 없다. 내가 프론트엔드 앱을 개발하기 시작한 시점에는 새로운 세대의 프레임워크들이 발전 중이었다: Angular.js, Ember.js, Backbone이 그것이었고, 이 외에도 여러 개가 있었다.&lt;/p&gt;
&lt;p&gt;이들이 등장하기 전 첨단의 기술은 jQuery나 MooTools 같은 것이었다. 이 라이브러리들은 당시에는 무척 중요했는데, 브라우저마다 다르게 구현된 자바스크립트의 차이를 줄여주는 역할을 했기 때문이다. 예를 들어 Internet Explorer와 Netscape는 Event 구현에 완전히 다른 방식을 사용했는데, 한쪽은 이벤트 버블링이었고 다른 한쪽은 이벤트 캡쳐링을 사용했다. 그리고 그런 차이가 지금의 웹 표준이 두 방식을 모두 포함하고 있는 이유가 되었으나, 어쨌든 예전에는 두 브라우저에서 모두 동작하는 코드를 구현하려면 라이브러리를 사용해야 했다. 그 라이브러리들은 작고 독립적으로 동작하는 UI 위젯을 만드는 데 주로 사용되었다. 어플리케이션의 주된 비지니스 로직은 여전히 form과 HTTP 리퀘스트에 의해 이뤄졌고, HTML을 서버에서 렌더링하여 클라이언트에서 제공하는 방식을 사용했다.&lt;/p&gt;
&lt;p&gt;그리고 이 시대에는 내가 아는 한 커뮤니티에서 언급되는 빌드 툴이 그렇게 많지 않았다. 자바스크립트는 아직 모듈을 가지고 있지 않았고(최소한 당시에는 표준이 아니었다), 코드를 import할 수 있는 방법도 없었다. 모든 것은 전역 공간에 존재했고, 코드를 정리하기도 쉽지 않았다.&lt;/p&gt;
&lt;p&gt;이런 환경이었으니 자바스크립트가 장난감스러운 언어로 취급당해도 이해할 만 했다. 풀 어플리케이션을 작성하는데 사용할 만한 언어로 인식되지는 않았다. 자바스크립트로 가장 많이 하는 일은 몇 개의 UI 위젯 동작에 필요한 스크립트를 jQuery와 함께 불러온 후 호출하는 것이었다. 시간이 지나면서 XHR이 도입되었고 인기를 얻었으며, 사람들은 클라이언트와 백엔드 사이의 복잡한 플로우가 필요한 UI의 일부분을 싱글 페이지로 구현하기 시작했다. 하지만 앱의 중요한 부분은 여전히 서버에 견고히 유지되었다.&lt;/p&gt;
&lt;p&gt;이런 모습은 모바일 앱이 시장을 강타하기 시작했을 때 큰 대비를 보여줬다. iOS와 Android 앱은 처음부터 Objective C와 Java라는 Serious Languages&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;로 어플리케이션이 모두 구현되었기 때문이다. 게다가 완전히 API 기반이다. 모든 UI 로직은 디바이스 위에서 돌아가며, 서버와의 통신은 단순히 데이터 포맷만 전달하는 것으로 가능하다. 이는 더 나은 사용자 경험을 가능하게 했고, 모바일 앱의 인기는 폭발적으로 성장했다. 그리고 그 인기는 모바일 앱과 웹 앱 중에서 어느 것이 더 나은지 토론하고 있는 오늘날까지 이어지고 있다.&lt;/p&gt;
&lt;p&gt;저런 작업을 전부 자바스크립트로 하는 건 처음에는 비웃음당할만한 일이었다. 하지만 시간이 지남에 따라 점점 더 많은 것들이 야심 차게 구현되어 나갔다. 소셜 네트워크는 채팅과 DM 등의 실시간 기능을 추가했고, Gmail과 Google Docs는 데스크탑 어플리케이션과 똑같은 기능을 브라우저에서 구현할 수 있다는 것을 보여줬다. 그리고 점점 더  많은 기업이 점점 더 많은 사용 사례를 웹 앱으로 구현하기 시작했다. 왜냐면 웹은 어디서든 동작하며 유지보수가 더 쉽기 때문이다. 이제 자바스크립트는 더 이상 사소한 앱을 만드는 언어가 아니라는 사실을 세상에 알리기 시작했다. &lt;/p&gt;
&lt;p&gt;하지만 자바스크립트를 사용한 앱 개발은 여전히 어려운 일이긴 했다. 자바스크립트는 오늘날 그것이 가진 기능을 모두 가지고 있지 않았다. 앞서 말했듯 모듈이라는 개념이 없었으므로 모든 변수는 전역 공간에 있었고, NPM도 없었으므로 개발자는 외부 라이브러리를 직접 다운로드해서 프로젝트 소스코드에서 정적인 파일들이 있는 폴더에 옮겨둔 후 HTML 파일에 script 태그를 추가하여 불러와야 했다. 그리고 당시의 자바스크립트는 오늘날 갖고 있는 기능의 절반도 없었다. 앱의 대부분은 페이지마다 맞춤 형식으로 개발되었는데, 상태 관리와 렌더링 업데이트에 플러그인마다 다른 시스템을 사용하는 식이었다. 이런 이슈들을 해결하기 위해 최초의 프레임워크가 떠오르기 시작했다.&lt;/p&gt;
&lt;h2 id=&quot;최초의-프레임워크&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%B4%88%EC%9D%98-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최초의 프레임워크&lt;/h2&gt;
&lt;p&gt;2000년대 말과 2010년대 초반 최초의 JS 프레임워크가 풀 클라이언트 어플리케이션 구현에 맞춰 설계되었고 등장하기 시작했다. 이 시대의 주목할 만한 프레임워크 중 일부분은 다음과 같았다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://backbonejs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Backbone.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://angularjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Angular 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://knockoutjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Knockout.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sproutcore.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;SproutCore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://emberjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Ember.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.meteor.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Meteor.js&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;물론 이 외에도 많은 프레임워크가 있다, 그리고 아마 어떤 면에서 더 큰 규모를 가진 것도 있었을 것이다. 이들은 상대적으로 인기가 좋았고 내가 프로토타이핑이나 개발에 직접 사용했기에 기억하는 것들이다.&lt;/p&gt;
&lt;p&gt;그들은 미지의 영토에 발을 들여놓으려는 세대의 프레임워크들이었다. 달리 말하면, 그들이 하려는 것은 매우 야심적이고, 많은 사람이 결코 할 수 없을 것이라 생각했던 것이었다. 당시 단일 페이지(single-page) 자바스크립트는 근본적으로 나쁘다고 비방하는 사람들이 많았다. 그건 많은 면에서 사실이었다. 클라이언트 사이드 렌더링은 검색 엔진이 페이지를 쉽게 크롤링하지 못하도록 했고, 유저는 웹페이지가 페인팅을 시작하기 전까지 몇초 이상 기다려야 하기도 했다. 많은 앱이 웹 접근성에서 최악이었고, 심지어 브라우저에서 자바스크립트 기능을 꺼 버리면 아예 동작하지 않았다.&lt;/p&gt;
&lt;p&gt;그러나, 우리는 자바스크립트만으로 앱을 만들었던 경험이 없었기에, 모두 단결하여, 어떤 방법이 최선일지에 대한 아이디어를 끊임없이 쏟아냈다. 대부분의 프레임워크는 다른 프레임워크에 기반한 변형품들이었다. 그래서 그들 대부분이 모델-뷰-something 패턴(Model-View-Controller, Model-View-Producer, Model-View-ViewModel, 기타 등등)의 반복이었다. 이들 중 무엇도 그 길었던 경주를 끝내지 못했다. 어떤 프레임워크도 특별히 직관적이지도 못했고, 사용하다 보면 순식간에 복잡해져 버리곤 했다.&lt;/p&gt;
&lt;p&gt;또 이 시대는 자바스크립트를 &lt;em&gt;컴파일&lt;/em&gt;하는 방법에 대한 제대로 된 실험이 시작된 시대이기도 하다. Node.js는 2009년에 릴리즈되었고, 이어서 2010년에 NPM이 패키지(서버 사이드)를 도입하면서 등장했다. CommonJS와 AMD는 JS 모듈을 어떻게 정의하는 것이 최선인지에 대해 경쟁했고, Grunt, Gulp, Broccoli 같은 빌드 툴은 모듈들을 어떻게 프로덕션 빌드에 묶어낼 것인지를 놓고 경쟁했다. 이들 대부분은 일반적인 태스크 러너 툴이었는데, 무엇이든 빌드할 수도 있었다. 그러다 보니 자연스럽게 웹 앱 안에 들어가는 요소인 자바스크립트, HTML, CSS/SASS/LESS, 그리고 이 외의 다양한 것들을 빌드하는 데 쓰이게 되었다. &lt;/p&gt;
&lt;p&gt;우리는 이 시대에서 매우 중요하고 근본적인 교훈을 배웠다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;URL 기반의 라우팅은 필수적이다. 그것을 갖고 있지 않은 앱은 웹을 망가뜨린다. 이는 프레임워크의 시작부터 고려되어야 한다.&lt;/li&gt;
&lt;li&gt;템플릿 언어로 HTML을 확장하는 것은 강력한 추상화 계층이다. 다소 투박하게 느껴질지는 몰라도, UI를 앱의 상태와 동기화시키는 것을 더 쉽게 만들어준다.&lt;/li&gt;
&lt;li&gt;SPA에서 퍼포먼스를 높이는 것은 어렵다, 그리고 네이티브 앱에 없는 많은 제한 요소가 있다. 우리는 앱의 코드를 전부 네트워크에 실어서 전송해야 하는데, 그 전송을 가능한 빨리 끝내고 앱을 실행시켜야 한다. 반면 네이티브 앱은 이미 다운로드와 컴파일이 끝난 상태로 제공된다. 웹 앱을 시작하기 위한 그 일련의 과정들은 엄청난 작업이다.&lt;/li&gt;
&lt;li&gt;자바스크립트는 언어로서 많은 이슈를 가지고 있었다. 그리고 개선되어야 할 부분이 정말로 많았다. 그걸 프레임워크가 혼자 할 수는 없다.&lt;/li&gt;
&lt;li&gt;우리가 큰 규모의 앱을 구현하기 위해서는 정말로 더 좋은 빌드 툴, 모듈, 패키징이 필요했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;전반적으로, 이 시대는 무척 생산적이었다. 여러 결점에도 불구하고 많은 경우에서 백엔드 API에서 클라이언트를 분리함에서 오는 이익이 앱의 사이즈에 비례해서 커졌고, 그에 따르는 사용자 경험이 뛰어났다. 만약 역사가 조금 달랐더라면, 이 시대가 계속되어 우리는 지금도 여전히 MV* 스타일을 가진 프레임워크를 계속 만나보고 있었을지도 모른다.&lt;/p&gt;
&lt;p&gt;하지만 어디에선가 소행성이 하나 나타나 존재하는 패러다임을 완전히 깨부숴 버렸고, 일부 종의 멸종을 유발하며 우리를 다음 시대로 향하게 했다. 그 소행성의 이름은 React다.&lt;/p&gt;
&lt;h2 id=&quot;컴포넌트-중심-뷰-레이어&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%A4%91%EC%8B%AC-%EB%B7%B0-%EB%A0%88%EC%9D%B4%EC%96%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트 중심 뷰 레이어&lt;/h2&gt;
&lt;p&gt;나는 React가 컴포넌트를 발명했다고 생각하지 않는다. 하지만 솔직히 컴포넌트가 언제 처음 등장했는지는 확실히 잘 모르겠다. 최소한 닷넷의 XAML에 유사한 기술이 있었고, 웹 컴포넌트도 그 시기에 스펙 개발이 시작되고 있었다. 궁극적으로는 뭐가 처음이었는지는 상관없다. 어떤 아이디어가 등장하면, 모든 메이저 프레임워크는 그것을 재빠르게 수용한다.&lt;/p&gt;
&lt;p&gt;컴포넌트는 모든 것이 잘되도록 만들었다. HTML을 확장하고, 전역에서 오랫동안 살아남는 상태 변수를 줄이고, 자바스크립트 비즈니스 로직이 템플릿에 확실히 연결되도록 했다(JSX, Handlebar 또는 Directives 들이 그렇게 만들었다). 컴포넌트 기반의 어플리케이션은 기존 방식에서 필요했던 추상화의 대부분을 제거했다. 그리고 코드의 라이프사이클을 현저히 단순하게 만들었다. 모든 것은 앱이 아닌 컴포넌트의 라이프사이클과 묶이게 되었고, 개발자로서는 구현 중에 고려해야 할 사항을 훨씬 적게 만들었다.&lt;/p&gt;
&lt;p&gt;하지만, 그 시기에 또 다른 도약도 있었는데, 모든 기능을 갖춘 프레임워크가 아닌  “뷰 레이어” 프레임워크라는 말로 자신을 홍보하는 프레임워크들의 등장이었다. 그들은 프론트엔드 앱에 요구되는 모든 문제를 해결하기보다는, 오직 렌더링을 이슈를 해결하는 데 초점을 맞추고 있었다. 프론트엔드 앱의 다른 문제로는 API 통신, 상태 관리 등이 있는데 그것들은 사용자들에게 떠넘겨졌다. 이 시기의 유명한 프레임워크들은 다음과 같다. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vuejs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Vue.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://svelte.dev/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Svelte&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://polymer-library.polymer-project.org/3.0/docs/devguide/feature-overview&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Polymer.js&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;그리고 이 외에도 많고 많은 프레임워크가 있다. 돌이켜보면 뷰 레이어에 초점을 맞춘 두 번째 세대의 프레임워크의 인기는 다음의 두 가지를 가능하게 만들었기 때문이라고 본다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;스쿠프(scope)를 엄청나게 줄여버렸다. 눈앞에 높인 모든 문제를 해결하려고 하는 대신, 프레임워크의 핵심은 렌더링에 집중하고 있다. 상태 관리 같은 다른 문제들에 대해선 개발 생태계 속에서 많은 아이디어와 방향이 탐구되었다. 그중에는 끔찍한 솔루션들도 무척 많았지만, 좋은 것들도 있어서 다음 세대가 최선의 아이디어를 선택할 수 있도록 길을 닦아 주었다.&lt;/li&gt;
&lt;li&gt;프레임워크를 도입하기 쉽게 만들었다. 모노리스(monolith) 서버 사이드 앱에게 풀 프레임워크를 도입한다는 것은 사실 기존의 앱의 대부분을 새로 작성한다는 말과 같다. 하지만 React나 Vue같은 프레임워크는 서비스 중인 앱에도 중간에 도입해서 1개의 위젯이나 컴포넌트로부터 시작해서 기존의 코드를 점진적으로 마이그레이션 하는 것이 가능하다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 두 요소는 두 번째 시대의 프레임워크가 빠르게 성장하도록 하면서 첫 번째 시대가 저물도록 만들었다. 그리고 멀리서 보면 모든 것이 잘 들어맞고 논리적인 혁신을 이룬 것처럼 보인다. 하지만 저 시대를 지나는 중에는 때때로 꽤 좌절스러운 경험도 했었다.&lt;/p&gt;
&lt;p&gt;우선 저 관점은 어떤 프레임워크를 업무에 도입할지, 또는 앱을 새로 만들어야 하는지 토론할 때 자주 만나는 것은 아니었다. 대신, “더 빠르다!” 라거나, “더 작다!”, 또는 “당신에게 필요한 모든 것!”이라는 문구를 더 자주 접하게 된다. 또 함수형 프로그래밍이냐-객체 지향 프로그래밍이냐에 대한 토론도 있었다. 그리고 공정하게 얘기하면 이것들은 모두 사실이긴 했다. 뷰 레이어 컴포넌트는 작고(처음에는) 그리고 빠르고(처음에는) 그리고 당신에게 필요한 모든 것이다(당신이 만약 다른 많은 것들을 스스로 한땀 한땀 구현한다면). 그리고 확실히 함수형 프로그래밍 패턴은 자바스크립트를 오염시킨 수많은 문제를 해결했다. 그리고 나는 평균적인 자바스크립트 코드의 품질이 그들 덕분에 좋아졌다고 생각한다.&lt;/p&gt;
&lt;p&gt;하지만 현실은, 은제 탄환(silver bullet)따위는 없으며, 결코 있을 수 없다. 뷰 중심의 프레임워크들을 사용해도 여전히 앱은 비대해지고 복잡해진다. 상태는 여전히 관리하기 어렵고, 라우팅이나 SSR같은 근본적인 문제도 풀어야 할 문제다. 그리고 대부분의 사람은 마치 모든 것을 해결하려는 솔루션을 버리고 사용자에게 그 연습을 맡기는 프레임워크를 원하는 것 같았다. 내 경험상 이것은 새로운 제품이나 기능을 출시하려는 엔지니어링 조직에 보편적으로 존재하는 현상이다. 그리고 계획했던 그 모든 기능을 개발하는 데 시간을 충분히 투자하지 못하곤 한다.&lt;/p&gt;
&lt;p&gt;하지만 한편으로는 기존의 첫 번째 시대 풀서비스 프레임워크도 이 문제를 제대로 풀지 못하고 있었다. 부분적으로, 이는 수많은 기술적 부채 때문이었다. 첫 번째 시대의 프레임워크는 ES6이 나오기 전, 모듈이 있기 전, Babel과 Webpack이 있기 전이었으며, 그리고 우리가 찾아낸 수많은 해법이 존재하기 전에 만들어졌었다. 그 기술들을 꾸준히 도입하며 프레임워크를 발전시키기는 것은 무척 어려운 일이다(나는 이전에 Ember 프레임워크의 코어 팀 멤버였기 때문에 이를 잘 알고 있다). 그리고 Angular 2가 그랬던 것처럼 프레임워크를 완전히 새로 개발하는 것은 커뮤니티 활동 모멘텀을 크게 낮춰버리는 일이기도 하다. 그러므로 자바스크립트 프레임워크 세계로 오는 개발자들은 바위가 있는 힘든 환경에 오는 것과 같았다. 생긴지 얼마 안 된 올인원(all-in-one) 솔루션을 선택하거나, 모든 것을 자유롭게 고르되 그 길이 최선이길 바라며 프레임워크의 절반을 DIY 해야 한다.&lt;/p&gt;
&lt;p&gt;결과적으로(내 경험상 이런 일이 더 자주 일어났다) 이들 뷰 레이어와 함께 구성된 수제 프레임워크가 만들어지고, 그것들은 또 커지고, 복잡해지고, 작업하기가 매우 어렵게 되어버린다. 사람들이 SPA를 사용하면서 생기는 많은 문제는 파편화된 에코시스템이며, 그 문제는 SPA가 폭발적으로 성장하기 시작한 바로 그 시기에서 왔다. 나는 여전히 라우팅을 제대로 못 하거나 디테일을 챙기지 못하는 신규 웹사이트들을 자주 마주치곤 하는데, 그럴 때마다 큰 실망감을 느끼곤 한다.&lt;/p&gt;
&lt;p&gt;내가 말했듯 이 시기는 무척 좌절스러웠지만, 결과적으로는 혁신적인 기술이 아주 많이 등장했다. 자바스크립트 생태계는 정말로 빠르게 성장했기에 이들 프레임워크는 그들에게 필요한 최선이 무엇인지 찾아냈다. 그리고 또 몇몇 핵심적인 변화가 일어났다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Babel 같은 트랜스파일러의 사용이 일반화되어 언어를 현대화했다. 새로운 기능이 언어의 표준이 되기까지 몇 년을 기다리는 대신 오늘 당장 사용할 수 있게 되었다. 그리고 자바스크립트 언어 자체도 더 빠른 주기로 새로운 기능을 추가하기 시작했다.&lt;/li&gt;
&lt;li&gt;ES 모듈이 표준화되어 마침내 현대적인 빌드 툴인 Rollup, Webpack, 그리고 Parcel 등이 개발될 수 있도록 했다. Import 기반의 번들링은 서서히 일반되었고, 이제 자바스크립트가 아닌 스타일시트, 이미지 파일 같은 자원들도 번들링이 가능하다. 설정이 단순해지면서 빌드 툴들은 전반적으로 더 가볍고, 더 빠른 성능을 보여준다.&lt;/li&gt;
&lt;li&gt;점점 많은 API가 표준화되면서 Node와 웹 표준 사이의 차이가 조금씩이지만 확실히 줄어들었다. SSR은 실제로 가능성을 가지기 시작했고, 어떤 앱들은 이미 사용하고 있었다. 하지만 여전히 매번 앱마다 맞춤 설정이 필요했다.&lt;/li&gt;
&lt;li&gt;엣지 컴퓨팅이 공개되었고, 자바스크립트 기반의 서버 앱에 배포/응답 시간 측면에서 SPA에 있는 장점을 제공해줬다(SPA는 일반적으로 사용자로부터 가까이 있는 CDN에 있는 정적인 파일들 덕분에 로딩 시작을 빨리할 수 있었다. 비록 종단의 디바이스에서는 그 로딩이 완전히 끝나 렌더링이 되기까지 조금 더 오래 걸린다고 하더라도).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 시대의 끝에, 일부 문제는 여전히 남아 있었다. 예전보다는 더 좋은 패턴을 찾아냈음에도 상태 관리와 반응성은 여전히 어려운 문제였다(그리고 지금도 그렇다). 퍼포먼스는 여전히 어려운 문제다. 상황은 조금씩 나아지고 있긴 하지만 여전히 헤아릴 수 없이 많은 수의 비대한 SPA가 웹에 존재한다. 그리고 웹 접근성 상황은 개선되었지만, 기술 조직에서는 여전히 사후 고려 대상이었다(그리고 지금도). 하지만 이러한 변화들이 다음 시대의 프레임워크를 위한 길을 닦았으며, 이제 그들에 대해 이야기하려고 한다.&lt;/p&gt;
&lt;h2 id=&quot;풀-스택-프레임워크&quot;&gt;&lt;a href=&quot;#%ED%92%80-%EC%8A%A4%ED%83%9D-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;풀-스택 프레임워크&lt;/h2&gt;
&lt;p&gt;개인적으로는 프레임워크의 마지막 시대에서 무척 슬픈 감정을 느꼈다. 그건 내가 지난 4년 동안 Ember의 렌더링 레이어에 매우 깊이 참여하며 앞서 말한 첫 번째 시대의 프레임워크들이 가진 기술 부채들을 해결하려 노력하면서 시간을 보냈기 때문이라고 생각한다. 그리고 3번째 세대의 모든 프레임워크들은 훨씬 더 교묘하게, 이전 세대의 뷰 레이어 프레임워크를 기반으로 만들어졌기 때문이기도 하다. 주목할 만한 것들은 다음과 같다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Next.js&lt;/a&gt; (React)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nuxtjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Nuxt.js&lt;/a&gt; (Vue)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://remix.run/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Remix&lt;/a&gt; (React)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kit.svelte.dev/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;SvelteKit&lt;/a&gt; (Svelte)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gatsbyjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Gatsby&lt;/a&gt; (React)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://astro.build/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Astro&lt;/a&gt; (Any)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이들 프레임워크는 뷰 레이어의 고도화에서부터 시작했다. 이제 우리는 모두 뷰 레이어 컴포넌트는 프레임워크의 상위에 올라가는 핵심 요소이며, 앱의 다른 부분인 라우터, 빌드 시스템, 폴더 구조 등의 표준화를 시작하는 것이 합리적인 것이라고 동의한다. 느리지만 확실히, 이 메타-프레임워크들은 첫 번째 세대가 제안했던 즉시 사용할 수 있는 올인원 솔루션을 만들기 시작했다. 필요한 기능들은 오픈소스 생태계에서 최선의 패턴을 골라내어 충분히 성숙한 상태에 있다면 프레임워크에 통합하고 있다.&lt;/p&gt;
&lt;p&gt;그러고 나서 그들은 한 발짝 더 나아갔다.&lt;/p&gt;
&lt;p&gt;지금 시점까진 SPA는 전적으로 클라이언트에 포커스를 맞추고 있었다. SSR은 모든 프레임워크가 간절히 해결하길 바랐으나, 궁극적으로는 서버에서 렌더링 된 HTML을 메가바이트 단위의 자바스크립트 로딩이 완료된 후 교체하기 위한 최적화 수단이었다. 1세대 프레임워크 중 오직 하나, Meteor.js만이 더 크게 생각했었지만 그들의 아이디어인 동형(isomorphic) JS는 결코 뜨지 못했다.&lt;/p&gt;
&lt;p&gt;하지만 그 아이디어는 앱의 사이즈와 복잡도가 커지면서 다시 주목받았다. 우리는 그것이 백엔드와 프론트엔드가 함께할 때 정말 유용하다는 것을 알아차렸다. 그렇게 하면 어떤 리퀘스트에 사용할 API 시크릿을 숨기거나, 페이지를 리턴할 때 헤더를 수정하거나, API 리퀘스트 프록시를 구성하는 것 같은 일이 가능하다. 그리고 Node와 Deno가 점점 더 많은 웹 표준을 구현하고, 서버 사이드 JS와 클라이언트 사이드 JS의 간격이 매년 줄어들면서, 이제는 동형 JS가 그렇게 미친 아이디어는 아니었다고 여겨지기 시작한 것 같다. 이것과 엣지 컴퓨팅, 그리고 멋진 도구들을 함께 조합하면 당신은 놀랄 만한 가능성을 얻게 된다.&lt;/p&gt;
&lt;p&gt;이 마지막 세대의 프레임워크들은 그 가능성을 전부 활용하여 클라이언트와 서버를 끊어짐 없이 연결하는데, 나는 이것이 얼마나 놀라운지 말로 표현할 수가 없다. 내가 지난 9개월간 SvelteKit을 사용하며 작업을 했었는데, 몇 번이나 “이게 바로 우리가 해야 했던 방식이야”라고 중얼거리며 의자에 등을 기댔는지 셀 수가 없을 정도다.&lt;/p&gt;
&lt;p&gt;다음은 최근 내가 믿을 수 없을 정도로 쉽게 했던 작업의 일부다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;어플리케이션에 서버 사이드 OAuth 인증을 추가해서 인증 토큰이 결코 서버 밖으로 나가지 않도록 했다. 우리의 API에 리퀘스트를 보낼 때마다 헤더에 토큰을 추가해주는 API 프록시도 함께 구성했다.&lt;/li&gt;
&lt;li&gt;프록시를 사용해 앱의 특정 라우트를 우리의 CDN으로 연결되도록 하여, 어떤 프레임워크에서 만들어진 HTML 페이지든 호스팅이 가능하도록 했다. 이를 통해 유저들이 그들의 커스텀 페이지를 만들 수 있었다(우리의 고객 중 일부에게 제공되는 서비스).&lt;/li&gt;
&lt;li&gt;시크릿 키가 필요한 외부 서비스가 필요할 때 여러 가지 1회용 API 라우트를 추가했다(우리의 API에 완전히 새로운 라우트를 추가할 필요가 없고 백엔드 개발자들과 조율할 필요도 없다).&lt;/li&gt;
&lt;li&gt;LaunchDarkly가 사용되는 곳을 서버 사이드로 옮김으로써 더 적은 자바스크립트를 로드하여 전체적인 비용을 줄였다.&lt;/li&gt;
&lt;li&gt;백엔드 라우트에 Sentry 리퀘스트를 위한 프록시를 추가하여 광고 차단 확장 프로그램 때문에 기록되지 않는 에러를 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이는 빙산의 일각일 뿐이다. 이 모델에서 할 수 있는 멋진 일들은 이 외에도 아주 많이 있다. 가장 큰 일 중 하나는 점진적 향상(&lt;a href=&quot;https://en.wikipedia.org/wiki/Progressive_enhancement&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;progressive enhancement&lt;/a&gt;)에 어떻게 다시 활력을 불어넣느냐다. 서버와 클라이언트가 결합한 특징을 사용하여 클라이언트 디바이스에 자바스크립트가 비활성화되었을 때 완전한 앱 대신 기본적인 HTML과 HTTP를 제공받을 수 있게 할 수 있다. 내가 SPA를 사용하기 시작했을 때, SPA가 클라이언트 개발의 미래라 생각했었기에 점진적 향상의 구현을 완전히 포기했었다. 하지만 이제 그것이 다시 돌아오기 시작한 모습을 보고 있으니 너무나 기쁘다.&lt;/p&gt;
&lt;p&gt;지금까지 말한 내용이 이 프레임워크들을 새로운 세대로 분류하게 만든(어디까지나 실험적인 분류다) 새로운 모습들이다. 이전에는 해결이 어렵거나 불가능했던 문제들은 이제 응답 처리 로직을 조금 바꿈으로서 처리되는 사소한 문제가 되었다. 확실한 성능과 UX는 이제 별도의 설정 없이 즉시 사용하다. 새로운 서비스를 구성하고 만드는 대신 별도의 엔드포인트를 추가하거나 미들웨어를 필요한 만큼 추가할 수 있게 되었다. 그냥, 삶이 달라졌다. &lt;/p&gt;
&lt;p&gt;나는 이 세대가 1세대 프레임워크, 2세대 프레임워크, 그리고 유저들 사이에 있는 주요 긴장 지점도 해결했다고 생각했다. 무설정(zero-config) 방식으로 옮겨가는 데서 시작했지만, 나는 그것이 궁극적으로는 2세대 프레임워크를 발전시키고 안정화하려고 노력한 오픈소스 생태계가 이끌었다고 생각한다. 그것은 문화적인 변화였다. 세 번째 세대의 프레임워크는 렌더링만이 아닌 프론트엔드 개발에서 우리가 해결해야 하는 모든 근본적인 문제를 해결하려고 하면서 이제 다시 첫 번째 세대 같은 올인원 솔루션이 되려 하고 있다. &lt;/p&gt;
&lt;p&gt;지금 커뮤니티들은 어느 때보다 일치단결하여 SPA가 유발한 많은 문제를 해결하려고 노력하는 것처럼 보인다. 그리고 중요한 사실은, 함께 해결하고 있다는 것이다.&lt;/p&gt;
&lt;h2 id=&quot;다음은-어디로-갈까&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%9D%8C%EC%9D%80-%EC%96%B4%EB%94%94%EB%A1%9C-%EA%B0%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다음은 어디로 갈까?&lt;/h2&gt;
&lt;p&gt;전반적으로 자바스크립트 커뮤니티는 올바른 방향으로 가고 있다고 생각한다. 마침내 우리는 “오직 뷰 레이어”만이 아닌 풀 어플리케이션을 처음부터 만들어낼 수 있는 성숙한 솔루션을 개발하고 있다. 마침내 우리는 모든 도구가 처음부터 바로 제공되는 네이티브 앱의 SDK와 같은 필드에서 경쟁할 수 있게 되었다. 하지만 여전히 해야 할 일들은 많이 남아있다. SPA에서 웹 접근성은 언제나 뒷전이었다. 그리고 나는 여전히 GraphQL 외부에서 데이터 스토리가 일부 작업을 사용할 수 있을 것으로 생각한다(그걸 좋아하든 아니든, 대부분의 웹은 REST에서 동작한다). 어쨌든 지금의 흐름은 올바르다. 만약 우리가 계속 서로의 솔루션을 공유하는 방향으로 나아간다면 어느 때보다 좋은 방법으로 이 문제들을 해결할 수 있으리라 생각한다.&lt;/p&gt;
&lt;p&gt;나는 이 발전 패턴이 더 위쪽으로, 즉 웹 플랫폼 그 자체에 적용되었을 때의 잠재성에 대해서도 무척 기대하고 있다. 웹 컴포넌트는 조용하지만, 여전히 진행되고 있고, SSR과 전역 등록 이슈 해결을 위한 작업이 진행 중이다. 그 스펙은 3세대 프레임워크들과 더 높은 호환성을 가지게 해 줄 것이다. 한편, 웹 어셈블리도 이 발전 패턴을 적용하여 놀라운 결과를 낳도록 만들 수도 있다. 어떤 언어로도 작성할 수 있는 풀-스택 프레임워크를 상상해보라. 동형 러스트, 동형 파이썬, 동형 스위프트, 동형 자바 등등은 마침내 프론트엔드와 백엔드 사이의 장벽을 시스템 끝부분에 아주 조금의 HTML 템플릿만 남겨두고 거의 사라지도록 만들어 버릴 수도 있다(역설적으로 그것은 우리에게 ‘완전함’과 더 나은 UX를 가져다준다). &lt;/p&gt;
&lt;p&gt;지금 나에게 미래에 대한 희망이 커 보이는 이유는 우리가 파편화의 시대를 지나왔다는 사실 때문이다. 매일 매일 새로운 자바스크립트 프레임워크가 등장하는 시대는 이제 지났다. 자유와 유연성은 혁신을 낳았지만, 그것들은 지저분하고, 끊어져 있고, 종종 근본적으로 망가진 웹 경험을 낳기도 했다. 개발자들이 한정된 자원과 빡빡한 일정 속에서 수십 가지 옵션들 중에서 고른 도구들을 직접 조립해야 했으니 그럴 만도 했다. 그 결과 어떤 앱들은 놀라울 정도로 빠르고, 안정적이고, 신뢰할 수 있고 사용하기 즐겁지만, 반면 어떤 다른 앱들은 불편하고, 어렵고, 느리고, 망가져 있었다.&lt;/p&gt;
&lt;p&gt;우리는 “처음부터 올바른 일을 해주는” 도구를 사용함으로써 개발이 더 쉬워지게 만들 수 있다. 그러면 어쩌면 웹사이트의 평균적인 품질이 조금 더 좋아질 수 있고, 평균적인 웹 경험이 더 부드러워 질 수 있다. 좋은 도구가 모든 사이트를 좋게 만들지는 못한다. 코드의 양은 나쁜 UX에 대한 해결책이 될 수 없다. 하지만 같은 기반을 제공함으로써 모든 사이트는 조금 더 나은 상태에서 시작하게 될 것이다. 그리고 모든 개발은 보다 더 중요한 일에 집중할 수 있게 될 것이다.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;C 언어처럼 다양한 형태의 소프트웨어를 구현하는데 사용할 수 있는 언어. Serious란 단순히 ‘작업량이 많다’는 것을 의미한다. 같은 내용을 구현하는데 스크립팅 언어는 더 짧은 시간 안에 작성 가능하다. 기계어는 궁극의 ‘serious’ 언어라고 할 수 있다. 출처 - &lt;a href=&quot;https://wiki.c2.com/?SeriousVersusScriptingLanguages&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://wiki.c2.com/?SeriousVersusScriptingLanguages&lt;/a&gt;&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[React hook 테스트 코드에서 Recoil snapshot 참조하기]]></title><description><![CDATA[React의 커스텀 훅 함수를 테스트하다가 아래와 같은 상황을 만나게 되었다. 커스텀 훅 C는 Recoil 상태를 업데이트하는 로직을 가지고 있다. Recoil 상태 A는 커스텀 훅 함수 안에서 업데이트되고, 리턴된다. Recoil 상태 B는 커스텀 훅 함수 안에서 …]]></description><link>https://blog.rhostem.com//posts/2022-03-13-ref-snapshot-from-react-hook-testing</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2022-03-13-ref-snapshot-from-react-hook-testing</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 13 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;React의 커스텀 훅 함수를 테스트하다가 아래와 같은 상황을 만나게 되었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;커스텀 훅 C는 Recoil 상태를 업데이트하는 로직을 가지고 있다.&lt;/li&gt;
&lt;li&gt;Recoil 상태 A는 커스텀 훅 함수 안에서 업데이트되고, 리턴된다.&lt;/li&gt;
&lt;li&gt;Recoil 상태 B는 커스텀 훅 함수 안에서 업데이트되지만, 리턴은 되지 않는다. 즉 커스텀 훅 C를 통해서는 상태 B에 접근할 수 없다.&lt;/li&gt;
&lt;li&gt;테스트 코드에서 커스텀 훅 C에 의한 상태 A의 변경은 물론, 상태 B의 변경도 확인하려고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;구체적인 예를 들자면 이미지 목록과 선택된 이미지를 관리하는 상태가 있다고 가정한다. 커스텀 훅에서는 이미지 목록을 백엔드 서버로부터 가져온 후 목록 상태를 업데이트하고, 선택된 이미지는 초기화하는 로직을 가진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; atom &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;recoil&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 이미지 목록&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imageListState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;imageListState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 선택된 이미지&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; selectedImageState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;selectedImageState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useImageList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;images&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setImages&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imageListState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; setSelectedImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSetRecoilState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImageState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://test.api.com/images&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setImages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 이미지 목록 상태 업데이트 &lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setSelectedImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 선택된 이미지 초기화&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;setImages&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setSelectedImage&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    images&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러면 위의 훅 함수로 이미지를 가져온 후 상태 업데이트를 확인하는 테스트 코드는 아래와 같이 작성할 수 있다. 네트워크 리퀘스트의 mocking에는 &lt;a href=&quot;https://mswjs.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;msw&lt;/a&gt;를 사용하고, 커스텀 훅의 실행에는 &lt;a href=&quot;https://github.com/testing-library/react-hooks-testing-library&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@testing-library/react-hooks&lt;/a&gt;를 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;useImageList&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;beforeAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    mockServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// start msw server&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token function&quot;&gt;afterEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	  mockServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetHandlers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// reset msw server&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;afterAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    mockServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// close msw server&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;이미지 목록 fetch 후 선택된 이미지는 초기화된다.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    mockServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://test.api.com/images&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&apos;https://cdn.images.com/collectionA/1.png&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&apos;https://cdn.images.com/collectionA/2.png&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderHook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      wrapper&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;RecoilRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;RecoilRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;images&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// TODO: 그런데, selectedImageState의 확인은 어떻게?&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 테스트 코드로는 이미지를 가져오는 것은 확인할 수 있지만, &lt;code class=&quot;language-text&quot;&gt;selectedImageState&lt;/code&gt;  상태가 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 값으로 바뀌었는지는 확인할 수 없다. useImageList 훅이 그 상태 값을 리턴하지 않기 때문이다. &lt;/p&gt;
&lt;p&gt;그럼 필요한 값을 훅에서 리턴하면 간단하지 않은가?라고 생각할 수 있다. 하지만 그 말은 실제로는 사용하지 않으면서 오직 테스트를 위해 Recoil 상태를 구독해야 한다는 의미가 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useImageList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; selectedImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImageState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	  selectedImage
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 커스텀 훅을 목적에 따라 분리해서 개발하다 보면 어떤 상태 관리 도구를 쓰든 특정 상태를 여러 커스텀 훅에서 업데이트하게 되는 상황이 종종 생긴다. 그럴 때마다 테스트를 위해 위와 같은 코드를 추가해야 할까? &lt;code class=&quot;language-text&quot;&gt;useSelectedImage&lt;/code&gt;라는 목적이 분명한 커스텀 훅이 있는데도?&lt;/p&gt;
&lt;p&gt;이 문제는 통합 테스트(Integration test)를 통해 자연스럽게 해결할 수도 있다. 테스트 코드에서 커스텀 훅을 사용하는 컴포넌트를 직접 렌더링 하여 서버에서 불러온 이미지 목록이 표시되고, 기존에 선택된 이미지는 해제되는지 확인하는 것이다. 하지만 통합 테스트는 리소스가 많이 사용되어 유닛 테스트보다 느리고, 유지 보수에 따른 디버깅이 더 어렵다는 단점이 있다. 예를 들어 이미지 목록 컴포넌트가 수정된다면 통합 테스트에서 이미지를 찾는데 사용하는 쿼리도 수정해야 할 가능성이 생기기 때문이다. 그리고 만약 유닛 테스트가 잘 작성되어 있다면 상태 관리 로직에 오류가 있는 것인지, 단순히 마크업이 변경되어서 생기는 테스트 실패인지 쉽게 파악할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;recoil-상태를-엿보기-위한-컴포넌트-구현&quot;&gt;&lt;a href=&quot;#recoil-%EC%83%81%ED%83%9C%EB%A5%BC-%EC%97%BF%EB%B3%B4%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EA%B5%AC%ED%98%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Recoil 상태를 엿보기 위한 컴포넌트 구현&lt;/h2&gt;
&lt;p&gt;커스텀 훅 함수에서 리턴하지 않는 Recoil 상태를 확인하기 위해서는 다른 방법이 필요하다. 필요한 요구사항은 다음과 같다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;renderHook&lt;/code&gt; 함수가 호출된 스쿠프에서 선언된 변수에 커스텀 훅에 의한 상태 변경을 반영하는 Recoil snapshot이 할당되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recoil 상태의 구독은 &lt;a href=&quot;https://recoiljs.org/ko/docs/api-reference/core/useRecoilSnapshot/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;useRecoilSnapshot&lt;/a&gt; 훅을 사용해서 가능하다. &lt;a href=&quot;https://recoiljs.org/ko/docs/api-reference/core/Snapshot&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Snapshot&lt;/a&gt;을 사용하면 &lt;code class=&quot;language-text&quot;&gt;RecoilRoot&lt;/code&gt; 컴포넌트 안에서 사용하는 모든 Recoil 상태에 접근이 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; snapshot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;snapshot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLoadable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImageState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contents&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;renderHook&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;wrapper&lt;/code&gt; 옵션에서 사용할 수 있도록 &lt;code class=&quot;language-text&quot;&gt;PeekSnapshot&lt;/code&gt; 컴포넌트를 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; SnapshotPeekRef &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  current&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    snapshot&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Snapshot&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; RecoilValue&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;) =&gt; any;
  };
};

export const PeekSnapshot: React.FC&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; peekRef&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; SnapshotPeekRef &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&gt; = (&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  peekRef&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;) =&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; snapshot &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  peekRef&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    snapshot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; RecoilValue&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;) =&gt; snapshot.getLoadable(state).contents,
  };
  return null;
};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;PeekSnapshot&lt;/code&gt; 컴포넌트의 &lt;code class=&quot;language-text&quot;&gt;peekRef&lt;/code&gt; props는 &lt;code class=&quot;language-text&quot;&gt;SnapshotPeekRef&lt;/code&gt; 타입의 객체다. 컴포넌트 안에서 Snapshot을 구독한 후 &lt;code class=&quot;language-text&quot;&gt;peekRef&lt;/code&gt; props의 &lt;code class=&quot;language-text&quot;&gt;current&lt;/code&gt; 필드에 필요한 기능을 할당한다.&lt;/p&gt;
&lt;p&gt;사실 컴포넌트 내부에서 props 객체를 조작하는 것은 React의 안티 패턴이긴 하다. React는 위에서 아래로의 단방향 데이터 흐름을 가지며, 2-way 바인딩을 가능하게 하는 API가 없다. 하지만 저런 방식이 아니면 테스트 코드에서 전달한 props 객체에 Snapshot을 전달할 방법을 찾기가 어렵다. 그리고 Snapshot 구독만 하기 때문에 다른 컴포넌트에 영향을 주지 않으므로 상관없다.&lt;/p&gt;
&lt;p&gt;이제 &lt;code class=&quot;language-text&quot;&gt;PeekSnapshot&lt;/code&gt; 컴포넌트를 활용하여 Recoil 상태를 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 이 객체를 PeekSnapshot의 props로 보낸다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; peekRef&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; SnapshotPeekRef &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderHook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useImageList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  wrapper&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;RecoilRoot&lt;/span&gt;
			&lt;span class=&quot;token attr-name&quot;&gt;initializeState&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 확인할 Recoil 상태의 초기값을 설정한다.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImageState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://cdn.images.com/another.png&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* Snapshot을 구독할 것이므로 RecoilRoot 아래에 추가해야 한다 */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;PeekSnapshot&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;peekRef&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;peekRef&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;PeekSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;RecoilRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; 
	&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;images&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// peekRef 객체를 통해 Recoil 상태가 null로 초기화 되었는지 확인한다.&lt;/span&gt;
	peekRef&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectedImageState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;컴포넌트의-props를-조작하면-어떤-문제가-생기는가&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-props%EB%A5%BC-%EC%A1%B0%EC%9E%91%ED%95%98%EB%A9%B4-%EC%96%B4%EB%96%A4-%EB%AC%B8%EC%A0%9C%EA%B0%80-%EC%83%9D%EA%B8%B0%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트의 props를 조작하면 어떤 문제가 생기는가?&lt;/h2&gt;
&lt;p&gt;문제는 해결되었지만 생각해 볼 만한 주제가 있다. 부모 컴포넌트의 props를 수정했을 때 어떤 문제가 생길까?&lt;/p&gt;
&lt;p&gt;props로 객체가 아닌 리터럴 데이터를 전달했다면 props를 조작해도 상위 컴포넌트에 영향을 미칠 수 없다. 자바스크립트에서 함수를 호출할 때 파라미터에 리터럴 값을 사용하면 변수의 값 자체가 전달될 뿐 변수의 레퍼런스가 전달되는 것이 아니기 때문이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;call with value does not modify original&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; original &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resetter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;resetter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 함수 안에서 파라미터를 덮어썼지만 original 값은 변하지 않았다.&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 파라미터로 객체를 넘긴다면 이야기가 다르다. 파라미터로 객체를 사용하면 값이 아닌 객체를 할당한 변수의 레퍼런스가 전달되고, 함수 안에서 그 객체의 필드를 수정하면 상위 스쿠프의 변수에 영향을 미치게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;call with reference modifies original&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; original &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mutator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;mutator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 함수 호출에 의해 객체의 필드가 수정되었다.&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;예를 들어 부모 컴포넌트에서 상태 객체 A의 필드 &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt;를 렌더링에 사용하고 있는데, 자식 컴포넌트에서 그 객체를 props로 받은 후 &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt; 필드를 다른 값으로 수정해서 렌더링에 사용했다고 가정하자. 부모와 자식 모두 각은 객체의 같은 필드를 사용해서 렌더링 했는데, 화면에는 다른 값이 표시되는 상황이 벌어진다. React는  2-way 바인딩을 지원하지 않으며, 객체의 조작(mutation)은 리렌더링을 유발하지 않기 때문이다.&lt;/p&gt;
&lt;p&gt;만약 부모의 상태 객체를 자식 컴포넌트에서 수정하고 싶다면 업데이터 함수를 작성한 후 그것도 자식 컴포넌트에 props로 전달해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Parent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setOriginal&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; updateOriginal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setOriginal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      value is &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Child&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;original&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onUpdateOriginal&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;updateOriginal&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Child&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;2-way 바인딩을 지원한다면 저런 추가적인 함수와 props가 필요 없어진다. 그리고 앱에서는 컴포넌트를 무식하게 큰 덩어리로 작성하지 않는 이상 부모 상태를 하위에서 수정해야 하는 케이스는 언제나 발생한다. 그러다 보면 자연스럽게 유연한 상태 관리를 위해 Redux, Mobx, Recoil 같은 라이브러리를 도입하게 된다.&lt;/p&gt;
&lt;p&gt;단방향 데이터 흐름은 데이터의 소유, 관리 주체를 확실히 구분하게 된다는 장점은 있지만 코드에서 해야 할 말이 더 많아진다는 단점도 가지고 있다.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;&lt;a href=&quot;#references&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/testing-library/react-hooks-testing-library&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;testing-library/react-hooks-testing-library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://recoiljs.org/ko/docs/api-reference/core/useRecoilSnapshot/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;useRecoilSnapshot() | Recoil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mswjs.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MSW – Seamless API mocking library for browser and Node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/26089532/why-cant-i-update-props-in-react-js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;javascript - Why can’t I update props in react.js? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.javascripttutorial.net/javascript-pass-by-value/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Understanding JavaScript Pass By Value&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Canvas, Web Audio API를 사용한 웹 오디오 플레이어 제작기]]></title><description><![CDATA[제작 동기 및 요구사항 오디오 파형(waveform)은 오디오, 영상 편집 툴 및 일부 오디오 플레이어에서 접할 수 있다. 업무에서 개발한 앱에서 오디오 재생 기능이 필요하여  wavesurfer.js 를 사용하고 있었다. 그런데 Safari 브라우저에서 알 수 없는…]]></description><link>https://blog.rhostem.com//posts/2022-01-08-canvas-web-audio-player</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2022-01-08-canvas-web-audio-player</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 08 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;제작-동기-및-요구사항&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EC%9E%91-%EB%8F%99%EA%B8%B0-%EB%B0%8F-%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제작 동기 및 요구사항&lt;/h2&gt;
&lt;p&gt;오디오 파형(waveform)은 오디오, 영상 편집 툴 및 일부 오디오 플레이어에서 접할 수 있다. 업무에서 개발한 앱에서 오디오 재생 기능이 필요하여 &lt;a href=&quot;https://wavesurfer-js.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;wavesurfer.js&lt;/a&gt;를 사용하고 있었다. 그런데 Safari 브라우저에서 알 수 없는 버그로 인해 기능을 하지 않았고, 커스터마이징 지원을 위해 플러그인과 API 사용이 다소 복잡하다는 불편함을 느끼고 있었다. 그래서 새로운 서비스 개발을 시작할 때 기술적 도전도 할 겸 최소한의 기능을 가진 오디오 플레이어를 만들어 보기로 했다. 오디오 플레이어에 필요한 요구사항은 다음과 같다.&lt;/p&gt;
&lt;ul&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;li&gt;오디오 탐색을 위한 슬라이더 바를 제공한다.&lt;/li&gt;
&lt;li&gt;오디오 파형 위를 클릭하면 해당하는 위치로 현재 재생 시간이 업데이트된다.&lt;/li&gt;
&lt;li&gt;오디오 파형은 확대, 축소가 가능하다.&lt;/li&gt;
&lt;li&gt;파형 위를 클릭 후 드래그하여 반복 재생 구간 설정이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;확대, 축소 및 반복구간 설정은 부가 기능이라 서비스에 따라 필요하지 않을 수도 있다. 그것을 제외한 나머지가 오디오 플레이어로서의 최소 기능이라고 할 수 있다. 이런 요구사항에 기반한 오디오 플레이어를 아래 그림과 같은 형태로 개발했다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4McdNBrFficwSuqIbxaiyR/9d3ddc5a64c053a94c32cf1985f878c8/audiowaveform.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 752px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 56.11702127659575%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;audiowaveform&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4McdNBrFficwSuqIbxaiyR/9d3ddc5a64c053a94c32cf1985f878c8/audiowaveform.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4McdNBrFficwSuqIbxaiyR/9d3ddc5a64c053a94c32cf1985f878c8/audiowaveform.gif?w=188 188w,
https://images.ctfassets.net/rpmifyuylbfw/4McdNBrFficwSuqIbxaiyR/9d3ddc5a64c053a94c32cf1985f878c8/audiowaveform.gif?w=376 376w,
https://images.ctfassets.net/rpmifyuylbfw/4McdNBrFficwSuqIbxaiyR/9d3ddc5a64c053a94c32cf1985f878c8/audiowaveform.gif?w=752 752w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;h2 id=&quot;소리의-디지털화에-대한-이야기&quot;&gt;&lt;a href=&quot;#%EC%86%8C%EB%A6%AC%EC%9D%98-%EB%94%94%EC%A7%80%ED%84%B8%ED%99%94%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%EC%95%BC%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;소리의 디지털화에 대한 이야기&lt;/h2&gt;
&lt;p&gt;소리라는 것은 공기의 진동이다. 진동은 그래프로 표현이 가능하며 Y 축은 진폭으로 소리의 세기를 나타낼 수 있고 X축은 시간을 나타낸다.  그래프가 Y축 0에서 출발하여 +, -피크에 도달한 후 다시 0으로 돌아왔을 때 이를 한 파장(period)라고 하는데, 이 주기가 짧을수록 높은 소리, 길수록 낮은 소리가 된다. 1초에 1번의 파장이 발생했다면 그 소리는 1Hz(헤르츠)의 소리가 된다. 현재 국제적으로 표준 음높이로 쓰는 “라” 음은 440Hz로 1초에 440번 진동하는 소리다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3N618x4cFCbVjjYuHVCm0n/86c709838fbbcfd6974b6349126f7d4c/Graphs-of-sound-waves20151209_v2.jpg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 517px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 78.33655705996131%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAfACgDASIAAhEBAxEB/8QAGgAAAgMBAQAAAAAAAAAAAAAAAAUBAwQCCf/EADAQAAICAQMDAgQDCQAAAAAAAAECAwQRAAUSEyExUYEUImGhBjJSFSMzNEFjcbHR/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQAC/8QAGREAAwEBAQAAAAAAAAAAAAAAAAERQQIS/9oADAMBAAIRAxEAPwD0q3r8TXtrvSQwVEmQ2EUzTTxxokZWPlxDMCWHItjtn1/ppnU3x7KgfAzcivIcJIirL+pTz7jV1iva6ztRs1oeTZlE0LSktxAHh1x2A7a2yQpMoWRFkA74YZGdIO4K9x3Kx0dukqQu7TTlDGCnjpucn5sEDAPY98ay7Zf3OomL9S3LzmlVWcwZUdRun+VgMFeP1zj104uw9URcXSOdG5QtICwDcSPAIz8pbtnUwQzNWMdx4rDtkMY4yikemCzf71FgQW2lk4PXkgbGR1GQ59ccWPqNGuoKsdcsV5FjgFnYscDwMnRoJXRV8Fbs7hfMdmOODrL+6eJjk9NO5IcZH08as/Z25QnlXuVY/wC2az8D7dTt7ffVu4wLPMrNXuSFPBr2DGD/AJAdc++opt8BE0cNG4ycs5kmWQnPoWkJ00PKM27VrVyHa42lWGwbB5OiHj/Ck8ANn7/81SYN8/mOvAJyOlx+HOMY/Njqfq+2mNmZrkLwSULfB/lbhIiED1yHBHt31mrVI6cqSpT3AtGpAMlsuMfUNKQfcaqD4pftUN6rLJBYeOWugyjrGVJz3xksc47/AG0aYoxZFJUqSM8T5GjQaSih/9k=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Graphs-of-sound-waves20151209 v2&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3N618x4cFCbVjjYuHVCm0n/86c709838fbbcfd6974b6349126f7d4c/Graphs-of-sound-waves20151209_v2.jpg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3N618x4cFCbVjjYuHVCm0n/86c709838fbbcfd6974b6349126f7d4c/Graphs-of-sound-waves20151209_v2.jpg?w=129 129w,
https://images.ctfassets.net/rpmifyuylbfw/3N618x4cFCbVjjYuHVCm0n/86c709838fbbcfd6974b6349126f7d4c/Graphs-of-sound-waves20151209_v2.jpg?w=259 259w,
https://images.ctfassets.net/rpmifyuylbfw/3N618x4cFCbVjjYuHVCm0n/86c709838fbbcfd6974b6349126f7d4c/Graphs-of-sound-waves20151209_v2.jpg?w=517 517w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;디지털 오디오는 이 그래프를 수치화한 것이다. 연속적인 값을 분산된 숫자로 저장하기 위해 1초당 몇개의 값을 기록(=샘플링)할 것인지 정해야 하는데, 그 값을 샘플링 레이트(Sampling rate)라고 한다. 인간의 가청 주파수는 대략 20Hz부터 20000hz(=20kHz)인데, 이에 기반해 사람이 자연스러운 소리로 인식할 수 있는 최소 샘플링 레이트가 40kHz라는 것을 계산해 내서 오디오 기술에 적용하고 있다. 현재 디지털 오디오에 사용하고 있는 샘플링 레이트로 44.1kHz(Compact Disc가 사용하는 수치), 48kHz, 88.2kHz, 192kHz 등이 있다.&lt;/p&gt;
&lt;h2 id=&quot;web-audio-api를-사용한-샘플링-데이터-추출&quot;&gt;&lt;a href=&quot;#web-audio-api%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%83%98%ED%94%8C%EB%A7%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B6%94%EC%B6%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Web Audio API를 사용한 샘플링 데이터 추출&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Web Audio API&lt;/a&gt;는 디지털 오디오 소스로부터 샘플링된 값들을 가져오는 API를 제공한다. 오디오 파일로부터 샘플링 데이터를 가져오는 과정은 아래처럼 작성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://cdn.data.com/audio.mp3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; arrayBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;arrayBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; audioCtx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; audioBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; audioCtx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decodeAudioData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arrayBuffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rawData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; audioBuffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getChannelData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/AudioContext&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;AudioContext&lt;/a&gt;는 디지털 오디오 처리를 위한 인터페이스로서 한번 생성해서 여러 개의 오디오 소스 처리에 활용할 수 있다. &lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;decodeAudioData&lt;/code&gt;는 AudioContext의 부모 인터페이스인 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;BaseAudioContext&lt;/a&gt;에 있는 메소드로서 ArrayBuffer에 저장된 오디오 파일을 디코딩하여 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;AudioBuffer&lt;/a&gt; 데이터를 만든다.&lt;/p&gt;
&lt;p&gt;AudioBuffer의 &lt;code class=&quot;language-text&quot;&gt;getChannelData&lt;/code&gt; 메소드를 사용하면 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%8E%84%EC%8A%A4_%EB%B6%80%ED%98%B8_%EB%B3%80%EC%A1%B0&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;PCM&lt;/a&gt; 데이터를 얻을 수 있다. 배열에는 -1(=그래프 Y축의 음수 피크)부터 1(양수 피크) 사이의 32비트 실수가 들어 있고, 오디오의 길이(초 단위)에 샘플링 레이트를 곱한 수만큼의 값이 들어 있다. 예를 들어 샘플링 레이트가 48000인 오디오의 길이가 20초일 때, 샘플링된 데이터는 총 960,000개가 된다. 위의 코드에서 &lt;code class=&quot;language-text&quot;&gt;rawData&lt;/code&gt; 값을 콘솔에 출력해 보면 아래와 같은 값들이 나온다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.000030517578125&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.000030517578125&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.000030517578125&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.000030517578125&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token number&quot;&gt;0.000030518509447574615&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token number&quot;&gt;0.00006103701889514923&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token number&quot;&gt;0.000030518509447574615&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;음수에서 양수로 변화하는 값들이다. 소리의 진동 그래프를 디지털로 수치화했음을 확인할 수 있다. &lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;getChannelData&lt;/code&gt; 함수의 호출 파라미터로 &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;을 사용한 것은 첫번째 채널에 저장된 데이터를 사용하겠다는 의미다. 일반적으로 사용하는 2채널 스테레오 오디오라면 전체 채널의 수는 2개, 5.1채널 오디오라면 채널이 6개가 있다. 양쪽 채널에서 나오는 소리를 모두 사용하지 않은 것은 구현의 편의를 위해서다. 실제 구현된 플레이어에서 표시된 파형을 통해 소리가 없는 부분, 소리가 큰 부분을 찾았을 때 기대했던 것과 특별히 다른 경우는 없었다.&lt;/p&gt;
&lt;h2 id=&quot;샘플링-데이터의-리샘플링&quot;&gt;&lt;a href=&quot;#%EC%83%98%ED%94%8C%EB%A7%81-%EB%8D%B0%EC%9D%B4%ED%84%B0%EC%9D%98-%EB%A6%AC%EC%83%98%ED%94%8C%EB%A7%81&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;샘플링 데이터의 리샘플링&lt;/h2&gt;
&lt;p&gt;샘플링 데이터는 가져왔지만 그 값을 그대로 쓰기에는 너무 많다. 1초당 대략 4만개의 데이터가 있으므로 1분만 되어도 240만개의 데이터를 얻게 된다. 하지만 캔버스의 넓이가 500px이라면 500개의 데이터만 있어도 1px당 1개의 점을 찍는 것으로 선을 표현할 수 있기에 그만큼 많은 데이터는 필요가 없다.&lt;/p&gt;
&lt;p&gt;그래서 1초당 표시할 데이터의 수를 정한 후, 샘플링 데이터를 구간별로 잘라서 평균을 내는 방식을 사용해서 전체 데이터의 수를 줄인다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;본문에 실린 코드는 React로 구현한 코드에서 이 글을 읽는 데 도움이 될 정도로만 발췌하고 수정했기에 언급하지 않은 멤버 변수와 함수가 포함되어 있을 수 있다는 점 양해 바랍니다. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; samplesPerSec &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1초당 표시할 샘플의 수&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  duration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 오디오 길이 (초 단위)&lt;/span&gt;
  sampleRate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 샘플링 레이트. 보통 48000 또는 44100.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; audioBuffer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rawData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; audioBuffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getChannelData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 첫번쨰 채널의 AudioBuffer&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; totalSamples &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; duration &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; samplesPerSec&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 구간 처리 후 전체 샘플 수&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; blockSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sampleRate &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; samplesPerSec&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 샘플링 구간 사이즈&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; filteredData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; totalSamples&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; blockStart &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; blockSize &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 샘플 구간 시작 포인트&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; blockSum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; j &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; j &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; blockSize&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; j&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rawData&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;blockStart &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; j&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      blockSum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; blockSum &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rawData&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;blockStart &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; j&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  filteredData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;blockSum &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; blockSize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 구간 평균치를 결과 배열에 추가&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;1초당 100개의 샘플 데이터만 가져오겠다고 한다면, 1초당 48000개의 샘플링을 100으로 나눠 480개의 구간을 만든 다음, 각 구간의 평균값을 구하면 된다. &lt;/p&gt;
&lt;p&gt;다만 평균값을 구할 때 &lt;code class=&quot;language-text&quot;&gt;Math.abs&lt;/code&gt; 메소드를 사용해 샘플링 데이터의 절대값을 사용해야 한다. 샘플링 데이터는 소리의 세기를 나타내는 값이지만 파장 그래프 표현상 음수에서 양수로 변화하기 때문이다.&lt;/p&gt;
&lt;h2 id=&quot;샘플링-데이터-정규화normalizing&quot;&gt;&lt;a href=&quot;#%EC%83%98%ED%94%8C%EB%A7%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%95%EA%B7%9C%ED%99%94normalizing&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;샘플링 데이터 정규화(normalizing)&lt;/h2&gt;
&lt;p&gt;리샘플링한 데이터를 사용하기 전에, 정규화 과정을 거쳐야 한다. 정규화 과정을 통해 샘플링된 데이터의 최대값이 1이 되도록 만든다. &lt;/p&gt;
&lt;p&gt;오디오 파일마다 전체적으로 소리가 클 수도 있고 작을 수도 있다. 캔버스에 파형을 그릴 때 그런 값들을 정규화없이 그대로 반영하면 어떤 오디오는 파형이 너무 작게, 어떤  것은 너무 높게 표시될 것이다. 예를 들어 캔버스 높이가 300px일 때, 어떤 오디오든 그 안에서 가장 큰 소리가 &lt;code class=&quot;language-text&quot;&gt;(x,0)&lt;/code&gt; 좌표에, 가장 작은 소리는 &lt;code class=&quot;language-text&quot;&gt;(x,300)&lt;/code&gt; 좌표에 표시되게 할 필요가 있다(캔버스는 왼쪽 위 모서리의 좌표가 &lt;code class=&quot;language-text&quot;&gt;(0,0)&lt;/code&gt; 이다).&lt;/p&gt;
&lt;p&gt;물론 작은 소리는 작은 파형으로 표시되길 원하는 서비스도 있을 수 있으므로 선택적으로 적용하면 되는 과정이다. 만약 오디오 파형을 통해 소리가 튀는 구간, 소리가 없는 구간 같은 정보를 직관적으로 얻기 위해서는 정규화를 거치는 편이 더 좋을 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;normalizeData&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filteredData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; peak &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;filteredData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; multiplier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;peak&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; filteredData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; multiplier&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;샘플링 데이터에서 최대값을  &lt;code class=&quot;language-text&quot;&gt;peak&lt;/code&gt; 에 저장한 후 샘플링 데이터 배열을 &lt;code class=&quot;language-text&quot;&gt;샘플링 * (1 / peak)&lt;/code&gt; 공식으로 맵핑해주면 정규화가 완료된다.&lt;/p&gt;
&lt;p&gt;이렇게 오디오 데이터는 모두 준비되었다. 이제 캔버스에 샘플링 데이터를 그리는 과정이 필요하다.&lt;/p&gt;
&lt;h2 id=&quot;canvas-api를-사용한-도형-그리기&quot;&gt;&lt;a href=&quot;#canvas-api%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%8F%84%ED%98%95-%EA%B7%B8%EB%A6%AC%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Canvas API를 사용한 도형 그리기&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Canvas API&lt;/a&gt;는 HTML canvas 요소와 자바스크립트를 사용해서 그래픽을 표현할 수 있다. 애니메이션, 게임, 데이터 시각화, 사진 편집 등에 사용된다. 주로 2D 그래픽에 사용되고, 3D 그래픽 표현에는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;WebGL API&lt;/a&gt;를 사용한다. 오디오 파형은 선, 사각형, 텍스트, 다각형에 색 칠하기 등만 가능하면 구현 가능하기에 Canvas API를 깊게 알 필요까지는 없고 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;기초 지식&lt;/a&gt;(튜토리얼의 1~4장) 정도만 공부한 후 API 레퍼런스를 찾아보며 작업을 하면 된다.&lt;/p&gt;
&lt;h3 id=&quot;requestanimationframe-함수&quot;&gt;&lt;a href=&quot;#requestanimationframe-%ED%95%A8%EC%88%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;requestAnimationFrame 함수&lt;/h3&gt;
&lt;p&gt;오디오 재생이 시작되면 오디오 파형 위에 현재 재생 위치를 표시하는 수직선을 표시해줘야 한다. 그것은 재생이 진행됨에 따라 오른쪽으로 움직여야 한다.&lt;/p&gt;
&lt;p&gt;캔버스에서 CSS의 &lt;code class=&quot;language-text&quot;&gt;transition&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;@keyframes&lt;/code&gt; 등으로 만드는 것 같은 애니메이션 효과를 만드는 API는 별도로 없다. 캔버스에 그리고 지우는 것을 빠르게 반복함으로써 그런 효과를 만드는 방법을 사용한다. 그리고 그 반복 작업을 할 때 사용하는 것이 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;requestAnimationFrame&lt;/code&gt;&lt;/a&gt; 함수다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 함수는 브라우저의 리페인트(repaint)가 일어나기 전에 콜백을 실행한다. &lt;code class=&quot;language-text&quot;&gt;setInterval&lt;/code&gt; 같은 함수를 사용하지 않고, 재귀 호출을 사용해 &lt;code class=&quot;language-text&quot;&gt;requestAnimationFrame&lt;/code&gt; 을 반복 호출해도 그 실행 간격은 무한 루프에 빠진 것처럼 빠르게 실행되지 않으며 브라우저에 의해 디스플레이의 주사율에 맞춰진다. 즉 디스플레이가 60Hz의 주사율을 가지고 있다면 콜백의 호출 간격은 1/60초가 되는 셈이다. CPU 리소스를 무리하게 사용하지 않으면서도 사람의 눈에는 자연스러운 애니메이션 효과를 제공할 수 있게 된다.&lt;/p&gt;
&lt;p&gt;파형을 그리는 &lt;code class=&quot;language-text&quot;&gt;drawWaveForm&lt;/code&gt; 이라는 함수가 있을 때, 애니메이션의 시작과 종료는 아래처럼 작성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AudioPlayer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	drawWaveForm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	   &lt;span class=&quot;token comment&quot;&gt;// Canvas API를 사용한 오디오 파형 그리기 구현&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  rafId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  startDraw &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; 
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drawWaveForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rafId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startDraw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	stopDraw &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; 
	  &lt;span class=&quot;token function&quot;&gt;cancelAnimationFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rafId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;requestAnimationFrame&lt;/code&gt; 함수는 정수로 된 고유한 콜백 아이디를 리턴한다. &lt;code class=&quot;language-text&quot;&gt;cancelAnimationFrame&lt;/code&gt; 함수에 그 아이디를 넣어서 호출하면 다음 리페인트 직전에 &lt;code class=&quot;language-text&quot;&gt;requestAnimationFrame&lt;/code&gt;의 콜백이 실행되지 않으면서 캔버스를 업데이트하는  &lt;code class=&quot;language-text&quot;&gt;drawWaveForm&lt;/code&gt; 메소드 더 이상 실행되지 않게 된다. 실제 구현에서는 오디오 재생 버튼을 클릭했을 때 &lt;code class=&quot;language-text&quot;&gt;startDraw&lt;/code&gt;를, 멈춤 버튼을 클릭할 때  &lt;code class=&quot;language-text&quot;&gt;stopDraw&lt;/code&gt;를 호출하게 될 것이다.&lt;/p&gt;
&lt;h2 id=&quot;오디오-파형-그리기&quot;&gt;&lt;a href=&quot;#%EC%98%A4%EB%94%94%EC%98%A4-%ED%8C%8C%ED%98%95-%EA%B7%B8%EB%A6%AC%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오디오 파형 그리기&lt;/h2&gt;
&lt;p&gt;샘플링된 데이터가 있고 Canvas API가 있으니 이제 파형을 그리면 된다. 그려야 하는 것들에는 다음과 같은 것들이 있다.&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;h3 id=&quot;code-classlanguage-textcanvasrenderingcontext2dcode-인터페이스&quot;&gt;&lt;a href=&quot;#code-classlanguage-textcanvasrenderingcontext2dcode-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;CanvasRenderingContext2D&lt;/code&gt; 인터페이스&lt;/h3&gt;
&lt;p&gt;실제로 도형을 그리는 데는 &lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;CanvasRenderingContext2D&lt;/code&gt;&lt;/strong&gt; 인스턴스를 사용한다. 이것은 canvas 표면에 2D 렌더링 컨텍스트를 제공하며, 다각형, 텍스트, 이미지 등을 그릴 수 있다. 인스턴스는 canvas 요소를 통해 만들 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;canvas&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;my-house&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;300&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;300&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;canvas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; canvas &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;my-house&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;2d&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;code-classlanguage-textdevicepixelratiocode-값에-맞춰서-scale-조정하기&quot;&gt;&lt;a href=&quot;#code-classlanguage-textdevicepixelratiocode-%EA%B0%92%EC%97%90-%EB%A7%9E%EC%B6%B0%EC%84%9C-scale-%EC%A1%B0%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;devicePixelRatio&lt;/code&gt; 값에 맞춰서 scale 조정하기&lt;/h3&gt;
&lt;p&gt;디스플레이에 따라 가로 1920px 사이즈의 화면을 구성하는 픽셀의 수는 1920개일 수도 있고, 3840개일 수도 있다. 후자인 경우 어플리케이션이 사용하는 논리적 픽셀은 1개인데 실제로 사용하는 물리적 픽셀 수는 2개가 된다. 과거에는 이런 구분이 필요하지 않았지만, 아이폰 4S에서 레티나 디스플레이가 등장하면서 상황이 달라졌다. 웹사이트에 들어가는 캔버스를 사용할 때 화면의 DPI(Dots per Inch)에 맞춰서 업스케일링, 다시말해 논리적 픽셀 사이즈를 실제 디바이스의 픽셀 사이즈만큼 늘려줄 필요가 생긴 것이다.&lt;/p&gt;
&lt;p&gt;하지만 고맙게도 브라우저의 API로 코드만 잘 작성해주면 업스케일링은 브라우저가 알아서 잘 해준다. 도형을 그리기 전에 아래 코드만 넣어주면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvasEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;2d&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dpr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;devicePixelRatio &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dpr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dpr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/scale&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;scale&lt;/code&gt;&lt;/a&gt; 함수는 캔버스에서 사용하는 픽셀 단위를 늘려주는 역할을 한다. 그리고 &lt;code class=&quot;language-text&quot;&gt;devicePixelRatio&lt;/code&gt;는 물리적 픽셀과 논리적 픽셀의 비율을 반환하는데, 저 값이 HD 모니터에서는 &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;, 4K(=QHD) 모니터에서는 &lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt;가 된다. &lt;code class=&quot;language-text&quot;&gt;devicePixelRatio&lt;/code&gt; 값을 사용하면 올바른 업스케일링 비율을 사용할 수 있다. 만약 업스케일링 없이 오디오 파형을 그리게 한 후 4K 모니터에서 확인하면 아래 스크린샷처럼 보이게 된다. &lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/Td9UdYSIo5kvZF2ahgfUO/26cf052ee5e23fc10f455aa750400a66/_____________2022-01-07________12.17.44.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 58.46817691477886%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;non scaled canvas&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/Td9UdYSIo5kvZF2ahgfUO/26cf052ee5e23fc10f455aa750400a66/_____________2022-01-07________12.17.44.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/Td9UdYSIo5kvZF2ahgfUO/26cf052ee5e23fc10f455aa750400a66/_____________2022-01-07________12.17.44.png?w=464 464w,
https://images.ctfassets.net/rpmifyuylbfw/Td9UdYSIo5kvZF2ahgfUO/26cf052ee5e23fc10f455aa750400a66/_____________2022-01-07________12.17.44.png?w=927 927w,
https://images.ctfassets.net/rpmifyuylbfw/Td9UdYSIo5kvZF2ahgfUO/26cf052ee5e23fc10f455aa750400a66/_____________2022-01-07________12.17.44.png?w=1854 1854w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;파형의 넓이와 높이가 캔버스의 그것에서 정확히 절반이 되어버렸다. 하지만 &lt;code class=&quot;language-text&quot;&gt;scale&lt;/code&gt; 함수로 업스케일링을 해두면 아래처럼 오디오 파형이 정상적으로 캔버스 사이즈에 맞게 표시된다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tLGIoVI0mIXkxGKO1vWuM/01ee274b82a239156d3bb05197d174a5/_____________2022-01-07________12.17.27.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 924px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 58.44155844155845%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;scaled canvas&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tLGIoVI0mIXkxGKO1vWuM/01ee274b82a239156d3bb05197d174a5/_____________2022-01-07________12.17.27.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tLGIoVI0mIXkxGKO1vWuM/01ee274b82a239156d3bb05197d174a5/_____________2022-01-07________12.17.27.png?w=231 231w,
https://images.ctfassets.net/rpmifyuylbfw/1tLGIoVI0mIXkxGKO1vWuM/01ee274b82a239156d3bb05197d174a5/_____________2022-01-07________12.17.27.png?w=462 462w,
https://images.ctfassets.net/rpmifyuylbfw/1tLGIoVI0mIXkxGKO1vWuM/01ee274b82a239156d3bb05197d174a5/_____________2022-01-07________12.17.27.png?w=924 924w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;CSS로 스타일링을 할 때는 업스케일링을 자동으로 해 주기 때문에 신경쓸 필요가 없지만 캔버스로 도형을 그릴 때는 꼭 필요한 과정이다.&lt;/p&gt;
&lt;h3 id=&quot;배경-그리기&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%EA%B2%BD-%EA%B7%B8%EB%A6%AC%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배경 그리기&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;requestAnimationFrame&lt;/code&gt;을 사용해서 캔버스에 계속 그림을 그릴 것이기에 매번 &lt;code class=&quot;language-text&quot;&gt;clearRect&lt;/code&gt; 를 사용해서 캔버스 위에 그려진 것을 지워줘야 한다. 안그러면 그렸던 도형들 위에 덧칠하는 형태가 되어 버린다. 예를 들어 좌표가 왼쪽에서 오른쪽으로 바뀌는 정사각형을 그리는 함수를 작성했다고 하자. 그런데  &lt;code class=&quot;language-text&quot;&gt;clearRect&lt;/code&gt;를 빼먹었다면 canvas 위에는 오른쪽으로 움직이는 정사각형 대신 오른쪽으로 점점 늘어나는 직사각형이 보일 것이다.&lt;/p&gt;
&lt;p&gt;아래 코드는 canvas 전체를 지우고 배경색을 입혀준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clearRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvasWidth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvasHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fillStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;#ccc&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 캔버스 배경색&lt;/span&gt;
ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fillRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvasWidth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvasHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;샘플링-데이터로-그래프-그리기&quot;&gt;&lt;a href=&quot;#%EC%83%98%ED%94%8C%EB%A7%81-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A1%9C-%EA%B7%B8%EB%9E%98%ED%94%84-%EA%B7%B8%EB%A6%AC%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;샘플링 데이터로 그래프 그리기&lt;/h3&gt;
&lt;p&gt;오디오 파형 시각화의 핵심인 샘플링 데이터로 그래프를 그리기를 구현해야 한다. 아래의 값을 계산하면 라인 그래프를 그릴 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;전체 샘플의 수&lt;/li&gt;
&lt;li&gt;canvas의 넓이&lt;/li&gt;
&lt;li&gt;샘플 1개가 차지할 넓이 = canvas 넓이 / 샘플의 수&lt;/li&gt;
&lt;li&gt;샘플의 x 좌표 = 샘플의 인덱스 * 샘플 1개가 차지할 넓이&lt;/li&gt;
&lt;li&gt;샘플의 y 좌표 = 샘플 데이터(최대값 1로 정규화된 상태) * canvas의 높이&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;포인터를 캔버스의 좌하단으로 이동시킨 후 샘플 데이터 배열의 루프를 돌며 라인 그래프를 그린다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 샘플 1개가 차지할 넓이&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sampleWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; waveAreaWidth &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;audioSamples&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lastX &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// x축 좌표&lt;/span&gt;

ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;beginPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 선을 그리기 위해 새로운 경로를 만든다.&lt;/span&gt;
ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;moveTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastX&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvasHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strokeStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;blue&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 라인 컬러 설정&lt;/span&gt;
ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fillStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;gray&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 그래프 내부를 채울 컬러 설정&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;audioSamples&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sample&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 샘플 데이터 배열 루프&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sampleWidth &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// x 좌표&lt;/span&gt;
  ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lineWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 라인 그래프의 두께&lt;/span&gt;
  ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lineTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    canvasHeight &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sample &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; waveAreaHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// y축 좌표&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  lastX &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 라인 그래프의 하단을 선으로 연결해서 닫힌 형태로 만든 후, 색을 채운다&lt;/span&gt;
ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lineTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastX&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; canvasHeight&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;moveTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stroke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 그래프가 완성되었으므로 경로를 닫는다.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 코드에서는 상하좌우 패딩을 반영하지 않았다. 라인 그래프의 시작과 끝이 canvas의 좌측 끝과 우측 끝에 위치하게 된다. 특히 아래쪽에 타임라인을 추가하고 포지션 바가 canvas 끝에 겹치지 않게 하려면 전후좌우 패딩을 고려해서 라인을 그려줘야 한다.&lt;/p&gt;
&lt;p&gt;이 방식을 활용하면 일정한 넓이를 가진 막대 그래프 형태로 만들 수도 있을 것이다. 다만 막대 그래프를 위해선 샘플링 데이터의 수를 더 줄여야 할 필요가 있다. 앞서서 샘플링 데이터를 정리할 때 1초당 100개가 되도록 했는데, 캔버스에서 1초가 차지하는 넓이가 100px이라도 막대 그래프의 넓이는 1px밖에 되지 않는다. 그러면 스크린샷에서 보여준 라인 그래프와 별다른 차이가 없게 된다. 일정한 넓이의 막대 그래프를 제공하기 위해서는 앞서 구현한 것과 다른 방식으로 샘플링 데이터를 정리할 필요가 있을 것이다.&lt;/p&gt;
&lt;h2 id=&quot;오디오-재생-기능-구현&quot;&gt;&lt;a href=&quot;#%EC%98%A4%EB%94%94%EC%98%A4-%EC%9E%AC%EC%83%9D-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오디오 재생 기능 구현&lt;/h2&gt;
&lt;p&gt;오디오 재생을 위해서는 아래의 기능이 필수적으로 필요하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;재생 / 멈춤&lt;/li&gt;
&lt;li&gt;탐색 (seeking, 재생 위치 업데이트)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;오디오-플레이어-시각화&quot;&gt;&lt;a href=&quot;#%EC%98%A4%EB%94%94%EC%98%A4-%ED%94%8C%EB%A0%88%EC%9D%B4%EC%96%B4-%EC%8B%9C%EA%B0%81%ED%99%94&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오디오 플레이어 시각화&lt;/h3&gt;
&lt;p&gt;오디오 소스를 audio 요소에 연결해서 재생에 활용했지만 화면에 표시하지는 않도록 했다. audio 요소에 &lt;code class=&quot;language-text&quot;&gt;controls&lt;/code&gt; 속성을 지정해서 브라우저 네이티브 오디오 컨트롤러를 표시하게 할 수도 있지만 브라우저마다 다른 형태로 제공되기에 일관적인 디자인의 플레이어를 제공하기는 어렵기 때문이다.&lt;/p&gt;
&lt;p&gt;재생 위치 슬라이더는 range 타입의 input 요소를 활용했는데, 스타일링을 위해서는 생각보다 많은 양의 CSS 작성이 필요했다(&lt;a href=&quot;https://www.cssportal.com/style-input-range/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;링크&lt;/a&gt; 참조). &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;audio&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;waveAudio&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;audioUrl&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  Your browser does not support the
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;audio&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; element.
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;audio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;seekSlider&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;range&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;AudioPlayer__progressInput&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isInputDisabled&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;오디오-재생--멈춤&quot;&gt;&lt;a href=&quot;#%EC%98%A4%EB%94%94%EC%98%A4-%EC%9E%AC%EC%83%9D--%EB%A9%88%EC%B6%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오디오 재생 / 멈춤&lt;/h3&gt;
&lt;p&gt;재생 / 멈춤 기능은 버튼에 이벤트 핸들러만 추가하면 간단히 구현 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;togglePlayAudioEl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isPlayingAudio&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;playAudioEl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pauseAudioEl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function-variable function&quot;&gt;playAudioEl&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;audioEl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; isPlayingAudio&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;audioEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// audio 요소의 play 함수 호출&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function-variable function&quot;&gt;pauseAudioEl&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;audioEl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; isPlayingAudio&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;audioEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// audio 요소의 pause 함수 호출&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;togglePlayAudioEl&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;InlineIcon&lt;/span&gt; 
    &lt;span class=&quot;token attr-name&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Iconify 라이브러리의 아이콘 활용&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isPlayingAudio &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mdi:pause-circle&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mdi:play-circle&apos;&lt;/span&gt;
    &lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;InlineIcon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;캔버스-파형-클릭시-오디오-탐색&quot;&gt;&lt;a href=&quot;#%EC%BA%94%EB%B2%84%EC%8A%A4-%ED%8C%8C%ED%98%95-%ED%81%B4%EB%A6%AD%EC%8B%9C-%EC%98%A4%EB%94%94%EC%98%A4-%ED%83%90%EC%83%89&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캔버스 파형 클릭시 오디오 탐색&lt;/h3&gt;
&lt;p&gt;캔버스 파형 위를 클릭했을 때의 오디오 탐색 위치는 로딩된 오디오의 전체 길이, 캔버스의 넓이, 마우스 클릭 지점으로 계산할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token function-variable function&quot;&gt;getTimeByPositionOfCanvas&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MouseEvent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 클래스의 멤버 함수&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rect &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;canvasEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBoundingClientRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 캔버스의 포지션 정보&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 마우스 위치와 캔버스 위치를 사용해 x축 좌표를 계산한다. &lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientX &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; rect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// (전체 시간 * x축 좌표 / 파형의 널이) 값이 이동할 위치가 된다.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;duration &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getWaveAreaSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;계산한 값을 오디오 요소의 &lt;code class=&quot;language-text&quot;&gt;currentTime&lt;/code&gt; 속성에 할당해주면 재생 위치가 업데이트된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;audioEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTimeByPositionOfCanvas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;나머지-구현-사항&quot;&gt;&lt;a href=&quot;#%EB%82%98%EB%A8%B8%EC%A7%80-%EA%B5%AC%ED%98%84-%EC%82%AC%ED%95%AD&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;나머지 구현 사항&lt;/h2&gt;
&lt;p&gt;여기까지가 오디오 파형의 시각화에 필수적으로 필요한 부분이다. 스크린샷에 제공한 플레이어가 가진 기능 중 이 글에서 설명하지 않은 부분은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;파형 확대, 축소&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;화면을 벗어날 정도로 커진 캔버스가 좌우 스크롤 되도록 wrapper 요소 추가&lt;/li&gt;
&lt;li&gt;줌 배율에 따른 그리기 값 계산&lt;/li&gt;
&lt;li&gt;현재위치 바가 화면을 벗어났을 때 스크롤 이동시키기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;구간 반복 기능&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mousedown,  mousemove, mouseup 이벤트로 반복구간 설정&lt;/li&gt;
&lt;li&gt;반복 구간 설정 중인 구간을 박스 형태로 canvas에 그리기&lt;/li&gt;
&lt;li&gt;반복 구간 재생. AudioContext 사용해서 재생하기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;파형 확대, 축소 기능이 들어가게 되면 파형 그래프 포지션의 계산, 오디오 탐색을 위한 공식에 확대/축소 상태를 고려해야 한다. 그리고 확대되었을 때 캔버스 사이즈를 늘리고 좌우 스크롤이 가능하게 할 것인지, 스크롤 없이 현재 포지션 바를 항상 가운데에 두게 하고 파형을 좌우로 움직이게 할 것인지에 따라 구현 방식이 달라지게 된다. &lt;/p&gt;
&lt;p&gt;그리고 구간 반복을 위해서는 우선 구간 설정을 할 수 있는 이벤트 핸들러를 구현해야 하고, 구간 설정 중에 위치를 파악할 수 있도록 캔버스에 반투명 사각형 영역을 그려줘야 한다. 그리고 설정된 구간은 이동 및 시작/종료 지점 수정이 가능하도록 만들어야 한다. 구간 반복 오디오는 audio를 직접 재생하는 것이 아니라 AudioContext 인스턴스로 오디오 소스를 별도로 생성해서 재생했다.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;&lt;a href=&quot;#references&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Web_Audio_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Web Audio API - Web API | MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Canvas API - Web APIs | MDN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Recoil의 쓰기 가능한 셀렉터]]></title><description><![CDATA[파생된 상태를 만드는 단방향 흐름의 selector Recoil은  selector  API를 제공한다. React의  ,  Redux의 selector  처럼 다른 상태를 기반으로 파생된 상태(derived state) 만들어내는 역할을 한다. 간단한 예를 들면 작…]]></description><link>https://blog.rhostem.com//posts/2021-11-24-recoil-writable-selector</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-11-24-recoil-writable-selector</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Wed, 24 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;파생된-상태를-만드는-단방향-흐름의-selector&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%83%9D%EB%90%9C-%EC%83%81%ED%83%9C%EB%A5%BC-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%8B%A8%EB%B0%A9%ED%96%A5-%ED%9D%90%EB%A6%84%EC%9D%98-selector&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파생된 상태를 만드는 단방향 흐름의 selector&lt;/h2&gt;
&lt;p&gt;Recoil은 &lt;a href=&quot;https://recoiljs.org/docs/api-reference/core/selector&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;selector&lt;/a&gt; API를 제공한다. React의 &lt;code class=&quot;language-text&quot;&gt;useMemo&lt;/code&gt;, &lt;a href=&quot;https://redux.js.org/usage/deriving-data-selectors&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Redux의 selector&lt;/a&gt; 처럼 다른 상태를 기반으로 파생된 상태(derived state) 만들어내는 역할을 한다. 간단한 예를 들면 작업 목록에서 완료된 작업만 추려내고 싶을 때 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; atom&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selector &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;recoil&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Job &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; isDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; jobListState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Job&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;jobListState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; completedJobsSelector &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;completedJobsSelector&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; jobList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jobListState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; completed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jobList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDone&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; completed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;쓰기까지-가능한-양방향-흐름을-가진-selector&quot;&gt;&lt;a href=&quot;#%EC%93%B0%EA%B8%B0%EA%B9%8C%EC%A7%80-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%96%91%EB%B0%A9%ED%96%A5-%ED%9D%90%EB%A6%84%EC%9D%84-%EA%B0%80%EC%A7%84-selector&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쓰기까지 가능한 양방향 흐름을 가진 selector&lt;/h2&gt;
&lt;p&gt;그런데 Recoil은 “쓰기 가능한 selector 상태”, writable selector라는 개념을 구현해 두었다. React를 사용해 온 사람이라면 ‘selector’라는 명사는 파생된 상태를 의미하며, 단방향 흐름을 가진 데이터라는 인식을 가지고 있어서 다소 의아하게 느껴질 수 있다. selector인데 양방향 데이터 흐름을 가진 것이라니?&lt;/p&gt;
&lt;p&gt;예를 들어 속도 정보를 관리해야 할 때 원본 데이터는 마일(mile)/h인데 화면 상에는 킬로미터(km)/h로 표시해야 하는 경우가 있을 수 있다. 그 관계를 atom과 selector로 표현하면 다음과 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mphState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;atom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mphState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; kphState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;kphState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mph &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mphState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; mph &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.609&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1마일은 약 1.609킬로부터&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 작성한 &lt;code class=&quot;language-text&quot;&gt;kphState&lt;/code&gt; 상태는 쓰기가 불가능한 상태다. 즉 &lt;code class=&quot;language-text&quot;&gt;useSetRecoilState&lt;/code&gt; 나 &lt;code class=&quot;language-text&quot;&gt;useRecoilState&lt;/code&gt; 훅의 파라미터로 넣을 수 없다. 하지만 selector에 &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt; 속성을 추가한다면 쓰기가 가능해진다. 컴포넌트에서 &lt;code class=&quot;language-text&quot;&gt;useSetRecoilState&lt;/code&gt; 을 사용해 &lt;code class=&quot;language-text&quot;&gt;kphState&lt;/code&gt;의 setter 함수를 만들면 킬로미터/h 값을 넘겨서 &lt;code class=&quot;language-text&quot;&gt;mphState&lt;/code&gt; 상태를 업데이트할 수 있게 되는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; kphState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selector&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;kphState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mph &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mphState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; mph &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.609&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; newValue &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;number&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mphState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newValue &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.609&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 킬로미터를 마일로 변환한다&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;);

const KPHInput: React.FC&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; = () =&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; setKph &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSetRecoilState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;kphState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setKph&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;쓰기-가능한-selector의-초기화-기능&quot;&gt;&lt;a href=&quot;#%EC%93%B0%EA%B8%B0-%EA%B0%80%EB%8A%A5%ED%95%9C-selector%EC%9D%98-%EC%B4%88%EA%B8%B0%ED%99%94-%EA%B8%B0%EB%8A%A5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쓰기 가능한 selector의 초기화 기능&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;kphState&lt;/code&gt; selector의 &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt; 속성의 두번째 파라미터인 &lt;code class=&quot;language-text&quot;&gt;newValue&lt;/code&gt;의 타입을 확인해보면 &lt;code class=&quot;language-text&quot;&gt;number | DefaultValue&lt;/code&gt; 로 되어있음을 알 수 있다. &lt;code class=&quot;language-text&quot;&gt;DefaultValue&lt;/code&gt; 타입은 &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt; 콜백이 setter(업데이트) 함수를 통해 호출되었는지, resetter(초기화) 함수로 호출되었는지 구분해주는 역할을 한다. 컴포넌트에서 &lt;code class=&quot;language-text&quot;&gt;useSetRecoilState&lt;/code&gt; 를 사용해서 만들었다면 setter 함수가 되고, &lt;code class=&quot;language-text&quot;&gt;useResetRecoilState&lt;/code&gt;를 사용해서 만들었다면 resetter 함수가 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; kphState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selector&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;kphState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mph &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mphState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; mph &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.609&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reset &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// selector의 resetter를 호출하면 newValue는 DefaultValue 인스턴스가 된다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newValue &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DefaultValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mphState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; newValue &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;number&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mphState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newValue &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.609&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;);

const ResetKPHButton: React.FC&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; = () =&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; resetKph &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useResetRecoilState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;kphState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// resetter 함수 생성&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;resetKph&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Reset KPH&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;언제-사용하면-좋은가&quot;&gt;&lt;a href=&quot;#%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%A2%8B%EC%9D%80%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;언제 사용하면 좋은가?&lt;/h2&gt;
&lt;p&gt;쓰기 가능한 selector에 대한 개념은 파악이 되었다. 그런데 더 중요한 질문이 남았다. 그래서 왜, 언제 사용하면 좋을까이다.&lt;/p&gt;
&lt;p&gt;위의 예제에 있는 것처럼 마일-킬로미터 같은 관계를 차치하고 생각하면 쓰기 가능한 selector는 그저 “컴포넌트 외부에 모듈 형태로 선언 가능한 Recoil 상태 업데이트 콜백 함수”라는 개념이 되긴 한다. 하지만 완전히 자유롭게 아무 일이나 할 수는 없다. &lt;code class=&quot;language-text&quot;&gt;selector&lt;/code&gt; 함수의 타입 선언을 보면 &lt;code class=&quot;language-text&quot;&gt;get&lt;/code&gt; 콜백이 필수 값이라서 &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt;만 선언할 수는 없으며, &lt;code class=&quot;language-text&quot;&gt;get&lt;/code&gt; 콜백이 리턴하는 타입과 &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt; 콜백에 전달되는 &lt;code class=&quot;language-text&quot;&gt;newValue&lt;/code&gt;의 타입이 일치해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; selector&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; GetRecoilValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    getCallback&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; GetCallback&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; Promise&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; | RecoilValue&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;, // 타입 T에 해당하는 값, T를 리턴하는 Promise,

  set?: (
    &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; GetRecoilValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; SetRecoilState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      reset&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ResetRecoilState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;,
    newValue: T | DefaultValue, // setter로 전달하는 값은 T 타입 값이어야 한다.
  ) =&gt; void,
})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Redux의 reducer 함수처럼 전달할 값과 업데이트할 상태를 자유롭게 구현하라는 용도로 제공된 기능은 아님을 알 수 있다. &lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt; 타입을 가진 파생된 상태를 만들어야 하고, 동시에 &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt; 타입의 값을 전달해서 다른 상태를 업데이트할 필요가 있을 때&lt;/strong&gt; 쓰기 가능한 selector를 사용할 필요가 생긴다. 또는 파생된 상태를 초기화시키고 싶을 때 유용하게 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;그런데 초기화라면 굳이 selector의 &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt; 콜백을 사용할 필요 없이 selector의 소스가 되는 &lt;code class=&quot;language-text&quot;&gt;atom&lt;/code&gt;상태를 직접 초기화시키면 되는 것 아닌가?라는 질문이 나올 수 있다. 물론 그렇게 초기화 로직을 구현해도 상관없다. 하지만 코드의 가독성과 유지 보수성에 차이가 올 것이다. 파생된 상태는 여러 개의 &lt;code class=&quot;language-text&quot;&gt;atom&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;selector&lt;/code&gt; 상태를 기반으로 복잡한 로직을 거쳐 만들어질 수도 있다. 그렇다면 초기화 시키는 로직 역시 여러 과정이 필요할 수 있다. 그런데 reset이 여러 곳에서 필요하다면 그 로직을 어떻게 모듈화할 것인가? 커스텀 훅 함수를 사용할 수도 있겠지만 selector의 &lt;code class=&quot;language-text&quot;&gt;set&lt;/code&gt; 콜백을 사용하면 더 간편하다. 상태 업데이트를 위한 기능도 제공하며 자연스럽게 모듈화도 되는 셈이니 말이다.&lt;/p&gt;
&lt;p&gt;Redux의 reducer 역할은 쓰기 가능한 selector보다는 &lt;code class=&quot;language-text&quot;&gt;useRecoilTransaction&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;useRecoilCallback&lt;/code&gt;에 커스텀 훅 함수를 조합해서 구현하는 편이 좋다.&lt;/p&gt;
&lt;h2 id=&quot;code-classlanguage-textuserecoiltransactioncode을-사용한-recoil-상태-업데이트&quot;&gt;&lt;a href=&quot;#code-classlanguage-textuserecoiltransactioncode%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-recoil-%EC%83%81%ED%83%9C-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;useRecoilTransaction&lt;/code&gt;을 사용한 Recoil 상태 업데이트&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;useRecoilTransaction&lt;/code&gt;은 &lt;code class=&quot;language-text&quot;&gt;useCallback&lt;/code&gt;처럼 컴포넌트 안에서 사용할 수 있는 함수를 만드는 훅 함수다. 파라미터로 팩토리 함수를 전달하는 방식을 사용해 Recoil의 상태를 관리할 수 있는 인터페이스를 제공한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TransactionInterface&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(RecoilValue&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;) =&gt; T; // Recoil 상태를 가져온다
  set: &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(RecoilState&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;,  (T =&gt; T) | T) =&gt; void; // 상태를 업데이트한다
  reset: &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(RecoilState&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;) =&gt; void; // 상태를 초기화한다
}

// Args가 실제로 사용할 콜백의 파라미터들이 된다.
function useRecoilTransaction_UNSTABLE&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(
  callback: TransactionInterface =&gt; (...Args) =&gt; void,
): (...Args) =&gt; void&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래는 useRecoilTransaction을 사용하는 예제다. 만들어진 함수는 &lt;code class=&quot;language-text&quot;&gt;goForward(100)&lt;/code&gt; 같은 방식으로 호출해서 &lt;code class=&quot;language-text&quot;&gt;positionState&lt;/code&gt; 상태를 업데이트하는데 쓸 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; goForward &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilTransaction_UNSTABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;distance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; heading &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;headingState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;positionState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;positionState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        y&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; position&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; distance&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;useRecoilCallback&lt;/code&gt;이 Recoil 상태에 접근하기 위해 &lt;code class=&quot;language-text&quot;&gt;snapshot&lt;/code&gt; 을 사용해야 하는 것에 비하면 비교적 간편하게 상태를 관리할 수 있다. 다만 리턴 값을 넘길 수 없으며, selector 상태와 비동기 atom은 사용할 수 없는 등의 한계가 있다. 그리고 상태 업데이트 외의 side effect를 만들고 싶다면 &lt;code class=&quot;language-text&quot;&gt;useRecoilTransaction&lt;/code&gt;이 아닌 &lt;code class=&quot;language-text&quot;&gt;useRecoilCallback&lt;/code&gt;을 사용하라는 가이드를 공식 문서에서 주고 있다.&lt;/p&gt;
&lt;h2 id=&quot;참고-자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://recoiljs.org/docs/api-reference/core/selector/#writeable-selectors&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;selector(options) | Recoil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://recoiljs.org/docs/api-reference/core/useRecoilTransaction/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;useRecoilTransaction_UNSTABLE(callback, deps) | Recoil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://recoiljs.org/docs/api-reference/core/useRecoilCallback&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;useRecoilCallback(callback, deps) | Recoil&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[React 커스텀 훅 함수의 테스트 코드 작성]]></title><description><![CDATA[커스텀 훅은 React 함수형 컴포넌트 API를 사용하는 함수 React에서 사용하는  커스텀 훅 (custom hook)은 함수 형태로 구현한다. 하지만 일반적인 함수처럼 테스트 코드를 작성할 수는 없다. 왜냐하면 훅 안에서는 React 함수형 컴포넌트에서 사용하는…]]></description><link>https://blog.rhostem.com//posts/2021-10-18T00:00:00.000Z</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-10-18T00:00:00.000Z</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Tue, 19 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;커스텀-훅은-react-함수형-컴포넌트-api를-사용하는-함수&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85%EC%9D%80-react-%ED%95%A8%EC%88%98%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-api%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%ED%95%A8%EC%88%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커스텀 훅은 React 함수형 컴포넌트 API를 사용하는 함수&lt;/h2&gt;
&lt;p&gt;React에서 사용하는 &lt;a href=&quot;https://ko.reactjs.org/docs/hooks-custom.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;커스텀 훅&lt;/a&gt;(custom hook)은 함수 형태로 구현한다. 하지만 일반적인 함수처럼 테스트 코드를 작성할 수는 없다. 왜냐하면 훅 안에서는 React 함수형 컴포넌트에서 사용하는 API를 사용하기 때문이다. 아래는 간단한 커스텀 훅으로써 &lt;code class=&quot;language-text&quot;&gt;useState&lt;/code&gt; API를 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useState &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSample&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;isOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setIsOpen&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    isOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    setIsOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;함수형 컴포넌트 안에서 사용하는 API를 포함하고 있기 때문에 커스텀 훅을 일반적인 함수처럼 호출하면 에러가 발생한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useIsOpen &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./useIsOpen&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;isOpen의 초기값은 false다&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useIsOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOpen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

      3 | export default function useSample() {
    &amp;gt; 4 |   const [isOpen, setIsOpen] = useState(false);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;통합 테스트를 중심으로 작성하고 있다면 커스텀 훅을 사용하고 있는 컴포넌트의 테스트를 작성하는 과정에서 자연스럽게 테스트가 이뤄지긴 할 것이다. 하지만 통합 테스트는 애초에 목적이 다르며, 커스텀 훅의 기능 하나하나를 확인하기에는 적절하지 않다. 커스텀 훅의 유닛 테스트를 위해서는 테스트 코드 안에서 간단한 래퍼 컴포넌트를 작성해줘야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useIsOpen &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./useIsOpen&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;isOpen의 초기값은 false다&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ReturnType&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;typeof&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;useIsOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;;

  const Wrapper = () =&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useIsOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;;

  render(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Wrapper&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;);

  expect(result.isOpen).toBe(false);
});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;result&lt;/code&gt; 변수를 선언한 후&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Wrapper&lt;/code&gt; 컴포넌트를 선언한다. &lt;code class=&quot;language-text&quot;&gt;useIsOpen&lt;/code&gt; 의 리턴 값을 할당받게 했다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;render&lt;/code&gt; 함수로 Wrapper를 마운트한다. &lt;code class=&quot;language-text&quot;&gt;result&lt;/code&gt;에 훅 함수가 리턴한 값이 할당된 상태가 된다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;result&lt;/code&gt; 객체에 기대한 값이 들어있는지 확인한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 기본 형태를 바탕으로 커스텀 훅 테스팅을 위한 유틸리티를 만들어서 써도 되겠지만, 언제나 먼저 앞서 나가는 사람들은 있는 법이다. 오픈소스인 &lt;a href=&quot;https://react-hooks-testing-library.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;react-hooks-testing-library&lt;/a&gt;가 있으니 활용하도록 하자.&lt;/p&gt;
&lt;h2 id=&quot;react-hooks-testing-library를-사용한-커스텀-훅-테스팅&quot;&gt;&lt;a href=&quot;#react-hooks-testing-library%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85-%ED%85%8C%EC%8A%A4%ED%8C%85&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;react-hooks-testing-library를 사용한 커스텀 훅 테스팅&lt;/h2&gt;
&lt;p&gt;react-hooks-testing-library는 커스텀 훅 테스팅을 위한 렌더링 API로 &lt;a href=&quot;https://react-hooks-testing-library.com/reference/api#renderhook&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;renderHook&lt;/code&gt;&lt;/a&gt; 함수를 제공한다. &lt;code class=&quot;language-text&quot;&gt;renderHook&lt;/code&gt; 콜백 함수를 파라미터로 받으며, 그 함수는 1개 이상의 훅 함수의 실행을 포함하고 있어야 한다. 그리고 콜백 함수가 리턴하는 값은 &lt;code class=&quot;language-text&quot;&gt;renderHook&lt;/code&gt; 리턴 값의 &lt;code class=&quot;language-text&quot;&gt;result&lt;/code&gt; 속성을 통해 참조 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderHook &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react-hooks&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useIsOpen &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./useIsOpen&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;isOpen의 초기값은 false다&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderHook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useIsOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOpen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;result.current&lt;/code&gt; 필드를 통해 콜백 함수가 마지막으로 리턴한 값(여기서는 &lt;code class=&quot;language-text&quot;&gt;useIsOpen&lt;/code&gt; 훅이 마지막으로 실행되어서 리턴한 값)을 참조할 수 있다. &lt;code class=&quot;language-text&quot;&gt;current&lt;/code&gt;라는 값이 있는 이유는, 커스텀 훅은 컴포넌트 안에서 실행되는 만큼 한번 이상 호출될 수 있기 때문이다.&lt;/p&gt;
&lt;h3 id=&quot;커스텀-훅의-상태state-업데이트-테스트&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85%EC%9D%98-%EC%83%81%ED%83%9Cstate-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커스텀 훅의 상태(state) 업데이트 테스트&lt;/h3&gt;
&lt;p&gt;커스텀 훅은 여러 컴포넌트에서 재활용하기 위해 복잡한 로직을 포함하고 있는 경우가 많고, 그 로직이 잘 구현되었는지 확인하는 과정도 반드시 필요하다. &lt;code class=&quot;language-text&quot;&gt;useIsOpen&lt;/code&gt; 훅으로 예시를 들자면 &lt;code class=&quot;language-text&quot;&gt;setIsOpen&lt;/code&gt; 메소드 호출을 통해 &lt;code class=&quot;language-text&quot;&gt;isOpen&lt;/code&gt; 상태가 변경되는지 확인이 필요하다는 말이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderHook&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; act &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react-hooks&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useIsOpen &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./useIsOpen&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;setIsOpen 호출을 통해 isOpen 상태를 업데이트할 수 있다.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderHook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useIsOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;act&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setIsOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOpen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://ko.reactjs.org/docs/test-utils.html#act&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;act&lt;/code&gt;&lt;/a&gt; 함수는 &lt;code class=&quot;language-text&quot;&gt;react-dom/test-utils&lt;/code&gt; 모듈에서 제공하고 있는 그것이다. 테스트 코드 내부에서 컴포넌트 렌더링 및 상태 변경은 &lt;code class=&quot;language-text&quot;&gt;act&lt;/code&gt; 함수 내부에서 실행해야 안전하다(여기서는 컴포넌트 렌더링이 없으므로 &lt;code class=&quot;language-text&quot;&gt;act&lt;/code&gt;로 래핑하지 않아도 테스트는 성공하긴 한다).&lt;/p&gt;
&lt;h3 id=&quot;useeffect-실행-확인-테스트&quot;&gt;&lt;a href=&quot;#useeffect-%EC%8B%A4%ED%96%89-%ED%99%95%EC%9D%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;useEffect 실행 확인 테스트&lt;/h3&gt;
&lt;p&gt;커스텀 훅 안의 &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; 훅이 실행되는지 확인하기 위해서는 useEffect가 의존하고 있는 변수가 업데이트되도록 만들어야 한다. 테스트를 위해 props를 받아서 상태를 업데이트하는 &lt;code class=&quot;language-text&quot;&gt;usePage&lt;/code&gt;라는 간단한 커스텀 훅을 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; useEffect &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;usePage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; initialPage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; initialPage&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setPage&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialPage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialPage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialPage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;initialPage&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    page&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    setPage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;renderHook&lt;/code&gt; 함수는 두 번째 파라미터로 &lt;a href=&quot;https://react-hooks-testing-library.com/usage/basic-hooks#providing-props&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;커스텀 훅의 props를 제공&lt;/a&gt;할 수 있다. 그리고 renderHook 함수의 리턴 값이 주는 &lt;code class=&quot;language-text&quot;&gt;rerender&lt;/code&gt; 함수를 사용하면, 마운트된 컴포넌트가 한번 더 렌더링되면서 커스텀 훅도 한번 더 실행된다.&lt;/p&gt;
&lt;p&gt;아래 테스트에서 &lt;code class=&quot;language-text&quot;&gt;rerender&lt;/code&gt;를 호출할 때 처음과 다른 &lt;code class=&quot;language-text&quot;&gt;initialPage&lt;/code&gt; 값을 전달했는데, 그 값이 커스텀 훅의 상태(&lt;code class=&quot;language-text&quot;&gt;page&lt;/code&gt;)에 제대로 반영되었음을 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderHook &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react-hooks&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; usePage &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./usePage&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;initialPage의 변경은 page 상태에 반영된다.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rerender &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderHook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;usePage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    initialProps&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      initialPage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;rerender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    initialPage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;context를-사용하는-커스텀-훅의-테스트&quot;&gt;&lt;a href=&quot;#context%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85%EC%9D%98-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Context를 사용하는 커스텀 훅의 테스트&lt;/h2&gt;
&lt;p&gt;커스텀 훅이 Redux의 &lt;code class=&quot;language-text&quot;&gt;useDispatch&lt;/code&gt; 훅을 사용하거나, Recoil의 &lt;code class=&quot;language-text&quot;&gt;useRecoilState&lt;/code&gt;같은 훅을 사용한다면 테스트 코드도 거기에 맞는 Provider를 제공해야 한다. react-hooks-testing-library는 그를 위한 API를 제공한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; RecoilRoot &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;recoil&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useData &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./hooks&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// recoil을 사용하는 커스텀 훅&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Wrapper&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;FC&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/** Recoil의 훅 사용을 위해 RecoilRoot로 컴포넌트를 래핑한다  */&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;RecoilRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;RecoilRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;some state&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderHook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    wrapper&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Wrapper&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;비동기-코드가-포함된-커스텀-훅의-테스트&quot;&gt;&lt;a href=&quot;#%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%BD%94%EB%93%9C%EA%B0%80-%ED%8F%AC%ED%95%A8%EB%90%9C-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85%EC%9D%98-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비동기 코드가 포함된 커스텀 훅의 테스트&lt;/h2&gt;
&lt;p&gt;실무에서는 백엔드 API 호출 데이터를 상태에 반영하는 커스텀 훅도 구현할 일이 많이 생긴다. 그런 커스텀 훅의 테스트는 testing-library를 사용해봤다면 익숙할 &lt;a href=&quot;https://react-hooks-testing-library.com/reference/api#waitfor&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;waitFor&lt;/code&gt;&lt;/a&gt; 함수로 가능하다.&lt;/p&gt;
&lt;p&gt;그리고 react-hooks-testing-library가 제공하는 &lt;code class=&quot;language-text&quot;&gt;waitForNextUpdate&lt;/code&gt;라는 기능이 있는데, 이는 &lt;code class=&quot;language-text&quot;&gt;waitFor&lt;/code&gt;와는 다르게 콜백 함수가 없다. 말 그대로 비동기적으로 실행되는 코드에 의한 태스크가 실행될 때까지 대기하는 역할을 한다. 예제를 위해 &lt;code class=&quot;language-text&quot;&gt;setTimeout&lt;/code&gt;을 사용해 상태를 업데이트하는 훅을 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; useEffect &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useAsyncCounter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setCount&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;setCount&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;첫 번째 &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; 안에서 &lt;code class=&quot;language-text&quot;&gt;setCount&lt;/code&gt;를 세 번 호출했지만 모두 동기적으로 업데이트된다. 그러므로 테스트 코드에서 &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; 는 처음부터 &lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt;으로 확인될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 300ms 후에 &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; 상태가 1000이 되도록 작성했다. 이 코드의 동작을 확인하기 위해서는 테스트 코드도 상태가 업데이트될 때까지 기다려야 한다. 이런 경우에 &lt;code class=&quot;language-text&quot;&gt;waitForNextUpdate&lt;/code&gt; 를 사용한다. 달리 말하자면 &lt;code class=&quot;language-text&quot;&gt;waitForNextUpdate&lt;/code&gt;가 리턴하는 Promise는 비동기 코드에 의해 컴포넌트가 다시 렌더링된 직후에 resolve된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderHook &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react-hooks&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useAsyncCounter &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./useAsyncCounter&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;setTimeout을 사용하면 비동기적으로 상태가 업데이트된다&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; waitForNextUpdate &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderHook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useAsyncCounter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;waitForNextUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 호출하지 않으면 테스트가 실패한다&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다만 커스텀 훅의 로직이 복잡하고 비동기 코드에 의해 업데이트되는 상태가 여러 개라면 &lt;code class=&quot;language-text&quot;&gt;waitFor&lt;/code&gt;를 사용하는 편이 더 간단하고 확실하게 테스트 결과를 확인할 수 있을 것이다.&lt;/p&gt;
&lt;h2 id=&quot;관련-자료&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://react-hooks-testing-library.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React Hooks Testing Library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Vue 3의 setup 기능이 제공하는 간결한 컴포넌트 문법]]></title><description><![CDATA[Vue는 스크립트, 마크업, 스타일시트를 한 파일 안에서 작성할 수 있는 SFC(Single File Component) 문법으로 컴포넌트를 작성할 수 있는   파일을 지원한다. SFC는 아래와 같은 방식으로 작성할 수 있다.   태그 안은 마크업이며   안에서 선언…]]></description><link>https://blog.rhostem.com//posts/2021-09-17-vue-3-script-setup</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-09-17-vue-3-script-setup</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Fri, 17 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Vue는 스크립트, 마크업, 스타일시트를 한 파일 안에서 작성할 수 있는 SFC(Single File Component) 문법으로 컴포넌트를 작성할 수 있는 &lt;code class=&quot;language-text&quot;&gt;*.vue&lt;/code&gt; 파일을 지원한다. SFC는 아래와 같은 방식으로 작성할 수 있다. &lt;code class=&quot;language-text&quot;&gt;template&lt;/code&gt; 태그 안은 마크업이며 &lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt; 안에서 선언한 변수를 사용할 수 있다. 마크업 안에서는 &lt;code class=&quot;language-text&quot;&gt;style&lt;/code&gt;  태그 안에서 작성한 스타일시트도 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;example&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ msg }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      msg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Hello world!&apos;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style language-css&quot;&gt;
&lt;span class=&quot;token selector&quot;&gt;.example&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Vue는 기본적으로 컴포넌트 모듈을 객체 형태로 선언하며 props 정의(&lt;code class=&quot;language-text&quot;&gt;props&lt;/code&gt;), 상태 변수(&lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt;), 메소드(&lt;code class=&quot;language-text&quot;&gt;methods&lt;/code&gt;), 라이프사이클(&lt;code class=&quot;language-text&quot;&gt;mounted&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;updated&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;beforeUnmount&lt;/code&gt;, …), 계산된 값(&lt;code class=&quot;language-text&quot;&gt;computed&lt;/code&gt;), 특정 값의 업데이트 탐지(&lt;code class=&quot;language-text&quot;&gt;watch&lt;/code&gt;) 등의 기능을 객체의 속성과 메소드를 사용해서 선언한다. &lt;/p&gt;
&lt;p&gt;속성별로 역할과 책임 소재가 분명히 나눠져 있다는 점에서 컴포넌트가 어떻게 동작하는지 이해하는 데 도움을 준다. 하지만 앱의 규모가 커지면서 컴포넌트 수가 늘어나고 코드가 길어지면 문제가 생긴다. &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;methods&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;template&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;watch&lt;/code&gt; 등은 모두 유기적으로 동작하는데, 컴포넌트의 덩치가 크면 여기저기로 점프를 하며 코드를 읽어야 하는 일이 자주 발생하게 된다. 즉 가독성이 매우 떨어지게 된다. 그런 상태가 되면 특정 기능과 관련된 변수와 메소드는 한곳에 모여 있으면 보기 좋겠다는 생각이 자연히 들게 된다. 그래서인지 Vue 3에서는 &lt;a href=&quot;https://v3.vuejs.org/guide/composition-api-introduction.html#why-composition-api&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Composition API&lt;/a&gt;를 제공한다.&lt;/p&gt;
&lt;h2 id=&quot;composition-api-와-code-classlanguage-textsetupcode-함수의-등장&quot;&gt;&lt;a href=&quot;#composition-api-%EC%99%80-code-classlanguage-textsetupcode-%ED%95%A8%EC%88%98%EC%9D%98-%EB%93%B1%EC%9E%A5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Composition API 와 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 함수의 등장&lt;/h2&gt;
&lt;p&gt;Vue 3에서는 컴포넌트 객체에 &lt;a href=&quot;https://v3.vuejs.org/guide/composition-api-setup.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt;&lt;/a&gt; 함수를 사용할 수 있다. &lt;a href=&quot;https://v3.vuejs.org/guide/composition-api-introduction.html#reactive-variables-with-ref&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ref&lt;/code&gt;&lt;/a&gt; 를 사용해서 반응형 변수로 &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt;를, 보통의 자바스크립트 함수로 &lt;code class=&quot;language-text&quot;&gt;methods&lt;/code&gt;를 대체한다. 라이프사이클 메소드는 &lt;code class=&quot;language-text&quot;&gt;onMounted&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;onUpdated&lt;/code&gt; 같은 &lt;a href=&quot;https://v3.vuejs.org/api/composition-api.html#lifecycle-hooks&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;라이프사이클 훅&lt;/a&gt; 함수가 대신한다. 반응형 변수의 변경 탐지 역시 &lt;a href=&quot;https://v3.vuejs.org/guide/composition-api-introduction.html#reacting-to-changes-with-watch&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;watch&lt;/code&gt;&lt;/a&gt; 함수로 구현 가능하며 계산된 값을 위한 &lt;code class=&quot;language-text&quot;&gt;computed&lt;/code&gt; 함수도 제공된다. 컴포넌트 객체애서 속성으로 분리되었던 기능 대부분이 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 함수 안에서 사용 가능하다. &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt;을 사용하는 컴포넌트는 아래와 같은 방식으로 작성한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ref&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onMounted &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;vue&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  props&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; String
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isSubmited &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onSubmit&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      isSubmited&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;onMouted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;component mounted&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// template에 전달한다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
      isSubmited&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      onSubmit
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ name }} {{ isSubmited ? &apos;submited&apos; : &apos;not yet&apos; }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;onSubmit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Submit&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;중요한 차이점은 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 함수는 컴포넌트 인스턴스가 생성되기 전에 실행된다는 점이다. 즉 컴포넌트 인스턴스에 접근이 필요한 기능은 사용할 수 없다. 즉 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 안에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 통해 컴포넌트 객체의 &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;computed&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;methods&lt;/code&gt; 에서 선언한 것들에는 접근이 불가능하다.  그리고 인스턴스가 생성된 직후에 호출되는 &lt;code class=&quot;language-text&quot;&gt;created&lt;/code&gt; 메소드에 매칭되는 라이프 사이클 훅도 존재하지 않는다.&lt;/p&gt;
&lt;h2 id=&quot;커스텀-훅을-사용한-로직-분리-및-재활용&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-%EB%A1%9C%EC%A7%81-%EB%B6%84%EB%A6%AC-%EB%B0%8F-%EC%9E%AC%ED%99%9C%EC%9A%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커스텀 훅을 사용한 로직 분리 및 재활용&lt;/h2&gt;
&lt;p&gt;Vue에서는 &lt;a href=&quot;https://v3.vuejs.org/guide/mixins.html#basics&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;mixins&lt;/code&gt;&lt;/a&gt;를 사용해서 로직 재활용이 가능하다. 하지만 &lt;a href=&quot;https://v3.vuejs.org/guide/mixins.html#option-merging&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;컴포넌트 속성이 병합&lt;/a&gt;된다는 특징을 가질 수밖에 없다. 예를 들어 컴포넌트와 믹스인에서 선언한 &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt; 함수에서 리턴하는 변수의 이름이 같으면 의도하지 않게 덮어씌워지거나 하는 등의 충돌이 발생할 수 있기에 사용에 있어 주의가 필요하다. 하지만 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt;을 사용하면 외부 함수를 호출하는 방식으로 완전한 로직의 분리가 가능하다. React를 사용한다면 함수형 컴포넌트에서 로직 공유를 위해 커스텀 훅을 구현하는 일에 익숙할텐데, Vue 3에서는 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 함수 안에서 그와 똑같은 방법을 사용할 수 있게 되었다.&lt;/p&gt;
&lt;p&gt;예를 들어 Select 컴포넌트를 위한 옵션을 서버에서 가져와야 하는데, 그 과정이 여러 페이지에서 필요하다고 가정하자. 그렇다면 옵션 데이터를 가져오는 과정을 훅 함수 안에서 구현한 후 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 안에서 호출하여 결과만 가져오는 방식을 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSomeOptions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;onMounted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://api.server.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		options
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(item, index) in options&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ item }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;option&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useSomeOptions &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./useSomeOptions&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Page component&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; options &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSomeOptions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      options&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style language-css&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Vue의 기존 문법을 선호하는 사람이라면  &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 함수와 커스텀 훅을 볼 때  Vue를 React스럽게 쓴다고 거부감을 표현할 수도 있을 것 같다. &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 안에서는 심지어 React처럼 &lt;a href=&quot;https://v3.vuejs.org/guide/render-function.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;render 함수&lt;/a&gt;와 JSX까지 지원하며 &lt;code class=&quot;language-text&quot;&gt;template&lt;/code&gt; 파트가 완전히 사라지게 구현할 수도 있다(타입스크립트를 사용하는 입장이라면 더욱 선호할 것이다).  하지만 Vue 3에서도 &lt;code class=&quot;language-text&quot;&gt;mixins&lt;/code&gt; 등 기존 문법은 여전히 지원하므로 개인이 선호하는 방식으로 구현하면 된다. 대신 React를 사용하는 사람 입장에서는 친숙한 패턴으로 인해 진입 장벽이 많이 낮아졌다고 할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;script-setup-문법&quot;&gt;&lt;a href=&quot;#script-setup-%EB%AC%B8%EB%B2%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;script setup 문법&lt;/h2&gt;
&lt;p&gt;컴포넌트 객체의 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 함수는 확실히 혁신적이고 큰 변화지만 여전히 아쉬운 점이 있다. &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;methods&lt;/code&gt; 와 마찬가지로 &lt;code class=&quot;language-text&quot;&gt;return&lt;/code&gt; 을 사용해서 템플릿으로 값을 넘기는 과정이 필요하다는 것이다. React 함수형 컴포넌트에서 상태 변수, 메소드를 선언한 후 JSX 안에서 바로 사용하는 데 익숙한 입장에서는 다소 번거롭고 버그가 많이 발생하는 부분이었다. &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt;은 물론 다른 옵션들도 마찬가지다. 예를 들어 &lt;code class=&quot;language-text&quot;&gt;template&lt;/code&gt; 안에서 사용할 외부 컴포넌트는 모듈 import 후에 컴포넌트의 &lt;code class=&quot;language-text&quot;&gt;components&lt;/code&gt; 속성에 추가해야 한다. import만으로 끝나지 않는다는 말은 상용구(boilerplate) 코드가 별도로 필요하다는 것, 그리고 앱의 사이즈가 커짐에 따라 반복 작업이 늘어난다는 말이 된다.&lt;/p&gt;
&lt;p&gt;Vue 3에서는 이런 불편함을 해소할 수 있는 방법을 제공한다. script setup 문법이 그것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;
&lt;span class=&quot;token comment&quot;&gt;// 변수 선언&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; msg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Hello!&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 함수 선언&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- 템플릿 안에서 바로 사용 가능 --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ msg }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;뭔가 코드가 허전할 정도로 많이 줄었다. &lt;code class=&quot;language-text&quot;&gt;return&lt;/code&gt;을 통해서 template에 명시적으로 전달하는 과정이 생략된 덕분이다. 컴포넌트 모듈도 import만 해주면 template 안에서 사용할 수 있다. 특히 컴포넌트를 타입스크립트로 사용할 때 더 좋다. 컴포넌트 객체로 &lt;code class=&quot;language-text&quot;&gt;props&lt;/code&gt;를 선언하기 위해서는 Javascript의 Primitive 데이터의 생성자 함수(ex. &lt;code class=&quot;language-text&quot;&gt;String&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Object&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Function&lt;/code&gt;)와 &lt;code class=&quot;language-text&quot;&gt;PropType&lt;/code&gt;을 조합해야 했지만, script setup 안에서는 &lt;code class=&quot;language-text&quot;&gt;defineProps&lt;/code&gt;에 제네릭과 타입스크립트의 타입을 그대로 사용하면 된다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; defineProps&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  foo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  bar&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    isSetup&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;setup-기능의-장단점&quot;&gt;&lt;a href=&quot;#setup-%EA%B8%B0%EB%8A%A5%EC%9D%98-%EC%9E%A5%EB%8B%A8%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;setup 기능의 장단점&lt;/h2&gt;
&lt;p&gt;Vue 3에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt;의 장점과 단점은 아래와 같이 정리할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;장점&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;간결한 문법으로 상용구(boilerplate)를 줄일 수 있음&lt;/li&gt;
&lt;li&gt;타입스크립트를 사용해 props와 emmited value 선언이 가능&lt;/li&gt;
&lt;li&gt;런타임 성능의 향상(템플릿이 setup 스크립트와 같은 스코프(scope)에 있는 &lt;a href=&quot;https://v3.vuejs.org/guide/render-function.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;render 함수&lt;/a&gt;로 컴파일되므로 프록시가 필요없음)&lt;/li&gt;
&lt;li&gt;더 뛰어난 IDE 타입 추론 성능 (language 서버가 코드로부터 타입을 추론해내는 데 비용이 덜 든다)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Vue 3는 정식 배포 전이므로 버그 및 API 스펙 변경 가능성 있음&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;아직(2021년 9월 현재) vscode의 공식 툴인 Vetur의 지원이 완벽하지 않음&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ex. import한 컴포넌트를 render 함수가 아닌 template에서 사용하면 “사용하지 않은 변수”라는 문법 오류를 표시함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;타입스크립트로 Vue 3를 사용해 본 입장에서는 setup 기능을 굳이 사용하지 않을 이유가 없었다. script setup이 제공하는 간결한 문법에 Vue의 반응형 시스템, 편리한 directive들까지 생각하면 Vue 3는 예전보다 훨씬 더 매력적인 도구로 다가오고 있다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Recoil atomFamily를 사용한 상태 관리]]></title><description><![CDATA[Recoil에 대한 간략한 소개 Recoil 은 Facebook에서 만든 React를 위한 상태 관리 라이브러리다. 아직은 버전이  0.3 이며(2021년 7월 현재) Redux의 미들웨어처럼 활용할 수 있는  의 스펙이 아직 안정화된 상태가 아니긴 하다. 하지만 A…]]></description><link>https://blog.rhostem.com//posts/2021-07-07-state-management-with-recoil-atomfamily</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-07-07-state-management-with-recoil-atomfamily</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Wed, 07 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;recoil에-대한-간략한-소개&quot;&gt;&lt;a href=&quot;#recoil%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B0%84%EB%9E%B5%ED%95%9C-%EC%86%8C%EA%B0%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Recoil에 대한 간략한 소개&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://recoiljs.org/ko/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Recoil&lt;/a&gt;은 Facebook에서 만든 React를 위한 상태 관리 라이브러리다. 아직은 버전이 &lt;a href=&quot;https://recoiljs.org/blog/2021/05/14/0.3.0-released&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;0.3&lt;/a&gt;이며(2021년 7월 현재) Redux의 미들웨어처럼 활용할 수 있는 &lt;a href=&quot;https://recoiljs.org/docs/guides/atom-effects&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;atomEffect&lt;/code&gt;&lt;/a&gt;의 스펙이 아직 안정화된 상태가 아니긴 하다. 하지만 API 스펙의 안정화, 성능 개선과 함께 Recoil을 기반으로 한 라이브러리가 늘어나면 Redux를 점점 대체해 나갈 것으로 기대된다. &lt;/p&gt;
&lt;p&gt;Redux의 상태 관리는 기본적으로 상용구 코드(boilerplate code)가 많이 발생하는데다 &lt;a href=&quot;https://redux.js.org/api/combinereducers&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;combineReducer&lt;/code&gt;&lt;/a&gt;를 통한 reducer 분할이 가능하다 하더라도 Recoil의 atom과 같은 유연함은 결코 제공할 수 없다. 컴포넌트의 &lt;a href=&quot;https://ko.reactjs.org/docs/hooks-reference.html#usememo&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;useMemo&lt;/code&gt;&lt;/a&gt;처럼 파생 데이터를 생성할 수 있는 기능은 자체적으로 가지고 있지 않아서 &lt;a href=&quot;https://github.com/reduxjs/reselect&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;reselect&lt;/a&gt;같은 외부 라이브러리의 힘을 빌려야 한다. 그리고 &lt;code class=&quot;language-text&quot;&gt;atomEffect&lt;/code&gt;처럼 특정 상태가 업데이트 되었을 때 실행할 사이드 이펙트는 자체적으로 정의할 수 없으며 컴포넌트에서 상태를 구독한 후 &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; 안에서 구현해야 한다. Redux 미들웨어는 상태 업데이트 후에 실행되는 것이 아니라 dispatch된 모든 액션이 거쳐가는 곳이라 성격이 다르다. &lt;/p&gt;
&lt;p&gt;그리고 결정적으로 데이터 구조가 다르다. Redux의 상태 구조는 트리 형태지만 Recoil은 &lt;a href=&quot;https://en.wikipedia.org/wiki/Directed_graph&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;유향 그래프(directed graph)&lt;/a&gt; 형태로 구성된다. Recoil에서 데이터의 소스인 atom은 고유한 문자열 키를 가지는데, Recoil은 atom의 키를 구성하는 알파벳을 노드로 하여 그래프를 구성한다(&lt;a href=&quot;https://en.wikipedia.org/wiki/Trie&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Trie&lt;/a&gt; 참조). Recoil에서 atom의 키는 고유한 값을 가져야 하므로 그래프 상의 노드에는 언제나 1개의 atom만 존재하게 된다. 이는 곧 atom이 다른 atom의 영향을 받지 않는 독립적인 상태라는 말이기도 하다. Redux에서 그랬던 것처럼 reducer를 구현할 때 업데이트 대상이 아닌 값(=상태 객체의 다른 필드)들의 불변성을 보장하기 위해 &lt;a href=&quot;https://immutable-js.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;immutable.js&lt;/a&gt;, &lt;a href=&quot;https://github.com/immerjs/immer&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;immer&lt;/a&gt; 같은 라이브러리의 사용할 고려할 필요가 없다.&lt;/p&gt;
&lt;p&gt;이 외에도 Recoil은 React의 동시성 모드(Concurrent Mode) 및 신규 기능과의 호환성이 제공될 수 있으며 atom, selector 앱 개발에 따라 점진적으로 추가할 수 있어서 코드 분할에 용이하다는 점, 비동기 selector를 통한 원격 데이터 구독 등 다양한 특징을 가지고 있다. &lt;/p&gt;
&lt;h2 id=&quot;atom으로-할일-목록-관리&quot;&gt;&lt;a href=&quot;#atom%EC%9C%BC%EB%A1%9C-%ED%95%A0%EC%9D%BC-%EB%AA%A9%EB%A1%9D-%EA%B4%80%EB%A6%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;atom으로 할일 목록 관리&lt;/h2&gt;
&lt;p&gt;앱에서는 같은 타입의 상태 객체로 구성된 그룹, 배열이 필요한 경우가 생긴다. 그리고 상태 객체는 동적으로 추가, 수정, 제거될 수 있어야 한다. 예를 들면 할일 목록이 그렇다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Todo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  isDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; TodoList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Todo&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 할일 항목은 동적으로 추가, 삭제되므로 atom으로 하나하나 선언하는 것은 불가능하다. 그러면 &lt;code class=&quot;language-text&quot;&gt;TodoList&lt;/code&gt; 타입을 가진 atom을 선언해서 상태를 관리하면 되지 않을까?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoListState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TodoList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;todoListState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 가능하다. 하지만 할일 목록의 업데이트를 위해서 atom이 가진 상태는 항상 새로운 배열로 교체되어야 한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;todoList&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setTodoList&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todoListState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;addTodo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;setTodoList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;todoList&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;removeTodo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; todoList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; updatedList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;todoList&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    updatedList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;splice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setTodoList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updatedList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 

  &lt;span class=&quot;token comment&quot;&gt;// update todo and so on....&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Redux로 배열을 제어할 때 많이 보던 방식이다. 그리고 atom 업데이트를 위해 배열을 완전히 교체하는 비용이 발생하므로 불변성을 제공하기 위해 immer 같은 라이브러리를 도입해야 할 것 같다. useState를 쓰는 것과 별다른 점이 없는 것 같은데, 이러면 그냥 Redux를 쓰고 싶어질 것 같다.&lt;/p&gt;
&lt;p&gt;하지만 이렇게 atom으로 할일 목록을 관리하는 것이 아니라, 1개의 할일 객체는 1개의 atom에 대응되게 만들 수는 없을까? 그러면 할일을 렌더링하는 컴포넌트에서 1개의 atom만 직접 구독하면 될 것이다. 업데이트할 때도 배열을 교체하는 것이 아니라 구독한 atom만 업데이트해주면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;TodoComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	 &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setTodo&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;동적으로 만들어진 atom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이런 케이스를 위해 Recoil은 atomFamily API를 제공한다.&lt;/p&gt;
&lt;h2 id=&quot;atomfamily&quot;&gt;&lt;a href=&quot;#atomfamily&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://recoiljs.org/docs/api-reference/utils/atomFamily&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;atomFamily&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;atomFamily API는 atom을 리턴하는 팩토리 함수를 만들며, 그 팩토리 함수로 독립적인 atom state를 만들 수있다. 할일 상태를 atomFamily로 작성하면 다음과 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoItemState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; atomFamily&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;todoItemState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      isDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;atomFamily의 &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; 옵션은 다양한 형태로 사용할 수 있는데, 기본적으로 함수 형태로 작성한다. 함수의 파라미터로 팩토리 함수를 호출할 때 사용한 값을 전달할 수 있기 때문이다. &lt;/p&gt;
&lt;p&gt;팩토리 함수를 호출할 때는 atom을 구분할 수 있는 고유한 값을 사용해야 상태의 독립을 보장할 수 있다.  &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;TodoComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;	
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setTodo&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;todoItemState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; 함수에 atom을 구분할 수 있는 고유한 키 값이 전달되므로 그 값을 바탕으로 기본값을 구성할 수 있다. 특히 Recoil의 데이터 그래프는 비동기적으로 동작할 수 있기 때문에 &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; 함수는 Promise를 리턴하는 것도 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Parameter &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; RecoilValue&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; | Promise&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoItemState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; atomFamily&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;
  Todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 리턴 데이터 타입&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// atomFamily 팩토리 함수 호출 파라미터&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;todoItemState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getTodoData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
      title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      isDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt;에 &lt;a href=&quot;https://recoiljs.org/docs/api-reference/utils/selectorFamily/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;selectorFamily&lt;/code&gt;&lt;/a&gt;를 사용하면 다른 상태를 참조하는 것이 가능하다. 예를 들어 서버 데이터가 다른 상태에 저장되어 있고, 그 상태를 사용해서 기본값을 구성할 때 유용하게 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoItemState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; atomFamily&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;todoItemState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;selectorFamily&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;todoItemState/default&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// 아래의 serverDataState는 recoil atom 또는 selector &lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dataList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;serverDataState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; serverData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	      id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
	      title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	      isDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;atomfamily로-할일-상태-구현&quot;&gt;&lt;a href=&quot;#atomfamily%EB%A1%9C-%ED%95%A0%EC%9D%BC-%EC%83%81%ED%83%9C-%EA%B5%AC%ED%98%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;atomFamily로 할일 상태 구현&lt;/h2&gt;
&lt;p&gt;예제이므로 여기서는 할일을 서버에도, 로컬스토리지에도 저장하지 않는 형태로 구현해보고자 한다.&lt;/p&gt;
&lt;p&gt;atomFamily를 사용하기 위해서는 atom을 구분할 수 있는 키가 필요하다는 것을 알았다. 그러면 그 키를 관리할 수 있는 상태가 필요할 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoIdsState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; atom&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;todoIdsState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 할일이 서버에 저장된다면 &lt;code class=&quot;language-text&quot;&gt;todoIdsState&lt;/code&gt; 는 문자열의 배열 형태가 아니라 아이디를 포함한 객체의 배열 형태로 사용할 수도 있다. 중요한 것은 &lt;strong&gt;고유한 아이디를 가진 데이터의 배열&lt;/strong&gt;이어야 한다는 점이다. 그래야 그 데이터를 사용해 atomFamily를 사용할 수 있기 때문이다.&lt;/p&gt;
&lt;p&gt;할일 상태를 관리할 atomFamily는 앞서 살펴본 것과 같다. 마찬가지로 서버 데이터를 필요하다면 default 함수에 Promise를 리턴하는 비동기 쿼리를 쓰거나 selectorFamily를 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoItemState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; atomFamily&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  key&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;todoItemState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      isDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;상태는 준비가 되었다. 이제 새로운 할일을 추가하기 위해 todoIdsState에 새로운 아이디를 추가하는 컴포넌트가 필요하다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NewTodoForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; addTodo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; snapshot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoIds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; snapshot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLoadable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todoIdsState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todoIdsState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;todoIds&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;addTodo&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      Add New Item
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; TodoInputForm&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여기서는 &lt;a href=&quot;https://recoiljs.org/docs/api-reference/core/useRecoilCallback&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;useRecoilCallback&lt;/code&gt;&lt;/a&gt;을 사용했다. &lt;code class=&quot;language-text&quot;&gt;useRecoilState&lt;/code&gt;를 사용해도 되지만  저 훅을 사용하면 컴포넌트에서 &lt;code class=&quot;language-text&quot;&gt;todoIdsState&lt;/code&gt; 상태의 업데이트를 구독하지 않게 된다는 장점이 있다.&lt;/p&gt;
&lt;p&gt;이제 할일 아이디 목록을 컴포넌트 목록으로 맵핑해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TodoList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoIdList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todoIdsState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;todoIdList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TodoItem&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; TodoList&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;TodoItem&lt;/code&gt;에 atomFamily에 사용할 &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt;를 props로 전달했다. 할일 컴포넌트에서 이렇게 전달된 id로 atomFamily 팩토리 함수를 호출하여 상태를 만든다. 아래의 예제 코드에서는 할일의 제목(&lt;code class=&quot;language-text&quot;&gt;title&lt;/code&gt; 필드)을 업데이트하는 기능만 작성해 두었다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Props&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; TodoItem&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FC&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; = (&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;) =&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setTodo&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRecoilState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;todoItemState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handleChangeTodoTitle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleChangeTodoTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;new todo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;;

export default React.memo(TodoItem);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;React.memo&lt;/code&gt;를 사용하여 &lt;code class=&quot;language-text&quot;&gt;TodoItem&lt;/code&gt; 을 래핑해두면 새로운 할일이 추가되어도 이미 렌더링된 컴포넌트를 다시 렌더링하지 않는다. 전달하는 props가 &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; 1개인데다 문자열이기 때문에 다른 할일이 삭제, 추가되어도 memoization이 적용될 것임을 쉽게 추측할 수 있다. &lt;/p&gt;
&lt;p&gt;만약 처음 살펴본 것처럼 atomFamily를 사용하지 않고 &lt;code class=&quot;language-text&quot;&gt;todoListState&lt;/code&gt; 에 할일 객체 배열을 저장하는 방식을 사용한다면 새로운 할일이 추가될 때마다 모든 &lt;code class=&quot;language-text&quot;&gt;TodoItem&lt;/code&gt; 컴포넌트가 다시 렌더링된다. 아래와 같은 방식으로 업데이트하면 새로운 배열이 생성되며 객체의 레퍼런스도 모두 바뀌기 때문이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;addTodo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;setTodoList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;todoList&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;렌더링 방지를 위해서는 배열에 포함된 객체의 불변성이 보장되도록 해야 한다. 또는 객체의 데이터를 여러 props에 분할해서 제공하거나(레퍼런스 변경이 없는 literal 데이터만 전달되도록) &lt;a href=&quot;https://ko.reactjs.org/docs/react-api.html#reactmemo&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React.memo&lt;/a&gt;의 두번째 파라미터에 전달할 비교 함수를 직접 구현해야 할 것이다. 이런 점을 따져보면 atomFamily를 사용한 상태 관리는 다른 atom을 신경쓸 필요가 없는 단순함이 있으며, 최적화에도 도움이 된다는 사실을 확인할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;참고자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://recoiljs.org/docs/api-reference/utils/atomFamily&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;atomFamily | Recoil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://recoiljs.org/docs/introduction/motivation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Motivation | Recoil&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/whats-new-in-recoil-0-3/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;What’s new in Recoil 0.3? - LogRocket Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Tailwind CSS의 설치와 활용]]></title><description><![CDATA[이 글은 Tailwind CSS v2를 기준으로 작성되었습니다. Tailwind CSS의 특징 Tailwind CSS는 유틸리티 퍼스트( Utility-first )를 지향하는 CSS 프레임워크다. CSS를 작성하는 방법론 중 하나인  BEM 은 마크업을 컴포넌트 단…]]></description><link>https://blog.rhostem.com//posts/2021-06-05-tailwind-css</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-06-05-tailwind-css</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 05 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;이 글은 Tailwind CSS v2를 기준으로 작성되었습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;tailwind-css의-특징&quot;&gt;&lt;a href=&quot;#tailwind-css%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tailwind CSS의 특징&lt;/h2&gt;
&lt;p&gt;Tailwind CSS는 유틸리티 퍼스트(&lt;a href=&quot;https://tailwindcss.com/docs/utility-first&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Utility-first&lt;/a&gt;)를 지향하는 CSS 프레임워크다. CSS를 작성하는 방법론 중 하나인 &lt;a href=&quot;http://getbem.com/introduction/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;BEM&lt;/a&gt;은 마크업을 컴포넌트 단위로 구분하고 상태에 따라 스타일을 변경하는 식으로 재활용할 수 있도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button button--success&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button-label&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Submit&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.button&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.button-label&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.button--success&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #FFF&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;border-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #4A993E&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.button--danger&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #900&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 유틸리티를 우선하는 CSS 프레임워크는 클래스와 스타일을 새로 작성하지 않는다. 레이아웃, 포지션, 스페이스, 컬러, 폰트 등 스타일링에 필요한 대부분의 속성이 수많은 클래스로 사전 정의되어 있고, 사용자는 그 클래스들을 조합해서 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.inline-block&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.rounded&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.25rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.p-4&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;inline-block rounded p-4&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	Submit
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;CSS를 유틸리티 클래스 기반으로 사용할 때의 장점은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;컴포넌트를 작성할 때 CSS 클래스 이름을 고민할 필요가 없다.&lt;/li&gt;
&lt;li&gt;컴포넌트의 수가 늘어나도 CSS 파일의 사이즈는 크게 늘어나지 않는다.&lt;/li&gt;
&lt;li&gt;복잡한 구조의 클래스+태그 셀렉터를 사용하지 않기 때문에 버그 발생 가능성이 낮고 수정이 쉽다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;클래스를 여러 개 붙이는 것에서 인라인 스타일과 다를게 없다거나, &lt;code class=&quot;language-text&quot;&gt;className&lt;/code&gt; 속성이 너무 길어지지 않느냐고 말할 수도 있다. 하지만 다음과 같은 장점도 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사전 정의된 디자인 시스템을 기반으로 클래스를 생성하기 때문에 컬러, 사이즈, 간격에서 디자인 일관성을 유지할 수 있다.&lt;/li&gt;
&lt;li&gt;반응형 유틸리티 클래스(ex. &lt;code class=&quot;language-text&quot;&gt;md:p-4&lt;/code&gt;)를 사용해서 미디어 쿼리를 쉽게 적용할 수 있다.&lt;/li&gt;
&lt;li&gt;hover, focus 등 여러가지 상태를 위한 스타일(ex. &lt;code class=&quot;language-text&quot;&gt;hover:font-bold&lt;/code&gt;)을 쉽게 추가할 수 있다. 인라인 스타일에서는 &lt;code class=&quot;language-text&quot;&gt;hover&lt;/code&gt;등의 상태 선택자를 사용할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그리고 Tailwind CSS는 기본 제공하는 클래스 외에도 사용자가 직접 테마를 확장하고, 유틸리티 클래스를 추가할 수 있다. 그래고 반복되는 마크업과 스타일은 새로운 클래스를 추가하는 것보다 컴포넌트 모듈로 분리하는 것을 권장한다.&lt;/p&gt;
&lt;h2 id=&quot;클래스-자동완성을-위한-tailwind-css-intellisense&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%9E%90%EB%8F%99%EC%99%84%EC%84%B1%EC%9D%84-%EC%9C%84%ED%95%9C-tailwind-css-intellisense&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클래스 자동완성을 위한 Tailwind CSS Intellisense&lt;/h2&gt;
&lt;p&gt;사용 가능한 클래스가 수백개가 넘기 때문에 처음 접하는 사람이나 조금 익숙해진 사람이나 그 많은 클래스를 모두 외운 상태로 사용하기는 어렵다. 그리고 &lt;code class=&quot;language-text&quot;&gt;className&lt;/code&gt; 속성이 길어지면 같은 속성을 설정하는 유틸리티 클래스가 중복되어(ex. &lt;a href=&quot;https://tailwindcss.com/docs/display&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;display 속성을 설정하는 &lt;code class=&quot;language-text&quot;&gt;flex&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;block&lt;/code&gt; 클래스&lt;/a&gt;) 버그를 발생시킬 수도 있다. 그래서 Tailwind CSS는 Visual Studio Code의 확장 프로그램으로 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Tailwind CSS IntelliSense&lt;/a&gt;를 제공하고 있다. &lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1c2mvLaFJJjZVQ4GBu75Uj/26c7447710beb22673a6827d26509442/autocomplete.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 56.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Autocomplete of Tailwind CSS Intellisense&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1c2mvLaFJJjZVQ4GBu75Uj/26c7447710beb22673a6827d26509442/autocomplete.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1c2mvLaFJJjZVQ4GBu75Uj/26c7447710beb22673a6827d26509442/autocomplete.png?w=520 520w,
https://images.ctfassets.net/rpmifyuylbfw/1c2mvLaFJJjZVQ4GBu75Uj/26c7447710beb22673a6827d26509442/autocomplete.png?w=1040 1040w,
https://images.ctfassets.net/rpmifyuylbfw/1c2mvLaFJJjZVQ4GBu75Uj/26c7447710beb22673a6827d26509442/autocomplete.png?w=2080 2080w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;컬러 유틸리티는 좌측에 색상까지 표시해 준다 👍&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;확장 프로그램을 사용하면 에디터 안에서 유틸리티 클래스 자동완성을 제공하며 충돌하는 클래스도 체크해 준다. 그리고 단순히 기본 제공하는 클래스의 자동 완성만 지원하는 것이 아니라 &lt;strong&gt;사용자가 직접 설정한 클래스도 확장 프로그램이 인식하여 자동완성에 제공&lt;/strong&gt;해 준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// tailwind.config.js&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  theme&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    extend&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      spacing&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        xs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; defaultTheme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;spacing&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        sm&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; defaultTheme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;spacing&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;    
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 테마 확장으로 spacing 을 추가하면 &lt;code class=&quot;language-text&quot;&gt;p-4&lt;/code&gt; 같은 기본 클래스 뿐만 아니라 &lt;code class=&quot;language-text&quot;&gt;p-xs&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;p-sm&lt;/code&gt; 처럼 &lt;a href=&quot;https://tailwindcss.com/docs/customizing-spacing&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;간격에 관련된 모든 유틸리티 클래스에 suffix로 형태로 추가된다&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;그리고 클래스네임이 대부분 어떤 속성을 설정하는지 유추하기 쉽게 만들어져 있어서 레퍼런스를 찾아보면서 파악하면 큰 어려움은 없다. 그리고 이런 프레임워크는 사용하면 사용할수록 작업 속도가 빨라진다는 장점이 있다.&lt;/p&gt;
&lt;h2 id=&quot;tailwind-css-설치&quot;&gt;&lt;a href=&quot;#tailwind-css-%EC%84%A4%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tailwind CSS 설치&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://tailwindcss.com/docs/installation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Installation - Tailwind CSS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tailwind CSS는 &lt;a href=&quot;https://www.google.com/search?q=PostCSS&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;PostCSS&lt;/a&gt;를 기반으로 한다. 유틸리티 클래스를 생성하기 위해서 필요한 사항은 다음과 같다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;npm 모듈&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TailwindCSS&lt;/li&gt;
&lt;li&gt;PostCSS&lt;/li&gt;
&lt;li&gt;Autoprefixer&lt;/li&gt;
&lt;li&gt;postcss-cli&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;설정 파일&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tailwind.config.js&lt;/li&gt;
&lt;li&gt;tailwind.css (postcss로 처리할 파일. 이름은 상관없음)&lt;/li&gt;
&lt;li&gt;postcss.config.js&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;여기서는 postcss-cli를 사용해서 스타일시트를 직접 생성한 후 앱의 entry 모듈에서 import하는 방식을 사용할 것이다. 만약 webpack을 사용한다면 style-loader, css-loader와 함께 &lt;a href=&quot;https://webpack.js.org/loaders/postcss-loader/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;postcss-loader&lt;/a&gt;를 사용하면 된다.&lt;/p&gt;
&lt;h3 id=&quot;postcss-설정-파일&quot;&gt;&lt;a href=&quot;#postcss-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PostCSS 설정 파일&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// postcss.config.js&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  plugins&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;tailwindcss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;autoprefixer&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Babel의 &lt;code class=&quot;language-text&quot;&gt;.babelrc&lt;/code&gt; 파일처럼 프로젝트 루트에 PostCSS 설정 파일이 있으면 postcss-cli, webpack postcss-loader에서 모두 활용이 가능하다. &lt;/p&gt;
&lt;h3 id=&quot;tailwind-설정-파일&quot;&gt;&lt;a href=&quot;#tailwind-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tailwind 설정 파일&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// tailwind.config.js&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  purge&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  darkMode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  theme&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    extend&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  variants&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  plugins&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;프로젝트 루트에 추가해 준다. 더 자세한 내용은 &lt;a href=&quot;https://tailwindcss.com/docs/configuration&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Configuration&lt;/a&gt; 문서에서 확인이 가능하다.&lt;/p&gt;
&lt;h3 id=&quot;베이스-css-파일&quot;&gt;&lt;a href=&quot;#%EB%B2%A0%EC%9D%B4%EC%8A%A4-css-%ED%8C%8C%EC%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;베이스 CSS 파일&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sass&quot;&gt;&lt;pre class=&quot;language-sass&quot;&gt;&lt;code class=&quot;language-sass&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/tailwind.css&lt;/span&gt;
&lt;span class=&quot;token atrule-line&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;@tailwind&lt;/span&gt; base;&lt;/span&gt;
&lt;span class=&quot;token atrule-line&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;@tailwind&lt;/span&gt; components;&lt;/span&gt;
&lt;span class=&quot;token atrule-line&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;@tailwind&lt;/span&gt; utilities;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@tailwind&lt;/code&gt; 라는 키워드는 postcss.config.js 파일의 플러그인 설정에 &lt;code class=&quot;language-text&quot;&gt;require(&amp;#39;tailwindcss&amp;#39;)&lt;/code&gt;를  추가했기 때문에 사용 가능한 것이다. 위의 코드를 추가하면 PostCSS가 &lt;code class=&quot;language-text&quot;&gt;tailwindcss&lt;/code&gt; 플러그인과 tailwind.config.js 파일을 참조하여 유틸리티 클래스들을 만들어준다.&lt;/p&gt;
&lt;h3 id=&quot;css-파일-변환-npm-스크립트-추가&quot;&gt;&lt;a href=&quot;#css-%ED%8C%8C%EC%9D%BC-%EB%B3%80%ED%99%98-npm-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%B6%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CSS 파일 변환 npm 스크립트 추가&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// package.json
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;tw:watch&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TAILWIND_MODE=watch NODE_ENV=development postcss ./src/tailwind.css -o ./src/tailwind_out.css -w&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token property&quot;&gt;&quot;tw:build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NODE_ENV=production postcss ./src/tailwind.css -o ./src/tailwind_out.css/&quot;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;tw:watch&lt;/code&gt; 스크립트를 실행하면 postcss-cli가 tailwind.css 파일을 읽어서 &lt;code class=&quot;language-text&quot;&gt;tailwind_out.css&lt;/code&gt; 파일을 생성한다. 그리고 후술할 JIT 모드를 사용하기 위해 소스 코드에 변경이 있을 때 &lt;code class=&quot;language-text&quot;&gt;tailwind_out.css&lt;/code&gt; 파일이 업데이트되도록 watch 옵션을 함께 설정했다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;tw:build&lt;/code&gt; 스크립트는 앱 배포를 위해 빌드를 할 때 사용할 것이다. production 모드에서 CSS 속성에 autoprefixer 모듈을 사용해서 벤더 프리픽스(vendor prefix)를 붙여주기 위해서는 &lt;a href=&quot;https://github.com/browserslist/browserslist&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;browserslist&lt;/a&gt; 설정 파일이 필요하다. 프로젝트 루트에 .browserslistrc 파일을 추가해도 되고, package.json에 추가해도 된다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// package.json
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&quot;browserslist&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;production&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;0.2%&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;not dead&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;not op_mini all&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;development&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;last 1 chrome version&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;last 1 firefox version&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;last 1 safari version&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;개발 모드에서는 주요 브라우저의 최신 버전에만 맞추지만, production 모드에서는 거의 모든 브라우저를 지원하게 했으므로 필요한 대부분의 벤더 프리픽스가 추가된다.&lt;/p&gt;
&lt;h3 id=&quot;앱에서-생성된-스타일시트-가져오기&quot;&gt;&lt;a href=&quot;#%EC%95%B1%EC%97%90%EC%84%9C-%EC%83%9D%EC%84%B1%EB%90%9C-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%8B%9C%ED%8A%B8-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;앱에서 생성된 스타일시트 가져오기&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/index.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; ReactDOM &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-dom&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./tailwind_out.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// compiled tailwind stylesheet&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Index&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; Index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;React 앱의 루트 컴포넌트에서 스타일시트를 불러오도록 했다. 사용하는 번들러에 따라 CSS 파일을 import하기 위해 추가적인 설정이 필요할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;개발-서버-실행-스크립트&quot;&gt;&lt;a href=&quot;#%EA%B0%9C%EB%B0%9C-%EC%84%9C%EB%B2%84-%EC%8B%A4%ED%96%89-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;개발 서버 실행 스크립트&lt;/h3&gt;
&lt;p&gt;postcss-cli로 watch 모드를 사용한다면 개발 서버도 동시에 실행해야 하므로 &lt;a href=&quot;https://www.npmjs.com/package/concurrently&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;concurrently&lt;/a&gt;를 사용한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// package.json
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token property&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;개발 서버 실행&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;concurrently \&quot;yarn:tw:watch\&quot; \&quot;yarn:dev\&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;storybook-설정&quot;&gt;&lt;a href=&quot;#storybook-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Storybook 설정&lt;/h3&gt;
&lt;p&gt;Storybook에서 Tailwind CSS 로 생성한 스타일시트를 사용하려면 위에서 설정한 postcss-cli를 사용해도 된다. 하지만 Storybook 서버는 Webpack 기반이므로 실행되므로 &lt;a href=&quot;https://storybook.js.org/addons/@storybook/addon-postcss/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@storybook/addon-postcss&lt;/a&gt; 모듈을 사용하는 편이 더 간단하다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// .storybook/main.js&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  stories&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;../src/**/*.stories.@(js|jsx|ts|tsx)&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  addons&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;@storybook/addon-postcss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// .storybook/preview.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../src/tailwind.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;tailwind-css-활용&quot;&gt;&lt;a href=&quot;#tailwind-css-%ED%99%9C%EC%9A%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tailwind CSS 활용&lt;/h2&gt;
&lt;h3 id=&quot;jitjust-in-time-mode-모드&quot;&gt;&lt;a href=&quot;#jitjust-in-time-mode-%EB%AA%A8%EB%93%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://tailwindcss.com/docs/just-in-time-mode&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;JIT(Just-in-Time Mode) 모드&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tailwind CSS 2.1 이상에서 지원하는 기능으로 스타일시트를 개발 서버가 실행되기 전 생성해두는 것이 아니라 소스 파일 변경을 탐지하면서 HTML, 컴포넌트에서 사용하는 클래스만 스타일시트에 추가하도록 한다. 이 기능을 필요한 클래스만 추가되므로 스타일시트 사이즈를 크게 줄일 수 있다. &lt;/p&gt;
&lt;p&gt;비슷한 기능으로 사용하지 않는 유틸리티를 스타일시트에서 제거하는 &lt;a href=&quot;https://tailwindcss.com/docs/optimizing-for-production#removing-unused-css&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;purge 옵션&lt;/a&gt;을 제공하고 있었는데 이것은 production 모드에서만 사용 가능하다. JIT 모드는 이 기능을 개발 모드에서도 사용 가능하도록 확장한 것이라 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;테마-확장&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EB%A7%88-%ED%99%95%EC%9E%A5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://tailwindcss.com/docs/configuration#theme&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;테마 확장&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;앞서 잠깐 살펴봤지만 Material-UI에서도 가능한 것처럼 기본 테마를 확장할 수 있다. 기존 값을 덮어쓸 수도 있고, 새로운 속성을 추가할 수도 있다.&lt;/p&gt;
&lt;p&gt;기본 테마 객체가 어떻게 구성되었는지는 아래 파일에서 확인 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://unpkg.com/browse/tailwindcss@2.1.2/stubs/defaultConfig.stub.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://unpkg.com/browse/tailwindcss@2.1.2/stubs/defaultConfig.stub.js&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;variants&quot;&gt;&lt;a href=&quot;#variants&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://unpkg.com/browse/tailwindcss@2.1.2/stubs/defaultConfig.stub.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Variants&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;hover:&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;focus:&lt;/code&gt; 등의 프리픽스는 모든 CSS 속성을 지원하지 않으며 &lt;code class=&quot;language-text&quot;&gt;backgroundColor&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; 등의 일부 속성에만 사용할 수 있도록 설정되어 있다. 모든 속성을 지원하면 유틸리티 클래스 수가 너무 늘어날 것이고 자연히 스타일시트 파일의 덩치도 커질 것이기 때문이다. 기본 제공하는 속성 외의 다른 속성에 variants를 조합하려면 추가 설정이 필요하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;// tailwind.config.js
module.exports = &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  variants&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    fill&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    extend&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      borderColor&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&apos;focus-visible&apos;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      opacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&apos;disabled&apos;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;다크-모드&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%ED%81%AC-%EB%AA%A8%EB%93%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://tailwindcss.com/docs/dark-mode&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;다크 모드&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tailwind CSS에서 다크 모드는 테마에서 설정하는 방식이 아니라 &lt;code class=&quot;language-text&quot;&gt;dark:&lt;/code&gt; 프리픽스가 붙은 클래스로 스타일을 직접 추가하는 방식이다. Material-UI는 &lt;code class=&quot;language-text&quot;&gt;ThemeProvider&lt;/code&gt;에 테마 객체를 전달할 수 있어서 컬러 모드에 따라 테마를 통째로 교체할 수 있는데, 그에 비하면 조금 불편한 방식이라 할 수 있다. Tailwind CSS에서 다크 모드를 사용하기 위해서는 컴포넌트 모듈화가 철저하게 이뤄져야 반복 작업을 줄일 수 있을 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bg-white dark:bg-gray-800&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text-gray-900 dark:text-white&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Dark mode is here!&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text-gray-600 dark:text-gray-300&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    Lorem ipsum...
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;tailwind-css-2의-postcss-7-호환-버전-설치&quot;&gt;&lt;a href=&quot;#tailwind-css-2%EC%9D%98-postcss-7-%ED%98%B8%ED%99%98-%EB%B2%84%EC%A0%84-%EC%84%A4%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tailwind CSS 2의 Postcss 7 호환 버전 설치&lt;/h3&gt;
&lt;p&gt;Tailwind CSS 2는 PostCSS 버전 8을 기본으로 사용한다. 하지만 PostCSS 8은 배포된지 얼마 되지 않았기 때문에 아직 반영하지 않은 라이브러리들이 많다. 특히 Webpack 4에서는 PostCSS 8을 사용할 수 없기에 Webpack 5를 설치해야 한다. 그래서 PostCSS 7로 Tailwind CSS 2 를 사용할 수 있는 방법을 제공하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tailwindcss.com/docs/installation#post-css-7-compatibility-build&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://tailwindcss.com/docs/installation#post-css-7-compatibility-build&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;@storybook/react의 최신 릴리즈 버전(2021.06 기준 6.2)은 Webpack 4를 사용하기 때문에 위의 링크에 있는 방법을 적용할 필요가 있다.&lt;/p&gt;
&lt;h3 id=&quot;twin-라이브러리로-css-in-js-스타일로-사용하기&quot;&gt;&lt;a href=&quot;#twin-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EB%A1%9C-css-in-js-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/ben-rogerson/twin.macro&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Twin&lt;/a&gt; 라이브러리로 CSS-in-JS 스타일로 사용하기&lt;/h3&gt;
&lt;p&gt;styled-components나 emotion에 익숙한 사람이라면 선호할 만한 방법이다. &lt;code class=&quot;language-text&quot;&gt;css&lt;/code&gt; 함수를 사용하면 template literal 안에 직접 CSS를 작성하면서 Tailwind CSS의 클래스도 조합할 수 있다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; tw&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; css &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;twin.macro&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hoverStyles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; css&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
  &amp;amp;:hover {
    border-color: black;
    &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;tw&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`text-black`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;  }
`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Input&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; hasHover &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;tw&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`border`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hasHover &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; hoverStyles&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tailwind CSS는 대부분의 스타일을 유틸리티 클래스로 추가할 수 있지만, &lt;code class=&quot;language-text&quot;&gt;:nth-child(n)&lt;/code&gt; 처럼 유틸리티 클래스로 만들기 어려운 선택자는 지원하지 않는다(&lt;code class=&quot;language-text&quot;&gt;:first-child&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;:last-child&lt;/code&gt;는 지원함). 이처럼 클래스로 추가하기 어려운 스타일링은 스타일시트를 직접 작성해야 하는데, 이 방법을 사용하면 Tailwind의 유틸리티 클래스도 그대로 활용할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;tailwind-css-유저-커뮤니티&quot;&gt;&lt;a href=&quot;#tailwind-css-%EC%9C%A0%EC%A0%80-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tailwind CSS 유저 커뮤니티&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://tailwindcomponents.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;tailwindcomponents.com&lt;/a&gt;에서 Tailwind CSS를 기반으로 만든 컴포넌트를 공유하고 있다. 독립된 컴포넌트 뿐만 아니라 하나의 완성된 페이지 형태의 작업물도 공유되고 있으니 참고하면 좋을 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Tailwind CSS는 유틸리티 클래스만 제공하기 때문에 다른 CSS 프레임워크처럼 사전 정의된 스타일과 기능을 가진 컴포넌트를 바로 사용할 수는 없다. 하지만 필요한 기능만 가진 가벼운 컴포넌트를 직접 구현해서 사용하는 쪽을 선호한다면 좋은 선택이 될 것이다. 특히 테스트 케이스 작성도 그렇지만, 유틸리티 클래스를 활용하는 것도 사용할 수록 작성 속도가 빨라지는 것을 실제로 사용하면서 경험할 수 있었다.&lt;/p&gt;
&lt;h2 id=&quot;관련-자료&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/docs/installation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Installation - Tailwind CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://postcss.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;PostCSS - a tool for transforming CSS with JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ben-rogerson/twin.macro&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;GitHub - twin.macro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcomponents.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Tailwind CSS Components. Examples and templates&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[linear-gradient 속성에 transition 적용하기]]></title><description><![CDATA[블로그를 호스팅하고 있는  Vercel  서비스의 웹사이트를 방문했다가 멋진 CSS 기술을 보게 되었다. 텍스트에 linear gradient를 사용해서 컬러를 입히고, 거기에 일정한 간격을 두고 차례대로 transition 효과를 주고 있었다.  
 3개의 단어와,…]]></description><link>https://blog.rhostem.com//posts/2021-05-12-linear-gradient-text</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-05-12-linear-gradient-text</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Wed, 12 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;블로그를 호스팅하고 있는 &lt;a href=&quot;https://vercel.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Vercel&lt;/a&gt; 서비스의 웹사이트를 방문했다가 멋진 CSS 기술을 보게 되었다. 텍스트에 linear gradient를 사용해서 컬러를 입히고, 거기에 일정한 간격을 두고 차례대로 transition 효과를 주고 있었다. &lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/7kTKuHDDlm3TTolUTqj1ab/13c1eda8e92d8c954640ded5f1cf633c/May-12-2021_01-20-02_standard.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 71.11111111111111%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;vercel_linear_gradient_text.gif&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/7kTKuHDDlm3TTolUTqj1ab/13c1eda8e92d8c954640ded5f1cf633c/May-12-2021_01-20-02_standard.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/7kTKuHDDlm3TTolUTqj1ab/13c1eda8e92d8c954640ded5f1cf633c/May-12-2021_01-20-02_standard.gif?w=270 270w,
https://images.ctfassets.net/rpmifyuylbfw/7kTKuHDDlm3TTolUTqj1ab/13c1eda8e92d8c954640ded5f1cf633c/May-12-2021_01-20-02_standard.gif?w=540 540w,
https://images.ctfassets.net/rpmifyuylbfw/7kTKuHDDlm3TTolUTqj1ab/13c1eda8e92d8c954640ded5f1cf633c/May-12-2021_01-20-02_standard.gif?w=1080 1080w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;3개의 단어와, 컬러, 애니메이션 효과만으로 서비스의 핵심을 아주 효과적으로 설명해주고 있지 않은가?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;개발 도구로 확인해 보니 GIF 이미지나 canvas를 사용한 효과는 아니었고 HTML &lt;code class=&quot;language-text&quot;&gt;span&lt;/code&gt; 태그로 추가한 텍스트와 CSS로 구성되어 있었다. 단순히 linear gradient를 사용한 텍스트라면 모르겠는데, 애니메이션까지 적용되어 있으니 한번 카피해 보면 좋겠다는 생각이 들었다.&lt;/p&gt;
&lt;h2 id=&quot;linear-gradient를-사용한-텍스트&quot;&gt;&lt;a href=&quot;#linear-gradient%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%ED%85%8D%EC%8A%A4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Linear gradient를 사용한 텍스트&lt;/h2&gt;
&lt;p&gt;CSS &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/CSS/linear-gradient()&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;linear-gradient&lt;/code&gt;&lt;/a&gt; 속성은 기본적으로 백그라운드 컬러를 지정하기 위한 속성이다. 시작점, 종료점에 지정할 2개의 색상이 기본적으로 필요하며 중간 지점에 들어갈 컬러는 더 많이 추가할 수도 있다. 색이 변화하는 각도도 지정할 수 있는데, 예를 들어 &lt;code class=&quot;language-text&quot;&gt;to left&lt;/code&gt; 값은 좌에서 우로 컬러가 변화한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to right, red, blue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* 왼쪽 끝은 빨강, 오른쪽 끝은 파랑 */&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;red, blue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* 각도가 생략되면 to bottom */&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to right, red, yellow, blue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* 중간 컬러를 넣을 수 있다. */&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;45deg, red, yellow 40%, blue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* 각도, 컬러의 위치도 지정 가능하다. */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 &lt;code class=&quot;language-text&quot;&gt;linear-gradient&lt;/code&gt;는 기본적으로 &lt;code class=&quot;language-text&quot;&gt;background-image&lt;/code&gt; 속성에 지정하는 값이다. &lt;code class=&quot;language-text&quot;&gt;background&lt;/code&gt; 속성에도 할당될 수 있지만 &lt;code class=&quot;language-text&quot;&gt;background&lt;/code&gt; 는 여러가지 백그라운드 관련 속성(&lt;code class=&quot;language-text&quot;&gt;background-color&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;background-repeat&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;background-position&lt;/code&gt; 등)을 조합해서 사용하는 값이므로 별도로 지정하는 편이 CSS 버그를 방지하는데 도움이 된다.&lt;/p&gt;
&lt;h2 id=&quot;백그라운드-컬러-이미지를-텍스트에-적용하기&quot;&gt;&lt;a href=&quot;#%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%BB%AC%EB%9F%AC-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-%ED%85%8D%EC%8A%A4%ED%8A%B8%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;백그라운드 컬러, 이미지를 텍스트에 적용하기&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/CSS/background-clip&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;background-clip&lt;/code&gt;&lt;/a&gt; CSS 속성을 사용하면 배경이 컨텐츠 박스 중에서 어디까지 차지할지 결정할 수 있다. &lt;code class=&quot;language-text&quot;&gt;box-sizing&lt;/code&gt; 속성에 사용하는 &lt;code class=&quot;language-text&quot;&gt;border-box&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;content-box&lt;/code&gt;가 아닌 &lt;code class=&quot;language-text&quot;&gt;text&lt;/code&gt; 속성을 사용하면, 백그라운드 컬러, 이미지가 텍스트 위에만 입혀진다. 그리고 텍스트 컬러는 투명으로 지정해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to right, red, blue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;-webkit-background-clip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 하면 텍스트에 그래디언트 컬러가 적용된 멋진 텍스트를 표현할 수 있게 된다.&lt;/p&gt;
&lt;h2 id=&quot;linear-gradient에-transition-효과-설정&quot;&gt;&lt;a href=&quot;#linear-gradient%EC%97%90-transition-%ED%9A%A8%EA%B3%BC-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;linear gradient에 transition 효과 설정&lt;/h2&gt;
&lt;p&gt;CSS &lt;code class=&quot;language-text&quot;&gt;transition&lt;/code&gt; 속성으로 엘레멘트의 위치, 크기, 컬러 등이 변경될 때 부드러운 전환 효과를 넣을 수 있다. 하지만 일반적인 백그라운드 컬러와 달리, &lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;linear-gradient&lt;/code&gt; 를 사용한 백그라운드 컬러에는 &lt;code class=&quot;language-text&quot;&gt;transition&lt;/code&gt; 속성의 timing function(ex. &lt;code class=&quot;language-text&quot;&gt;linear&lt;/code&gt;) 값이 적용되지 않는다&lt;/strong&gt;. 다시 말해 중간 단계 없이 색깔이 빨강에서 파랑으로 바로 바뀌어버리는 것이었다.&lt;/p&gt;
&lt;p&gt;그래서 여기서는 트릭을 사용해야 한다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;부모 요소(ex. &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;span&lt;/code&gt;)에 텍스트를 추가한다.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;부모 요소의 의사 요소(pseudo-element, ex. &lt;code class=&quot;language-text&quot;&gt;::before&lt;/code&gt;)에 CSS 속성들을 추가한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;content&lt;/code&gt; 속성에 부모 요소에 입력한 것과 동일한 텍스트를 입력한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;linear-gradient&lt;/code&gt;를 사용해 컬러를 입힌다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;absolute&lt;/code&gt; 포지션을 사용해 부모 요소의 텍스트와 크기, 포지션이 완전히 겹치도록 만든다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; 값을 0으로 둔다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;transition&lt;/code&gt; 속성에 &lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; 값이 변할 때 전환 효과가 발생하도록 설정한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;z-index&lt;/code&gt; 값은 부모 요소의 값보다 커야 한다. 그래야 의사 요소의 &lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; 값을 1로 변경했을 때 의사 요소가 먼저 보이게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;gradientText&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;HTML&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.gradientText&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to right, #000, #000&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
  &lt;span class=&quot;token property&quot;&gt;-webkit-background-clip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.gradientText::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;HTML&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to right, #007cf0, #00dfd8&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
  &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; opacity 0.5s linear&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;-webkit-background-clip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* 이 클래스를 추가하면 텍스트에 컬러가 추가된 의사 요소의 텍스트가 보이게 된다. */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.transitionOn::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;정리하면 의사 요소에 부모와 똑같은 텍스트를 지정하고 컬러를 입힌 후, &lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; 값으로 전환 효과를 주는 것&lt;/strong&gt;이다. 의사 요소의 &lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; 값이 0에서 1로 바뀌는 과정에서 마치 부모 요소의 텍스트 컬러가 전환되는 것처럼 보이는 효과를 준다. &lt;/p&gt;
&lt;h2 id=&quot;자바스크립트로-transition-실행&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A1%9C-transition-%EC%8B%A4%ED%96%89&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자바스크립트로 transition 실행&lt;/h2&gt;
&lt;p&gt;이제 스타일은 준비가 되었으니 마크업을 추가하고 자바스크립트로 &lt;code class=&quot;language-text&quot;&gt;setTimeout&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;setInterval&lt;/code&gt;을 조합해 클래스를 일정한 주기에 맞춰 붙였다 떼었다 하는 작업만 해주면 된다.&lt;/p&gt;
&lt;h2 id=&quot;결과물&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EA%B3%BC%EB%AC%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결과물&lt;/h2&gt;
&lt;iframe height=&quot;500&quot; style=&quot;width: 100%;&quot; scrolling=&quot;no&quot; title=&quot;linear gradient text &quot; src=&quot;https://codepen.io/rhostem/embed/eYvppxe?height=484&amp;theme-id=dark&amp;default-tab=result&quot; frameborder=&quot;no&quot; loading=&quot;lazy&quot; allowtransparency=&quot;true&quot; allowfullscreen=&quot;true&quot;&gt;
  See the Pen &lt;a href=&apos;https://codepen.io/rhostem/pen/eYvppxe&apos;&gt;linear gradient text &lt;/a&gt; by rhostem
  (&lt;a href=&apos;https://codepen.io/rhostem&apos;&gt;@rhostem&lt;/a&gt;) on &lt;a href=&apos;https://codepen.io&apos;&gt;CodePen&lt;/a&gt;.
&lt;/iframe&gt;
&lt;h2 id=&quot;references&quot;&gt;&lt;a href=&quot;#references&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/CSS/linear-gradient()&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;linear-gradient - CSS: Cascading Style Sheets | MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/CSS/background-clip&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;background-clip - CSS: Cascading Style Sheets | MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://keithjgrant.com/posts/2017/07/transitioning-gradients/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Transitioning Gradients&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Mock Service Worker로 만드는 모의 서버]]></title><description><![CDATA[Mock Service Worker MSW (=Mock Service Worker)는 API 모킹(mocking) 라이브러리로, 서버를 향한 네트워크 요청을 가로채서(intercept) 모의 응답(mocked response)을 내려주는 역할을 한다. 이 라이브러리의…]]></description><link>https://blog.rhostem.com//posts/2021-03-20-mock-service-worker</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-03-20-mock-service-worker</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 20 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;mock-service-worker&quot;&gt;&lt;a href=&quot;#mock-service-worker&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Mock Service Worker&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://mswjs.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MSW&lt;/a&gt;(=Mock Service Worker)는 API 모킹(mocking) 라이브러리로, 서버를 향한 네트워크 요청을 가로채서(intercept) 모의 응답(mocked response)을 내려주는 역할을 한다. 이 라이브러리의 장점은 사용자가 정의한 모킹 핸들러를 테스트를 위한 노드(node.js) 환경, 개발과 디버깅을 위한 브라우저 환경에서 모두 사용할 수 있다는 것이다. 이미 구현된 소스 코드를 수정할 일은 없으며, 모킹이 필요한 환경에서만 MSW 인스턴스를 실행해주기만 하면 된다.&lt;/p&gt;
&lt;h2 id=&quot;msw가-작동하는-방식&quot;&gt;&lt;a href=&quot;#msw%EA%B0%80-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MSW가 작동하는 방식&lt;/h2&gt;
&lt;p&gt;브라우저에 서비스 워커(Service Woker)를 등록하여 &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; 이벤트를 사용해 외부로 나가는 네트워크 리퀘스트를 감지한다. 그리고 그 요청을 실제 서버가 아닌 MSW 클라이언트 사이드 라이브러리로 보낸 후, 등록된 핸들러를 통해 모의 응답을 브라우저로 보낸다. 네트워크 요청 흐름은 아래와 같다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3XIZI0nnDkI4CjgNMqpnoM/05d4203347d75435c386053ad3b6f4ee/msw_diagram.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 59.13528591352859%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;Mock Service Worker 리퀘스트 흐름도&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3XIZI0nnDkI4CjgNMqpnoM/05d4203347d75435c386053ad3b6f4ee/msw_diagram.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3XIZI0nnDkI4CjgNMqpnoM/05d4203347d75435c386053ad3b6f4ee/msw_diagram.png?w=359 359w,
https://images.ctfassets.net/rpmifyuylbfw/3XIZI0nnDkI4CjgNMqpnoM/05d4203347d75435c386053ad3b6f4ee/msw_diagram.png?w=717 717w,
https://images.ctfassets.net/rpmifyuylbfw/3XIZI0nnDkI4CjgNMqpnoM/05d4203347d75435c386053ad3b6f4ee/msw_diagram.png?w=1434 1434w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Mock Service Worker 리퀘스트 흐름도 (이미지 출처: &lt;a href=&quot;https://mswjs.io/docs/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://mswjs.io/docs/&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;서비스 워커는 브라우저에서만 사용 가능하기 때문에 노드 환경에서는 &lt;a href=&quot;https://github.com/mswjs/node-request-interceptor&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;node-request-interceptor&lt;/a&gt;로 네이티브 &lt;code class=&quot;language-text&quot;&gt;http&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;https&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;XMLHttpRequest&lt;/code&gt; 모듈을 확장(extending)해서 리퀘스트를 처리한다.&lt;/p&gt;
&lt;h2 id=&quot;일반적인-모킹-방식&quot;&gt;&lt;a href=&quot;#%EC%9D%BC%EB%B0%98%EC%A0%81%EC%9D%B8-%EB%AA%A8%ED%82%B9-%EB%B0%A9%EC%8B%9D&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;일반적인 모킹 방식&lt;/h2&gt;
&lt;p&gt;두 가지 방법을 사용한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실제 서버를 대체하는 모킹 서버의 제공&lt;/li&gt;
&lt;li&gt;네트워크 요청을 가로채기 위해 네이티브 &lt;code class=&quot;language-text&quot;&gt;http&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;https&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;XMLHttpRequest&lt;/code&gt; 모듈을 스텁(stub, 바꿔치기)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;모킹 서버는 실제 서버와 다른 방식으로 구현되고 동작하기 때문에 실제 서버를 완전히 대체할 수 없다. 목 서버 구현, 실행 및 관리 외에도 목 서버에서만 다르게 동작하는 버그 때문에 기존 코드를 수정해야 하는 일도 생길 수 있다.&lt;/p&gt;
&lt;p&gt;그리고 스텁은 모킹 서버를 구동하는 것보다는 효율적이긴 하다. 하지만 네이티브 모듈을 수정하게 되면 모킹을 하지 않은 환경과 어쨌거나 차이를 만들어내기 때문에 종단간(end-to-end) 테스트에는 좋지 않다.&lt;/p&gt;
&lt;p&gt;MSW는 모킹 서버를 사용하지 않고 네이티브 라이브러리를 스텁하지 않는다(Service Worker를 사용할 수 없는 노드 환경에서는 nock 라이브러리처럼 &lt;code class=&quot;language-text&quot;&gt;http&lt;/code&gt;같은 네트워크 모듈을 monkey patching하는 방식으로 구현한다). 그리고 다른 모킹 라이브러리와 달리 어플리케이션 레벨이 아닌 네트워크 레벨에서 리퀘스트를 가로채서 응답을 보내기 때문에 별도의 설정 없이도 &lt;code class=&quot;language-text&quot;&gt;axios&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;react-query&lt;/code&gt;등의 모든 종류의 네트워크 요청 라이브러리와 네이티브 &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; API를 사용할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;h2 id=&quot;사용-방법&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사용 방법&lt;/h2&gt;
&lt;p&gt;로그인을 위해 서버의 &lt;code class=&quot;language-text&quot;&gt;/user&lt;/code&gt; 라우트에 HTTP POST 요청을 보낸다고 가정하자. MSW는 네트워크 요청을 처리하기 위해 &lt;code class=&quot;language-text&quot;&gt;rest[METHOD]&lt;/code&gt; 함수로 &lt;a href=&quot;https://mswjs.io/docs/basics/request-handler&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;핸들러&lt;/a&gt;를 정의한다. 여기서는 POST 요청이므로 &lt;code class=&quot;language-text&quot;&gt;rest.post&lt;/code&gt; 함수를 사용한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// handlers.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rest &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;msw&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; loginHandler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&apos;https://api.server.com/user&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			jwt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c&apos;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;핸들러 함수의 첫번째 파라미터인 리퀘스트 엔드포인트에는 express 서버처럼 파라미터를 사용할 수 있다(ex. &lt;code class=&quot;language-text&quot;&gt;/user/:id&lt;/code&gt;). 상세정보 API가 라우트에 고유 아이디를 포함하고 있을 때 유용하다. 핸들러의 두번째 파라미터인 콜백 함수(=&lt;a href=&quot;https://mswjs.io/docs/basics/response-resolver&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;response resolver&lt;/a&gt;)에서 라우트 파라미터에 접근하는 것도 물론 가능하다.&lt;/p&gt;
&lt;p&gt;정의한 핸들러로 워커 인스턴스를 생성한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; setupWorker &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;msw&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// node 환경에서는 setupServer 모듈을 대신 사용&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; handlers &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./handlers&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; worker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;handlers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 서버 인스턴스를 실행하면 된다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;worker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;실제 환경별 사용 방법은 아래 링크를 통해 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mswjs.io/docs/getting-started/integrate&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://mswjs.io/docs/getting-started/integrate&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;활용-사례&quot;&gt;&lt;a href=&quot;#%ED%99%9C%EC%9A%A9-%EC%82%AC%EB%A1%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;활용 사례&lt;/h2&gt;
&lt;h3 id=&quot;브라우저-서비스-워커에-등록하기&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9B%8C%EC%BB%A4%EC%97%90-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브라우저 서비스 워커에 등록하기&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://mswjs.io/docs/getting-started/integrate/browser&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://mswjs.io/docs/getting-started/integrate/browser&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;브라우저에서 사용하기 위해서는 MSW를 서비스 워커에 등록하는 과정이 필요하다. 고맙게도 그 과정은 MSW 라이브러리에서 직접 제공한다. 커맨드 라인에서 아래의 명령어를 실행하면 서비스 워커 등록을 위한 파일이 &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt; 폴더에 추가된다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx msw init public/ --save&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt;은 웹 서버가 호스팅하는 폴더로 &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; 파일이 있는 곳을 가리킨다. 보통 &lt;code class=&quot;language-text&quot;&gt;build&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;dist&lt;/code&gt; 등의 이름을 사용하는데 create-react-app, next.js를 사용하고 있다면 프로젝트 소스 최상위에 있는 &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt; 폴더가 그것에 해당한다.&lt;/p&gt;
&lt;p&gt;이제 앱의 소스에 워커를 실행하는 코드를 추가한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/index.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; ReactDOM &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-dom&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; App &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./App&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 개발 환경에서만 실행되도록 환경 변수를 확인하는 과정이 필요하다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;development&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; worker &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./mocks/worker&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  worker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

ReactDOM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 개발 서버에서 실행된 앱이 서버에 리퀘스트를 보낼 때 실제 서버가 아닌 MSW에서 응답을 보낼 수 있게 되었다.&lt;/p&gt;
&lt;h3 id=&quot;jest를-사용하는-테스트-환경에서-사용하기&quot;&gt;&lt;a href=&quot;#jest%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jest를 사용하는 테스트 환경에서 사용하기&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://mswjs.io/docs/getting-started/integrate/node&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://mswjs.io/docs/getting-started/integrate/node&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;모든 테스트 케이스에서 msw가 동작하도록 전역에 설정하는 방법과 테스트 케이스마다 직접 설정하는 방법이 있다.&lt;/p&gt;
&lt;h4 id=&quot;전역-설정&quot;&gt;&lt;a href=&quot;#%EC%A0%84%EC%97%AD-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;전역 설정&lt;/h4&gt;
&lt;p&gt;Jest 설정에서 &lt;code class=&quot;language-text&quot;&gt;setupFilesAfterEnv&lt;/code&gt;에 할당한 모듈의 코드는 각각의 테스트 파일을 실행하기에 앞서서 먼저 실행된다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// jest.config.js&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  setupFilesAfterEnv&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./jest.setup.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 &lt;code class=&quot;language-text&quot;&gt;jest.setup.js&lt;/code&gt; 파일에 모의 서버를 설정하는 코드를 작성하면 된다. 테스트는 node 환경에서 실행할 것이므로 &lt;code class=&quot;language-text&quot;&gt;setupWorker&lt;/code&gt; 대신 &lt;code class=&quot;language-text&quot;&gt;setupServer&lt;/code&gt; 함수를 사용해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// jest.setup.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; setupServer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;msw/node&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; handlers &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./handlers&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;handlers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 테스트 시작 전에 서버를 실행한다.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;beforeAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 테스트가 끝날 때마다 리퀘스트 핸들러를 초기화하여 서로 영향을 미치지 않도록 한다.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;afterEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetHandlers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 테스트가 완료되면 서버를 종료한다.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;afterAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;테스트-케이스에-직접-설정&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BC%80%EC%9D%B4%EC%8A%A4%EC%97%90-%EC%A7%81%EC%A0%91-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 케이스에 직접 설정&lt;/h4&gt;
&lt;p&gt;테스트 파일에서 서버 인스턴스를 만드는 것 외에 전역 설정과 큰 차이는 없다. 테스트에 필요한 핸들러만 추가하면 된다는 장점이 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; setupServer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;msw/node&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; listRequestHandler&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; detailRequestHandler &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./handlers&apos;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sample test suite&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		listRequestHandler&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		detailRequestHandler&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token function&quot;&gt;beforeAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token function&quot;&gt;afterEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetHandlers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token function&quot;&gt;afterAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;check response of mock server&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token comment&quot;&gt;// write test&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;쿼리-파라미터-가져오기&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쿼리 파라미터 가져오기&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://mswjs.io/docs/recipes/query-parameters&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://mswjs.io/docs/recipes/query-parameters&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;테스트마다 리퀘스트 파라미터에 따라 다른 리스펀스를 보내줘야 하는 경우가 있다. 핸들러에서 &lt;code class=&quot;language-text&quot;&gt;req&lt;/code&gt; 객체를 통해  파라미터에 접근 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; productDetailHandler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/products&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; productId&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;response-patching&quot;&gt;&lt;a href=&quot;#response-patching&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Response patching&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://mswjs.io/docs/recipes/response-patching&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://mswjs.io/docs/recipes/response-patching&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;모의 응답(mocked response)를 실제 리퀘스트의 응답에 기반한 데이터로 구성할 수도 있다. 핸들러에서 실제 서버에 리퀘스트를 보낸 후 받은 데이터에 디버깅 등에 필요한 정보를 임의로 덧붙이는 방식이다. &lt;/p&gt;
&lt;p&gt;아래는 Github API를 사용한 리스펀스 패칭 예제다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; setupWorker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rest &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;msw&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; worker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupWorker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://api.github.com/users/:username&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// msw 핸들러에 가로챈 리퀘스트 정보로 살제 서버에 요청을 보낸다.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// msw에서 정의한 핸들러에 매칭되는 것을 방지하기 위해 window.fetch가 아닌 ctx.fetch를 대신 사용한다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originalResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originalResponseData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; originalResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        location&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; originalResponseData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        firstName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Not the real first name&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

worker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[React Query로 서버 상태 관리하기]]></title><description><![CDATA[React Query 는 서버 상태(server state)를 관리하는 라이브러리다. 서버 상태란 원격에 위치한 공간에 저장되며 앱이 소유하거나 제어하지 않는다 데이터를 가져오고 업데이트하기 위해선 비동기 API가 필요하다 다른 사람과 함께 사용하며, 내가 모르는 사…]]></description><link>https://blog.rhostem.com//posts/2021-02-01T00:00:00.000Z</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-02-01T00:00:00.000Z</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 01 Feb 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://react-query.tanstack.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React Query&lt;/a&gt;는 서버 상태(server state)를 관리하는 라이브러리다. 서버 상태란&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;원격에 위치한 공간에 저장되며 앱이 소유하거나 제어하지 않는다&lt;/li&gt;
&lt;li&gt;데이터를 가져오고 업데이트하기 위해선 비동기 API가 필요하다&lt;/li&gt;
&lt;li&gt;다른 사람과 함께 사용하며, 내가 모르는 사이에 업데이트될 수 있다&lt;/li&gt;
&lt;li&gt;앱에서 사용하는 데이터가 “유효 기간이 지난” 상태가 될 가능성을 가진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;예를 들어 쇼핑몰의 상품 목록, 게시판의 댓글, 배달앱의 주문 진행 상황 등은 모두 위와 같은 특성을 가지고 있다. 그렇기에 다음과 같은 작업에 대한 필요가 생긴다.&lt;/p&gt;
&lt;ul&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;li&gt;만료된 데이터는 가능한 빨리 업데이트하기&lt;/li&gt;
&lt;li&gt;페이지네이션, 레이지 로딩 데이터의 성능 최적화&lt;/li&gt;
&lt;li&gt;서버 상태의 메모리 관리 및 가비지 콜렉션&lt;/li&gt;
&lt;li&gt;쿼리 결과의 구조 공유를 통한 메모이제이션&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이러한 기능들이 없어도 앱을 구현할 수 있다. 하지만 높은 품질의 앱을 위해서는 필요한 작업이라 할 수 있다. React Query는 위와 같은 기능을 사용할 수 있도록 도와준다. &lt;/p&gt;
&lt;h2 id=&quot;도입할-필요가-있는가&quot;&gt;&lt;a href=&quot;#%EB%8F%84%EC%9E%85%ED%95%A0-%ED%95%84%EC%9A%94%EA%B0%80-%EC%9E%88%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;도입할 필요가 있는가?&lt;/h2&gt;
&lt;p&gt;위에서 제공하는 기능만 본다면 사용하면 좋을 것 같다. 하지만 새로운 라이브러리의 도입은 항상 신중히 결정해야 한다.&lt;/p&gt;
&lt;h3 id=&quot;상태-관리-라이브러리에서-요구하는-boilerplate-코드-제거&quot;&gt;&lt;a href=&quot;#%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%97%90%EC%84%9C-%EC%9A%94%EA%B5%AC%ED%95%98%EB%8A%94-boilerplate-%EC%BD%94%EB%93%9C-%EC%A0%9C%EA%B1%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상태 관리 라이브러리에서 요구하는 boilerplate 코드 제거&lt;/h3&gt;
&lt;p&gt;React Query를 사용한다면 서버 데이터 처리 방식을 바꿔야 한다. React에서는 보통 redux, mobx같은 상태 관리 라이브러리를 사용하며, 그것으로 서버 상태를 관리하는 방식이 일반적이다. Redux를 사용하고 있다면 redux-thunk, redux-observable, redux-saga 등의 미들웨어를 사용해 서버 데이터 요청 액션이 들어오면 API를 호출하여 redux 상태를 업데이트하는 방식을 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; filter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; map&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mergeMap &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/operators&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ajax &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rxjs/ajax&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// redux-observable 예제&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// SAMPLE_DATA_REQUEST 타입의 액션을 받아서 API 호출 후 리스펀스를 담은 액션으로 맵핑하는 epic&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;sampleAsyncEpic&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action$&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; action$&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;SAMPLE_DATA_REQUEST&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;mergeMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; 
			ajax&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getJSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`https://api.server.com/samples`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		    &lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;SAMPLE_DATA_RESPONSE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;	
	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 React Query는 기본적으로 함수형 컴포넌트 안에서 훅 형태로 사용하며 굳이 서버 상태를 다른 장소에 저장할 필요가 없다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useQuery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-query&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; isLoading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isFetching &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;repoData&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&apos;https://api.github.com/repos/tannerlinsley/react-query&apos;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isLoading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Loading...&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;An error has occurred: &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  );
}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;필요에 따라 서버 데이터를 현재 컴포넌트에서 멀리 떨어진 컴포넌트 트리에 전달할 필요가 생길 수도 있다. 하지만 서버 상태는 그 데이터를 가져온 컴포넌트와 1~2단계 아래의 하위 컴포넌트(ex. 목록의 아이템 컴포넌트)에서 사용하는 경우가 대부분이다. &lt;strong&gt;즉, 서버 데이터 처리와 관련된 redux 액션, 리듀서, 미들웨어 코드를 작성할 필요가 없어진다.&lt;/strong&gt; Redux는 훌륭한 상태 관리 기능을 제공하지만, 동시에 많은 양의 boilerplate 코드라는 피로감도 제공하고 있기에 주목할 필요가 있다. &lt;/p&gt;
&lt;p&gt;하지만 상태 관리 라이브러리를 사용해서 서버 데이터를 제어하는 쪽을 더 선호한다면 React Query는 도입하지 말거나 앱에서 꼭 필요한 부분에만 사용하는 편이 좋을 것이다.&lt;/p&gt;
&lt;h3 id=&quot;캐싱--리프레쉬&quot;&gt;&lt;a href=&quot;#%EC%BA%90%EC%8B%B1--%EB%A6%AC%ED%94%84%EB%A0%88%EC%89%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캐싱 &amp;#x26; 리프레쉬&lt;/h3&gt;
&lt;p&gt;채팅 앱처럼 소켓 통신을 사용한다면 서버 상태가 즉시 업데이트되겠지만 그렇지 않다면 주기적으로 업데이트해주는 기능을 직접 구현해야 한다. React Query는 &lt;code class=&quot;language-text&quot;&gt;useQuery&lt;/code&gt; 훅의 파라미터를 통해 API 데이터의 만료 시간, 리프레쉬 간격, 데이터를 캐시에서 유지할 기간, 브라우저 포커스시 데이터 리프레쉬 여부, 성공 or 에러 콜백 등 다양한 기능을 제어할 수 있다.  &lt;/p&gt;
&lt;p&gt;예를 들어 앱 안에 게시판이 있다고 가정하자. 다음과 같은 시나리오가 가능하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;게시글 필터에서 사용하는 옵션은 서버에서 변경될 가능성이 낮으므로 만료 시간을 무한(&lt;code class=&quot;language-text&quot;&gt;Infinity&lt;/code&gt;)으로 설정하여 API 추가 호출을 방지할 수 있다.&lt;/li&gt;
&lt;li&gt;게시글 목록의 만료 시간을 1분으로 설정하여 유저가 페이지 번호를 1에서 2로 반복해서 바꾸는 등의 행동을 취하더라도 API 중복 호출을 방지할 수 있다.&lt;/li&gt;
&lt;li&gt;게시글 목록의 만료 시간이 1분으로 설정되어 있는데 어떤 사용자는 게시글을 그 시간 안에 작성하거나 수정할 수도 있다. 그래서 게시글 작성 후에는 캐시를 강제로 무효화(invalidate)하여 목록을 새로고침한다.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;사용자가 게시글의 제목을 수정한 후 목록으로 돌아갔을 때 API 호출을 통해 게시글 목록을 서버에서 다시 가져온 후에야 수정 사항이 반영되었음을 확인해줄 수 있다. 하지만 React Query에서는 수정 성공시 캐시되어 있는 게시글 제목을 임시로 변경하여 사용자에게 서버에 다시 요청한 게시글 목록의 응답이 즉시 온 것처럼 보이게 만들 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;게시글 수정 후 목록 새로고침에 딜레이가 전혀 없는 듯한 사용자 경험을 제공할 수 있다.&lt;/li&gt;
&lt;li&gt;실제 API 호출 및 데이터 업데이트는 백그라운드에서 진행되며 에러 발생시 원래 데이터로 복구시킬 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://react-query.tanstack.com/guides/optimistic-updates&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Optimistic update&lt;/a&gt; 참조&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;브라우저의 다른 탭을 보다가 다시 열었을 때 게시글 목록을 자동으로 불러오게 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;캐싱 관련 기능은 처음 언급한대로 구현되어 있지 않아도 앱 사용하는 데는 문제가 없다. 하지만 클라이언트에서 사용하는 서버 데이터의 종류와 양이 늘어나고 서버에서 관리하고 있는 데이터의 양도 늘어난다면 양쪽 모두의 작업 처리량을 줄일 필요가 자연스럽게 생긴다. 또 더 좋은 사용자 경험을 구현하는데도 도움을 준다.&lt;/p&gt;
&lt;h2 id=&quot;react-query-사용-방법&quot;&gt;&lt;a href=&quot;#react-query-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;React Query 사용 방법&lt;/h2&gt;
&lt;p&gt;React Query를 통해 관리하는 쿼리 데이터는 라이프사이클에 따라 fetching, fresh, stale, inactive, delete 상태를 가진다.  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fetching - 요청 중인 쿼리&lt;/li&gt;
&lt;li&gt;fresh - 만료되지 않은 쿼리. 컴포넌트가 마운트, 업데이트되어도 데이터를 다시 요청하지 않는다&lt;/li&gt;
&lt;li&gt;stale - 만료된 쿼리. 컴포넌트가 마운트, 업데이트되면 데이터를 다시 요청한다.&lt;/li&gt;
&lt;li&gt;inactive - 사용하지 않는 쿼리. 일정 시간이 지나면 가비지 컬렉터가 캐시에서 제거한다&lt;/li&gt;
&lt;li&gt;delete  - 가비지 컬렉터에 의해 캐시에서 제거된 쿼리&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;important-defaults&quot;&gt;&lt;a href=&quot;#important-defaults&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://react-query.tanstack.com/guides/important-defaults&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;important defaults&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;다음은 React Query에서 제공하는 API의 기본이 되는 설정이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;useQuery&lt;/code&gt;(그리고 &lt;code class=&quot;language-text&quot;&gt;useInfiniteQuery&lt;/code&gt;)로 가져온 데이터는 기본적으로 stale 상태가 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; 옵션으로 데이터가 stale 상태로 바뀌는데 걸리는 시간을 늘릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;stale 쿼리는 다음 경우에 백그라운드에서 다시 가져온다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;새로운 쿼리 인스턴스가 마운트되었을 때&lt;/li&gt;
&lt;li&gt;브라우저 윈도우가 다시 포커스되었을 때&lt;/li&gt;
&lt;li&gt;네트워크가 다시 연결되었을 때&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;refetchInterval&lt;/code&gt; 옵션이 있을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;활성화된 &lt;code class=&quot;language-text&quot;&gt;useQuery&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;useInfiniteQuery&lt;/code&gt; 인스턴스가 없는 쿼리 결과는 “inactive” 라벨이 붙으며 다음에 사용될 때까지 남아있는다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;inactive 쿼리는 300초(5분) 후에 메모리에서 해제된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;백그라운드에서 3회 이상 실패한 쿼리는 에러 처리된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;retry&lt;/code&gt; 옵션으로 쿼리 함수에서 오류 발생시 재시도할 횟수, &lt;code class=&quot;language-text&quot;&gt;retryDelay&lt;/code&gt; 옵션으로 재시도 대기 시간을 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;쿼리 결과는 memoization을 위해 structural sharing을 사용하며 데이터 reference는 변경되지 않는다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;immutable.js에서 사용하는 기술.(&lt;a href=&quot;https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;참고할만한 글&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;99.9% 케이스에서는 이 옵션을 끌 필요가 없음.&lt;/li&gt;
&lt;li&gt;structural sharing은 JSON 호환 데이터에만 적용되며, 다른 타입의 쿼리 결과는 항상 변경되었다고 판단한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-classlanguage-textqueryclientprovidercode-설정&quot;&gt;&lt;a href=&quot;#code-classlanguage-textqueryclientprovidercode-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;QueryClientProvider&lt;/code&gt; 설정&lt;/h3&gt;
&lt;p&gt;React Query는 캐시를 관리하기 위해 &lt;code class=&quot;language-text&quot;&gt;QueryClient&lt;/code&gt; 인스턴스를 사용한다. 컴포넌트가 &lt;code class=&quot;language-text&quot;&gt;useQuery&lt;/code&gt; 훅 안에서 &lt;code class=&quot;language-text&quot;&gt;QueryClient&lt;/code&gt; 인스턴스에 접근할 수 있도록 &lt;code class=&quot;language-text&quot;&gt;QueryClientProvider&lt;/code&gt;를 컴포넌트 트리 상위에 추가해줘야 한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; QueryClient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; QueryClientProvider &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-query&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; queryClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;QueryClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 인스턴스 생성&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;QueryClientProvider&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;queryClient&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;QueryClientProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;code-classlanguage-textusequerycode로-서버-데이터-가져오기&quot;&gt;&lt;a href=&quot;#code-classlanguage-textusequerycode%EB%A1%9C-%EC%84%9C%EB%B2%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://react-query.tanstack.com/reference/useQuery&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;useQuery&lt;/code&gt;&lt;/a&gt;로 서버 데이터 가져오기&lt;/h3&gt;
&lt;p&gt;서버에서 데이터를 가져오고 캐싱을 하는데 사용하는 기본이며 가장 많이 사용하게 되는 훅이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isLoading &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;queryKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; queryFunction&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;queryFunction&lt;/code&gt;에는 서버에서 데이터를 요청하고 Promise를 리턴하는 함수를 전달한다. 즉 &lt;code class=&quot;language-text&quot;&gt;axios.get(...)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;fetch(...)&lt;/code&gt; 등을 리턴하는 함수.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;queryKey&lt;/code&gt; 에는 문자열과 배열을 넣을 수 있다. 쿼리 키가 가지는 유연함이 곧 캐싱을 처리를 쉽게 만들어준다. 쿼리 키가 다르면 캐싱도 별도로 관리하기 때문이다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code class=&quot;language-text&quot;&gt;todo&lt;/code&gt; 라는 키는 아래와 같이 확장할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 다른 키로 취급한다. &lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 객체 필드의 값이 달라도 다른 키로 취급한다&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; preview&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; preview&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 객체 필드의 순서가 달라도 내용이 같으면 같은 키로 취급한다&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; preview&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;done&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;done&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; preview&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;useQuery 훅이 리턴하는 데이터와 옵션의 종류는 매우 다양하다. 주요 사항은 다음과 같다. (자세한 내용은 공식 &lt;a href=&quot;https://react-query.tanstack.com/reference/useQuery&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;문서&lt;/a&gt; 참조)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;리턴 데이터&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt; - 쿼리 함수가 리턴한 Promise에서 resolve된 데이터&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;isLoading&lt;/code&gt; - 저장된 캐시가 없는 상태에서 데이터를 요청중일 때 &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;isFetching&lt;/code&gt; - 캐시가 있거나 없거나 데이터가 요청중일 때 &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;옵션&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;cacheTime&lt;/code&gt; - unused 또는 inactive 캐시 데이터가 메모리에서 유지될 시간. 기본값은 5분이며 설정한 시간을 초과하면 메모리에서 제거된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Infinity&lt;/code&gt;로 설정하면 쿼리 데이터는 캐시에서 제거되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; - 쿼리 데이터가 fresh 에서 stale로 전환되는데 걸리는 시간. 기본값은 &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Infinity&lt;/code&gt;로 설정하면 쿼리 데이터는 직접 캐시를 무효화할 때까지 fresh 상태로 유지된다. &lt;/li&gt;
&lt;li&gt;캐시는 메모리에서 관리되므로 브라우저 새로고침 후에는 다시 가져온다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;enabled&lt;/code&gt; -  &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt; 값이 전달되면 쿼리가 비활성화된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;데이터 요청에 사용할 파라미터가 유효한 값일 때만 true를 할당하는 식으로 활용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;onSuccess&lt;/code&gt; - 쿼리 함수가 성공적으로 데이터를 가져왔을 때 호출되는 함수.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;onError&lt;/code&gt; - 쿼리 함수에서 오류가 발생했을 때&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;onSettled&lt;/code&gt; - 쿼리 함수의 성공, 실패 두 경우 모두 실행된다.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;keepPreviousData&lt;/code&gt; - 쿼리 키(ex.페이지 번호)가 변경되어서 새로운 데이터를 요청하는 동안에도 마지막  &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt;값을 유지한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;페이지네이션을 구현할 때 유용하다. 캐시되지 않은 페이지를 가져올 때 화면에서 목록이 사라지는 깜빡임 현상을 방지할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;isPreviousData&lt;/code&gt; 값으로 현재의 쿼리 키에 해당하는 값인지 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;initialData&lt;/code&gt; - 캐시된 데이터가 없을 때 표시할 초기값. &lt;code class=&quot;language-text&quot;&gt;placeholder&lt;/code&gt;로 전달한 데이터와 달리 캐싱이 된다. 브라우저 로컬 스토리지에 저장해 둔 값으로 데이터를 초기화할 때 사용할 수 있을 것이다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;refetchOnWindowFocus&lt;/code&gt; - 윈도우가 다시 포커스되었을 때 데이터를 호출할 것인지 여부. 기본값은 &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;이므로 필요없다고 판단되면 끄면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-classlanguage-textusemutationcode으로-서버-데이터-업데이트&quot;&gt;&lt;a href=&quot;#code-classlanguage-textusemutationcode%EC%9C%BC%EB%A1%9C-%EC%84%9C%EB%B2%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://react-query.tanstack.com/reference/useMutation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;useMutation&lt;/code&gt;&lt;/a&gt;으로 서버 데이터 업데이트&lt;/h3&gt;
&lt;p&gt;서버 데이터를 가져오는 것은 reactive하게 동작하는 useQuery를 사용하면 되겠지만, 서버 데이터 업데이트는 그런 방식으로 사용하기에는 적절하지 않다. 데이터 생성/수정/삭제에는 &lt;code class=&quot;language-text&quot;&gt;useMutation&lt;/code&gt; 훅을 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mutation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMutation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTodo &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/todos&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newTodo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handleSubmit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTodo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    mutation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTodo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mutation&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;useQuery의 옵션처럼 &lt;code class=&quot;language-text&quot;&gt;onSuccess&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;onError&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;onSettled&lt;/code&gt; 콜백을 전달할 수 있으며 거기에 더해 mutate를 호출했을 때 실행할 &lt;code class=&quot;language-text&quot;&gt;onMutate&lt;/code&gt; 콜백도 사용할 수 있다. &lt;/p&gt;
&lt;p&gt;Redux를 사용한다면 리퀘스트 성공 액션을 미들웨어에서 확인하여 추가 액션을 실행할 것이다. &lt;code class=&quot;language-text&quot;&gt;useMutation&lt;/code&gt;을 사용한다면 &lt;code class=&quot;language-text&quot;&gt;onSuccess&lt;/code&gt; 콜백을 사용해도 되지만, 코드 가독성을 위해 &lt;code class=&quot;language-text&quot;&gt;mutateAsync&lt;/code&gt; 함수를 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mutation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMutation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTodo &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/todos&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newTodo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handleSubmit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTodo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; mutation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mutateAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTodo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;setAnotherState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
		&lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createAnotherAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mutation&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;쿼리-무효화invalidation&quot;&gt;&lt;a href=&quot;#%EC%BF%BC%EB%A6%AC-%EB%AC%B4%ED%9A%A8%ED%99%94invalidation&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://react-query.tanstack.com/guides/query-invalidation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;쿼리 무효화&lt;/a&gt;(Invalidation)&lt;/h3&gt;
&lt;p&gt;쿼리 데이터가 stale 상태로 바뀌기만을 기다릴 수만은 없는 케이스가 있다. 예를 들어 게시글에 댓글을 작성한 후에는 서버에서 댓글 목록을 다시 가져올 필요가 있다. 이와 같은 경우에는 지정한 &lt;code class=&quot;language-text&quot;&gt;staleTime&lt;/code&gt; 이 지나기 전에 직접 쿼리를 무효화해서 데이터를 새로 가져오도록 해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; queryClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useQueryClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 캐시에 있는 모든 쿼리를 무효화한다.&lt;/span&gt;
queryClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;invalidateQueries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// todo로 시작하는 모든 쿼리를 무효화한다. ex) [&apos;todos&apos;, 1], [&apos;todos&apos;, 2], ...&lt;/span&gt;
queryClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;invalidateQueries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todos&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// [&apos;todos&apos;, 1] 키를 가진 쿼리를 무효화한다.&lt;/span&gt;
queryClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;invalidateQueries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todos&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;predicate&lt;/code&gt; 옵션을 사용하면 무효화할 쿼리를 더 자세하게 설정할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 쿼리 키 배열의 두번째 객체의 version 필드의 값이 10 이상인 쿼리만 무효화한다.&lt;/span&gt;
queryClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;invalidateQueries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  predicate&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; query &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
	   query&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;queryKey&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;todos&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; query&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;queryKey&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;version &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 위의 코드로 무효화된다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoListQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todos&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; version&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchTodoList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 위의 코드로 무효화되지 않는다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoListQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;todos&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; version&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fetchTodoList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[TypeScript에서 generic과 index type의 활용]]></title><description><![CDATA[타입스크립트를 사용하면 타입을 직접 지정하는 것도 가능하지만 컴파일러의 추론을 통해 타입을 유연하게 지정하는 것도 가능하다. 예를 들어 아래와 같은 함수가 있다고 하자. 는 객체에서 지정된 키를 제거하는 함수다. 파라미터 전개 연산자를 사용해서 삭제할 키를 자유롭게 …]]></description><link>https://blog.rhostem.com//posts/2021-01-20T00:00:00.000Z</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-01-20T00:00:00.000Z</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Wed, 20 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;타입스크립트를 사용하면 타입을 직접 지정하는 것도 가능하지만 컴파일러의 추론을 통해 타입을 유연하게 지정하는 것도 가능하다.&lt;/p&gt;
&lt;p&gt;예를 들어 아래와 같은 함수가 있다고 하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;omitByKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;rest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;isKeyMatches&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; target &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isKeyMatches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;omitByKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;omitByKey&lt;/code&gt;는 객체에서 지정된 키를 제거하는 함수다. 파라미터 전개 연산자를 사용해서 삭제할 키를 자유롭게 추가할 수 있도록 했다.&lt;/p&gt;
&lt;p&gt;이 함수에 타입을 추가하려고 한다면 &lt;code class=&quot;language-text&quot;&gt;rest&lt;/code&gt; 배열에 들어갈 수 있는 문자열의 집합을 &lt;code class=&quot;language-text&quot;&gt;obj&lt;/code&gt; 파라미터로 전달된 객체의 키로 한정하고 싶을 것이다. 그런데, 함수 작성 시점에서는 &lt;code class=&quot;language-text&quot;&gt;obj&lt;/code&gt; 에 어떤 키가 들어있을 지 결코 알 수가 없다. &lt;/p&gt;
&lt;p&gt;이런 케이스를 위해 타입스크립트는 타입 추론을 위한 문법을 제공한다. 여기서는 제네릭(generic)과 인덱스 타입(index type)을 사용해서 추론이 가능하다.&lt;/p&gt;
&lt;h2 id=&quot;제네릭&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%84%A4%EB%A6%AD&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제네릭&lt;/h2&gt;
&lt;p&gt;제네릭 타입은 함수 선언 시점에는 타입이 고정되어 있지 않으며 사용할 때 타입이 지정되는 형태를 말한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; identity&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(arg: T): T &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arg&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;identity&lt;/code&gt; 함수 이름 옆에 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;T&amp;gt;&lt;/code&gt; 를 추가해서 이 함수에서 &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt;라는 이름으로 제네릭을 사용하겠다고 선언했다. 그리고 파라미터가 &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt; 타입을 가지며, 리턴 값에도 &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt; 타입을 부여했다. 이 함수는 파라미터 &lt;code class=&quot;language-text&quot;&gt;arg&lt;/code&gt;가 어떤 타입을 가졌느냐에 따라 리턴 타입이 유연하게 달라지게 된다. 만약 제네릭이 없다면 number, string 등 다양한 타입에 대한 identity 함수를 별도로 선언해야 할 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;identityNum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arg&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;identityStr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arg&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 그리고 다른 타입도 계속...😓&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 &lt;code class=&quot;language-text&quot;&gt;omitByKey&lt;/code&gt; 함수도 타입이 유연하게 지정되어야 하므로 제네릭이 필요하다. 그리고 또 하나, 객체가 가지고 있는 키를 가져오기 위해 인덱스 타입이 필요하다.&lt;/p&gt;
&lt;h2 id=&quot;인덱스-타입index-type&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%83%80%EC%9E%85index-type&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인덱스 타입(index type)&lt;/h2&gt;
&lt;p&gt;객체의 키로 타입을 만들 때는 index type query 연산자 &lt;code class=&quot;language-text&quot;&gt;keyof&lt;/code&gt;를 사용한다. 아래의 코드에서 타입 &lt;code class=&quot;language-text&quot;&gt;K&lt;/code&gt;는 객체 &lt;code class=&quot;language-text&quot;&gt;numbers&lt;/code&gt;의 키로 구성된 union 타입이 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; numbers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  one&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  two&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  three&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

type &lt;span class=&quot;token constant&quot;&gt;K&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; keyof &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// type K = &apos;one&apos; | &apos;two&apos; | &apos;three&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그럼 필요한 도구는 준비가 되었으니 &lt;code class=&quot;language-text&quot;&gt;omitByKey&lt;/code&gt;에 타입을 지정해 보자.&lt;/p&gt;
&lt;h2 id=&quot;제네릭과-인덱스-타입을-함께-사용하기&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%84%A4%EB%A6%AD%EA%B3%BC-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%ED%83%80%EC%9E%85%EC%9D%84-%ED%95%A8%EA%BB%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제네릭과 인덱스 타입을 함께 사용하기&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; omitByKey&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;K&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;keyof&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Omit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  obj&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;rest&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;isKeyMatches&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isKeyMatches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;K&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 함수에는 &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;K&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;R&lt;/code&gt;이라는 3개의 제네릭 타입을 사용했다. &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt;는 객체의 타입에 지정될 것이고 &lt;code class=&quot;language-text&quot;&gt;K&lt;/code&gt;는 위의 예제에서 본 대로 객체 &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt;의 키로 구성된 union 타입을 상속했다. &lt;code class=&quot;language-text&quot;&gt;R&lt;/code&gt;은 함수의 리턴 타입으로 Omit 유틸리티 타입을 활용했다.&lt;/p&gt;
&lt;p&gt;이렇게 작성하면 &lt;code class=&quot;language-text&quot;&gt;rest&lt;/code&gt; 배열에는 &lt;code class=&quot;language-text&quot;&gt;obj&lt;/code&gt; 객체의 키에 해당하는 문자열만 들어갈 수 있다. 만약 다른 키를 넣으려 한다면 아래와 같이 컴파일러가 즉시 오류를 확인시켜 줄 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; numbers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  one&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  two&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  three&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;omitByKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;numbers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;another&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Argument of type &apos;another&apos; is not assignable to parameter of type &apos;one&apos; | &apos;two&apos; | &apos;three&apos; &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고-자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/advanced-types.html#index-types&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;TypeScript: Documentation - Advanced Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mariusschulz.com/blog/keyof-and-lookup-types-in-typescript&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;keyof and Lookup Types in TypeScript — Marius Schulz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[동적으로 import된 컴포넌트의 테스트]]></title><description><![CDATA[앱을 개발할 때 번들 파일의 사이즈가 커지면 로딩 속도를 개선하기 위해 코드를 분할할 필요가 생긴다. React에서는  react-loadable 을 사용하는 방법도 있지만,  를 사용해서 간단하게 동적 import를 사용할 수 있다. 다만 이렇게 구현한 컴포넌트를 …]]></description><link>https://blog.rhostem.com//posts/2021-01-16-test-dynamically-imported-component</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-01-16-test-dynamically-imported-component</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 16 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;앱을 개발할 때 번들 파일의 사이즈가 커지면 로딩 속도를 개선하기 위해 코드를 분할할 필요가 생긴다. React에서는 &lt;a href=&quot;https://github.com/jamiebuilds/react-loadable&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;react-loadable&lt;/a&gt;을 사용하는 방법도 있지만, &lt;code class=&quot;language-text&quot;&gt;React.lazy&lt;/code&gt;를 사용해서 간단하게 동적 import를 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Login &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lazy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;page/Login&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;React.Suspense&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;loading...&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Login&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;React.Suspense&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다만 이렇게 구현한 컴포넌트를 테스트할 때는 주의가 필요하다. 테스트 코드는 비동기적으로 가져온 컴포넌트가 완전히 렌더링된  후에 실행될 수도 있고, 아닐 수도 있기 때문이다. &lt;strong&gt;비동기 컴포넌트가 렌더링되었음을 확실히 보장하지 않으면 테스트도 성공을 보장할 수 없다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;동적-import로-가져온-컴포넌트-렌더링-확인&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%A0%81-import%EB%A1%9C-%EA%B0%80%EC%A0%B8%EC%98%A8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81-%ED%99%95%EC%9D%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동적 import로 가져온 컴포넌트 렌더링 확인&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Login&lt;/code&gt; 컴포넌트가 아래와 같이 구현되어 있다고 가정하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Login.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Element &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;htmlFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;User Id&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;htmlFor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Password&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;password&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 &lt;code class=&quot;language-text&quot;&gt;Login&lt;/code&gt; 컴포넌트를 마운트한 &lt;code class=&quot;language-text&quot;&gt;App&lt;/code&gt; 컴포넌트 테스트 코드를 작성한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// App.test.tsx&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; screen &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; App &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./App&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;App component test&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;arrangeRender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;아이디, 패스워드 입력을 제공한다.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;arrangeRender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;textbox&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/User Id/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;textbox&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/Password/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 테스트 코드는 잘 동작할 것 같다. 하지만 실패할 가능성이 있는 테스트 코드다. 테스트 케이스가 실행되기 전에 동적으로 가져온 컴포넌트가 렌더링되지 않았다면 &lt;code class=&quot;language-text&quot;&gt;/User Id/i&lt;/code&gt;로 찾을 수 있는 &lt;code class=&quot;language-text&quot;&gt;textbox&lt;/code&gt; 요소가 없다면서 테스트 케이스가 실패할 것이다.&lt;/p&gt;
&lt;p&gt;비동기 컴포넌트의 렌더링은 &lt;a href=&quot;https://testing-library.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;testing-library&lt;/a&gt;를 사용해서 확인 가능하다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Element &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-testid&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;loginComponent&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
				...
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여기에서는 컴포넌트에 테스트 아이디 &lt;code class=&quot;language-text&quot;&gt;loginComponent&lt;/code&gt;를 추가해서 컴포넌트의 렌더링을 확인하도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; waitFor &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;App component test&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; arrangeRender &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// waitFor 인스턴스는 Promise이므로 resolve될때까지 기다린다.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByTestId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/loginComponent/i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;아이디, 패스워드 입력을 제공한다.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;arrangeRender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;textbox&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/User Id/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;textbox&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/Password/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://testing-library.com/docs/dom-testing-library/api-async/#waitfor&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;waitFor&lt;/a&gt; 함수를 사용해서 &lt;code class=&quot;language-text&quot;&gt;Login&lt;/code&gt; 컴폰넌트 안에 있는 테스트 아이디가 확인될 때까지 기다리게 했다. 이렇게 작성하면 테스트 케이스에서 &lt;code class=&quot;language-text&quot;&gt;expect&lt;/code&gt; 메소드가 호출되기 전에 비동기 컴포넌트가 렌더링됨을 확실히 보장할 수 있다. 이렇게 테스트 아이디를 추가해서 확인해도 되고, 이미 존재하는 라벨 텍스트로 확인해도 된다.&lt;/p&gt;
&lt;h2 id=&quot;테스트-케이스가-실패할-때는-비동기-코드를-의심하자&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%BC%80%EC%9D%B4%EC%8A%A4%EA%B0%80-%EC%8B%A4%ED%8C%A8%ED%95%A0-%EB%95%8C%EB%8A%94-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9D%98%EC%8B%AC%ED%95%98%EC%9E%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 케이스가 실패할 때는 비동기 코드를 의심하자&lt;/h2&gt;
&lt;p&gt;mokcing한 API가 제대로 동작하지 않은 케이스, intersection observer 때문에 리스트가 일부만 렌더링된 케이스, 비동기 컴포넌트 때문에 DOM 요소를 찾지 못하는 케이스, 단순한 내 실수(🙀) 등  테스트 케이스가 실패하는 원인은 무척 다양하다. 게다가 렌더링 결과를 브라우저로 바로 확인이 불가능하고 &lt;code class=&quot;language-text&quot;&gt;screen.debug()&lt;/code&gt; 같은 함수를 사용해서 텍스트로만 문제를 파악해야 하기 때문에 원인을 파악하는 것이 더 어려운 것 같다. 그래도 가장 많은 원인은 이 글에서 살펴본 것처럼 비동기적으로 실행되는 코드에 의한 것이었다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Web Worker를 사용한 이미지 로딩]]></title><description><![CDATA[웹 워커(Web worker) 자바스크립트는 싱글 스레드로 동작하며 작성된 코드가 순서대로 실행된다. 하지만 자바스크립트로 작성된 웹 어플리케이션도 API 호출 같은 비동기 작업이 완료될 때까지 멈춰있지 않고 빠르게 실행되는 것이 가능하다. 그 이유는 자바스크립트를 …]]></description><link>https://blog.rhostem.com//posts/2021-01-03-image-load-by-web-worker</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2021-01-03-image-load-by-web-worker</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 03 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;웹-워커web-worker&quot;&gt;&lt;a href=&quot;#%EC%9B%B9-%EC%9B%8C%EC%BB%A4web-worker&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹 워커(Web worker)&lt;/h2&gt;
&lt;p&gt;자바스크립트는 싱글 스레드로 동작하며 작성된 코드가 순서대로 실행된다. 하지만 자바스크립트로 작성된 웹 어플리케이션도 API 호출 같은 비동기 작업이 완료될 때까지 멈춰있지 않고 빠르게 실행되는 것이 가능하다. 그 이유는 자바스크립트를 실행하는 브라우저(또는 node.js) 런타임에서 이벤트 루프, 태스크 큐 등을 통해 비동기 작업을 실행하고 관리하기 때문이다. &lt;/p&gt;
&lt;p&gt;하지만 그런 비동기 작업이 너무 많아진다면 사용자의 입출력을 처리하는 메인 스레드의 실행 속도에 문제가 생길 가능성이 높아진다. 예를 들어 메인 스레드에서 &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; API로 수백개의 이미지를 가져오는 비동기 코드를 실행하는 동시에 사용자는 화면에서 텍스트를 입력하거나 마우스 조작 등을 통해 이벤트를 발생시킨다고 가정해 보자. 그러면 얼마 후 &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; 가 완료되었을 때 실행할 수백 개의 콜백 함수가 비슷한 시점에 브라우저의 태스크 큐에 쌓이게 될 것이다. 그리고 동시에 UI 상호작용에 의한 컴포펀트 상태 업데이트 콜백 함수도 태스크 큐에 들어올 수 있다. 그렇게 많은 작업을 한꺼번에 처리하다 보면 UI 업데이트에는 지연이 생길 것이며, 사용자는 앱이 느리게 반응한다고 느낄 것이다. &lt;/p&gt;
&lt;p&gt;그래서 브라우저에서는 자바스크립트로 작성된 어플리케이션의 멀티스레딩을 위해 웹 워커 API를 제공한다. 웹 워커에서 실행되는 스크립트는 메인 스레드에서 분리되어 독립된 스레드에서 실행되므로 앱이 실행되는 머신의 CPU, 메모리 리소스를 더 효율적으로 활용할 수 있다. &lt;/p&gt;
&lt;p&gt;최신 버전의 메이저 웹 브라우저는 모두 웹 워커 API를 제공하며, IE는 10버전부터 제공한다.&lt;/p&gt;
&lt;h2 id=&quot;웹-워커-사용법&quot;&gt;&lt;a href=&quot;#%EC%9B%B9-%EC%9B%8C%EC%BB%A4-%EC%82%AC%EC%9A%A9%EB%B2%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹 워커 사용법&lt;/h2&gt;
&lt;p&gt;메인 스레드와 웹 워커로 생성된 스레드 사이에서는 &lt;code class=&quot;language-text&quot;&gt;message&lt;/code&gt; 이벤트를 통해 데이터를 주고받는다. 아래는 웹 워커가 메인 스레드로부터 데이터로 문자열로 구성된 데이터를 전달받은 후 &lt;code class=&quot;language-text&quot;&gt;postMessage&lt;/code&gt; 메소드를 사용해 메인 스레드로 데이터를 보내는 간단한 예제 코드다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// sample_worker.ts&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;message&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MessageEvent&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; urls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
  &lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;urls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;웹 워커의 소스에서 사용하는 &lt;code class=&quot;language-text&quot;&gt;addEventListener&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;postMessage&lt;/code&gt; 모두 전역 객체(&lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt;)의 메소드다. &lt;/p&gt;
&lt;p&gt;구현된 웹 워커는 메인 스레드에서 객체로 선언되어 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// main.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; worker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./sample_worker.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 웹 워커의 소스가 타입스크립트라면 자바스크립트로의 transpile 과정이 반드시 필요하다. &lt;/span&gt;
worker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;str&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ing&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;sample&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

worker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 주고받는 데이터는 모두 이벤트 객체의 data 속성을 통해 전달된다.&lt;/span&gt;
	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&apos;STR&apos;, &apos;ING&apos;, &apos;SAMPLE&apos;] &lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;메인 스레드에서 웹 워커 스레드로의 데이터 전송은 &lt;code class=&quot;language-text&quot;&gt;postMessage&lt;/code&gt;를 사용하며, 데이터를 받을 때는 워커 객체의 &lt;code class=&quot;language-text&quot;&gt;onmessage&lt;/code&gt; 속성에 이벤트 핸들러를 할당해서 사용한다. 웹 워커의 기본적인 사용법은 이렇듯 간단하다. (더 자세한 내용은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MDN 문서&lt;/a&gt; 참조)&lt;/p&gt;
&lt;h2 id=&quot;이미지-로딩-워커&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A1%9C%EB%94%A9-%EC%9B%8C%EC%BB%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이미지 로딩 워커&lt;/h2&gt;
&lt;p&gt;예제 앱으로 이미지를 로딩하는 웹 워커를 React와 함께 구현해 볼 것이다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;웹 워커에서 &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; API로 이미지를 가져온 후 &lt;code class=&quot;language-text&quot;&gt;URL.createObjectURL&lt;/code&gt; API를 사용해 문자열 형태로 메인 스레드로 전달한다.&lt;/li&gt;
&lt;li&gt;CPU 코어 수만큼 웹 워커 인스턴스를 만들어 작업을 분할한다.&lt;/li&gt;
&lt;li&gt;메인 스레드에서 모든 워커의 작업이 완료되었음을 확인한 후 이미지를 표시한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;구현된 앱은 아래 링크에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rhostem.github.io/image-load-worker&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://rhostem.github.io/image-load-worker&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;이미지-로드-웹-워커-구현&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%A1%9C%EB%93%9C-%EC%9B%B9-%EC%9B%8C%EC%BB%A4-%EA%B5%AC%ED%98%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이미지 로드 웹 워커 구현&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://picsum.photos/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://picsum.photos&lt;/a&gt;에서 제공하는 랜덤한 이미지를 &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; API를 사용해서 가져온 후 Response 객체를 ObjectURL로 변환해서 메인 스레드로 전달한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ImageLoadWorker.ts&lt;/span&gt;
self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;message&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; MessageEvent&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; urls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 메인 스레드로부터 전달받은 이미지 URL 배열&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; images &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    urls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fileBlob &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;blob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/image\/.+/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileBlob&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileBlob&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;images&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt;의 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Response&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Response&lt;/a&gt; 객체는 JSON 형식으로 구성된 객체가 아니라 이진 데이터 스트림이므로 자바스크립트에서 사용할 수 있는 형태로 변환이 필요하다. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Body&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Body&lt;/a&gt; 인터페이스를 구현한 Response 객체는 &lt;code class=&quot;language-text&quot;&gt;arrayBuffer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;blob&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;json&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;formData&lt;/code&gt; 등의 메소드를 가지고 있는데, 여기서는 데이터를 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Blob&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Blob&lt;/a&gt; 객체로 변환하는 &lt;code class=&quot;language-text&quot;&gt;blob&lt;/code&gt; 메소드를 사용했다. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Blob은 파일류 객체를 표현하는 불변(immutable), 원시(raw) 데이터다. 자바스크립트 네이티브 형식에는 없는 데이터를 표현할 수 있다. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이미지 데이터를 담고 있는 Blob 객체는 DOM에 직접 연결할 수 없으므로, &lt;code class=&quot;language-text&quot;&gt;ObjectURL&lt;/code&gt;로 변환하는 과정을 한번 더 거친다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileBlob&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이미지 데이터를 &lt;code class=&quot;language-text&quot;&gt;ObjectURL&lt;/code&gt;로 변환하면 HTML &lt;code class=&quot;language-text&quot;&gt;img&lt;/code&gt; 태그의 &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt; 속성에 할당하거나, &lt;code class=&quot;language-text&quot;&gt;style&lt;/code&gt; 속성의 &lt;code class=&quot;language-text&quot;&gt;background-image&lt;/code&gt;에 할당해서 이미지를 표시할 수 있다. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ObjectURL은 웹 페이지가 unload될 때 자동으로 할당 해제된다. 하지만 React처럼 페이지가 동적으로 업데이트 되는 앱에서는 &lt;code class=&quot;language-text&quot;&gt;URL.revokeObjectURL&lt;/code&gt; 메소드로 직접 할당 해제해줘야 메모리 누수를 막을 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;메인-스레드에서-웹-워커-인스턴스-생성-및-작업-처리&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%9D%B8-%EC%8A%A4%EB%A0%88%EB%93%9C%EC%97%90%EC%84%9C-%EC%9B%B9-%EC%9B%8C%EC%BB%A4-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%9E%91%EC%97%85-%EC%B2%98%EB%A6%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메인 스레드에서 웹 워커 인스턴스 생성 및 작업 처리&lt;/h2&gt;
&lt;p&gt;웹 워커가 백그라운드에서 동시에 작업을 진행한다고 해서 100개의 이미지를 불러오는데 워커 인스턴스도 100개를 만들어서 사용하면 오히려 더 느릴 수 있다. 앱이 실행되는 환경의 CPU 코어 수만큼 만들어서 사용하는 것이 가장 좋다. 그리고 그 값은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;navigator.hardwareConcurrency&lt;/code&gt;&lt;/a&gt;를 통해 가져올 수 있다. &lt;code class=&quot;language-text&quot;&gt;hardwareConcurrency&lt;/code&gt; 값은 사용자의 컴퓨터에서 스레드를 실행하는 데 사용할 수 있는 논리 프로세서의 수를 리턴한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; maxWorkers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hardwareConcurrency &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Safari 브라우저에서는 지원하지 않는다&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;프로세서 수만큼 웹 워커 인스턴스를 만들어 둔다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;workers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setWorkers&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; useState&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Worker&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;workers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setWorkers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxWorkers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;undefined&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./ImageLoadWorker.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;meta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    workers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;worker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; worker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;terminate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;maxWorkers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; workers&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 이미지 주소를 웹 워커에 보내고 결과를 돌려받는 함수를 구현한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 1개의 웹 워커에서 가져올 이미지의 수 = (전체 이미지 개수) / (웹 워커 개수)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; chunkSizeForWorker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMemo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ceil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;images&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; maxWorkers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;images&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxWorkers&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; loadAllImagesAtOnce &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imageUrls&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// fetch로 가져올 이미지 주소 배열을 파라미터로 전달받음&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Worker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// ObjectURL을 저장하기 위한 배열을 미리 생성해 둔다.&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setImageBlobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imageUrls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;undefined&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 웹 워커에서 fetch가 실패했을 때 null을 리턴하게 했으므로, undefined 값은 이미지 로딩 중이라는 의미도 가진다.&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imageChunks &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// 웹 워커들에 할당하기 위해 이미지 배열을 분리해서 저장해 둔다.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; maxWorkers&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; startIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; chunkSizeForWorker&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        imageChunks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          imageUrls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;startIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; startIndex &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; chunkSizeForWorker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// 이미지 로딩 작업이 끝날 때까지 기다릴 Promise 객체 배열을 구성한다.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imagePromises &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageChunks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chunk&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chunkIndex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; chunkWorker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; workers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;chunkIndex&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chunkWorker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
							&lt;span class=&quot;token comment&quot;&gt;// 워커에 이미지 배열을 전달한다.&lt;/span&gt;
              chunkWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chunk&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

              chunkWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
								&lt;span class=&quot;token comment&quot;&gt;// 워커에서 ObjectURL이 전송되면 Promise를 해결한다.&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// 모든 워커의 작업이 끝날 때까지 기다린다.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imageBlobsChunks &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imagePromises&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// 2차원 배열을 1차원 배열로 변환하는 작업을 거친다&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; allImageBlobs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageBlobsChunks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chunk&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;chunk&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// ObjectURL 배열로 상태를 업데이트한다.&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setImageBlobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;allImageBlobs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;chunkSizeForWorker&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxWorkers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; workers&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여러 개의 워커를 생성하면 개발자 도구에서 메모리가 할당되고 네트워크 입출력이 발생하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5fN4sVmHeaKxCFOLZfHviq/6efda23a950cb2bbc0ece72c08fba35e/________________________________________________________________.jpg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 75.71028411364546%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAkACQAAD/4hAISUNDX1BST0ZJTEUAAQEAAA/4YXBwbAIQAABtbnRyUkdCIFhZWiAH5QABAAEAAAARAARhY3NwQVBQTAAAAABBUFBMAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJkZXNjAAABXAAAAGJkc2NtAAABwAAABJxjcHJ0AAAGXAAAACN3dHB0AAAGgAAAABRyWFlaAAAGlAAAABRnWFlaAAAGqAAAABRiWFlaAAAGvAAAABRyVFJDAAAG0AAACAxhYXJnAAAO3AAAACB2Y2d0AAAO/AAAADBuZGluAAAPLAAAAD5jaGFkAAAPbAAAACxtbW9kAAAPmAAAACh2Y2dwAAAPwAAAADhiVFJDAAAG0AAACAxnVFJDAAAG0AAACAxhYWJnAAAO3AAAACBhYWdnAAAO3AAAACBkZXNjAAAAAAAAAAhEaXNwbGF5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAmAAAADGhySFIAAAAUAAAB2GtvS1IAAAAMAAAB7G5iTk8AAAASAAAB+GlkAAAAAAASAAACCmh1SFUAAAAUAAACHGNzQ1oAAAAWAAACMGRhREsAAAAcAAACRm5sTkwAAAAWAAACYmZpRkkAAAAQAAACeGl0SVQAAAAYAAACiGVzRVMAAAAWAAACoHJvUk8AAAASAAACtmZyQ0EAAAAWAAACyGFyAAAAAAAUAAAC3nVrVUEAAAAcAAAC8mhlSUwAAAAWAAADDnpoVFcAAAAKAAADJHZpVk4AAAAOAAADLnNrU0sAAAAWAAADPHpoQ04AAAAKAAADJHJ1UlUAAAAkAAADUmVuR0IAAAAUAAADdmZyRlIAAAAWAAADim1zAAAAAAASAAADoGhpSU4AAAASAAADsnRoVEgAAAAMAAADxGNhRVMAAAAYAAAD0GVuQVUAAAAUAAADdmVzWEwAAAASAAACtmRlREUAAAAQAAAD6GVuVVMAAAASAAAD+HB0QlIAAAAYAAAECnBsUEwAAAASAAAEImVsR1IAAAAiAAAENHN2U0UAAAAQAAAEVnRyVFIAAAAUAAAEZnB0UFQAAAAWAAAEemphSlAAAAAMAAAEkABMAEMARAAgAHUAIABiAG8AagBpzuy37AAgAEwAQwBEAEYAYQByAGcAZQAtAEwAQwBEAEwAQwBEACAAVwBhAHIAbgBhAFMAegDtAG4AZQBzACAATABDAEQAQgBhAHIAZQB2AG4A/QAgAEwAQwBEAEwAQwBEAC0AZgBhAHIAdgBlAHMAawDmAHIAbQBLAGwAZQB1AHIAZQBuAC0ATABDAEQAVgDkAHIAaQAtAEwAQwBEAEwAQwBEACAAYQAgAGMAbwBsAG8AcgBpAEwAQwBEACAAYQAgAGMAbwBsAG8AcgBMAEMARAAgAGMAbwBsAG8AcgBBAEMATAAgAGMAbwB1AGwAZQB1AHIgDwBMAEMARAAgBkUGRAZIBkYGKQQaBD4EOwRMBD4EQAQ+BDIEOAQ5ACAATABDAEQgDwBMAEMARAAgBeYF0QXiBdUF4AXZX2mCcgBMAEMARABMAEMARAAgAE0A4AB1AEYAYQByAGUAYgBuAP0AIABMAEMARAQmBDIENQRCBD0EPgQ5ACAEFgQaAC0ENAQ4BEEEPwQ7BDUEOQBDAG8AbABvAHUAcgAgAEwAQwBEAEwAQwBEACAAYwBvAHUAbABlAHUAcgBXAGEAcgBuAGEAIABMAEMARAkwCQIJFwlACSgAIABMAEMARABMAEMARAAgDioONQBMAEMARAAgAGUAbgAgAGMAbwBsAG8AcgBGAGEAcgBiAC0ATABDAEQAQwBvAGwAbwByACAATABDAEQATABDAEQAIABDAG8AbABvAHIAaQBkAG8ASwBvAGwAbwByACAATABDAEQDiAOzA8cDwQPJA7wDtwAgA78DuAPMA70DtwAgAEwAQwBEAEYA5AByAGcALQBMAEMARABSAGUAbgBrAGwAaQAgAEwAQwBEAEwAQwBEACAAYQAgAEMAbwByAGUAczCrMOkw/ABMAEMARHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIEluYy4sIDIwMjEAAFhZWiAAAAAAAADzFgABAAAAARbKWFlaIAAAAAAAAIL0AAA9ZP///7xYWVogAAAAAAAATCQAALSFAAAK5lhZWiAAAAAAAAAnvgAADhcAAMiLY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA2ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKMAqACtALIAtwC8AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJnAnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t//9wYXJhAAAAAAADAAAAAmZmAADypwAADVkAABPQAAAKW3ZjZ3QAAAAAAAAAAQABAAAAAAAAAAEAAAABAAAAAAAAAAEAAAABAAAAAAAAAAEAAG5kaW4AAAAAAAAANgAArgAAAFIAAABDwAAAsMAAACaAAAANQAAAUAAAAFRAAAIzMwACMzMAAjMzAAAAAAAAAABzZjMyAAAAAAABDHIAAAX4///zHQAAB7oAAP1y///7nf///aQAAAPZAADAcW1tb2QAAAAAAAAGEAAAoEQAAAAA2ZNV+AAAAAAAAAAAAAAAAAAAAAB2Y2dwAAAAAAADAAAAAmZmAAMAAAACZmYAAwAAAAJmZgAAAAIzMzQAAAAAAjMzNAAAAAACMzM0AP/bAEMAAwICAwICAwMDAwQDAwQFCAUFBAQFCgcHBggMCgwMCwoLCw0OEhANDhEOCwsQFhARExQVFRUMDxcYFhQYEhQVFP/bAEMBAwQEBQQFCQUFCRQNCw0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFP/AABEIAB4AKAMBIgACEQEDEQH/xAAaAAACAgMAAAAAAAAAAAAAAAAACAIEBQYJ/8QALxAAAgEDAgQFAwMFAAAAAAAAAQIDBAURAAYHEhMhCCIxQWEJFFEVMoEzYpHB8P/EABcBAQADAAAAAAAAAAAAAAAAAAABAgP/xAAeEQACAgICAwAAAAAAAAAAAAAAAQIhERIxQXGB4f/aAAwDAQACEQMRAD8AfyC2Ukz8sbc4U8pH28fbv7nk/j/s6yVl25G1bk0zVrQ+Yo8UKxsD+T08H2xg6X2h8Q9zpK5aGXa1dS1lbVS/pz/fUE0NTTL3LoCQeYjmGGAP8DGqUvjtqrdfoauq2F0dqSzSUsdxYL9ys8b9MrIqsQnmVxn+3sCBqHKKjtkRTlLVI0j6qqw2nZvD90s9JSPJX1Q8nY9o4znKY/3rnfRIl0pJ55Q0TqxQTIjMVXAOOYn5P+dO59RLignFzhBw8vqULW5Bea6nFPI4Mi8sMJ86+qN5v2tgjSTbequSiZTTJLyzY5s4JyB86nNZC5Kc9KIiStwndD2/qNk5/ICnRqe6Kj9OuXSihZEdA5WSaQ9yTn0b07aNAOBvTd9rvd5uO47Ml4pqRXHfo0PQWOOQGTlQU/Kkg9eXK8wxnIODC/Xa5vu+0UNYlur6elti3aoqq6OlpRTwSCJuR2AEcXaRsqRJgj1GNOzwy8OMdLdN8NdrvVPDNcpqejFJJGepSvTw+aoDQ4aUOZMYyoUJ29dbBD4UdtRbbWzvf9xzqs4mFXLUQGcIAQIQRCAIhlfIAAORfYEHJZqvNlda5z6+nNvxE7isu4vDLsM7fiq4LRR7muVJTxV1OkMyItLSlVbkUBuzDDEsSMeY4ACz2mogigkWWWFG6oOJc5xgfj209X1GODlg4I8KdhWLbpqTQ1F7r65vuihYO0MCkZVFyAEUDOT8+mEGis81cOqjIFL9PDE5zgfHzrXosqZY3bPDUXNGgkhkTpKMwElc5P599GsbXUL2+oMMjKzAA5X00aA//9k=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;웹 워커 인스턴스들의 메모리, 네트워크 사용량&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5fN4sVmHeaKxCFOLZfHviq/6efda23a950cb2bbc0ece72c08fba35e/________________________________________________________________.jpg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5fN4sVmHeaKxCFOLZfHviq/6efda23a950cb2bbc0ece72c08fba35e/________________________________________________________________.jpg?w=625 625w,
https://images.ctfassets.net/rpmifyuylbfw/5fN4sVmHeaKxCFOLZfHviq/6efda23a950cb2bbc0ece72c08fba35e/________________________________________________________________.jpg?w=1250 1250w,
https://images.ctfassets.net/rpmifyuylbfw/5fN4sVmHeaKxCFOLZfHviq/6efda23a950cb2bbc0ece72c08fba35e/________________________________________________________________.jpg?w=2499 2499w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;구현이 완료된 소스는 &lt;a href=&quot;https://github.com/rhostem/image-load-worker&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/rhostem/image-load-worker&lt;/a&gt; 에서 확인 가능하다.&lt;/p&gt;
&lt;h2 id=&quot;결과-비교&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EA%B3%BC-%EB%B9%84%EA%B5%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결과 비교&lt;/h2&gt;
&lt;p&gt;아래 영상은 웹 워커를 통해 이미지를 가져와서 한꺼번에 렌더링하는 것과 이미지 URL을 DOM &lt;code class=&quot;language-text&quot;&gt;style&lt;/code&gt; 속성에 직접 할당해서 불러오는 것의 비교 화면이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5rSkMWn7ChJahrDJeoSqtD/41a7d62de26b3a68883850ba6affc833/imageload-comparison.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 892px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 105.60538116591928%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;imageload comparison between image worker and direct url&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5rSkMWn7ChJahrDJeoSqtD/41a7d62de26b3a68883850ba6affc833/imageload-comparison.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5rSkMWn7ChJahrDJeoSqtD/41a7d62de26b3a68883850ba6affc833/imageload-comparison.gif?w=223 223w,
https://images.ctfassets.net/rpmifyuylbfw/5rSkMWn7ChJahrDJeoSqtD/41a7d62de26b3a68883850ba6affc833/imageload-comparison.gif?w=446 446w,
https://images.ctfassets.net/rpmifyuylbfw/5rSkMWn7ChJahrDJeoSqtD/41a7d62de26b3a68883850ba6affc833/imageload-comparison.gif?w=892 892w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;웹 워커를 사용한 목록은 모든 이미지를 가져온 후 한꺼번에 업데이트되고, 직접 가져오게 한 목록에서는 이미지 로딩이 완료되는대로 표시됨을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;웹 워커를 사용했을 때의 장점을 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;화면 업데이트를 최소화하여 성능을 향상시킨다.&lt;/li&gt;
&lt;li&gt;이미지 로딩 작업이 다른 DOM의 업데이트와 UI 상호작용에 미치는 영향을 최소화한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Image&lt;/code&gt; 객체를 생성하지 않고도 이미지 로딩 상태, 성공, 실패 여부를 UI에 동적으로 업데이트할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;웹 워커를 사용하지 않아도 웹 개발과 성능에 큰 문제가 없는 경우가 대부분이다. 하지만 메인 스레드에서 많은 메모리를 사용하고 있고 성능에 영향을 미치는 것 같다면 백그라운드로 진행해도 되는 작업을 웹 워커에서 처리하는 방법을 고려해볼 수 있다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[create-react-app에서 snowpack으로의 마이그레이션]]></title><description><![CDATA[최근 실무에서 Webpack 기반의  create-react-app (이하 CRA)으로 구성된 환경으로 개발을 진행하고 있다. CRA는 간편하게 앱을  부트스트랩핑해서  개발을 시작할 수 있다는 장점이 있지만 커스터마이징에 한계가 있다. 그래서 CRA 커스터마이징을 …]]></description><link>https://blog.rhostem.com//posts/2020-12-16-migration-from-cra-to-snowpack</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-12-16-migration-from-cra-to-snowpack</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Wed, 16 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;최근 실무에서 Webpack 기반의 &lt;a href=&quot;https://github.com/facebook/create-react-app&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;create-react-app&lt;/a&gt;(이하 CRA)으로 구성된 환경으로 개발을 진행하고 있다. CRA는 간편하게 앱을 &lt;a href=&quot;https://stackoverflow.com/questions/1254542/what-is-bootstrapping&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;부트스트랩핑해서&lt;/a&gt; 개발을 시작할 수 있다는 장점이 있지만 커스터마이징에 한계가 있다. 그래서 CRA 커스터마이징을 위한 &lt;a href=&quot;https://www.npmjs.com/package/react-app-rewired&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;react-app-rewired&lt;/a&gt; 같은 모듈도 존재한다. CRA로 만든 앱을 &lt;a href=&quot;https://create-react-app.dev/docs/available-scripts/#npm-run-eject&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;eject&lt;/a&gt; 했을 때 숨겨져있던 다양하고 복잡한 설정 파일을 눈으로 본 사람이라면 저런 패키지에 대한 수요가 왜 생겼는지 공감할 것이다.&lt;/p&gt;
&lt;p&gt;커스터마이징의 자유도가 낮은 것도 있지만, Webpack은 앱의 덩치가 커지면 커질수록 빌드 속도가 느려진다. 파일을 하나만 변경해도 번들 파일을 새로 만들기 때문이다. 그래서 더 빠른 빌드 시스템으로의 마이그레이션을 계속 생각해오고 있었다.&lt;/p&gt;
&lt;p&gt;자바스크립트 번들러에는 과거에 grunt, gulp 같은 도구도 있었지만 그 후 등장한 Webpack이 사실상 왕좌의 자리를 차지하고 있다. 다운로드 수로 보자면 경쟁자인 &lt;a href=&quot;https://rollupjs.org/guide/en/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;rollup&lt;/a&gt;은 절반에도 미치지 못하며 이 글에서 살펴볼 snowpack은 다운로드 수 차트에서만 보자면 바닥을 기고 있다. 차트에는 없지만 &lt;a href=&quot;https://parceljs.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;parcel&lt;/a&gt;도 일간 다운로드 수가 10만에 미치지 못한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/zCHvi1JPDMniM5BkWPyW8/fc4f35d16c3a749001e1fbfd6ab926c0/20201215__________________________________.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 53.04162219850587%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;출처 - [https://www.npmtrends.com](https://www.npmtrends.com/)&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/zCHvi1JPDMniM5BkWPyW8/fc4f35d16c3a749001e1fbfd6ab926c0/20201215__________________________________.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/zCHvi1JPDMniM5BkWPyW8/fc4f35d16c3a749001e1fbfd6ab926c0/20201215__________________________________.jpeg?w=469 469w,
https://images.ctfassets.net/rpmifyuylbfw/zCHvi1JPDMniM5BkWPyW8/fc4f35d16c3a749001e1fbfd6ab926c0/20201215__________________________________.jpeg?w=937 937w,
https://images.ctfassets.net/rpmifyuylbfw/zCHvi1JPDMniM5BkWPyW8/fc4f35d16c3a749001e1fbfd6ab926c0/20201215__________________________________.jpeg?w=1874 1874w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;출처 - &lt;a href=&quot;https://www.npmtrends.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://www.npmtrends.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;snowpack-더-빠른-프론트엔드-빌드-도구&quot;&gt;&lt;a href=&quot;#snowpack-%EB%8D%94-%EB%B9%A0%EB%A5%B8-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B9%8C%EB%93%9C-%EB%8F%84%EA%B5%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Snowpack, 더 빠른 프론트엔드 빌드 도구&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.snowpack.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Snowpack&lt;/a&gt;은 Webpack처럼 복잡하고 무거운 빌드 시스템의 대안으로 만들어졌다. 자바스크립트 네이티브 모듈 시스템(&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ESM&lt;/a&gt;, Javascript Modules)을 사용해서 무거운 번들링 작업을 제거하고 프로젝트 크기와 상관없이 빠른 속도를 제공하도록 만들어졌다. 특징은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;개발서버는 처음에만 의존 모듈 설치 시간을 필요로 하며, 그 다음부터는 50ms 안에 실행된다.&lt;/li&gt;
&lt;li&gt;같은 파일을 두번 빌드하지 않는다. 브라우저 안에서 자바스크립트 네이티브 모듈(ESM) 사용으로 가능하다.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;소스가 변경되어도 브라우저 새로고침이 필요하지 않다. React, Preact, Svelte에서 &lt;a href=&quot;https://www.snowpack.dev/concepts/hot-module-replacement&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;HMR+Fast refresh&lt;/a&gt;를 지원한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;코드가 수정되어도 컴포넌트의 상태를 유지한다. 예를 들어 React에서 &lt;code class=&quot;language-text&quot;&gt;useState&lt;/code&gt; 등으로 설정된 값을 초기화하지 않음으로서 더 빠른 리프레쉬가 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JSX, 타입스크립트, React, Preact, CSS 모듈을 기본 지원한다.&lt;/li&gt;
&lt;li&gt;최적화된 빌드를 지원하며 선호하는 번들러를 사용하기 위한 플러그인을 지원한다&lt;/li&gt;
&lt;li&gt;Babel, Sass, MDX 등 기능 확장을 위한 &lt;a href=&quot;https://www.snowpack.dev/plugins&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;빌드 플러그인&lt;/a&gt;을 지원한다&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;ESM을 사용하려면 &lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt; 요소에 &lt;code class=&quot;language-text&quot;&gt;type=&amp;quot;module&amp;quot;&lt;/code&gt; 속성을 추가해야 한다. 그러면 인라인 스크립트 안에서도 &lt;code class=&quot;language-text&quot;&gt;import&lt;/code&gt; 구문을 쓸 수 있게 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;IE는 네이티브 모듈을 지원하지 않는다. 하지만 Snowpack의 Webpack 플러그인으로 빌드된 결과에서는 네이티브 모듈을 사용하지 않으므로 상관없다. 다만 IE를 반드시 지원해야 한다면 IE에서 개발 서버를 사용할 수 없으므로 디버깅에 어려움이 있을 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;어떻게-더-빠른가&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8D%94-%EB%B9%A0%EB%A5%B8%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어떻게 더 빠른가?&lt;/h3&gt;
&lt;p&gt;webpack, parcel같은 전통적인 빌드 도구는 파일이 하나 바뀔 때마다 어플리케이션의 모든 조각(chunks)들을 다시 번들링하는 과정을 거친다. 하지만 Snowpack은 각각의 소스를 빌드만 할 뿐 번들이 되지 않은 상태로 개발 서버에 호스팅한다. 그리고 파일이 변경되면 그것만 다시 빌드하기 때문에 수정사항이 브라우저에 매우 빠르게 반영될 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/39n9V6ZOszZSreZ00U33IA/cf4e6b348fa535b6839015929fcd9d85/20201215_snowpack-unbundled-example.jpg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 56.73289183222958%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;출처 - [https://www.snowpack.dev](https://www.snowpack.dev)&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/39n9V6ZOszZSreZ00U33IA/cf4e6b348fa535b6839015929fcd9d85/20201215_snowpack-unbundled-example.jpg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/39n9V6ZOszZSreZ00U33IA/cf4e6b348fa535b6839015929fcd9d85/20201215_snowpack-unbundled-example.jpg?w=453 453w,
https://images.ctfassets.net/rpmifyuylbfw/39n9V6ZOszZSreZ00U33IA/cf4e6b348fa535b6839015929fcd9d85/20201215_snowpack-unbundled-example.jpg?w=906 906w,
https://images.ctfassets.net/rpmifyuylbfw/39n9V6ZOszZSreZ00U33IA/cf4e6b348fa535b6839015929fcd9d85/20201215_snowpack-unbundled-example.jpg?w=1812 1812w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;출처 - &lt;a href=&quot;https://www.snowpack.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://www.snowpack.dev&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;물론 배포를 위한 빌드도 제공하며, &lt;a href=&quot;https://www.skypack.dev/view/@snowpack/plugin-webpack&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;배포용 빌드에 webpack을 사용하기 위한 플러그인&lt;/a&gt;도 제공한다. 빌드를 위한 복잡한 설정도 필요하지 않다.&lt;/p&gt;
&lt;p&gt;개발서버에서는 소스를 Babel이나 Typescript로 변환만 할 뿐 번들은 하지 않은 상태로 제공하는 것, 이것이 개발 서버에서도 번들이 된 소스를 제공하는 다른 도구와 가장 큰 차이다. 그것(unbundled development)의 장점은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;파일 1개의 빌드 속도는 빠르다&lt;/li&gt;
&lt;li&gt;파일 1개의 빌드는 결정론적(deterministic, 언제나 같은 결과를 낳으므로 예측 가능함)이다.&lt;/li&gt;
&lt;li&gt;파일 1개의 빌드는 디버그하기 쉽다&lt;/li&gt;
&lt;li&gt;프로젝트 사이즈가 개발 속도에 영향을 미치지 않는다&lt;/li&gt;
&lt;li&gt;개별 파일이 캐시하기에 더 좋다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;물론 빌드된 수백개의 소스 파일을 브라우저에서 처음 불러올 때는 시간이 조금 걸린다. 현재 작업 중인 프로젝트에서는 450여개의 소스 파일이 있는데, 최초 로딩시 6~7초로 아주 오랜 시간은 아니었다. 그 다음부터는 모두 웹 브라우저 캐시에서 불러오므로 로딩 속도가 훨씬 빠르다.&lt;/p&gt;
&lt;h3 id=&quot;snowpack에서-npm-패키지를-사용하는-방식&quot;&gt;&lt;a href=&quot;#snowpack%EC%97%90%EC%84%9C-npm-%ED%8C%A8%ED%82%A4%EC%A7%80%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Snowpack에서 NPM 패키지를 사용하는 방식&lt;/h3&gt;
&lt;p&gt;NPM 패키지는 Common.js 모듈 문법을 사용해서 배포되기 때문에 빌드 프로세스를 거치지 않고서는 웹 브라우저에서 실행할 수 없다. 하지만 Snowpack에서는 패키지를 개발 서버에서 ESM 형태로 사용하기 위해 1개의 파일로 번들링한 후 호스팅한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;markdown&quot;&gt;&lt;pre class=&quot;language-markdown&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;node_modules/react/&lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;/\* -&gt; http://localhost:3000/web&lt;span class=&quot;token italic&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;_&lt;/span&gt;modules/react.js
node&lt;span class=&quot;token punctuation&quot;&gt;_&lt;/span&gt;&lt;/span&gt;modules/react-dom/&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;/\* -&gt; http://localhost:3000/web_modules/react-dom.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;React 모듈을 소스에 번들링하지 않아도 개발 서버에서 ESM 형태로 제공하면 컴포넌트 소스에서 &lt;code class=&quot;language-text&quot;&gt;import&lt;/code&gt; 문을 사용해서 직접 불러올 수 있다. NPM 패키지는 덩치가 크므로 빌드하는 데 시간이 걸리긴 하지만, 처음 1번만 빌드된 후 아래 폴더에 캐시된다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;project root&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;/node_modules/.cache/snowpack/development&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;캐시를 삭제하지 않는 이상 더 이상 빌드하지 않기 때문에 많은 시간이 절약된다. 이렇게 NPM 패키지를 소스에 번들링하지 않고 웹 브라우저가 지원하는 ESM 문법을 사용한다는 이 특징이 Snowpack이 제공하는 번들링 없는 개발의 기반이다.&lt;/p&gt;
&lt;h2 id=&quot;cra에서-snowpack으로의-마이그레이션&quot;&gt;&lt;a href=&quot;#cra%EC%97%90%EC%84%9C-snowpack%EC%9C%BC%EB%A1%9C%EC%9D%98-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CRA에서 Snowpack으로의 마이그레이션&lt;/h2&gt;
&lt;h3 id=&quot;snowpack-설정-파일-추가&quot;&gt;&lt;a href=&quot;#snowpack-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC-%EC%B6%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;snowpack 설정 파일 추가&lt;/h3&gt;
&lt;p&gt;프로젝트 루트에 snowpack.config.js 파일을 추가한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @type {import(&quot;snowpack&quot;).SnowpackUserConfig } */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  mount&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    src&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/_dist_&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  plugins&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;@snowpack/plugin-react-refresh&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;@snowpack/plugin-dotenv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;@snowpack/plugin-typescript&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;@snowpack/plugin-webpack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  installOptions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    polyfillNode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  devOptions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    port&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  buildOptions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    sourceMaps&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  proxy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  alias&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    __mocks__&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/__mocks__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    component&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/component&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    constants&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/constants&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    hook&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/hook&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    page&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/page&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    store&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/store&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    style&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/style&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    types&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/types&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    styles&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/styles&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    utils&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/utils&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    vendor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/vendor&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  testOptions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    files&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;**/*.@(spec|test).*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;code-classlanguage-textmountcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textmountcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;mount&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;빌드된 소스와 정적인 파일을 호스팅할 개발 서버의 경로를 지정한다. 위처럼 설정하면 소스 저장소의 &lt;code class=&quot;language-text&quot;&gt;public/index.html&lt;/code&gt; 파일은 개발 서버의 &lt;code class=&quot;language-text&quot;&gt;/index.html&lt;/code&gt; 경로로, &lt;code class=&quot;language-text&quot;&gt;src/index.tsx&lt;/code&gt; 파일은 빌드되어 &lt;code class=&quot;language-text&quot;&gt;/_dist_/index.js&lt;/code&gt; 경로로 접근할 수 있게 된다.&lt;/p&gt;
&lt;p&gt;CRA를 사용했다면 &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; 파일에 엔트리 파일을 불러오는 부분이 없을 것이다. 하지만 Snowpack에서는 필요하므로 루트 컴포넌트를 연결할 요소 아래쪽에 아래 코드를 추가해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    ... (생략)
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/_dist_/index.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;code-classlanguage-textplugincode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textplugincode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;plugin&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Snowpack은 확장성을 위한 플러그인을 지원한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@snowpack/plugin-react-refresh&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HMR 사용을 위한 플러그인&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@snowpack/plugin-dotenv&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; 파일에 있는 환경 변수 파일을 &lt;code class=&quot;language-text&quot;&gt;import.meta.env&lt;/code&gt; 객체를 통해 접근할 수 있게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@snowpack/plugin-typescript&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;타입스트립트 소스를 자바스크립트로 변환해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@snowpack/plugin-webpack&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Snowpack은 번들링 기능을 포함하고 있지 않다. 하지만 서비스 배포를 위해선 소스 압축이 필요하므로 플러그인을 통해 webpack의 기능을 활용한다(Webpack의 옵션을 직접 전달할 수도 있지만 기본적인 설정이 되어 있어서 별도로 필요하진 않았다). rollup 플러그인은 현재 개발 중이며, Snowpack 버전 3에는 자체적으로 번들러를 제공할 예정이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;code-classlanguage-textinstalloptionscode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textinstalloptionscode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;installOptions&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;모듈 설치와 관련된 옵션. &lt;code class=&quot;language-text&quot;&gt;polyfillNode&lt;/code&gt; 옵션은 소스에서 &lt;code class=&quot;language-text&quot;&gt;path&lt;/code&gt;같은 Node.js 모듈을 사용할 경우 필요하다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textdevoptionscode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textdevoptionscode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;devOptions&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;개발 서버 관련 옵션. &lt;code class=&quot;language-text&quot;&gt;port&lt;/code&gt;에 개발 서버의 포트를 지정한다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textbuildoptionscode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textbuildoptionscode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;buildOptions&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;빌드 옵션. &lt;code class=&quot;language-text&quot;&gt;sourceMaps&lt;/code&gt;을 &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;로 지정하면 소스맵도 함께 생성한다. 배포된 서비스에서 에러가 발생했을 때 Sentry같은 모니터링 서비스에서 에러가 발생한 부분과 원본 소스를 매칭시켜 줄 때 사용한다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textproxycode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textproxycode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;proxy&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;개발 서버의 프록시를 설정한다. 예를 들어 &lt;code class=&quot;language-text&quot;&gt;/api&lt;/code&gt; 로 접근하면 &lt;code class=&quot;language-text&quot;&gt;https://pokeapi.co/api/v2/&lt;/code&gt;로 연결시켜주는 등의 기능을 한다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textaliascode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textaliascode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;alias&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;아래처럼 소스 파일을 절대 경로로 접근하려고 할 때 필요하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; useTypedSelector &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hook/useTypedSelector&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;code-classlanguage-texttestoptionscode&quot;&gt;&lt;a href=&quot;#code-classlanguage-texttestoptionscode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;testOptions&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;테스트 파일의 위치를 지정한다. 테스트 파일 관련 모듈은 개발 서버 실행에만 설치되며, 빌드 작업에는 포함되지 않는다.&lt;/p&gt;
&lt;h3 id=&quot;환경-변수-설정&quot;&gt;&lt;a href=&quot;#%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;환경 변수 설정&lt;/h3&gt;
&lt;p&gt;CRA에서는 환경 변수를 .env 파일에 추가하면 node.js에서 실행되는 소스와 브라우저에서 실행되는 소스 모두에서 &lt;code class=&quot;language-text&quot;&gt;process.env&lt;/code&gt; 객체를 통해 접근할 수 있도록 Webpack 설정이 되어 있다. Snowpack에서는 그 방법 대신&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import.meta&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import.meta.env&lt;/code&gt;&lt;/a&gt; 객체를 통해 환경 변수를 제공한다.&lt;/p&gt;
&lt;p&gt;그리고 변수 이름 앞에 &lt;code class=&quot;language-text&quot;&gt;SNOWPACK_PUBLIC_&lt;/code&gt;이 붙지 않은 변수는 브라우저 실행되는 소스에서 접근할 수 없도록 되어 있다. 저렇게 구분한 이유는 환경 변수에 서버 접속 시크릿 키 같은 중요한 값이 포함되어 있을 수 있기 때문이다. 그런 값들은 실수로라도 노출되어선 안되기에 보안을 위해 사용자에게 어떤 환경변수가 브라우저에서 노출되는지 확실히 알게 하려는 목적을 가진다.&lt;/p&gt;
&lt;p&gt;CRA로 프로젝트가 구성되어 있다면 node.js 환경에서만 사용되는 값을 제외한 모든 환경변수의 이름 앞에 &lt;code class=&quot;language-text&quot;&gt;REACT_APP_&lt;/code&gt; 프리픽스 대신&lt;code class=&quot;language-text&quot;&gt;SNOWPACK_PUBLIC_&lt;/code&gt;을 추가해줘야 한다. 소스는 물론 배포 환경에도 영향을 미칠 수 있는 사항이기에 새로운 프리픽스가 붙은 환경변수를 먼저 추가해준 후, 마이그레이션이 완료되면 필요없는 변수를 지우는 방식을 쓰는 편이 좋을 것이다&lt;/p&gt;
&lt;h3 id=&quot;jest-설정&quot;&gt;&lt;a href=&quot;#jest-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jest 설정&lt;/h3&gt;
&lt;p&gt;CRA는 Jest 실행 설정을 포함하고 있다. 하지만 Snowpack에는 없으므로 설정 파일을 직접 추가해줘야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// jest.config.js 파일&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; snowpackJestConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;@snowpack/app-scripts-react/jest.config.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parsed&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; env &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dotenv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;snowpackJestConfig&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Snowpack에서 제공하는 설정을 가져와서 커스터마이징 하는 방식을 쓴다&lt;/span&gt;
  verbose&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  preset&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ts-jest&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 타입스트립트를 사용한다면 ts-jest 모듈이 필요하다.&lt;/span&gt;
  testEnvironment&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;jsdom&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  setupFilesAfterEnv&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./src/setupTests.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  testPathIgnorePatterns&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;/node_modules/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/build&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/cypress&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  testMatch&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/**/?(*.)+(spec|test).[jt]s?(x)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  moduleNameMapper&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Snowpack 설정의 alias를 그대로 가져와서 쓸 수 없어서 jest에서 사용하는 방식으로 설정한다.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 키와 밸류는 String.prototype.replace 메소드의 파라미터에 차례대로 들어간다고 보면 된다.&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^__mocks__(/?.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/__mocks__$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^component(/?.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/component$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^constants/(.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/constants/$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^hook(/?.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/hook$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^page(/?.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/page$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^store(/?.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/store$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^style(/?.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/style$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^translation(/?.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/translation$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^types/(.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/types/$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^utils(/?.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/utils$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;^vendor(/?.*)$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/vendor$1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;storybook-설정&quot;&gt;&lt;a href=&quot;#storybook-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Storybook 설정&lt;/h3&gt;
&lt;p&gt;Storybook은 빌드 툴로 Webpack을 내부적으로 사용한다. 실행을 위해서는 &lt;code class=&quot;language-text&quot;&gt;.storybook/main.js&lt;/code&gt; 파일에서 Webpack 설정을 확장해야 한다.&lt;/p&gt;
&lt;p&gt;우선 소스에서 모듈을 절대 경로로 가져오기 위해 &lt;code class=&quot;language-text&quot;&gt;alias&lt;/code&gt; 설정을 추가해줘야 한다. snowpack.config.js 모듈에 있는 것을 재사용할 수 있다. 다만 &lt;code class=&quot;language-text&quot;&gt;snowpack.config.js&lt;/code&gt; 파일과 &lt;code class=&quot;language-text&quot;&gt;main.js&lt;/code&gt; 파일이 다른 디렉토리에 있기 때문에 &lt;code class=&quot;language-text&quot;&gt;path.join(__dirname, &amp;#39;경로&amp;#39;)&lt;/code&gt; 형태로 설정하지 않았다면 오류가 발생할 것이다.&lt;/p&gt;
&lt;p&gt;그리고 역시 소스에서 &lt;code class=&quot;language-text&quot;&gt;import.meta.env&lt;/code&gt; 를 사용하기 위한 설정이 필요하다. &lt;code class=&quot;language-text&quot;&gt;webpack.DefinePlugin&lt;/code&gt; 으로 &lt;code class=&quot;language-text&quot;&gt;__SNOWPACK_ENV__&lt;/code&gt; 전역 변수를 선언한 후, importMetaLoader.js 로더를 추가해서 &lt;code class=&quot;language-text&quot;&gt;import.meta.env&lt;/code&gt; 코드를 환경 변수를 포함한 객체 형태로 변환해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; webpack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;webpack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; snowpackConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;../snowpack.config.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;dotenv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  webpackFinal&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Assign aliases from snowpack.config.js&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;alias &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;alias&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;snowpackConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;alias&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Add __SNOWPACK_ENV__ global&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plugins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;webpack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DefinePlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        __SNOWPACK_ENV__&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// process.env 객체에서 SNOWPACK_PUBLIC_ 프리픽스가 붙은 변수만 필터링&lt;/span&gt;
          Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filtered&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
              &lt;span class=&quot;token regex&quot;&gt;/^SNOWPACK_PUBLIC_.+/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;filtered&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; filtered&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Add rules for supporting import.meta&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      test&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/\.[tj]sx?$/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      loader&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;require&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./importMetaLoader.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// importMetaLoader.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; regex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/import\.meta/g&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * import.meta 코드 변환을 위한 storybook용 webpack 로더
 * 참조) https://github.com/snowpackjs/snowpack/blob/main/plugins/plugin-webpack/plugins/import-meta-fix.js
 */&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;source&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; found &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; rewrittenSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;regex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    found &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`({ env: __SNOWPACK_ENV__ })`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;found&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rewrittenSource&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 로더는 &lt;a href=&quot;https://www.npmjs.com/package/@snowpack/plugin-webpack&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@snowpack/plugin-webpack&lt;/a&gt;에 있는 것을 수정한 것이다. &lt;code class=&quot;language-text&quot;&gt;import.meta.url&lt;/code&gt;을 사용하지 않는다면 위의 간단한 버전을 사용해도 상관없다.&lt;/p&gt;
&lt;h2 id=&quot;snowpack-셋업-후-속도-측정&quot;&gt;&lt;a href=&quot;#snowpack-%EC%85%8B%EC%97%85-%ED%9B%84-%EC%86%8D%EB%8F%84-%EC%B8%A1%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Snowpack 셋업 후 속도 측정&lt;/h2&gt;
&lt;p&gt;실무에서 작업 중인 프로젝트를 Snowpack으로 마이그레이션 한 후 속도를 측정해 보았다.&lt;/p&gt;
&lt;h3 id=&quot;개발-서버-실행-속도&quot;&gt;&lt;a href=&quot;#%EA%B0%9C%EB%B0%9C-%EC%84%9C%EB%B2%84-%EC%8B%A4%ED%96%89-%EC%86%8D%EB%8F%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;개발 서버 실행 속도&lt;/h3&gt;
&lt;h4 id=&quot;최초&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%B4%88&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최초&lt;/h4&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4OMuyBWmI1d6ct4bhtX1L5/7b4dac2c0536119b9ce59fd78cefcd2f/20201215_____________________________-_______.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 27.931960608773498%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;20201215 개발 서버 실행 속도 - 최초&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4OMuyBWmI1d6ct4bhtX1L5/7b4dac2c0536119b9ce59fd78cefcd2f/20201215_____________________________-_______.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4OMuyBWmI1d6ct4bhtX1L5/7b4dac2c0536119b9ce59fd78cefcd2f/20201215_____________________________-_______.jpeg?w=279 279w,
https://images.ctfassets.net/rpmifyuylbfw/4OMuyBWmI1d6ct4bhtX1L5/7b4dac2c0536119b9ce59fd78cefcd2f/20201215_____________________________-_______.jpeg?w=559 559w,
https://images.ctfassets.net/rpmifyuylbfw/4OMuyBWmI1d6ct4bhtX1L5/7b4dac2c0536119b9ce59fd78cefcd2f/20201215_____________________________-_______.jpeg?w=1117 1117w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;h4 id=&quot;2회차&quot;&gt;&lt;a href=&quot;#2%ED%9A%8C%EC%B0%A8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2회차&lt;/h4&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3S6otftKxUWqdslU4hs8QU/0164eb29f22b1ce1537a29a216feaa7e/20201215_____________________________-_2______.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 14.708520179372197%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;20201215 개발 서버 실행 속도 - 2회차&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3S6otftKxUWqdslU4hs8QU/0164eb29f22b1ce1537a29a216feaa7e/20201215_____________________________-_2______.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3S6otftKxUWqdslU4hs8QU/0164eb29f22b1ce1537a29a216feaa7e/20201215_____________________________-_2______.jpeg?w=279 279w,
https://images.ctfassets.net/rpmifyuylbfw/3S6otftKxUWqdslU4hs8QU/0164eb29f22b1ce1537a29a216feaa7e/20201215_____________________________-_2______.jpeg?w=558 558w,
https://images.ctfassets.net/rpmifyuylbfw/3S6otftKxUWqdslU4hs8QU/0164eb29f22b1ce1537a29a216feaa7e/20201215_____________________________-_2______.jpeg?w=1115 1115w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;최초에는 소스 및 NPM 모듈 빌드를 위해 시간이 40초 정도 걸리지만, 그 다음부터는 빌드 시간이 거의 필요없는 수준이다.&lt;/p&gt;
&lt;h3 id=&quot;개발-서버-리소스-로딩-속도&quot;&gt;&lt;a href=&quot;#%EA%B0%9C%EB%B0%9C-%EC%84%9C%EB%B2%84-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EB%A1%9C%EB%94%A9-%EC%86%8D%EB%8F%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;개발 서버 리소스 로딩 속도&lt;/h3&gt;
&lt;h4 id=&quot;최초-1&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%B4%88-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최초&lt;/h4&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1JgTTIb96q4nFHuiA3uBdJ/e8626dab9107c23b7304b0488f5658d9/20201215______________________________________-________________dependency____________.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 65.70743405275779%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;20201215 로컬서버 페이지 로드 시간 - 최초 로딩 (dependency 설치 후)&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1JgTTIb96q4nFHuiA3uBdJ/e8626dab9107c23b7304b0488f5658d9/20201215______________________________________-________________dependency____________.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1JgTTIb96q4nFHuiA3uBdJ/e8626dab9107c23b7304b0488f5658d9/20201215______________________________________-________________dependency____________.jpeg?w=417 417w,
https://images.ctfassets.net/rpmifyuylbfw/1JgTTIb96q4nFHuiA3uBdJ/e8626dab9107c23b7304b0488f5658d9/20201215______________________________________-________________dependency____________.jpeg?w=834 834w,
https://images.ctfassets.net/rpmifyuylbfw/1JgTTIb96q4nFHuiA3uBdJ/e8626dab9107c23b7304b0488f5658d9/20201215______________________________________-________________dependency____________.jpeg?w=1668 1668w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;소스와 NPM 모듈이 캐시가 되지 않은 상태로 5초 정도가 걸렸다. 스크린샷에 보면 react, react-dom, react-redux 등의 모듈을 브라우저에서 직접 불러오고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;h4 id=&quot;2회차-이상&quot;&gt;&lt;a href=&quot;#2%ED%9A%8C%EC%B0%A8-%EC%9D%B4%EC%83%81&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2회차 이상&lt;/h4&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3Xdd5r4K18rZYAAMYUV4Rd/a767ef094538a5da4a79b6569508c7c2/20201215______________________________________-_____________.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 64.74735605170387%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;20201215 로컬서버 페이지 로드 시간 - 새로고침&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3Xdd5r4K18rZYAAMYUV4Rd/a767ef094538a5da4a79b6569508c7c2/20201215______________________________________-_____________.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3Xdd5r4K18rZYAAMYUV4Rd/a767ef094538a5da4a79b6569508c7c2/20201215______________________________________-_____________.jpeg?w=426 426w,
https://images.ctfassets.net/rpmifyuylbfw/3Xdd5r4K18rZYAAMYUV4Rd/a767ef094538a5da4a79b6569508c7c2/20201215______________________________________-_____________.jpeg?w=851 851w,
https://images.ctfassets.net/rpmifyuylbfw/3Xdd5r4K18rZYAAMYUV4Rd/a767ef094538a5da4a79b6569508c7c2/20201215______________________________________-_____________.jpeg?w=1702 1702w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;파일들이 캐시되어 로딩에 대부분 100ms를 넘기지 않음을 확인할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;빌드-시간-비교&quot;&gt;&lt;a href=&quot;#%EB%B9%8C%EB%93%9C-%EC%8B%9C%EA%B0%84-%EB%B9%84%EA%B5%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;빌드 시간 비교&lt;/h3&gt;
&lt;h4 id=&quot;cra&quot;&gt;&lt;a href=&quot;#cra&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CRA&lt;/h4&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1hxzTowcioOX2hizPsVAAY/a456f63004fc6e7fbfa45c0ee14161c1/20201215_build_log_-_CRA_.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 116.91842900302115%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;20201215 build log - CRA&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1hxzTowcioOX2hizPsVAAY/a456f63004fc6e7fbfa45c0ee14161c1/20201215_build_log_-_CRA_.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1hxzTowcioOX2hizPsVAAY/a456f63004fc6e7fbfa45c0ee14161c1/20201215_build_log_-_CRA_.jpeg?w=331 331w,
https://images.ctfassets.net/rpmifyuylbfw/1hxzTowcioOX2hizPsVAAY/a456f63004fc6e7fbfa45c0ee14161c1/20201215_build_log_-_CRA_.jpeg?w=662 662w,
https://images.ctfassets.net/rpmifyuylbfw/1hxzTowcioOX2hizPsVAAY/a456f63004fc6e7fbfa45c0ee14161c1/20201215_build_log_-_CRA_.jpeg?w=1324 1324w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;129초 정도가 걸렸다.&lt;/p&gt;
&lt;h4 id=&quot;snowpack&quot;&gt;&lt;a href=&quot;#snowpack&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Snowpack&lt;/h4&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3oTD1HasvAnThXCbIPYjhP/c6f591dfbc9108dbcd43e1a5651e70f4/20201215_build_log_-_snowpack.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 123.10469314079424%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;20201215 build log - snowpack&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3oTD1HasvAnThXCbIPYjhP/c6f591dfbc9108dbcd43e1a5651e70f4/20201215_build_log_-_snowpack.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3oTD1HasvAnThXCbIPYjhP/c6f591dfbc9108dbcd43e1a5651e70f4/20201215_build_log_-_snowpack.jpeg?w=277 277w,
https://images.ctfassets.net/rpmifyuylbfw/3oTD1HasvAnThXCbIPYjhP/c6f591dfbc9108dbcd43e1a5651e70f4/20201215_build_log_-_snowpack.jpeg?w=554 554w,
https://images.ctfassets.net/rpmifyuylbfw/3oTD1HasvAnThXCbIPYjhP/c6f591dfbc9108dbcd43e1a5651e70f4/20201215_build_log_-_snowpack.jpeg?w=1108 1108w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;@snowpack/plugin-webpack 플러그인을 사용해서 73초 정도가 걸렸다. CRA에 비하면 1.7배 정도 빠르다. (Snowpack의 번들 파일 용량이 더 큰 것처럼 나오지만, Webpack 결과에는 웹 서버에서 gzip으로 압축 전송을 적용했을 때의 크기를 보여주고 있기 때문에 그렇다.)&lt;/p&gt;
&lt;h3 id=&quot;테스트-속도-비교&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%86%8D%EB%8F%84-%EB%B9%84%EA%B5%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테스트 속도 비교&lt;/h3&gt;
&lt;h4 id=&quot;cra--jest&quot;&gt;&lt;a href=&quot;#cra--jest&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CRA + Jest&lt;/h4&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5E9KRI5ZPAcrX6uB7jgNN7/d090e92371ca080a7cd56aa0a2a634d7/20201215__________________-_CRA.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 14.93624772313297%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;20201215 테스트 속도 - CRA&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5E9KRI5ZPAcrX6uB7jgNN7/d090e92371ca080a7cd56aa0a2a634d7/20201215__________________-_CRA.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5E9KRI5ZPAcrX6uB7jgNN7/d090e92371ca080a7cd56aa0a2a634d7/20201215__________________-_CRA.jpeg?w=275 275w,
https://images.ctfassets.net/rpmifyuylbfw/5E9KRI5ZPAcrX6uB7jgNN7/d090e92371ca080a7cd56aa0a2a634d7/20201215__________________-_CRA.jpeg?w=549 549w,
https://images.ctfassets.net/rpmifyuylbfw/5E9KRI5ZPAcrX6uB7jgNN7/d090e92371ca080a7cd56aa0a2a634d7/20201215__________________-_CRA.jpeg?w=1098 1098w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;h4 id=&quot;snowpack--jest&quot;&gt;&lt;a href=&quot;#snowpack--jest&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Snowpack + Jest&lt;/h4&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/egwb7HZhOHD8SlEWIq99f/50262c3dc948a78f497b30ed689b732b/20201215__________________-_snowpack.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 15.135135135135137%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;20201215 테스트 속도 - snowpack&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/egwb7HZhOHD8SlEWIq99f/50262c3dc948a78f497b30ed689b732b/20201215__________________-_snowpack.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/egwb7HZhOHD8SlEWIq99f/50262c3dc948a78f497b30ed689b732b/20201215__________________-_snowpack.jpeg?w=278 278w,
https://images.ctfassets.net/rpmifyuylbfw/egwb7HZhOHD8SlEWIq99f/50262c3dc948a78f497b30ed689b732b/20201215__________________-_snowpack.jpeg?w=555 555w,
https://images.ctfassets.net/rpmifyuylbfw/egwb7HZhOHD8SlEWIq99f/50262c3dc948a78f497b30ed689b732b/20201215__________________-_snowpack.jpeg?w=1110 1110w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;테스트는 Snowpack에서 추천하는 &lt;a href=&quot;https://www.notion.so/create-react-app-snowpack-e73bf7a9056e4f5f8df58233bb7d3d1e&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;esbuild&lt;/a&gt; 기반의 &lt;a href=&quot;https://modern-web.dev/docs/test-runner/overview/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Web test runner&lt;/a&gt;를 사용하지 않는 이상 아주 큰 차이가 없을 것으로 보인다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;esbuild는 Go로 만들어졌으며 무거운 번들링 작업을 할때 싱글 스레드인 자바스크립트로는 불가능한 병렬 처리가 구현되어 있다. Snowpack은 v2에서도 개별 파일을 빌드할 때는 esbuild를 사용하고 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;결론&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;
&lt;p&gt;Snowpack은 &lt;a href=&quot;https://www.snowpack.dev/posts/2020-12-03-snowpack-3-release-candidate&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;2021년 1월에 배포 예정인 3.0 버전&lt;/a&gt;에서 NPM 패키지를 설치하는 대신 &lt;a href=&quot;https://skypack.dev/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Skypack CDN&lt;/a&gt;으로 스트리밍하기, esbuild를 사용해서 webpack보다 100배 이상 빠른 번들링 지원, 라우팅등 지금보다 더 다양하고 혁신적인 기능을 선보일 것이라서 기대가 된다.&lt;/p&gt;
&lt;p&gt;Snowpack은 많은 사람의 관심에 비해 실제 사용량이 아직은 적다. 하지만 2020년 12월 현재 아직 만 2년도 안된 프로젝트인데다(Webpack은 8년) 라이브러리가 더 성숙해지고 플러그인의 종류도 다양해지면 사용자가 앞으로 더 많아질 것으로 기대된다. 개인적으로는 신규 프로젝트를 진행한다면 빌드 시스템으로 Snowpack을 우선 고려할 것 같다. 하지만 Webpack에 비하면 성숙도가 낮은 도구라서 대규모 상용 서비스에 즉시 도입을 권하기는 애매한 상황이다. 하지만 지금 진행 중이거나, 새로 시작할 프로젝트가 Webpack이나 다른 번들러의 기능에 크게 의존하지 않는다면 사용을 고려해볼만 하다.&lt;/p&gt;
&lt;h2 id=&quot;참고-자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.snowpack.dev/tutorials/quick-start&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Snowpack - Quick Start&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Modules&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;JavaScript modules - MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import.meta&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;import.meta - MDN&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://esbuild.github.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;esbuild - An extremely fast JavaScript bundler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jaredlunde.com/posts/how-to/set-up-storybook-in-a-snowpack-project&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Set up Storybook in a Snowpack project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[[번역] 초보자를 위한 React 어플리케이션 테스트 심층 가이드 (2)]]></title><description><![CDATA[이 글은 ” An in-depth beginner’s guide to testing React applications “를 번역한 글입니다 React 앱 테스팅 개요 테스트에 사용할 어플리케이션 무엇을 테스트해야 하는가? 테스트 작성 어둠 속에서 찔러보지 말라 렌더링…]]></description><link>https://blog.rhostem.com//posts/2020-10-15-beginners-guide-to-testing-react-2</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-10-15-beginners-guide-to-testing-react-2</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Thu, 15 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 ”&lt;a href=&quot;https://jkettmann.com/beginners-guide-to-testing-react&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;An in-depth beginner’s guide to testing React applications&lt;/a&gt;“를 번역한 글입니다&lt;/p&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li&gt;React 앱 테스팅 개요&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;/li&gt;
&lt;li&gt;렌더링된 DOM 트리에 접근하는 방법&lt;/li&gt;
&lt;li&gt;DOM 요소와 상호작용 하기&lt;/li&gt;
&lt;li&gt;올바른 페이지가 렌더링 되었는지 확인하기&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#testing_the_form&quot;&gt;폼 테스팅&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#prevent_duplication_with_a_setup_function&quot;&gt;셋업 함수에서 중복 방지하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#changing_and_submitting_the_form&quot;&gt;폼을 변경하고 제출하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#accessing_an_element_without_aria_role&quot;&gt;ARIA role 없이 요소에 접근하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#waiting_for_the_data&quot;&gt;데이터 기다리기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mocking_api_requests&quot;&gt;API 요청 위조하기(mocking)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#testing_mock_functions&quot;&gt;Mock 함수 테스팅&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;a-nametesting_the_form폼-테스팅a&quot;&gt;&lt;a href=&quot;#a-nametesting_the_form%ED%8F%BC-%ED%85%8C%EC%8A%A4%ED%8C%85a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;testing_the_form&quot;&gt;폼 테스팅&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;훌륭하다, 우리는 헤더에 있는 링크를 위한 첫번째 테스트를 작성했다. 이제 조금 더 복잡한 것을 해 볼 차례다. 우리는 폼을 테스트할 것이다. &lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 640px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 46.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,R0lGODlhKAASAPcAMQD/AJOUk5mam5qamZ2dnZ6foKGioaOjoaampainp6ipp6ipqampqKuqqKurqaysqqysraytra2sqa6urK+vrq+wsLCxsbGxsLOzsrOzs7S0tLm6u7u8vby8vL29vL6+vsDAv8DAw8DBwcLDwsPCwcTEw8bHxsjIx8nJycnKycnKy8rJy8vMzMzMzMzN0M3Lzc3O0c7Ozc/Ozs/Pzs/Pz9DR0dLS0tPU1NXQ0dXW1dXX19bW1tbX2NfU1tfX2NnX1dnZ2Nna2dvFmdva2d3d3d7Prd7d297e3d7e3t7f3t/e3OCqMuDg4OG1V+Li4eS2UOTe1uTk5OXl5efm5+fn5+fo5+jo6Ojp6enr7eqwLurr6uvZuevl3uvo5uvr6uy9U+zt6+zt7O3t7O3t7e7t7e+8S+/s7O/v7vDw8PDx8PDx8vHarfHs6fHw8fHx8fHy8fHy8vLq5vLy8/Lz8/PaqvPy8vPy8vPz8vTapfTlwvTz8/T09PWafvXer/XnzfX19fbhs/bx7vb29vb29/eiiPffq/fz8Pf39vf39/f49/f4+PjBQ/j39vj5+fm4Jfm5JPm5K/m5LPm8Mfm9MPnCRvn08fn49/n5+fn5+vn6+/q8L/q8L/rCQ/rCRvrDSPrER/rVifrfqfrpw/rt1vr4+Pr6+vr7+vvd0/vepPvfqfvgq/vhrfvn4fvo4fvq0Pvx2Pv27/v28/v48fv7+/v7/Pv8/fzCPPzbjvzip/ziq/zkrvzkrvzlsPznvvzowvzowvzq5PzrxPzt0vz06vz06/z18vz29Pz38vz39Pz39fz6+vz8/Pz9/Pz+//3EQf3Wh/3mtv3ow/3rwf3sxv3s0v3tx/3t0P3w1f3y4P324/348f349f359P369/38+v38+/39/f39/v3+/v7EQf7Xhv7Xh/7hqP7nvP7qv/724/749f749v759v78+/78/P79+/79/P79/f79/f79//7+/P7+/f7+/v7//v7////7+f/+/f/+/v/+/////f///v///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBwAAACwAAAAAKAASAIcA/wCTlJOZmpuampmdnZ2en6ChoqGjo6GmpqWop6eoqaeoqampqairqqirq6msrKqsrK2sra2trKmurqyvr66vsLCwsbGxsbCzs7Kzs7O0tLS5uru7vL28vLy9vby+vr7AwL/AwMPAwcHCw8LDwsHExMPGx8bIyMfJycnJysnJysvKycvLzMzMzMzMzdDNy83NztHOzs3Pzs7Pz87Pz8/Q0dHS0tLT1NTV0NHV1tXV19fW1tbW19jX1NbX19jZ19XZ2djZ2tnbxZnb2tnd3d3ez63e3dve3t3e3t7e397f3tzgqjLg4ODhtVfi4uHktlDk3tbk5OTl5eXn5ufn5+fn6Ofo6Ojo6enp6+3qsC7q6+rr2bnr5d7r6Obr6+rsvVPs7evs7ezt7ezt7e3u7e3vvEvv7Ozv7+7w8PDw8fDw8fLx2q3x7Onx8PHx8fHx8vHx8vLy6uby8vPy8/Pz2qrz8vLz8/L02qX05cL08/P09PT1mn713q/158319fX24bP28e729vb29vf3ooj336v38/D39/b39/f3+Pf3+Pj4wUP49/b4+fn5uCX5uST5uSv5uSz5vDH5vTD5wkb59PH5+Pf5+fn5+fr5+vv6vC/6wkP6wkb6w0j6xEf61Yn636n66cP67db6+Pj6+vr6+/r73dP73qT736n74Kv74a375+H76OH76tD78dj79u/79vP7+PH7+/v7+/z7/P38wjz824784qf84qv85K785bD857786ML86uT868T87dL89Or89Ov89fL89vT89/L89/T89/X8+vr8/Pz8/fz8/v/9xEH91of95rb96MP968H97Mb97NL97cf97dD98NX98uD99uP9+PH9+PX9+fT9+vf9/Pr9/Pv9/f39/f79/v7+xEH+14b+14f+4aj+57z+6r/+9uP++PX++Pb++fb+/Pv+/Pz+/fv+/fz+/f3+/f/+/vz+/v3+/v7+//7+////+/n//v3//v7//v////3///7///////////////////////8I/wD16YsnsKBBggYTKlw4sCBBblPawLFihowabg0ZNrRHKlasY6PiIRt1jCBCgrJwrIARoscLF7EyMow3L98YLWnCVGFkSAqYYzIJepOjEKFGffnmHTUqEGG3Y9yOleRGtWq8qtyuZsVKNZ5XgkoPej1KNqHJeEkVYmwhIoiMGyRUMNmRw4cVIEFspLiC5EYUDUFMlOjAY6DXtAblYZQx4gKDGiASbLBBgQAKAxkqWEjhIcKNBg0mSJDQQl+7w2HNHtVj6VBZffAMIz4I+5hHj4LOdIvp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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 1,6-react-testing-intro-2&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif?w=160 160w,
https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif?w=320 320w,
https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif?w=640 640w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;앞서 얘기했듯이, 우리의 테스트 시나리오는 다음 단계를 거쳐야 한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;사용자는 폼 입력에 값을 입력하고 제출 버튼을 누른다.&lt;/li&gt;
&lt;li&gt;데이터를 기다리는 동안 로딩 메시지가 표시된다.&lt;/li&gt;
&lt;li&gt;API 응답이 도착하면 데이터가 렌더링된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;테스트는 헤더에서 했던 것과 같은 방법으로 시작할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Subreddit form&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;loads posts that are rendered on the page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;a-nameprevent_duplication_with_a_setup_function셋업-함수에서-중복-방지하기a&quot;&gt;&lt;a href=&quot;#a-nameprevent_duplication_with_a_setup_function%EC%85%8B%EC%97%85-%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C-%EC%A4%91%EB%B3%B5-%EB%B0%A9%EC%A7%80%ED%95%98%EA%B8%B0a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;prevent_duplication_with_a_setup_function&quot;&gt;셋업 함수에서 중복 방지하기&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;헤더 테스트에서 했던 것과 중복된 부분을 발견할 수 있다. 중복 방지를 위한 일반적인 방법은 셋업 함수를 만드는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Subreddit form&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;loads posts and renders them on the page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 우리가 할 일은 &lt;code class=&quot;language-text&quot;&gt;setup&lt;/code&gt; 함수를 호출하고 테스트를 시작하는 것만 남았다.&lt;/p&gt;
&lt;h2 id=&quot;a-namechanging_and_submitting_the_form폼을-변경하고-제출하기a&quot;&gt;&lt;a href=&quot;#a-namechanging_and_submitting_the_form%ED%8F%BC%EC%9D%84-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B3%A0-%EC%A0%9C%EC%B6%9C%ED%95%98%EA%B8%B0a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;changing_and_submitting_the_form&quot;&gt;폼을 변경하고 제출하기&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;위에서 설명했던 세 단계 중에서 첫 번째는 “사용자는 폼 입력에 값을 입력하고 제출 버튼을 누른다.”다.&lt;/p&gt;
&lt;p&gt;폼의 입력에 접근하기 전에 &lt;code class=&quot;language-text&quot;&gt;screen.debug&lt;/code&gt;를 사용해서 렌더링된 앱이 어떻게 생겼는지 다시 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4JNMpnATfmt6dNEs9rAZvt/5007ad5aad1a788e339ba0407054eb80/7-debug-form.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 599px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 100.8347245409015%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAACdlBMVEUeHh4dLjMcNTsbOUAdJygcLjQbPEQbOkEcMjccNDwbOUMcNz0dJSYcLzUbOUEwMR1HRxtFRB0qMi4bOS0cMCkaPzAaQjIdKiQaQzMaRjUdJSIdKCsdLDEcNDkcOUEdLTAuLx1GRRtDQxsrMi8bNiwcLicaQDEbPjAdLCYaRDMbPi8fHx4eHx8eHx4eIB8eIR8dIyEdKzAcMTUeHyAnLyY4OBw2NhwrMC4dKSQcMyobOy8dJiMbPC8bNywcMCgdJCYlJR4tLR0sLB0gIB8dJyMdKiUcLSYeIiAeIiMsLCwtLS0qKiowMTEqKiktLi4rKywmJiUtLS4yMjEoKSktLCwjIyQlJSUsLS0uLSwzMzIxMTEnJyceISIjLzIvOTspKiouLi0vMDAuLi4lJCQ1NTUjIyMkJSUoKCg0NDQtLSw0NDMyMjIdKCwcNjwdJykeMTQkOTciIx4eISAeICAdJiI+PhxAQBwtLi0cNSsbNiscMikcNCsbPjEbPC4cNCoeISEcNz4eQUcdKCpAQRtEQxsxNC4dKCMbOi4aQzQcMioaPzEaRzUaQTIcNj4bO0IcMDQ/QBw0Ny0bOy4aRTQcLCYdJCEuMB1OTRtFRiQjMCsbOCwuLh0/PxxERCQsLR1MSxs8PBwzNi0gKiolJR0mJh0hIB4dJSgcLjIcMzocMjYdJyocNj0bOkIcNDsdJCUeIyQjNTItNygrNywlLCoeIiEdKyYcLygcLydBQSAqMywbPS8cLScnJx1AQRw4NyMgJiMbNSskJCQgICAjIyIsMDFBQ0U2NzYdKy8cOD4dKS0cMTYbOkQcNzwcNTwdKi0dLTIcNz8bO0QIR9ZLAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAatJREFUOMtjYAADRiZmFgYiACszGzsHMQo5ubh5GIgCvHwsxClk4BcQFBIWERUTl5CQlMKjTpqBWCDDLCsnT5RKBUUlZRVVNXUNSU0tvHZra2sz6DDo6unr6BjgNdHQiEhHGpuYmpmrWlhaWdvY2unhUWjv4Ojk7GLg6uZu4YLPjQwenl7ePr5+/gGBQcEhoWHhEZFR0VgVxsTGeccnJCYl+6WkJqWlZ2RmZYdhVZiTy0ScZ/LyCwrdi4pL3Ev0iixc8PnGsbSs3NKlorKquqa2TgdfbNcb1xNlt3RDYxOQ0iMitptbWm3b2ms7Oru6e9ypkdLi47WJUyjd29dPnErHCYoTK8QnTbaZomnTPhWPwmnTZ8xUm6UpZknIxNlz5s4UrpQibPW8+QsWWgpXVRFSar9o8ZKlelJTXQioW7Z8xcplxPh51eo1a9cRo3D9ho2bNltscdtqobFtuwXeUmXCjp22lrtsLVRVd1vjy4Z79u7bf6B6CmE3Hjx06DBRMXjk6LEgYtQdb2hYe8KDsLqTp7hP6xBj3tozZ9ecI6zu/IVe4mqFi+yECjMALOFyZl4pp5kAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 7-debug-form&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4JNMpnATfmt6dNEs9rAZvt/5007ad5aad1a788e339ba0407054eb80/7-debug-form.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4JNMpnATfmt6dNEs9rAZvt/5007ad5aad1a788e339ba0407054eb80/7-debug-form.png?w=150 150w,
https://images.ctfassets.net/rpmifyuylbfw/4JNMpnATfmt6dNEs9rAZvt/5007ad5aad1a788e339ba0407054eb80/7-debug-form.png?w=300 300w,
https://images.ctfassets.net/rpmifyuylbfw/4JNMpnATfmt6dNEs9rAZvt/5007ad5aad1a788e339ba0407054eb80/7-debug-form.png?w=599 599w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;서브레딧 검색을 위한 &lt;code class=&quot;language-text&quot;&gt;r /&lt;/code&gt; 라벨이 붙어있는 &lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt; 요소를 확인할 수 있다. &lt;a href=&quot;https://testing-library.com/docs/guide-which-query&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;쿼리 목록의 우선순위&lt;/a&gt;의 다른 부분을 살펴보면, 폼 인풋을 찾기 위한 쿼리로는 &lt;code class=&quot;language-text&quot;&gt;getByLabelText&lt;/code&gt;가 바람직한 선택임을 알 수 있다.&lt;/p&gt;
&lt;p&gt;입력 값을 변경하기 위해서는 &lt;code class=&quot;language-text&quot;&gt;@testing-library/user-event&lt;/code&gt;의 &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; 함수를 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subredditInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByLabelText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;r /&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subredditInput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;reactjs&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다음으로, 우리는 폼을 제출해야 한다. &lt;code class=&quot;language-text&quot;&gt;screen.debug()&lt;/code&gt;의 출력에서 폼 안에 버튼 요소가 있는 것을 확인할 수 있다. 그것은 &lt;code class=&quot;language-text&quot;&gt;getByRole&lt;/code&gt;의 좋은 사용 사례다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subredditInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByLabelText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;r /&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subredditInput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;reactjs&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; submitButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;button&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/search/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;submitButton&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우리는 앱의 현재 상태가 어떤지 확인하기 위해 아래쪽에 또 다른 debug 구문을 추가했다. 결과는 다음과 같다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3FjIkSYL2S0GV398NO0RBO/5093d7312088ce350f7579655f7c2b4f/4-debug-loading.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 599px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 97.6627712854758%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAnCAMAAABKdvqKAAACYVBMVEUeHh4dLDIcNDgeHh8oLiM7Oxw4OBwsMi8cMCgdKiUbNywbPjAdJyMaQDEbOi4cMikeIB8dJigkJB4pKh0qKh0fHh4dJSIeIiAdJiMeISAdIyEeISItLS0vLy8sLCwyMjMvLi0wMTEvLzAnJyY3NzcpKiovLy4kJSUoKCgvMDAwLy44ODczMzMeIyQhMDMtOTwpKCgoKCkqKSkqKiokJCMsLC0wMDAiIiIjIyQlJSYxMjIrKyokJCQdKCscNzwcNjwgMTInOTQlJh4eIiEdKCQdJCEdKSQrLB08PRw9PRwtLiwcMiocMyocMSkbPDAbOy4eHx4dKCweISEeICAcNz4eQUcdKCouLx1AQRtFRBsxNC4dKCMbPC4aRDQaSDYaQTIfHx4bO0IcMDQtLh0/QR1GRRs0Ny0cNSsdKiQbPS8cLCYbPC8uMB1PTRtGRyQjMSsaQDIbOC0aRDMaQzMtLR08PBxBQSQjMCsbNisbNCosLR1MSxsyNS0cMSgdJCUhKigoKB0hIR4dJiIdLTIcMjgdJCYdJyocNTsbO0QcNDodJSciNzYrNisnNjAkLCodLCYxMR07PRw9PBwqMCocLygbPzEbOS0dKyVGRxtBQCchKiccLyceHx8hISEgICAvMDFERUY5ODc1NTQbOkEbOEAcNTwcOD4eIiMdKS0cNj0dKy8cNDsbOkMdKy0dLjMdKSwvMB1HRxtFRRsrMi8bNy0aQTEbOy8cMCkaQzIcNiwtLi47Ojo/QUI3NjUhNz0fMjciLS8kJCUdJiocMzkdLDAdJSgcMjkcLjIcNj4cLjMcMTdl1cctAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAZlJREFUOMtjYIACRiYG4gAzCysbOwcnFzcPLx+/AB6FgkLCIqJi4hKckmJiUvhMlJaRlZNXUFRSllVRVVPX0NTS1tHAqlBXT9/A0EjG2MTUzFzOwtLKWtbMBqtCWzt74jwj6ODo5Cwl7uLKIynF6YbPN+4enl7eEvw+vj5+/vwBuNUFMgQFSxNlt21IaBiQEiCsMjwiMoormi8m1oc3Ll4cn93EAlXVBAYiHZmYRJzK5JTUtPQM/9jMrNhsf1c8CnNy8/ILCouKfQiZWFJaVl5RKUbY6qpq1pr0Qo5aQkrr6htEGiWjm9wIqWtuSWolxs9t7R2dXcQobO3u6e3jEXdz4XHL6o/Gp3LCxEmT06dMnTad39cXr8KGGTNn8fHNnkPI6sC5DAzziIrB+QsWLiJGXeDiJUuXLSesbkXnyo45RJi3arXdyjVriVC4bsl6ItP3ho2bNm+ZsnXb9h07/fkEqJFjdlnv3rN3LxEZZt/+AwdtNAiqO3S45QgxgXO0vbMlmBh1K48dFyQmJa4+cZKo8unIqZWEQxsAHxFsAVmp920AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 4-debug-loading&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3FjIkSYL2S0GV398NO0RBO/5093d7312088ce350f7579655f7c2b4f/4-debug-loading.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3FjIkSYL2S0GV398NO0RBO/5093d7312088ce350f7579655f7c2b4f/4-debug-loading.png?w=150 150w,
https://images.ctfassets.net/rpmifyuylbfw/3FjIkSYL2S0GV398NO0RBO/5093d7312088ce350f7579655f7c2b4f/4-debug-loading.png?w=300 300w,
https://images.ctfassets.net/rpmifyuylbfw/3FjIkSYL2S0GV398NO0RBO/5093d7312088ce350f7579655f7c2b4f/4-debug-loading.png?w=599 599w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;아래 부분에서 우리는 앱이 “Is loading” 텍스트를 보여주고 있는 걸 확인할 수 있다. 그것은 우리가 제출 버튼을 클릭했을 때 기대했던 모습과 정확히 일치한다.&lt;/p&gt;
&lt;h2 id=&quot;a-nameaccessing_an_element_without_aria_rolearia-role-없이-요소에-접근하기a&quot;&gt;&lt;a href=&quot;#a-nameaccessing_an_element_without_aria_rolearia-role-%EC%97%86%EC%9D%B4-%EC%9A%94%EC%86%8C%EC%97%90-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;accessing_an_element_without_aria_role&quot;&gt;ARIA role 없이 요소에 접근하기&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;두 번째 단계는 “데이터를 기다리는 동안 로딩 메시지가 표시된다.”이다.&lt;/p&gt;
&lt;p&gt;로딩 메시지는 &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; 요소 안에 있어서 접근에 사용할 ARIA role이 없다. &lt;a href=&quot;https://testing-library.com/docs/guide-which-query&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Testing Library 문서&lt;/a&gt;에 의하면 이 경우에는 &lt;code class=&quot;language-text&quot;&gt;getByRole&lt;/code&gt; 대신  &lt;code class=&quot;language-text&quot;&gt;getByText&lt;/code&gt;를 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;submitButton&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/is loading/i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여기까지 작성한 테스트도 역시 통과할 것이다.&lt;/p&gt;
&lt;p&gt;이제 마지막 단계 - “API 응답이 도착하면 데이터가 렌더링된다&lt;strong&gt;”&lt;/strong&gt;에 파고 들 단계다.&lt;/p&gt;
&lt;h2 id=&quot;a-namewaiting_for_the_data데이터-기다리기a&quot;&gt;&lt;a href=&quot;#a-namewaiting_for_the_data%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B8%B0%EB%8B%A4%EB%A6%AC%EA%B8%B0a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;waiting_for_the_data&quot;&gt;데이터 기다리기&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;현재 우리는 제출 버튼을 클릭했고, 로딩 메시지가 표시되고 있는 중이다. 이는 API 요청을 보냈지만 아직 응답을 받지 못했다는 의미다. 데이터가 제대로 렌더링되는 것을 테스트하기 위해서 우리는 응답을 기다릴 필요가 있다.&lt;/p&gt;
&lt;p&gt;지금까지 우리는 &lt;code class=&quot;language-text&quot;&gt;getBy*&lt;/code&gt; 쿼리만 사용해 왔다. 하지만 처음에 언급했듯이 &lt;code class=&quot;language-text&quot;&gt;getBy&lt;/code&gt;로 시작하는 함수는 동기적이다. 그것들은 어플리케이션의 현재 상태만 확인한다. 만약 함수가 검색 타겟을 바로 찾을 수 없다면 테스트가 실패할 것이다. &lt;/p&gt;
&lt;p&gt;그러므로 이제는 다른 타입의 쿼리를 사용할 때가 되었다. 비동기적인 &lt;code class=&quot;language-text&quot;&gt;findBy*&lt;/code&gt; 함수는 타겟 요소가 나타날 때까지 최대 5초를 기다린다.&lt;/p&gt;
&lt;p&gt;새로운 쿼리를 사용하기 전에 비동기적으로 표시되는 요소의 식별자를 찾아야 한다. 우리는 앱이 API 요청의 응답을 받는데 성공하면 검색된 인기 포스트의 개수를 폼 아래쪽에 표시한다는 사실을 알고 있다. 표시되는 텍스트의 형식은 &lt;strong&gt;“Number of top posts: …”&lt;/strong&gt; 와 같다. 그러니 여기서는 &lt;code class=&quot;language-text&quot;&gt;findByText&lt;/code&gt; 쿼리를 사용하자.&lt;/p&gt;
&lt;p&gt;렌더링되는 결과에 들어가는 숫자는 모르기 때문에 정규표현식을 사용하면 편리하다. 기억하는가? 정규표현식은 문자열의 일부만으로 요소를 찾을 수 있게 해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;loads posts and renders them on the page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subredditInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByLabelText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;r /&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subredditInput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;reactjs&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; submitButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;button&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/search/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;submitButton&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; loadingMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/is loading/i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loadingMessage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; numberOfTopPosts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findByText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/number of top posts:/i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;numberOfTopPosts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;findByText&lt;/code&gt; 가 비동기적이기 때문에 &lt;code class=&quot;language-text&quot;&gt;await&lt;/code&gt;를 사용해야 한다. 그리고 &lt;code class=&quot;language-text&quot;&gt;await&lt;/code&gt; 때문에 테스트 함수 앞에 &lt;code class=&quot;language-text&quot;&gt;async&lt;/code&gt; 키워드도 추가해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;debug&lt;/code&gt; 함수의 결과는 아래와 같다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5xCgunNMmkqlXTHUkcMOc8/2c8eccbd5a825a1298b796888e4539b0/8-debug-number-of-top-posts.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 599px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 21.03505843071786%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAICAMAAAC8jE1pAAAA7VBMVEUeHh4kJCQlJSUhISEjIyMkIyMiIiImJiYlJiYlJCQmJyclJSQjIyIlJSYmJSUgICAoKCgnLzEmLzElMTQjLC4qLC0pKSgpKSooJycnJycpKCgkJSUoKCcqKioqKSkdLTEqPDInMywnODQgLSsdJiIdJCIdKSQeIiAdLCYdKiUdKyUdIyEeISEvLx04OBw0MyAiJCMcLSYcLiccNSsdJSIdLCccLCYdJSgqKissLSwrKyspKSkhISBGR0g+Pj05OTklJCUtLSw9Pj0tLi44ODcwLy8eISMwNzojKSodJCUdIyUdLTIcMTccLjMdJCacnEP9AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAJVJREFUGBl9wc0KAVEAhuH3O3MOozGUEjs3MTvZWrpcVyDJwk5uwIKSUn7mfwy2eB6B0Fvu0mZhVcYuk/GVZl7VvIeKCyhLLHT1cupfuokftx6PoaRz1rM5N6MiHxx7h0aCgJE+0MuWrywQ6uMeXNta850FAmlTRavO1Yu04AdRm6i2HEva7flBwFS1Of8ZwDlnzIz/np4RKi18wCFVAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 8-debug-number-of-top-posts&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5xCgunNMmkqlXTHUkcMOc8/2c8eccbd5a825a1298b796888e4539b0/8-debug-number-of-top-posts.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5xCgunNMmkqlXTHUkcMOc8/2c8eccbd5a825a1298b796888e4539b0/8-debug-number-of-top-posts.png?w=150 150w,
https://images.ctfassets.net/rpmifyuylbfw/5xCgunNMmkqlXTHUkcMOc8/2c8eccbd5a825a1298b796888e4539b0/8-debug-number-of-top-posts.png?w=300 300w,
https://images.ctfassets.net/rpmifyuylbfw/5xCgunNMmkqlXTHUkcMOc8/2c8eccbd5a825a1298b796888e4539b0/8-debug-number-of-top-posts.png?w=599 599w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;훌륭하다! 응답 데이터가 렌더링되었다. 우리가 앞서 정의했던 모든 단계를 다뤘다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;사용자는 폼 입력에 값을 입력하고 제출 버튼을 누른다.&lt;/li&gt;
&lt;li&gt;데이터를 기다리는 동안 로딩 메시지가 표시된다.&lt;/li&gt;
&lt;li&gt;API 응답이 도착하면 데이터가 렌더링된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;여기서 끝났다고 생각할 수도 있다. 하지만 안타깝게도, 마지막 하나가 더 남았다.&lt;/p&gt;
&lt;h2 id=&quot;a-namemocking_api_requestsapi-요청-위조mocking하기a&quot;&gt;&lt;a href=&quot;#a-namemocking_api_requestsapi-%EC%9A%94%EC%B2%AD-%EC%9C%84%EC%A1%B0mocking%ED%95%98%EA%B8%B0a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;mocking_api_requests&quot;&gt;API 요청 위조(mocking)하기&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;당신이 직접 테스트를 돌려 봤다면 아마도 폼 테스트에 걸리는 시간이 다른 테스트에 비해 상대적으로 길다는 사실을 깨달았을 것이다. 내 컴퓨터에서는 거의 1초가  걸린다. 그 이유는 우리가 진짜 Reddit API에 요청을 보내고 있기 때문이다. &lt;/p&gt;
&lt;p&gt;그것은 이상적이지 않다. 통합 테스트에는 서버 요청을 실제로 보내서는 안된다. 거기에는 몇 가지 이유가 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;API 요청은 시간이 많이 걸린다. 통합 테스트는 보통 코드를 원격 저장소(ex. Github)에 푸시하기 전에 로컬 환경에서 자주 실행한다. 그리고 코드가 푸시되었을 때 CI 파이프라인을 통해 실행되는 케이스도 많다. 만약 테스트의 수가 아주 많고 테스트들이 보내는 요청도 많다면, 테스트는 끝도 없이 돌아갈 것이다. 그렇게 되면 개발 환경과 퍼포먼스에 영향을 미치게 된다.&lt;/li&gt;
&lt;li&gt;우리는 API 요청을 컨트롤할 수 없다. 우리는 통합 테스트에서 앱이 가지는 다양한 상태를 테스트하고 싶어한다. 예를 들어 API 서버가 죽은 상황을 테스트하고 싶을 수도 있다. 특정 테스트가 실행되는 동안만 서버에 오류가 생기도록 만들 수는 없다. 하지만 우리가 요청을 위조한다면 어떤 타입의 응답도 쉽게 시뮬레이션할 수 있다.&lt;/li&gt;
&lt;li&gt;우리가 작성한 테스트에 아무 문제가 없어도 API 서버가 기대한 기대로 동작하지 않는다면 테스트가 실패할 수 있다. 예를 들어 API 서버가 죽은 상태라면 그런 상황이 발생할 수 있을 것이다. 테스트가 자동으로 그런 상황들을 대응하도록 만들면 좋겠지만, 그런 것은 통합 테스트가 아닌 E2E(end-to-end) 테스트를 사용해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;좋다, 이제 이유를 알았고 우리는 API 요청을 위조하야 한다. 그런데 어떻게?&lt;/p&gt;
&lt;p&gt;먼저,  API 요청이 어떻게 보내지는지 알아야 한다. 우리가 테스트하는 앱에서 그것은 &lt;code class=&quot;language-text&quot;&gt;Home&lt;/code&gt; 페이지 컴포넌트에서 이뤄진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Home&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;posts&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setPosts&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setStatus&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;idle&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; onSearch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subreddit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;loading&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`https://www.reddit.com/r/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;subreddit&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/top.json`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;resolved&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt;로 만들어진 요청을 위조하기 위해서는 &lt;a href=&quot;https://github.com/jefflau/jest-fetch-mock&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;jest-fetch-mock&lt;/a&gt; npm 패키지를 사용할 수 있다. 우선 패키지를 설치하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;yarn jest&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;fetch&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;mock&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 테스트 파일 상단에서 &lt;code class=&quot;language-text&quot;&gt;jest-fetch-mock&lt;/code&gt;을 초기화해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fetchMock &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;jest-fetch-mock&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

fetchMock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enableMocks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여기까지 했다면 폼 테스트는 실패할 것이다. 왜냐하면 위조된 fetch에게 요청에 대한 응답을 어떻게 할지 알려주지 않았기 때문이다.&lt;/p&gt;
&lt;p&gt;위조 응답을 만들기 위해 브라우저를 보자. 개발 도구의 네트워크 탭을 열고 폼을 제출한 후 API 응답을 복사한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5VDByrq8mKotCl9hTsZqQj/75578e4768d5bf35819a4df2472dae65/9-copy-response.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 600px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 52.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAVCAMAAAADxFwsAAABnlBMVEXs7Ozl5eXp6ene3t7x8fHz8/Ph4eHm5ubn5+fr6+vo6Ojq6urt7e3i4uLk5OTS0tLf39/v7+/w8PDg4ODj4+Pk4/LPzOXIxt7LyOHPzeXl4/LV1dXv39/sm5vu7u7Y2Njx2dnx1tbb29vc3Nzt5eXtu7vZ2dnw4uLw3d3////i4+Xi4+Tt7u/v7/DX2Nrl5ef7+/vh4uTg4ePn6Ony8vPW19rk5eb09fXk5Obd3uD19fbZ2t3m5+je3+H9/f3l7ez09PTw9PT4+Pj+/v7x8fj5+fn89PT5+P3p5/3+/v/t8vHK69/X7/bb7v3c7/3x8fz46urc8Oz4/P7q6fp9xcFzxrt4v8f6+f719fX67Oz39/ft7fX6+vr26+ve3+Dd3d/h4eLy8vLl5ebg4OHn5+ju7u/b2PHRzubMyuLQzubZ1u7t7PLJy83KzM3Ky83Y5/jJ2OrM3O7C0eK4xdXF1ebU5fm+zNy/zd7Azt7D0eLU5vnE0+S8ydnS4vXF1OXV5vrD0uK8ytr2+Pv5/P/2+fz4+/7Bw8bx9Pf3+v38/PyOkfx3AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAZRJREFUGBmNwcuK01AABuD/Pzm5nrQz1YlpBywI3bjSlVsfwKUIDl6eTNGFgvgULvsGgoqIOE47cXRSezlJ5pyYqS3YhZDvI7hROtJWQeXVVhYBiJXwC0K7wqJWhcPQLz02ZlWvEKDUF75RvDQrKxciOt9HHf2i0/dV8CMJJr41tbXGiMHcaJMzz7tzB0L1ZulptbDGueaawHRlJjo2+bnna11aN6qtEwF1XHTqeXiuCgAMboCkgSRZICCXUCRn2GOdo8eVWOAqzw549xQpeYJDfE88fkOSAbiuMwBJhsaQALiQCnh3jyPwI6A+4K9IHbzHRkyCXMgucPNzkmFHeJxhK8vQSARa4pXbAPoTNPo8OcQ0BcBJCmCaojFN0ZjyKfkaW4NlHmrjXSgs4+Xwi3GNMsWjt5UvSynJDra+yidvKJ2HLwJ5n2vP6wpeXf7u0T+KyWcaa+HwVsp/6ZA6JPOwZPzg1eMVNsIxjkhaAA5pLC4Ji4a8Mx6NsTUA/JdY+zTCDoH/GGGXQEsCLQm0JNCSQEt/AK8Mkd39BvIfAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 9-copy-response&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5VDByrq8mKotCl9hTsZqQj/75578e4768d5bf35819a4df2472dae65/9-copy-response.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5VDByrq8mKotCl9hTsZqQj/75578e4768d5bf35819a4df2472dae65/9-copy-response.png?w=150 150w,
https://images.ctfassets.net/rpmifyuylbfw/5VDByrq8mKotCl9hTsZqQj/75578e4768d5bf35819a4df2472dae65/9-copy-response.png?w=300 300w,
https://images.ctfassets.net/rpmifyuylbfw/5VDByrq8mKotCl9hTsZqQj/75578e4768d5bf35819a4df2472dae65/9-copy-response.png?w=600 600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;그 다음, 새로운 파일(ex. &lt;code class=&quot;language-text&quot;&gt;src/__mocks__/subreddit-reactjs-response.json&lt;/code&gt;) 을 만들어 거기에 개발 도구에서 복사한 응답 데이터를 붙여넣는다.&lt;/p&gt;
&lt;p&gt;그러면 &lt;code class=&quot;language-text&quot;&gt;jest-fetch-mock&lt;/code&gt; 덕분에 &lt;code class=&quot;language-text&quot;&gt;fetch.once&lt;/code&gt;를 호출하는 것만으로 위조 응답을 쉽게 만들 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; mockResponse &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./__mocks__/subreddit-reactjs-response.json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;loads posts and renders them on the page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  fetch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 테스트가 다시 성공해야 한다. 그리고 위조 응답을 사용했기 때문에 테스트하는 요소에 어떤 값이 표시되어야 하는지 확실하게 안다. 그러므로 관련된 테스트를 조금 수정할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findByText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/number of top posts: 25/i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;노트: 당신의 테스트가 이것보다 더 많은 API 요청을 보낸다면 이 방법은 다소 번거로울 수 있다. 그런 경우에는 MSW 패키지를 살펴보길 바란다. &lt;a href=&quot;https://kentcdodds.com/blog/stop-mocking-fetch&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Kent C. Dodds의 블로그 글&lt;/a&gt;에서 더 많은 정보를 확인할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;a-nametesting_mock_functionsmock-함수-테스팅a&quot;&gt;&lt;a href=&quot;#a-nametesting_mock_functionsmock-%ED%95%A8%EC%88%98-%ED%85%8C%EC%8A%A4%ED%8C%85a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;testing_mock_functions&quot;&gt;Mock 함수 테스팅&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;마지막 단계로, 우리는 올바른 API 주소로 요청이 전달되었는지 테스트하고 싶을 수 있다. 이것으로 유저가 정확한 데이터를 보는지 보장할 수 있다.&lt;/p&gt;
&lt;p&gt;위에서 &lt;code class=&quot;language-text&quot;&gt;jest-mock-fetch&lt;/code&gt;를 사용했으므로 현재 전역 &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt;는 위조 함수로 대체된 상황이다. 덕분에 정확한 URL이 사용되었는지 여부는 Jest의 &lt;code class=&quot;language-text&quot;&gt;toHaveBeenCalledWith&lt;/code&gt; 함수로 간단히 가능하다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://www.reddit.com/r/reactjs/top.json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;노트: 테스트 중에는 위조 함수를 직접 써야 할 상황이 때때로 생긴다. Jest를 사용하면 새로운 위조 함수를 &lt;code class=&quot;language-text&quot;&gt;jest.fn()&lt;/code&gt; 으로 쉽게 만들 수 있다. test-mock-fetch 패키지도 내부적으로 저 함수를 활용하고 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이제 되었다! 전체 테스트는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Subreddit form&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;loads posts and renders them on the page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    fetch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mockResponse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subredditInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByLabelText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;r /&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subredditInput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;reactjs&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; submitButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;button&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/search/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;submitButton&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/is loading/i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findByText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/Number of top posts: 25/i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://www.reddit.com/r/reactjs/top.json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;정리&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;여기까지 온 것을 축하한다🎉. 이제 당신의 앱에 테스트를 작성하는 일에 자신감을 얻게 되었길 바란다. &lt;/p&gt;
&lt;p&gt;핵심 사항은 아래와 같다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;유저 관점에서의 테스트 작성.&lt;/li&gt;
&lt;li&gt;테스트에서 뭐가 일어나고 있는지 확실하지 않을 때는 언제나 &lt;code class=&quot;language-text&quot;&gt;screen.debug()&lt;/code&gt;를 사용해라.&lt;/li&gt;
&lt;li&gt;가능하다면 DOM 트리에서 요소에 접근할 때 &lt;code class=&quot;language-text&quot;&gt;getByRole&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;findByRole&lt;/code&gt;, … 함수를 사용해라.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;그리고 &lt;a href=&quot;https://jkettmann.com/refactoring-and-debugging-a-react-test/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;이 블로그 포스트 링크&lt;/a&gt;에서는 테스트 리팩토링과 디버깅에 대해 더 심화된 내용을 확인할 수 있다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[[번역] 초보자를 위한 React 어플리케이션 테스트 심층 가이드 (1)]]></title><description><![CDATA[이 글은 ” An in-depth beginner’s guide to testing React applications “를 번역한 글입니다 대부분의 개발자는 다음의 사실을 알고 있다: 자동화된 테스트는 중요하다. 그렇게 주장하는 데는 많은 이유가 있다. 단지 코드 몇…]]></description><link>https://blog.rhostem.com//posts/2020-10-14-beginners-guide-to-testing-react-1</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-10-14-beginners-guide-to-testing-react-1</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Wed, 14 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 ”&lt;a href=&quot;https://jkettmann.com/beginners-guide-to-testing-react&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;An in-depth beginner’s guide to testing React applications&lt;/a&gt;“를 번역한 글입니다&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;대부분의 개발자는 다음의 사실을 알고 있다: 자동화된 테스트는 중요하다. 그렇게 주장하는 데는 많은 이유가 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;단지 코드 몇 줄을 바꾸는 것으로 자기도 모르게 앱을 망가뜨릴 수도 있다.&lt;/li&gt;
&lt;li&gt;코드를 수정한 후에 테스트를 수동으로 돌리는 일은 성가시다.&lt;/li&gt;
&lt;li&gt;테스트는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Edge_case&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;edge case&lt;/a&gt;&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;를 문서화 하는 데 도움을 준다.&lt;/li&gt;
&lt;li&gt;이직을 원하는 개발자 입장에서 다른 지원자보다 더 높은 경쟁력을 가질 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;안타까운 얘기지만, 테스트 케이스를 작성하는 일은 처음 시도하는 사람에게 쉬운 일이 아니다. 그것은 마치 완전히 다른 환경에서 개발을 진행하는 것과 유사하다. 당신은 대체 무엇을 테스트해야 할지 모를 수도 있다. 간단한 테스트 케이스를 작성에도 에러가 계속 발생해서 많은 시간이 걸릴 수도 있다. 당신은 브라우저를 기반으로 개발하는 일에 익숙하다. 브라우저는 물론 훌륭한 도구다. 하지만 테스트는 브라우저에서 작성하는 것과 같은 방식으로 동작하지 않는다.&lt;/p&gt;
&lt;p&gt;하지만 테스트가 그렇게 어려울 것은 없다. 올바른 접근법을 가진다면 초보자라도 확신을 가지고 테스트를 작성할 수 있다.&lt;/p&gt;
&lt;p&gt;이 블로그 포스트의 목표는 당신의 첫 번째 테스트를 위한 가이드가 되는 것이다. 우리는 조그만 예제 앱과 함께 테스트를 작성해 나갈 것이다. 그리고 무엇을 테스트 해야 할지, 무엇을 테스트 하면 안되는지에 대해 이야기 할 것이다. 우리는 시행착오 없이 확신을 갖고 테스트를 작성할 수 있는 다양한 테크닉을 살펴보려 한다.&lt;/p&gt;
&lt;p&gt;이 블로그 포스트는 꽤 길고 심층적인 내용을 다루고 있다. 아래의 차례를 통해 어떤 내용이 나올지 미리 확인할 수 있을 것이다. &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#overview_of_testing_react_apps&quot;&gt;React 앱 테스팅 개요&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the_application_to_test&quot;&gt;테스트에 사용할 어플리케이션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#what_should_we_test&quot;&gt;무엇을 테스트해야 하는가?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#writing_the_tests&quot;&gt;테스트 작성&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#don_t_take_a_stab_in_the_dark&quot;&gt;어둠 속에서 찔러보지 말라&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how_to_access_the_rendered_dom_tree&quot;&gt;렌더링된 DOM 트리에 접근하는 방법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#interacting_with_dom_elements&quot;&gt;DOM 요소와 상호작용 하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#test_if_the_correct_page_was_rendered&quot;&gt;올바른 페이지가 렌더링 되었는지 확인하기&lt;/a&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;ARIA role 없이 요소에 접근하기&lt;/li&gt;
&lt;li&gt;데이터 기다리기&lt;/li&gt;
&lt;li&gt;API 요청 위조하기(mocking)&lt;/li&gt;
&lt;li&gt;Mock 함수 테스팅&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;이 포스트는 많은 정보를 담고 있다. 구체적인 내용을 기억하는데 도움을 주기 위해 나는 모든 팁과 리소스의 목록을 담은 한장의 요약본(cheatsheet)를 만들었다. 그것은 글 마지막 부분에서 얻을 수 있다. &lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;테스트할 어플리케이션을 살펴보기 전에 일반적인 React 앱을 테스트하는 방법에 대해 전반적으로 살펴보자.&lt;/p&gt;
&lt;h2 id=&quot;a-nameoverview_of_testing_react_appsreact-앱-테스팅-개요a&quot;&gt;&lt;a href=&quot;#a-nameoverview_of_testing_react_appsreact-%EC%95%B1-%ED%85%8C%EC%8A%A4%ED%8C%85-%EA%B0%9C%EC%9A%94a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;overview_of_testing_react_apps&quot;&gt;React 앱 테스팅 개요&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;큰 규모의 앱을 작업할 때 중요한 파트의 코드를 건드리는 일은 무섭게 느껴질 수 있다. 사소한 수정도 매우 중요한 기능을 망가뜨릴 수 있기 때문이다. 개발자는 이런 위험을 최소화하기 위해 테스트를 작성한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;테스트 작성의 목적은 당신에게 그 앱이 제대로 동작한다는 확신을 주는 데 있다&lt;/strong&gt;. 만약 모든 중요한 유즈케이스(use-case)가 테스트로 커버되어 있다면 당신이 무언가를 망가뜨렸을 때 빠르게 피드백을 얻을 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;나는 이런 테스트가 개발자와 비지니스에 설명할 수도 없을 정도로 큰 이득을 준다고 힘주어 말할 수 있다.&lt;/p&gt;
&lt;p&gt;현재 React 앱의 자동화된 테스트에 꼭 맞는 라이브러리는 &lt;strong&gt;&lt;em&gt;Jest&lt;/em&gt;&lt;/strong&gt; 와 &lt;strong&gt;&lt;em&gt;@testing-library/react&lt;/em&gt;&lt;/strong&gt;(aka Testing Library)의 조합이다.&lt;/p&gt;
&lt;p&gt;물론 다른 라이브러리도 있다. Jest는 Mocha, Jasmine, 또는 AVA로 대신할 수 있고, Testing Library는 많은 개발자들이 사용하고 있는 Enzyme으로 대체할 수 있다.&lt;/p&gt;
&lt;p&gt;Testing Library는 테스트를 사용자 관점에서 접근한다. 그러므로 그것은 우리를 자연스럽게 복수의 컴포넌트가 함께 테스트되는 &lt;a href=&quot;https://kentcdodds.com/blog/write-tests&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Integration Test&lt;/a&gt;&lt;sup id=&quot;fnref-3&quot;&gt;&lt;a href=&quot;#fn-3&quot; class=&quot;footnote-ref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;로 이끈다.&lt;/p&gt;
&lt;p&gt;예를 들어 어떤 버튼을 생각해보자. Testing Library를 사용하면 버튼이 클릭되었을 때 &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; prop이 호출되는지는 잘 테스트하지 않는다. 그보다 특정 버튼이 특정 효과를 발동시키는지 확인한다. 예를 들어 삭제 버튼을 클릭하면 확인 모달이 열리는지 테스트한다.&lt;/p&gt;
&lt;h2 id=&quot;a-namethe_application_to_test테스트에-사용할-어플리케이션a&quot;&gt;&lt;a href=&quot;#a-namethe_application_to_test%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%97%90-%EC%82%AC%EC%9A%A9%ED%95%A0-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;the_application_to_test&quot;&gt;테스트에 사용할 어플리케이션&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 640px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 46.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,R0lGODlhKAASAPcAMQD/AJOUk5mam5qamZ2dnZ6foKGioaOjoaampainp6ipp6ipqampqKuqqKurqaysqqysraytra2sqa6urK+vrq+wsLCxsbGxsLOzsrOzs7S0tLm6u7u8vby8vL29vL6+vsDAv8DAw8DBwcLDwsPCwcTEw8bHxsjIx8nJycnKycnKy8rJy8vMzMzMzMzN0M3Lzc3O0c7Ozc/Ozs/Pzs/Pz9DR0dLS0tPU1NXQ0dXW1dXX19bW1tbX2NfU1tfX2NnX1dnZ2Nna2dvFmdva2d3d3d7Prd7d297e3d7e3t7f3t/e3OCqMuDg4OG1V+Li4eS2UOTe1uTk5OXl5efm5+fn5+fo5+jo6Ojp6enr7eqwLurr6uvZuevl3uvo5uvr6uy9U+zt6+zt7O3t7O3t7e7t7e+8S+/s7O/v7vDw8PDx8PDx8vHarfHs6fHw8fHx8fHy8fHy8vLq5vLy8/Lz8/PaqvPy8vPy8vPz8vTapfTlwvTz8/T09PWafvXer/XnzfX19fbhs/bx7vb29vb29/eiiPffq/fz8Pf39vf39/f49/f4+PjBQ/j39vj5+fm4Jfm5JPm5K/m5LPm8Mfm9MPnCRvn08fn49/n5+fn5+vn6+/q8L/q8L/rCQ/rCRvrDSPrER/rVifrfqfrpw/rt1vr4+Pr6+vr7+vvd0/vepPvfqfvgq/vhrfvn4fvo4fvq0Pvx2Pv27/v28/v48fv7+/v7/Pv8/fzCPPzbjvzip/ziq/zkrvzkrvzlsPznvvzowvzowvzq5PzrxPzt0vz06vz06/z18vz29Pz38vz39Pz39fz6+vz8/Pz9/Pz+//3EQf3Wh/3mtv3ow/3rwf3sxv3s0v3tx/3t0P3w1f3y4P324/348f349f359P369/38+v38+/39/f39/v3+/v7EQf7Xhv7Xh/7hqP7nvP7qv/724/749f749v759v78+/78/P79+/79/P79/f79/f79//7+/P7+/f7+/v7//v7////7+f/+/f/+/v/+/////f///v///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBwAAACwAAAAAKAASAIcA/wCTlJOZmpuampmdnZ2en6ChoqGjo6GmpqWop6eoqaeoqampqairqqirq6msrKqsrK2sra2trKmurqyvr66vsLCwsbGxsbCzs7Kzs7O0tLS5uru7vL28vLy9vby+vr7AwL/AwMPAwcHCw8LDwsHExMPGx8bIyMfJycnJysnJysvKycvLzMzMzMzMzdDNy83NztHOzs3Pzs7Pz87Pz8/Q0dHS0tLT1NTV0NHV1tXV19fW1tbW19jX1NbX19jZ19XZ2djZ2tnbxZnb2tnd3d3ez63e3dve3t3e3t7e397f3tzgqjLg4ODhtVfi4uHktlDk3tbk5OTl5eXn5ufn5+fn6Ofo6Ojo6enp6+3qsC7q6+rr2bnr5d7r6Obr6+rsvVPs7evs7ezt7ezt7e3u7e3vvEvv7Ozv7+7w8PDw8fDw8fLx2q3x7Onx8PHx8fHx8vHx8vLy6uby8vPy8/Pz2qrz8vLz8/L02qX05cL08/P09PT1mn713q/158319fX24bP28e729vb29vf3ooj336v38/D39/b39/f3+Pf3+Pj4wUP49/b4+fn5uCX5uST5uSv5uSz5vDH5vTD5wkb59PH5+Pf5+fn5+fr5+vv6vC/6wkP6wkb6w0j6xEf61Yn636n66cP67db6+Pj6+vr6+/r73dP73qT736n74Kv74a375+H76OH76tD78dj79u/79vP7+PH7+/v7+/z7/P38wjz824784qf84qv85K785bD857786ML86uT868T87dL89Or89Ov89fL89vT89/L89/T89/X8+vr8/Pz8/fz8/v/9xEH91of95rb96MP968H97Mb97NL97cf97dD98NX98uD99uP9+PH9+PX9+fT9+vf9/Pr9/Pv9/f39/f79/v7+xEH+14b+14f+4aj+57z+6r/+9uP++PX++Pb++fb+/Pv+/Pz+/fv+/fz+/f3+/f/+/vz+/v3+/v7+//7+////+/n//v3//v7//v////3///7///////////////////////8I/wD16YsnsKBBggYTKlw4sCBBblPawLFihowabg0ZNrRHKlasY6PiIRt1jCBCgrJwrIARoscLF7EyMow3L98YLWnCVGFkSAqYYzIJepOjEKFGffnmHTUqEGG3Y9yOleRGtWq8qtyuZsVKNZ5XgkoPej1KNqHJeEkVYmwhIoiMGyRUMNmRw4cVIEFspLiC5EYUDUFMlOjAY6DXtAblYZQx4gKDGiASbLBBgQAKAxkqWEjhIcKNBg0mSJDQQl+7w2HNHtVj6VBZffAMIz4I+5hHj4LOdIvp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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 1,6-react-testing-intro-2&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif?w=160 160w,
https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif?w=320 320w,
https://images.ctfassets.net/rpmifyuylbfw/19Wle6BDr1D3gmDCn4xp58/f455c27971c68957e912c0c2f86eb535/1_6-react-testing-intro-2.gif?w=640 640w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 앱은 &lt;a href=&quot;https://ooloo.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ooloo.io&lt;/a&gt;에 있는 내 강의에서 개발하는 앱의 간단한 버전이다. 만약 준비가 되지 않은 것 같다면, 전문적인 팀이 어떻게 실무에서 사용하는 앱을 개발 하는지 배울 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 앱은 사용자에게 서브레딧(subreddit)에서 가장 인기있는 포스트를 찾을 수 있도록 한다. 이것은 매우 간단한 앱으로 제목과 몇 개의 링크, 폼을 가지고 있을 뿐이다. 하지만 당신의 첫 번째 테스트를 위해서 좋은 예제가 될 것이다.&lt;/p&gt;
&lt;p&gt;헤더에 있는 다른 페이지를 가리키는 링크는 단지 헤드라인만 포함한 플레이스홀더 이상의 역할을 한다. 중요한 기능은 바로 다른 페이지로의 네비게이션이다.&lt;/p&gt;
&lt;p&gt;1개의 텍스트 입력을 가진 폼에서는 사용자가 검색할 서브레딧의 제목을 입력할 수 있다.&lt;/p&gt;
&lt;p&gt;제출(submit) 버튼을 클릭하면 Reddit API로 요청을 보낸다. 응답을 기다리는 동안 앱은 로딩 상태를 표시한다. 데이터는 응답을 받는 대로 표시되며, 단순함을 위해 상위 포스트의 개수만 표시한다.&lt;/p&gt;
&lt;p&gt;전체 코드는 &lt;a href=&quot;https://github.com/jkettmann/beginners-guide-to-testing-react&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;이 저장소&lt;/a&gt;에서 확인할 수 있다. 원한다면 저장소를 클론한 후에 이 글을 계속 읽어도 좋다.&lt;/p&gt;
&lt;h2 id=&quot;a-namewhat_should_we_test무엇을-테스트해야-하는가a&quot;&gt;&lt;a href=&quot;#a-namewhat_should_we_test%EB%AC%B4%EC%97%87%EC%9D%84-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;what_should_we_test&quot;&gt;무엇을 테스트해야 하는가?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;처음으로 떠오르는 질문은 “무엇을 테스트해야 하는가”일 것이다. 예제로 앱에 있는 폼 컴포넌트를 살펴보자. 코드는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; onSearch &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;subreddit&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setSubreddit&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;javascript&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onSubmit&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;onSearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subreddit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;FormContainer&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onSubmit&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onSubmit&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        r /
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Input&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;subreddit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;subreddit&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token attr-name&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setSubreddit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        Search
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;FormContainer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;폼의 입력값은 상태 변수(&lt;code class=&quot;language-text&quot;&gt;subreddit&lt;/code&gt;)와 계속 연동된다. 제출 버튼을 클릭하면 상위 컴포넌트에서 전달된 &lt;code class=&quot;language-text&quot;&gt;onSearch&lt;/code&gt; prop을 호출한다.&lt;/p&gt;
&lt;p&gt;어떻게 데이터를 가져오는지도 궁금할 것이다. 그것은 폼 컴포넌트의 부모인 홈 페이지 컴포넌트에서 이뤄진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Home&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;posts&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setPosts&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setStatus&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;idle&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; onSearch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subreddit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;loading&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`https://www.reddit.com/r/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;subreddit&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/top.json`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;resolved&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Headline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          Find the best time for a subreddit
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Headline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onSearch&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onSearch&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        status &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;loading&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            Is loading
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        status &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;resolved&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
          &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TopPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            Number of top posts: &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;posts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;TopPosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;홈 페이지 컴포넌트는 API 응답 데이터를 상태 변수에 저장하며 로딩 상황을 계속 추적한다. 폼에 의해 검색이 실행되면 Reddit API로 요청이 전달된다. 데이터가 도착하면 상태 변수들(&lt;code class=&quot;language-text&quot;&gt;posts&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;status&lt;/code&gt;)이 모두 업데이트되며 결과가 폼 아래에 표시된다.&lt;/p&gt;
&lt;p&gt;이제 당신은 코드에서 중요한 부분을 살펴보았고 더 진행하기 전에 &lt;strong&gt;스스로 다음의 질문의 답해 보기 바란다: 당신은 저 두 컴포넌트를 어떻게 테스트하겠는가?&lt;/strong&gt;  &lt;/p&gt;
&lt;p&gt;우리는 아마도 위의 컴포넌트를 살펴보고선 유닛 테스트를 먼저 작성하려 들 것이다. 우리는 아마 상태가 제대로 저장되는지 확인하고 현재의 &lt;code class=&quot;language-text&quot;&gt;subreddit&lt;/code&gt; 값과 함께 &lt;code class=&quot;language-text&quot;&gt;onSearch&lt;/code&gt; prop이 호출되는지 확인하길 원할 것이다. 이것이 많은 개발자들이 Enzyme으로 테스트하는 방식이다.&lt;/p&gt;
&lt;p&gt;하지만 Testing Library를 사용한다면 상태 변수에 접근하지 않는다. prop으로 전달된 함수를 테스트하긴 할 것이지만, 상태 변수가 정확한 값을 가지고 있는지는 테스트하지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이것은 약점이 아니다. 이것은 강점이다&lt;/strong&gt;. 상태 관리는 컴포넌트의 구현 세부사항(implementation detail)이다. 상태 변수가 지금은 컴포넌트에 있지만 부모로 옮겨질 수도 있고 그렇게 해도 앱은 똑같이 동작해야 한다&lt;sup id=&quot;fnref-4&quot;&gt;&lt;a href=&quot;#fn-4&quot; class=&quot;footnote-ref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;사실 &lt;a href=&quot;https://kentcdodds.com/blog/react-is-an-implementation-detail&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React 자체도 구현 세부사항이다&lt;/a&gt;. 우리는 앱 전체를 유저가 모르는 사이에 Vue.js로 옮길 수도 있다.&lt;/p&gt;
&lt;p&gt;앱의 코드와 그것의 구현 방식에 집중하는 대신, &lt;strong&gt;우리는 단순히 유저의 관점을 취한다. 그리고 이 관점은 우리가 앱의 중요한 부분을 테스트하는데 집중하도록 강제로 이끈다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;우리가 이 테스팅 철학을 받아들인다면 테스팅은 더 이상 알기 어렵고 무서운 것이 아니게 된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;노트:  사용자는 어플리케이션의 말단 사용자일 수도 있고 당신이 개발한 컴포넌트를 사용하는 개발자가 될 수도 있다. 당신이 소속된 팀의 다른 개발자들도 사용하는 이미지 갤러리를 개발했다고 가정해보자. 예를 들어 당신은 props가 변경되었을 때 앱이 제대로 동작하는지 테스트해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;좋다, 사용자 관점이다. 그러니 어플리케이션에서 저 컴포넌트에 대해서는 잠시 잊고 사용자가 직면하는 부분에 대해 집중해 보자. 사용자 입장에서 앱이 제대로 기능하기 위해서는 무엇이 중요할까?&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2OdigjN2lIvB2d46NGUi85/6f4c8cf02bafdae47e973dfe02c05939/2-react-testing-intro-form.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 640px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 46.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,R0lGODlhKAASAPcAMQD/AJ2dnaGioaenpqinpqqpqKqpqaqqqKuqqK2rqa2sqq2tra6tqq+vrq+wsLGysrKxsLOzsrW1tLq7vLq7vbq8vby8vL29vcC/vsDAv8DAwcDAxMHAv8PCwcPDwsTEw8bGxsnJyMnJycnKysrJy8zMz8zNzc3Mzs3O0c7MzM/Ozc/PztDR0dHR0dHS0dLS0tPS0dTU1NTV1dXQ0dXW1tfU1tfW1tfX1tfY2NnY1tnZ2NnZ2dva2d3d3N7c2t7c3N7d297e3d7e3t/PqeDg3+Dh4eLi4uPf1ePl6eSvN+S5WOTg1uTk5OXk5Oa6Vubdx+bm5ubm5+fm5+fn5+fp7ejn6Ojo6Ojp6erf3Orp6Orr6+u8Ueviz+vp6Ovr6uvs6+zq6ezt7O3t7e7t7O/r6u/s7O/t7e/v8PDw8PDx8vHkx/Hw8fHy8fHy8/H1/vK2KvLiwvLkzfLy8vLy8/O1KPPr2PPu6/Py8vPz9PSZfvSaf/TlxPTz8vT09PT19fXkwvX19fbx7vb39vflvPfy7/f39vf39/f3+Pi4Jfi4Lfi4Lvi5Jfj29vj5+fmki/m6Jvm8MPm9MfnCRPnCRfnESPn08fn08vn4+Pn5+frCQfrfqvrgrPrs1/r08fr6+vr6+/vWh/vbj/vd0vvfpPvkrvvo4vvq0Pvx2vv18Pv18/v28/v48fv7+vv7+/v7/Pv8+/v///zcjvziqPzirPzkrvzlsfzowPzow/zoxPzqxPzrxPzt0vzv0/zx7fz06/z18vz28vz28/z29Pz38/z39Pz49fz58/z6+Pz6+fz7+/z8/Pz8/vz9/vz9//3ipf3lrv3owP3rwf3sxf3sxf3tyP3x1v328v338v339P339f339v348f349f379v37+v38+/39/P39/f39/v3+/v3///7dkv7sw/7ux/7z3v724/759v78+v78+/79+/79/P79/f7+/P7+/f7+/v7+//7//v7////jqv/z3v/34v/59v/69//9+//+/v///f///v///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBwAAACwAAAAAKAASAIcA/wCdnZ2hoqGnp6aop6aqqaiqqamqqqirqqitq6mtrKqtra2uraqvr66vsLCxsrKysbCzs7K1tbS6u7y6u726vL28vLy9vb3Av77AwL/AwMHAwMTBwL/DwsHDw8LExMPGxsbJycjJycnJysrKycvMzM/Mzc3NzM7NztHOzMzPzs3Pz87Q0dHR0dHR0tHS0tLT0tHU1NTU1dXV0NHV1tbX1NbX1tbX19bX2NjZ2NbZ2djZ2dnb2tnd3dze3Nre3Nze3dve3t3e3t7fz6ng4N/g4eHi4uLj39Xj5enkrzfkuVjk4Nbk5OTl5OTmulbm3cfm5ubm5ufn5ufn5+fn6e3o5+jo6Ojo6enq39zq6ejq6+vrvFHr4s/r6ejr6+rr7Ovs6uns7ezt7e3u7ezv6+rv7Ozv7e3v7/Dw8PDw8fLx5Mfx8PHx8vHx8vPx9f7ytiry4sLy5M3y8vLy8vPztSjz69jz7uvz8vLz8/T0mX70mn/05cT08/L09PT09fX15ML19fX28e729/b35bz38u/39/b39/f39/j4uCX4uC34uC74uSX49vb4+fn5pIv5uib5vDD5vTH5wkT5wkX5xEj59PH59PL5+Pj5+fn6wkH636r64Kz67Nf69PH6+vr6+vv71of724/73dL736T75K776OL76tD78dr79fD79fP79vP7+PH7+/r7+/v7+/z7/Pv7///83I784qj84qz85K785bH86MD86MP86MT86sT868T87dL879P88e389Ov89fL89vL89vP89vT89/P89/T8+PX8+fP8+vj8+vn8+/v8/Pz8/P78/f78/f/94qX95a796MD968H97MX97cj98db99vL99/L99/T99/X99/b9+PH9+PX9+/b9+/r9/Pv9/fz9/f39/f79/v79///+3ZL+7MP+7sf+897+9uP++fb+/Pr+/Pv+/fv+/fz+/f3+/vz+/v3+/v7+/v/+//7+////46r/897/9+L/+fb/+vf//fv//v7///3///7///////8I/wD9+YsnsKBBggYTKlw4sCBBcFLWtKlSxkwacA0Z+uvnbx+rVq2UeYr3zZMygggJtppBAsWGGidKtMrIMJ68fmO0oAkzpVEhKF+U0SQobo5ChBo3JqUpEGE4ZeCUnQRHtWq8quCuZsVKNZ5XgvMSfl1K1uHAeP3oKcSYQsOOFTI6jCBig8YNKzp2vBBxRUiMJhJ2gPhgAcdZtGoNzsOowgMEAy4yEKgAo0GAEAIiOHgw4sKCGAgOKEjAwIQ/d17TLkS6sA8mQ2XhnVUt1h88Za0+tfIE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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 2-react-testing-intro-form&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2OdigjN2lIvB2d46NGUi85/6f4c8cf02bafdae47e973dfe02c05939/2-react-testing-intro-form.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2OdigjN2lIvB2d46NGUi85/6f4c8cf02bafdae47e973dfe02c05939/2-react-testing-intro-form.gif?w=160 160w,
https://images.ctfassets.net/rpmifyuylbfw/2OdigjN2lIvB2d46NGUi85/6f4c8cf02bafdae47e973dfe02c05939/2-react-testing-intro-form.gif?w=320 320w,
https://images.ctfassets.net/rpmifyuylbfw/2OdigjN2lIvB2d46NGUi85/6f4c8cf02bafdae47e973dfe02c05939/2-react-testing-intro-form.gif?w=640 640w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;일단 테스트할 앱을 클릭해서 사용해 보자. 앱 테스트에서 중요한 것은 그렇게 직접 사용해 본 내용들이다. 그에 대해서는 이미 위에서 설명했었다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;사용자는 폼 입력에 값을 입력하고 제출 버튼을 누른다.&lt;/li&gt;
&lt;li&gt;데이터를 기다리는 동안 로딩 메시지가 표시된다.&lt;/li&gt;
&lt;li&gt;API 응답이 도착하면 데이터가 렌더링된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;사용자는 홈 페이지나 폼 컴포넌트가 입력 값을 저장하는지 아닌지는 신경쓰지 않는다. 사용자 입장에서 응답으로 받은 포스트 데이터가 상태 변수에 저장이 되는지, 데이터 구조가 어떤지는 상관할 바가 아니다. 유저에게 중요한 것은 오직 위의 세 단계다.&lt;/p&gt;
&lt;p&gt;물론, 우리는 헤더에 있는 링크도 테스트해야 한다. 어찌됐든 깨진 링크(예. 회원 가입 페이지로 가는 링크)는 비지니스에 문제를 줄 수 있기 때문이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;노트: 보통 우리는 엣지 케이스를 테스트해야 하고 폼에서 발생하는 에러도 처리해야 한다. 하지만 그것까지 이 블로그 포스트에서 다루기에는 너무 양이 많다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;a-namewriting_the_tests테스트-작성a&quot;&gt;&lt;a href=&quot;#a-namewriting_the_tests%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%91%EC%84%B1a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;writing_the_tests&quot;&gt;테스트 작성&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;지난 섹션의 내용을 빨리 훑어보고 알게 된 것들을 기술적인 언어로 풀어 보자:&lt;/p&gt;
&lt;p&gt;우리는 2개의 테스트 묶음(suite)를 작성할 것이다. 하나는 헤더 링크를 위한 것이며 나머지 하나는 폼을 위한 것이다. 헤더를 위해서는 링크가 정확한 대상을 가리키는지 테스트해야 한다. 그리고 폼을 위해서는 입력 값 변경, 값 제출, 로딩 상태, 그리고 결과 렌더링을 테스트해야 한다.&lt;/p&gt;
&lt;p&gt;헤더를 위한 테스트부터 시작해 보자. 먼저, &lt;code class=&quot;language-text&quot;&gt;src/app.js&lt;/code&gt; 를 열어서 작성되어 있는 테스트를 제거한다. 그리고, 헤더 테스트 묶음을 Jest의 &lt;code class=&quot;language-text&quot;&gt;describe&lt;/code&gt; 함수를 사용해서 정의하자.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;노트: 테스트를 꼭 &lt;code class=&quot;language-text&quot;&gt;describe&lt;/code&gt;로 감쌀 필요는 없지만, 이 방법은 관련 있는 테스트를 한곳에 모아 준다. 그리고 터미널 출력을 보다 읽기 쉽게 만들어 주며 반복적인 테스트 메시지 출력을 막아 준다. 만약 테스트 파일이 길어지면 에디터에서 필요없는 테스트 코드 블록을 접어서 가독성을 높일 수도 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Header&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;테스트 케이스는 &lt;code class=&quot;language-text&quot;&gt;test(...)&lt;/code&gt; 로 정의될 수 있다. 그리고 &lt;code class=&quot;language-text&quot;&gt;it(...)&lt;/code&gt; 으로 대체할 수도 있다. Jest는 둘 다 제공한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Header&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&quot;How it works&quot; link points to the correct page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우리는 헤더 컴포넌트를 분리된 상태가 아닌 어플리케이션 컨텍스트 안에서 테스트하고 싶다. 그것이 우리가 이 테스트에서 App 컴포넌트를 사용해야 하는 이유다.&lt;/p&gt;
&lt;p&gt;App 컴포넌트는 아래와 같이 작성되어 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Switch&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Route &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-router-dom&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; GlobalStyle &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./GlobalStyle&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Header &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./components/Header&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Home &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./pages/Home&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;GlobalStyle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Header&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Switch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Route&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/how-it-works&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;How it works&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Route&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Route&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/about&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;About&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Route&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Route&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Home&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Route&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Switch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 App 컴포넌트는 실제로 운영되고 있는 많은 어플리케이션처럼  React Router를 사용한다. 그리고 헤더 컴포넌트와 홈 페이지를 포함한 몇 개의 라우트를 렌더링한다.&lt;/p&gt;
&lt;p&gt;참고로 여기에는 Router 컴포넌트가 없다. 테스트를 위해 Router는 App 컴포넌트를 렌더링하는 &lt;code class=&quot;language-text&quot;&gt;index.js&lt;/code&gt; 파일에 있다. &lt;a href=&quot;https://reacttraining.com/react-router/web/guides/testing&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;테스트 중에는 App 컴포넌트를 MemoryRouter로 감쌀 것이다&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;그래서 첫 번째 단계로, 우리는 앱 컴포넌트를 렌더링한다. Testing Library는 주어진 컴포넌트로 DOM을 생성하는  &lt;code class=&quot;language-text&quot;&gt;render&lt;/code&gt; 함수를 제공한다&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; App &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./App&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Header&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&quot;How it works&quot; link points to the correct page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 앱은 create-react-app으로 만들어졌기 때문에 Testing Library 사용에 필요한 모든 것은 이미 설치되어 있다.&lt;/p&gt;
&lt;h2 id=&quot;a-namedon_t_take_a_stab_in_the_dark어둠-속에서-찔러보지-말라a&quot;&gt;&lt;a href=&quot;#a-namedon_t_take_a_stab_in_the_dark%EC%96%B4%EB%91%A0-%EC%86%8D%EC%97%90%EC%84%9C-%EC%B0%94%EB%9F%AC%EB%B3%B4%EC%A7%80-%EB%A7%90%EB%9D%BCa&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;don_t_take_a_stab_in_the_dark&quot;&gt;어둠 속에서 찔러보지 말라&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;테스트를 작성하기 시작하면 당신은 마치 어두운 상자 안을 걷는 듯한 느낌을 받을 수 있다. 안에서 어떤 일이 일어나고 있는지 알 수가 없다. 그리고 당신은 브라우저에서 개발자 도구를 열어 DOM 트리를 직접 확인하며 작업하는 일이 익숙해져 있다.&lt;/p&gt;
&lt;p&gt;테스트를 작성하기 시작하면 새로운 환경에 적응할 필요가 있다. 테스트 과정에서 무엇이 일어나고 있는지 이해할 방법이 필요하다. 만약 테스트가 어떤 요소를 찾을 수 없어서 실패를 했는데, 원인을 파악할 수 없다면 어떻게 할 것인가?&lt;/p&gt;
&lt;p&gt;바로 그런 상황에서 &lt;code class=&quot;language-text&quot;&gt;debug&lt;/code&gt; 함수로 무척 편리하게 대처할 수 있다. 그것을 사용하면 원하는 실행 지점에서 DOM 트리를 출력할 수 있다. 브라우저의 개발 도구처럼 편리하고 상호작용적이지는 않지만 당신에게 무슨 일이 일어나고 있는지 확실히 파악할 수 있도록 도와준다.&lt;/p&gt;
&lt;p&gt;당신은 이제 막 시행착오에 기대지 않는 테스트를 작성하기 시작했다. 그러므로 시간이 조금 걸리겠지만 각각의 단계마다 &lt;code class=&quot;language-text&quot;&gt;debug&lt;/code&gt; 함수를 사용하자. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; screen &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Header&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&quot;How it works&quot; link points to the correct page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;yarn test&lt;/code&gt; 명령어로 테스트를 실행하면 아래의 결과를 보게 될 것이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1UOuAcPZHp0Mw2939y3R4r/5b2d4757aa08ee3b3b329d8fffc8909a/3-debug-app.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 599px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 128.54757929883138%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAzCAIAAABqjd1iAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAABEtJREFUWMPFWNuSozYQna/IJkbiLkCAuBuBsbnYnp2dzWNSeUvlLa+pyttUzcfvEaQmt9lNZcfGp2Qs2g1NN60+Ld/puv7u5+O3Px7e/XKmUjDGXNc99L0Qoml3AGPetirSgD9L+fz4+DxNie/pb8YdPt/8+hD+/hP77Qfx0HPOXZeN4yDrOsvzoijCMKrKQjDvaVs9te2TlLHHLmMYyPN8K2vZtDzwDcNgLsPRge8uc2wFzGzLwnEWupZlLUIILNNy/pSbzgwo+DMw/5JhgCplC/4h2J7nIeY8CHw/sEwTNoIAkXBjIZbbQQIVSKCDOSSe+nhKJ46jKIIwUoih8CXDxgzTNK15GMaLQM2tGZjgdsv8RQ7BMsd1aqa+7BcF6C9qy71wRGj+ZhivVqSZesIkwVOXZQmPE5Hg6YVI0jSFQiIU4tkjdZqk0FwkURzzMMyzLETE4CXuNityhSCO1YWBHwBJIv4ZapJLrTtrh/fUC/Tr4+6vJzQpSX0gVYvQr2pY2Y4LrZu08SOe4Krm7/4tSlP68KCdz1pcpFodk8TXdsnmXJLUx3EzlZv7ikbs0h4Hkb4KXvGYBvCyuIFhQEoyTqQ/+FbKKXdX8zi6TagX22S7v2rA7z73A49N2Tv7PSm2guScBo62EyQLaOzhVGsEMpwy+9Ie+6Ft655HfZ+apq7bphqORd15MFsN19Yt89IeGyaIKcuLNBEotKjVa71jh1lJnnSTqCWYEdV/FcO2S6OUikINn69YQPyQyIGGyeqVywtI1ZG8XpskFqepyJg7mzYNlb2Gym21fmbJnN62SvWLr+M0o21LHj9olRRkG4OOtH2KoRbxIdMOqSbFW6rpZw1nBR1H0nXMERy0CEtE+IoixwLVg1ShisQ1SuYra3udkpkV1cP9SWRZlWdN22FXcZrGVUii7mkobsFOaXkDWgREQoeRvL/XqhpZHZHY2wzZZipIwbUu3RwL5DZJvMsbLrcEWT0MzEnDzZhv+gy0+AcPmsYVQ32bDkRldZ5P0zQMvf0G0v0aj2ku7SRX26/rVOxXSYIjpYnsKY/WCzVlPnZvpBm0/T2NM9001zIcpmS7Ax+TbEtdb0WPQcZFQ5qRBuHqjQDz7aoMS2ykqeMz6jngYzAgmJF6Ng0ZJkR4Sn7hrDYMzmktya4lUQgqDGCVVBHJ56a6jLQa7X70lqb6v7YwyCzDXLWAwLbWHs2mVe/75Q8CVMql18FkGdYVWh+QRNctJBGrOMcemhBwA3YxoIrNqZx7EnZ5j29Wq9FoavvzRYjo/xkGooj2vYp208SkDLGEwI/fPUpFyYdMUfI+RZ97cY+jGzT0C5Ik3e3avu/rphn7Qy3l6XRMhNh13el8H0bROA48DIs8G8bxeJyGcfrw8XtZlW99x2ojI3vddtZOLkUVzUA9f+1Qc643DRl6ssVSzjgNXa2ONYllHaJQa63YHMvNIf262vkJfzMQrn+zQdAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 3-debug-app&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1UOuAcPZHp0Mw2939y3R4r/5b2d4757aa08ee3b3b329d8fffc8909a/3-debug-app.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1UOuAcPZHp0Mw2939y3R4r/5b2d4757aa08ee3b3b329d8fffc8909a/3-debug-app.png?w=150 150w,
https://images.ctfassets.net/rpmifyuylbfw/1UOuAcPZHp0Mw2939y3R4r/5b2d4757aa08ee3b3b329d8fffc8909a/3-debug-app.png?w=300 300w,
https://images.ctfassets.net/rpmifyuylbfw/1UOuAcPZHp0Mw2939y3R4r/5b2d4757aa08ee3b3b329d8fffc8909a/3-debug-app.png?w=599 599w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;훌륭하다. 출력에서 몇개의 링크를 포함하는 헤더를 확인할 수 있고, 그것은 우리가 테스트하고 싶은 “How it works” 링크를 포함하고 있다. 이제 우리는  저것에 접근할 수 있는 방법과 조작 방법을 알아야 한다.&lt;/p&gt;
&lt;h2 id=&quot;a-namehow_to_access_the_rendered_dom_tree렌더링된-dom-트리에-접근하는-방법a&quot;&gt;&lt;a href=&quot;#a-namehow_to_access_the_rendered_dom_tree%EB%A0%8C%EB%8D%94%EB%A7%81%EB%90%9C-dom-%ED%8A%B8%EB%A6%AC%EC%97%90-%EC%A0%91%EA%B7%BC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;how_to_access_the_rendered_dom_tree&quot;&gt;렌더링된 DOM 트리에 접근하는 방법&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;렌더링된 요소 접근에 &lt;a href=&quot;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library/#not-using-screen&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;선호되는 방법&lt;/a&gt;은 Testing Library에서 export된 screen 객체를 사용 하는 것이다.&lt;/p&gt;
&lt;p&gt;screen 객체는 다양한 쿼리를 제공한다. 그 쿼리들은 DOM에 접근할 수 있는 함수들이다. 아래에 몇개의 예제가 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;getBy*&lt;/strong&gt; 쿼리 (ex. &lt;code class=&quot;language-text&quot;&gt;getByTestId&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;getByText&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;getByRole&lt;/code&gt;): 이 함수들은 &lt;strong&gt;동기적&lt;/strong&gt;(synchronous)이며 그 요소가 현재 DOM 안에 있는지 확인한다. 그렇지 않으면 에러를 발생시킨다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;findBy*&lt;/strong&gt; 쿼리 (ex. &lt;code class=&quot;language-text&quot;&gt;findByText&lt;/code&gt;): 이 함수들은 &lt;strong&gt;비동기&lt;/strong&gt;적(asynchronous)이다. 그 요소를 찾을 때까지 일정 시간(기본 5초)을 기다린다. 만약 그 시간이 지난 후에도 요소를 찾을 수 없으면 에러를 발생시킨다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;queryBy*&lt;/strong&gt; 쿼리: 이 함수들은 getBy* 처럼 동기적이다. 하지만 요소를 찾을 수 없어도 에러를 발생시키지 않는다. 단지 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 값을 리턴한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위에서 제시한 쿼리만 해도 요소에 접근할 수 있는 많은 방법을 포함하고 있고, 심지어 Testing Library가 제공하는 함수의 &lt;a href=&quot;https://testing-library.com/docs/dom-testing-library/api-queries&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;전체 목록&lt;/a&gt;도 아니다. 그렇다면 이것들 중에서 “How it works” 링크에 접근하기 위해서는 무엇을 사용해야 할까?&lt;/p&gt;
&lt;p&gt;우리는 헤더가 항상 존재한다는 사실을 이미 알고 있다. 표시될 때까지 기다릴 필요가 없다. 이는 우리의 옵션을 &lt;code class=&quot;language-text&quot;&gt;getBy*&lt;/code&gt; 쿼리로 좁힌다. 하지만 그 중에서 무엇을 골라야 할까?&lt;/p&gt;
&lt;p&gt;처음 보기에는 &lt;code class=&quot;language-text&quot;&gt;getByTestId&lt;/code&gt;가 좋은 선택처럼 보인다. 우리가 해야 할 일은 확인하고 싶은 요소에 테스트 ID를 붙이는 일 뿐이다:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-testid&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;some-content&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  Some content
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 우리는 &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; 요소에 &lt;code class=&quot;language-text&quot;&gt;getByTestId(&amp;#39;some-content&amp;#39;)&lt;/code&gt;를 사용해서 접근할 수 있다. 간단하다, 그렇지 않은가?&lt;/p&gt;
&lt;p&gt;하지만 이 방법은 단지 테스트에 통과하기 위해 코드를 수정해야 한다는 말이다. 그다지 이상적이지 않다. 그러니 더 좋은 방법은 없을까?&lt;/p&gt;
&lt;p&gt;Testing Library의 문서는 훌륭하며 읽어 볼 가치가 있다. 사실 거기에는 &lt;a href=&quot;https://testing-library.com/docs/guide-which-query&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;어떤 쿼리를 사용하는 것이 적합한지를 설명하는 문서&lt;/a&gt;가 있다.&lt;/p&gt;
&lt;p&gt;모든 사람에게 접근 가능한 쿼리는 높은 우선 순위를 가진다. 그 중에서, &lt;code class=&quot;language-text&quot;&gt;getByRole&lt;/code&gt;이 가장 알맞은 쿼리가 되어야 한다. &lt;code class=&quot;language-text&quot;&gt;getByAltText&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;getByTestId&lt;/code&gt;는 오직 예외 상황에서만 사용되어야 한다. 그리고 가장 낮은 우선순위는 &lt;code class=&quot;language-text&quot;&gt;getByTestId&lt;/code&gt;다. &lt;strong&gt;당신은 반드시 다른 선택의 여지가 없는 상황에서만 테스트 ID를 사용해야 한다&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;좋다, 그럼 &lt;code class=&quot;language-text&quot;&gt;getByRole&lt;/code&gt;을 한번 사용해 보자. 첫번째 파라미터는 대상 요소의 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#Roles&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ARIA role&lt;/a&gt;이어야 한다. 여기서는 &lt;code class=&quot;language-text&quot;&gt;link&lt;/code&gt;를 사용할 수 있다. 한 페이지에는 한 개 이상의 링크가 있기 때문에 요소를 찾기 위해&lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; 옵션을 추가할 필요가 있다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; link &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;link&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/how it works/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여기서 문자열 &lt;code class=&quot;language-text&quot;&gt;&amp;#39;How it works&amp;#39;&lt;/code&gt; 대신 정규표현식 &lt;code class=&quot;language-text&quot;&gt;/how it works/i&lt;/code&gt;를 사용했음을 확인하자. 이 방법으로 대소문자가 달라서 요소를 찾지 못하는 경우를 방지할 수 있다(예. CSS &lt;code class=&quot;language-text&quot;&gt;text-transformation&lt;/code&gt; 속성을 사용해서 대소문자가 바뀌는 경우). 그리고 정규표현식을 사용하면 문자열의 일부를 타겟으로 삼을 수도 있다. &lt;code class=&quot;language-text&quot;&gt;/how it/i&lt;/code&gt; 정규표현식으로 검색에 성공하겠지만, &lt;code class=&quot;language-text&quot;&gt;&amp;#39;How it&amp;#39;&lt;/code&gt; 으로는 실패할 것이다.&lt;/p&gt;
&lt;p&gt;파일을 저장하면 테스트가 자동으로 실행된 후 성공했다는 메시지가 뜰 것이다. 이는 우리가 그 링크를 찾았다는 의미다!&lt;/p&gt;
&lt;p&gt;우리가 이제 막 시작했기 때문에 모든 것이 예상했던 대로 작동하는지 한번 더 확인하는 편이 좋다. &lt;code class=&quot;language-text&quot;&gt;debug&lt;/code&gt; 함수를 기억하는가? 많은 개발자는 그것에 파라미터를 넣어도 되는지 모른다. 파라미터에 옵션을 전달해서 콘솔에 1개의 요소만 출력할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; link &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;link&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/how it works/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;link&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래의 내용을 터미널에서 보게 될 것이다. 출력된 것은 “How it works” 링크. 정확히 우리가 예상했던 대로다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3pdtR4QIFCLReK7l9Oa2iX/85f32248b7f4759eec24b6bf7101cea5/4-debug-how-it-works-link.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 599px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 32.220367278797994%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAANCAMAAADsQdzaAAABg1BMVEUeHh4XXEEYVD0XXkMdNi0lJSQkJCQ8PD0tLS03ODk5OTklJSY0MzIvKCh6NzZ6SUl6QEAuJiYWaUknclUjcFMrRj0pKSklJSVCQkE4ODcyMjM0NDQqKyw2NTN+NjZ+PTx+OTktJiU0NDU4OTg5OjsnJiUeHx8fHx8iIiIjIyMhISIiIyMhISAiIiMhISEjIyQjIiIgICAdJCIsLS0mJSUnJyclJiYrKiopKSopKiovLy8oKCkoKCgnJyYkJCUoKCckJSQuLi4tLSwrKysoKSoqKikfHx4qKistLCw1NDQwLy8uLSwuLzAxMTEqKiovLy4uLy8uLS0rKyowLzArKywzMjIsLCwtLS4sKysuLi0uLi8pKCkxMC8cLzUdKCowMR1HRxtFRB0rMi4bOS4cMSkbPTAaQTIcLigbPjEaPzAaQzMdJSI5OxxCQRw+PyscMCgaQjIbNisbNiwcLicbPDAbOy4cMykdKSw/P0BBQEBDRUY+Pj4dKzAcNTsdJSYeICAeICEIKh/9AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAANxJREFUKM9jZGBEB+8ZsAEWBkEkNV94cSpkZGBQAil5oMjI+J+JkfEP6x9WkCXfGRg5vjNwPmMQvAFXyGD0jRts3rffAmdMwcb+/PRWlQUo8kyakXE3RCEzED8X/sVy9ulT7mvCjE9FPn77c07o7+WvXMJP/r5jOM16Sv2t+lu9lyAT3T/z/bpq9FYEbOprMSBxxOLTcR8w94XoARcGxt1uS8CmxsYxEAYgNzLEI4fOVAYcwQMEvAhlXfhMLWcg1uoKxntMiowTv+BTCLK6hpFxJUETmUBhydxE2GoAkQ49c06vfvEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 4-debug-how-it-works-link&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3pdtR4QIFCLReK7l9Oa2iX/85f32248b7f4759eec24b6bf7101cea5/4-debug-how-it-works-link.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3pdtR4QIFCLReK7l9Oa2iX/85f32248b7f4759eec24b6bf7101cea5/4-debug-how-it-works-link.png?w=150 150w,
https://images.ctfassets.net/rpmifyuylbfw/3pdtR4QIFCLReK7l9Oa2iX/85f32248b7f4759eec24b6bf7101cea5/4-debug-how-it-works-link.png?w=300 300w,
https://images.ctfassets.net/rpmifyuylbfw/3pdtR4QIFCLReK7l9Oa2iX/85f32248b7f4759eec24b6bf7101cea5/4-debug-how-it-works-link.png?w=599 599w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;h2 id=&quot;a-nameinteracting_with_dom_elementsdom-요소와-상호작용-하기a&quot;&gt;&lt;a href=&quot;#a-nameinteracting_with_dom_elementsdom-%EC%9A%94%EC%86%8C%EC%99%80-%EC%83%81%ED%98%B8%EC%9E%91%EC%9A%A9-%ED%95%98%EA%B8%B0a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;interacting_with_dom_elements&quot;&gt;DOM 요소와 상호작용 하기&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;이제 우리는 DOM 요소에 접근하는 방법을 알게 되었다. 구체적으로는 “How it works” 링크다. 하지만 그것으로는 충분하지 않다. 우리가 테스트하고 싶었던 것을 기억하는가?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;링크는 정확한 페이지를 가리켜야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;링크가 우리를 타겟으로 이끌기 위해서는 일단 그것을 클릭해야 한다. Testing Library에는 두 가지 옵션이 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@testing-library/react&lt;/code&gt; 모듈이 export하는 &lt;code class=&quot;language-text&quot;&gt;fireEvent.click&lt;/code&gt; 함수를 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@testing-library/user-event&lt;/code&gt; 모듈이 export하는 &lt;code class=&quot;language-text&quot;&gt;click&lt;/code&gt; 함수를 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;가능하다면 &lt;code class=&quot;language-text&quot;&gt;@testing-library/user-event&lt;/code&gt;를 사용하는 것이 &lt;a href=&quot;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library/#not-using-testing-libraryuser-event&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;권장된다&lt;/a&gt;.  그것은 실제 유저 이벤트에 가까운 더 많은 이벤트(예. 더블클릭)를 제공한다.&lt;/p&gt;
&lt;p&gt;그리고 놀라운 사실: package.json 파일을 살펴보면 create-react-app에는 이것이 기본적으로 설치 되어 있다.&lt;/p&gt;
&lt;p&gt;그러면 링크를 클릭 해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; screen &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; userEvent &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@testing-library/user-event&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; App &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./App&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Header&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&quot;How it works&quot; link points to the correct page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; link &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;link&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/how it works/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;link&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;a-nametest_if_the_correct_page_was_rendered올바른-페이지가-렌더링-되었는지-확인하기a&quot;&gt;&lt;a href=&quot;#a-nametest_if_the_correct_page_was_rendered%EC%98%AC%EB%B0%94%EB%A5%B8-%ED%8E%98%EC%9D%B4%EC%A7%80%EA%B0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%90%98%EC%97%88%EB%8A%94%EC%A7%80-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0a&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a name=&quot;test_if_the_correct_page_was_rendered&quot;&gt;올바른 페이지가 렌더링 되었는지 확인하기&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;다음은, 유저가 올바른 페이지로 보내졌는지 확인해야 한다.&lt;/p&gt;
&lt;p&gt;그걸 확인할 수 있는 방법 중 하나는 URL을 확인하는 것이다.  그리고 &lt;a href=&quot;https://reacttraining.com/react-router/web/guides/testing/checking-location-in-tests&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;react-router 문서에서 설명하고 있는 방법&lt;/a&gt;으로 가능하다. 하지만 유저는 사실 URL에는 신경을 안쓰지 않는가? 그리고 URL이 정확하다 해도 앱은 404 메시지를 표시할 가능성이 있다.&lt;/p&gt;
&lt;p&gt;유저가 확인하는 부분은 정확한 페이지를 표시하는지 여부다. 아래의 영상은 브라우저에서 링크를 클릭했을 때 화면이 어떻게 바뀌는지 보여준다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/77tY6PgTo5KrY895TYol6Z/7709facf2f076785e2d82d7fdc4ef39c/5-react-testing-intro-link.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 640px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 46.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,R0lGODlhKAASAPcAMQD/AJOTkpmam5ubmZ2enZ6foKKioaWjoqempaenp6eoqaiop6mpqaqpqaqqqKqrq6uqqKurqayrqaysqq2rqa2sqa2tq62tra6tqq6tq66urK6urq6vsK+vrbCwsLGwrrGxsLKysrOzsrW0tLW1tba2tre2tri4uLm5uLu7u7u8vby7u7y8u7y8vL69vcDAv8DAxMDBwcLCwcLCwsLDwsTDw8TExcXExMbGxcbGxsfGxsjIycnIx8nJyMnJysnJy8rIx8rIzMrKysvKysvM0MzLzszNzczO0s3NzM7MzM/Ozc/OztHR0dLS0dLS0tPU09PU1NTU1dXQ0dXV1dbW1tfU1tfX19fX2NjY19jY2NjZ2dnY2NnZ2dnZ2tvb29zd3N3d3d7d3d/e3t/f39/g3uDf4ODg3+Dg4ODg4eDh4ODh4eHh4eLi4uTj4+Tk4+Xk5Obm5ebm5ubm5+fm5+fn5ufn5+jn6ejo6Ojo6ejp6Ono6enq6erq6erq6urr6urr6+vq6+vr6+zs6+zs7Ozt7Ozt7e3t7O3u7e3u7u7t7u7u7u7v7u/r7O/s7e/u7u/u7+/w8PDv8PDw8PDw8fDw8vDx8PHw8PHw8fHy8/Ly8vLy8/Lz8vPy8vPy8/Pz8/Pz9PP09PT09PX09fX19fb19fb29vb29/f29vf39vf39/f3+Pf4+fj3+Pj4+Pj4+fj5+fm6JfnDQ/n4+Pn4+fn5+fn5+vq9MPrER/r5+fr5+vr6+vr6+/r7+/r7/Pvx2vv48fv6+vv6+/v7+/v7/Pzbj/ziqPzkrvzlsPz7/Pz8/Pz8/v3rwf3sxf3sxv3tx/3x1v369v38/P38/f39/f39/v3///714/79+/79/P79/f79/v7+/f7+/v7+///+/v/+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBwAAACwAAAAAKAASAIcA/wCTk5KZmpubm5mdnp2en6CioqGlo6KnpqWnp6enqKmoqKepqamqqamqqqiqq6urqqirq6msq6msrKqtq6mtrKmtrautra2uraqurauurqyurq6ur7Cvr62wsLCxsK6xsbCysrKzs7K1tLS1tbW2tra3tra4uLi5ubi7u7u7vL28u7u8vLu8vLy+vb3AwL/AwMTAwcHCwsHCwsLCw8LEw8PExMXFxMTGxsXGxsbHxsbIyMnJyMfJycjJycrJycvKyMfKyMzKysrLysrLzNDMy87Mzc3MztLNzczOzMzPzs3Pzs7R0dHS0tHS0tLT1NPT1NTU1NXV0NHV1dXW1tbX1NbX19fX19jY2NfY2NjY2dnZ2NjZ2dnZ2drb29vc3dzd3d3e3d3f3t7f39/f4N7g3+Dg4N/g4ODg4OHg4eDg4eHh4eHi4uLk4+Pk5OPl5OTm5uXm5ubm5ufn5ufn5+bn5+fo5+no6Ojo6Ono6ejp6Onp6unq6unq6urq6+rq6+vr6uvr6+vs7Ovs7Ozs7ezs7e3t7ezt7u3t7u7u7e7u7u7u7+7v6+zv7O3v7u7v7u/v8PDw7/Dw8PDw8PHw8PLw8fDx8PDx8PHx8vPy8vLy8vPy8/Lz8vLz8vPz8/Pz8/Tz9PT09PT19PX19fX29fX29vb29vf39vb39/b39/f39/j3+Pn49/j4+Pj4+Pn4+fn5uiX5w0P5+Pj5+Pn5+fn5+fr6vTD6xEf6+fn6+fr6+vr6+vv6+/v6+/z78dr7+PH7+vr7+vv7+/v7+/z824/84qj85K785bD8+/z8/Pz8/P7968H97MX97Mb97cf98db9+vb9/Pz9/P39/f39/f79///+9eP+/fv+/fz+/f3+/f7+/v3+/v7+/v///v7//v////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8I/wC5fRMIrqBBcAMHclvIsCG3bgcRfiuokOG3aXMmYbLDqBElaRUdNuzGDZgwYchyaYuWC5m2kOCGSQlyBEaVIkSEgRMpsps3Q4EqDarzClUcQcN2LhwIjpqmiDtJ8hwZcSJFhwmbJpOGLJm2aWDBSvs6TdrYstrMhp02daFUbVDjyo34si23aeCSxOCyJIqMH2ioTLFyZwsXJz7wiInixsSOGTdaXAGnzS5eJTRANGDyIoGKJh0I9DAgwkMIIS4uPIGwwEIFDEbAZbPLdG4oWanmHswWkue3bMiE6Qqm66SwYd+ocQtm/KSyX8aMHSv2DNw2u9zAtXrF6YsaMru8TU0jO16beW3YqvmyFesWLGLgrmEXyE1Ysvv48+tPpgyaM2bNLGONUnaRNNE3CCao4ILfeAPVQ/NFOBU2FGKzjVQSZqjhhhx26OGHIC4UEAAh+QQFBgAAACwDAAIABwAJAAAIEQB7ARhIsKDBgwgTKlx4MFNAACH5BAUHAAAALBoABAAGAAQAAAgcAAEk+8ZNGoCD074dbHMiRwodLKA4mKCBAoCAAAAh+QQFBwAAACwaAAQABgAEAAAIGQABANAGYJpAAODAHURhYwOPFE8gRMggMCAAIfkEBQYAAAAsGgAEAAYABAAACBgAAQgEJ00ggGSPBLop4YQEjhUGP0gAEBAAIfkEBQcAAAAsGgACAAYABgAACCEAAQD4A0mgwYPaWt35Jk0gOEAC34zoMqRGC4EOJlQAEBAAIfkEBQcAAAAsGgABAAYABwAACCUAAQgMBkCXQACgDAHgJrCMNoHSpCVCNu0guIMAuODACMCCwIAAACH5BAUGAAAALBoAAAAHAAcAAAglAAEI/PZNoEFw0nAZLKSFTh5X3QQqCmYQQDJw3KRV3Cgwh8GAAAAh+QQFBwAAACwbAAAABQAFAAAIHAABAAAHDoCuZJlo6QKwCMsaPgIJCpQmbdq0gAAAIfkEBQcAAAAsHAAAAAQABQAACBUAvwEYOE0Sq2AAtIzpM7AhAGkAAgIAIfkEBQYAAAAsJwARAAEAAQAACAQAAQQEACH5BAUHAAAALBwAAAAEAAUAAAgTAMEBGBgt0yxgALIMXLhwGoCAAAAh+QQFBwAAACwaAAAABgADAAAIEQABAPgmsKDAVsEIHvJyZk9AACH5BAUNAAAALBoAAAAGAAMAAAgUAAEAACewYLJQtHR5A4CIixk+AQEAIfkEBQcAAAAsGgACAAYAAQAACAoAvwE4lGXNnoAAACH5BAUoAAAALBoAAQAGAAQAAAgUAAEIJKULgDeBVNLwEciw4TSBAQEAIfkEBQYAAAAsBQADAB4ACwAACI8AAQgcKPAbwYMIE05buJDbN24AtHGDCGAiRYsYKUrE2M1hwoMPO2Yc6TDbmj+lTFmSk2qUKkdsaml7SHIkuGgHCiAQgIQDkAAPGAxQsApczYoYv2ULdSkUgFajcHXK9MlTpJkkkVrs+A2c169gvYYcqXUrN23aIp6lKHBj1opla8rFKBDi3Lt02X7cO5BsQAAh+QQFKAAAACwNAAYACQADAAAIDQC1ARhIsKDBgwU9BQQAIfkEBQcAAAAsHAABAAQAAgAACAwAAaQCAGBRFjB9AgIAIfkEBQcAAAAsGwABAAcABQAACBoAgQlLJooVNwAA/GzKEobXKYQQI0oEcDBiQAAh+QQFBgAAACwcAAAABwAFAAAIHQABCAQAbqCwZKn0TKv1pxIhOGcGDiwoEYA2gQEBACH5BAUHAAAALB4AAAAFAAUAAAgYAAEA+CYQWS5t0nIBqOMqlcCBDwFwExgQACH5BAXCAAAALCEAAQABAAIAAAgFAKOhCggAOw==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;[2020-10-14] 5-react-testing-intro-link&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/77tY6PgTo5KrY895TYol6Z/7709facf2f076785e2d82d7fdc4ef39c/5-react-testing-intro-link.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/77tY6PgTo5KrY895TYol6Z/7709facf2f076785e2d82d7fdc4ef39c/5-react-testing-intro-link.gif?w=160 160w,
https://images.ctfassets.net/rpmifyuylbfw/77tY6PgTo5KrY895TYol6Z/7709facf2f076785e2d82d7fdc4ef39c/5-react-testing-intro-link.gif?w=320 320w,
https://images.ctfassets.net/rpmifyuylbfw/77tY6PgTo5KrY895TYol6Z/7709facf2f076785e2d82d7fdc4ef39c/5-react-testing-intro-link.gif?w=640 640w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;링크를 클릭하면 “How it works” 제목이 있는 페이지를 나오길 기대한다.&lt;/p&gt;
&lt;p&gt;만약 제목이 ARIA role을 가지고 있다면 정확한 페이지가 표시되었는지 확인하기 위해 &lt;code class=&quot;language-text&quot;&gt;getByRole&lt;/code&gt;  함수를 다시 사용할 수 있다. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#Roles&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MDN 문서에 의하면&lt;/a&gt; 제목 요소는 &lt;strong&gt;heading&lt;/strong&gt; role을 가진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;link&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;heading&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/how it works/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 테스트는 성공한다. 이는 문서 안에 제목이 있다는 의미다. 그리고 또 우리가 정확한 페이지로 이동했다는 것을 의미한다. 아주 훌륭하다!&lt;/p&gt;
&lt;p&gt;마지막으로 하나 더: 우리는 어떤 요소가 렌더링 되었는지 확인하는데 &lt;strong&gt;getBy*&lt;/strong&gt; 를 사용하지 말아야 한다. 대신 &lt;code class=&quot;language-text&quot;&gt;expect(...).toBeInDocument()&lt;/code&gt; 로 확인해야 한다.&lt;/p&gt;
&lt;p&gt;아래에서 전체 테스트 코드를 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&quot;How it works&quot; link points to the correct page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;MemoryRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; link &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;link&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/how it works/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  userEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;link&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    screen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getByRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;heading&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/how it works/i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeInTheDocument&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;테스트는 무척 짧지만 우리가 여기까지 오는 데 많은 시간이 걸렸다. 그리고 그것은 많은 개발자가 처음 테스트를 시작했을 때 느끼는 감정이기도 하다. 하지만 일단 익숙해지면 훨씬 쉽고 빠르게 할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;폼 테스트를 시작하기 전에 하고 싶은 말: 우리는 단지 1개의 링크를 테스트 했을 뿐이다. 당신은 아마 헤더의 왼쪽에 홈 페이지로 이동시키는 로고와 “About” 페이지로 이동시키는 또 다른 링크가 헤더 오른편에 있다는 사실을 알고 있었을 것이다. &lt;/p&gt;
&lt;p&gt;그들 두 링크에 대한 테스트는 당신에게 훈련으로 남겨 두려고 한다. 2가지 짧은 힌트를 주자면:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;로고를 감싸고 있는 링크는 &lt;code class=&quot;language-text&quot;&gt;getByRole(&amp;#39;link&amp;#39;, { name })&lt;/code&gt; 으로 테스트할 수 있다. &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt;에 무엇을 넣어야 할지 모르겠다면, &lt;code class=&quot;language-text&quot;&gt;screen.debug()&lt;/code&gt; 의 결과를 확인해 보길 바란다.&lt;/li&gt;
&lt;li&gt;“How it works”, “About” 링크의 테스트는 &lt;a href=&quot;https://jestjs.io/docs/en/api#testeachtablename-fn-timeout&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;test.each&lt;/code&gt;&lt;/a&gt; 함수로 묶을 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;다음 글: &lt;a href=&quot;/posts/2020-10-15-beginners-guide-to-testing-react-2&quot;&gt;[번역] 초보자를 위한 React 어플리케이션 테스트 심층 가이드 (2)&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;알고리즘이 처리하는 데이터의 값이 알고리즘의 특성에 따른 일정한 범위를 넘을 경우에 발생하는 문제. 예를 들어 -128~127 까지의 값을 저장할 수 있는 8비트 변수에 범위를 벗어나는 값을 할당하는 경우 등.&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;원문 최하단에서 메일 주소 입력을 통해 받아볼 수 있음&lt;/p&gt;
&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;통합 테스트. 유닛 테스트의 다음 단계로 유닛/컴포넌트를 그룹으로 조합해서 테스트하는 것. 이 테스트의 목적은 컴포넌트가 연결되었을 때 발생하는 문제를 노출시키기 위함이다.&lt;/p&gt;
&lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;요구사항 문서에 있는 스펙은 개발자마다 다른 방법을 사용해서 구현할 수 있다. 그렇게 서로 달라질 수 있는 내용을 구현 세부 사항이라고 한다. 구현 세부 사항이 다르면 테스트 방법도 서로 달라야 할 것이다. 예를 들면 배열 정렬을 구현할 때 &lt;code class=&quot;language-text&quot;&gt;Array.prototype.sort&lt;/code&gt;를 사용할 수도 있고 직접 구현한 알고리즘을 사용할 수도 있다. 중요한 것은 정렬이 제대로 되는지 여부이며, 테스트도 거기에 초점을 맞춰야 한다는 관점.&lt;/p&gt;
&lt;a href=&quot;#fnref-4&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Gatsby 웹사이트에 적용한 다크 모드]]></title><description><![CDATA[몇년 전부터 Windows, macOS, iOS등의 운영체제가  다크 모드 를 지원하기 시작했고 그와 더불어 다크 모드를 지원하는 웹사이트도 많아졌다. 다크 모드는 어두운 곳에서의 가독성 향상은 물론 전력 소모도 줄어들기 때문에 무척 유용하다. 개인적으로는 낮에도 다…]]></description><link>https://blog.rhostem.com//posts/2020-06-25-dark-mode-for-gatsby-website</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-06-25-dark-mode-for-gatsby-website</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Thu, 25 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;몇년 전부터 Windows, macOS, iOS등의 운영체제가 &lt;a href=&quot;https://en.wikipedia.org/wiki/Light-on-dark_color_scheme&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;다크 모드&lt;/a&gt;를 지원하기 시작했고 그와 더불어 다크 모드를 지원하는 웹사이트도 많아졌다. 다크 모드는 어두운 곳에서의 가독성 향상은 물론 전력 소모도 줄어들기 때문에 무척 유용하다. 개인적으로는 낮에도 다크 모드로 노트북과 휴대폰을 사용하고 있기도 하다. 그래서 직접 제작한 이 블로그에도 다크 모드를 적용하기로 했다.&lt;/p&gt;
&lt;h2 id=&quot;요구사항&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EA%B5%AC%EC%82%AC%ED%95%AD&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요구사항&lt;/h2&gt;
&lt;p&gt;다크 모드를 개발하기 위해 정의한 요구사항은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;웹사이트는 라이트, 다크 2개의 컬러 테마를 가진다.&lt;/li&gt;
&lt;li&gt;웹사이트에 최초 방문시 운영체제의 화면 스타일로 테마를 설정한다.&lt;/li&gt;
&lt;li&gt;웹사이트의 테마 설정은 사용자의 브라우저에 저장해서 재방문 시에도 유지한다.&lt;/li&gt;
&lt;li&gt;테마 전환 버튼을 제공해서 사용자가 직접 바꿀 수 있도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;웹사이트에-스타일-테마-적용&quot;&gt;&lt;a href=&quot;#%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8%EC%97%90-%EC%8A%A4%ED%83%80%EC%9D%BC-%ED%85%8C%EB%A7%88-%EC%A0%81%EC%9A%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹사이트에 스타일 테마 적용&lt;/h2&gt;
&lt;h3 id=&quot;styled-components를-사용했다가-실패한-경험&quot;&gt;&lt;a href=&quot;#styled-components%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%96%88%EB%8B%A4%EA%B0%80-%EC%8B%A4%ED%8C%A8%ED%95%9C-%EA%B2%BD%ED%97%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;styled-components를 사용했다가 실패한 경험&lt;/h3&gt;
&lt;p&gt;이 블로그 웹사이트(blog.rhostem.com)는 &lt;a href=&quot;https://www.gatsbyjs.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Gatsby&lt;/a&gt;로 구현되어 있다. Gatsby는 React에 기반한 프레임워크라서 styled-components를 사용해서 개발을 시작했다. styled-components에는 컴포넌트에 테마 스타일 객체를 전달할 수 있도록  &lt;a href=&quot;https://styled-components.com/docs/api#themeprovider&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ThemeProvider&lt;/a&gt; 컴포넌트를 제공하고 있기도 하다.  &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; theme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  colors&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;#fefefe&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; DefaultLayout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; styled&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;div&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
  background: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; theme &lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;) =&gt; theme.colors.body};
`&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Layout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ThemeProvider&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;theme&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;DefaultLayout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;DefaultLayout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ThemeProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;ThemeProvider&lt;/code&gt;에 전달한 테마 객체는 styled-component의 템플릿 리터럴 안에서 인터폴레이션을 통해 접근할 수 있다. &lt;/p&gt;
&lt;p&gt;처음에는 이 방식으로 접근해서 테마  구현을 끝냈었다. 그런데 개발 서버에서는 문제가 발생하지 않았지만, Gatsby를 빌드한 후에 띄운 서버에서는 문제가 발생한다는 사실을 뒤늦게 알게 되었다. Gatsby는 빌드 결과물에 HTML과 CSS를 모두 포함하고 있기 때문이었다.&lt;/p&gt;
&lt;p&gt;Gatsby는 개발 모드에서 페이지에 접근하면 서버 렌더링을 하지 않는다. 일반적인 리액트 앱처럼 모든 처리를 클라이언트 사이드에서 하며 styled-components 같은 css-in-js 라이브러리로 작성한 스타일은 모두 앱이 구동된 후에 추가된다. 하지만 빌드를 한 후 페이지에 접근하면 styled-components로 작성한 스타일이 모두 &lt;code class=&quot;language-text&quot;&gt;style&lt;/code&gt; 태그에 추가된 상태의 HTML 페이지를 제공한다.  &lt;/p&gt;
&lt;p&gt;이런 특징이 테마가 분리되기 전에는 문제가 전혀  없었다. 하지만 테마가 분리된 후에는 문제가 생겼다. 왜냐하면 &lt;strong&gt;빌드 시점에 설정한 테마가 먼저 제공된 후 브라우저에 저장된 사용자의 테마를 적용하기 때문&lt;/strong&gt;이다. 예를 들어 기본 테마를 “라이트”로 해서 빌드를 해서 배포를 했는데, 사용자가 “다크” 테마를 선택한 후 &lt;strong&gt;새로 고침을 하면 “라이트” 테마가 우선 표시된 후 “다크” 테마로 바뀌는 깜빡임 현상이 발생&lt;/strong&gt;하게 된다.(테마 컬러 전환에 CSS &lt;code class=&quot;language-text&quot;&gt;transition&lt;/code&gt; 이펙트를 넣어뒀더니 눈이 아팠다…) &lt;/p&gt;
&lt;p&gt;서버 사이드 렌더링 웹사이트라면 쿠키나 DB에 저장된 사용자의 테마를 가져와서 깜빡임이 없도록 할 수도 있겠지만 Gatsby는 완전히 프리 렌더링된 정적인 웹사이트라서 불가능하다. 그렇다고 브라우저 스토리지에서 테마 설정값을 가져오기 전까지 웹사이트를 표시하지 않는다? 그것도 사용자에게 웹사이트 사용을 위해 기본적으로 필요한 네트워크 딜레이에 자바스크립트 파싱 및 실행 딜레이까지 추가하는 행위라서 좋지 않다. &lt;/p&gt;
&lt;p&gt;결국 styled-component의 ThemeProvider는 버리고, &lt;strong&gt;CSS 커스텀 속성&lt;/strong&gt;을 사용하기로 했다.&lt;/p&gt;
&lt;h3 id=&quot;css-custom-properties&quot;&gt;&lt;a href=&quot;#css-custom-properties&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CSS Custom Properties&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/CSS/Using_CSS_custom_properties&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CSS 커스텀 속성&lt;/a&gt;은 CSS에서 변수를 사용할 수 있도록 한다. 변수는 어디에 선언해도 상관없지만, 전역에서 사용할 수 있도록 하려면 &lt;code class=&quot;language-text&quot;&gt;:root&lt;/code&gt; 가상 선택자에 선언한 후 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--main-bg-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; brown&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--main-bg-color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 테마 설정을 위해서는 커스텀 속성을 아래와 같이 작성할 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;.light &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;46, 46, 46, 0.95&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;.dark &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;--text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;215, 215, 215, 0.9&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 하면 &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt; 태그에 어떤 클래스가 붙느냐에 따라 같은 이름을 가진 변수라도 다른 값을 가지게 된다. 다음으로 필요한 작업은 웹사이트 로딩 시점에 테마를 확인해서 &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt; 태그에 클래스를 추가하는 것이다.  &lt;/p&gt;
&lt;h2 id=&quot;시스템-컬러-설정으로-테마-초기화&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%BB%AC%EB%9F%AC-%EC%84%A4%EC%A0%95%EC%9C%BC%EB%A1%9C-%ED%85%8C%EB%A7%88-%EC%B4%88%EA%B8%B0%ED%99%94&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시스템 컬러 설정으로 테마 초기화&lt;/h2&gt;
&lt;p&gt;아까 발생했던 문제는 테마를 변경하는 과정이 HTML을 기반으로 DOM 트리가 구성된 후 100 킬로바이트가 넘는  자바스크립트 파일을 파싱한 후에 천천히(컴퓨터 관점에서는) 이뤄졌기 때문이다. 그렇다면 깜빡임 현상을 없애기 위해서는 &lt;strong&gt;테마를 설정하는 시점이 스타일시트를 불러온 후, DOM 트리가 구성되기 전&lt;/strong&gt;이어야 한다. 그러기 위해서는 &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt; 태그의 첫번째 child element로 &lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt; 태그를 추가하고 그 안에 자바스크립트를 작성하면 된다.&lt;/p&gt;
&lt;h3 id=&quot;gatsby에-htmljs-파일에-우선-실행될-스크립트-추가&quot;&gt;&lt;a href=&quot;#gatsby%EC%97%90-htmljs-%ED%8C%8C%EC%9D%BC%EC%97%90-%EC%9A%B0%EC%84%A0-%EC%8B%A4%ED%96%89%EB%90%A0-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%B6%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Gatsby에 html.js 파일에 우선 실행될 스크립트 추가&lt;/h3&gt;
&lt;p&gt;Gatsby에서 저것을 가능하게 하려면 gatsby-ssr.js 파일에 &lt;a href=&quot;https://www.gatsbyjs.org/docs/ssr-apis/#onRenderBody&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;onRenderBody&lt;/code&gt;&lt;/a&gt; 모듈을 작성해도 되고, html.js 파일을 추가해도 된다. html.js 파일은 Gatsby에서 index.html 파일 역할을 한다(Next.js의 _document.js와 유사). &lt;/p&gt;
&lt;p&gt;html.js 파일의 구성은 아래와 같다. 다른 내용은 자세히 볼 필요 없고, &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt; 태그 아래에 있는 &lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt; 태그의  &lt;code class=&quot;language-text&quot;&gt;dangerouslySetInnerHTML&lt;/code&gt;에 테마 설정에 필요한 스크립트를 추가할 것이라는 사실만 알면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTML&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;htmlAttributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charSet&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;utf-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;httpEquiv&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;x-ua-compatible&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ie=edge&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;width=device-width, initial-scale=1, shrink-to-fit=no&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headComponents&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

				&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* 빌드 시점의 테마 설정을 위해 className에 light를 추가했다. */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;bodyAttributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;light&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; 
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              __html&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
								// 테마 초기화 코드가 들어갈 곳
            `&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;preBodyComponents&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`body`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;___gatsby&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token attr-name&quot;&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; __html&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;postBodyComponents&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;테마-초기화-코드&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EB%A7%88-%EC%B4%88%EA%B8%B0%ED%99%94-%EC%BD%94%EB%93%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테마 초기화 코드&lt;/h3&gt;
&lt;p&gt;소스 코드는 &lt;a href=&quot;https://github.com/gaearon/overreacted.io/blob/master/src/html.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/gaearon/overreacted.io/blob/master/src/html.js&lt;/a&gt; 파일을 참고했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;__onThemeChange&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 컴포넌트에서 정의할 테마 변경 콜백&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; preferredTheme &lt;span class=&quot;token comment&quot;&gt;// 초기화에 사용할 테마&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	  preferredTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;theme&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 로컬 스토리지에 있는 것 사용&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// 테마 설정 함수&lt;/span&gt;
	window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;__setPreferredTheme&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__theme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newTheme &lt;span class=&quot;token comment&quot;&gt;// 테마는 전역 변수에 저장&lt;/span&gt;
	  preferredTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newTheme
	  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;className &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newTheme &lt;span class=&quot;token comment&quot;&gt;// 바디에 클래스 추가&lt;/span&gt;
	  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;__onThemeChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 콜백 실행&lt;/span&gt;

	  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	    localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;theme&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// 컬러모드 미디어 쿼리 객체를 가져온다&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; darkQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;(prefers-color-scheme: dark)&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// 컬러모드 변경 탐지 이벤트 리스너 추가&lt;/span&gt;
	darkQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;__setPreferredTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dark&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;light&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// 테마 설정. 저장된 테마가 없으면 시스템 설정을 사용한다.&lt;/span&gt;
	window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;__setPreferredTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	  preferredTheme &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;darkQuery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dark&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;light&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;코드는 IIFE(즉시실행) 함수에 둘러싸여 있어 바로 실행된다. 언더바 두개(&lt;code class=&quot;language-text&quot;&gt;__&lt;/code&gt;)가 붙은 변수와 함수는 나중에 React 모듈에서 사용하기 위한 목적으로 &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt; 객체에 추가되었다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Window/matchMedia&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;window.matchMedia&lt;/code&gt;&lt;/a&gt;는 주어진 미디어 쿼리를 분석한 결과를 나타내는 &lt;code class=&quot;language-text&quot;&gt;MediaQueryList&lt;/code&gt; 객체를 반환한다.  &lt;code class=&quot;language-text&quot;&gt;prefers-color-scheme: dark&lt;/code&gt;  쿼리로 생성한 객체의 &lt;code class=&quot;language-text&quot;&gt;matches&lt;/code&gt; 는 boolean 값으로 현재 컬러 스타일이 다크 모드인지 아닌지를 나타내며, &lt;code class=&quot;language-text&quot;&gt;addListener&lt;/code&gt; 함수는 웹사이트에서 시스템이 컬러 스타일을 변경하는 이벤트를 탐지할 수 있도록 한다.&lt;/p&gt;
&lt;p&gt;여기까지 하면 시스템 컬러 설정을 웹사이트의 테마에 반영할 수 있다. 남은 것은 사용자가 직접 테마를 변경할 수 있도록 하는 토글 버튼이다.&lt;/p&gt;
&lt;h2 id=&quot;테마-토글-기능&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EB%A7%88-%ED%86%A0%EA%B8%80-%EA%B8%B0%EB%8A%A5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테마 토글 기능&lt;/h2&gt;
&lt;p&gt;토글 버튼 콜백에서는 html.js에 추가한 &lt;code class=&quot;language-text&quot;&gt;__setPreferredTheme&lt;/code&gt; 함수를 호출해야 한다. 그리고 시스템 컬러 변경 이벤트를 토글 버튼에도 반영하기 위해 비어있는 &lt;code class=&quot;language-text&quot;&gt;__onThemeChange&lt;/code&gt;  함수에 토글 버튼의 상태를 변경하는 함수를 재할당해주면 된다.&lt;/p&gt;
&lt;h3 id=&quot;테마-초기화-커스텀-훅&quot;&gt;&lt;a href=&quot;#%ED%85%8C%EB%A7%88-%EC%B4%88%EA%B8%B0%ED%99%94-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;테마 초기화 커스텀 훅&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useDarkMode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;theme&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setTheme&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; toggleTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nextTheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        theme &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; themeNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LIGHT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; themeNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DARK&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; themeNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LIGHT&lt;/span&gt;

      &lt;span class=&quot;token function&quot;&gt;setTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nextTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;__setPreferredTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nextTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;theme&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 클라이언트에서는 window.__theme 값으로 테마를 설정한다&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; window &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;object&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__theme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 테마 변경 시점에 실행할 로직을 추가한다. &lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// __setPreferredTheme은 변경할 수 없으므로 여기에 React에서 사용할 수 있는 로직을 추가한다.&lt;/span&gt;
    window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;__onThemeChange&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newTheme &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newTheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; theme&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; toggleTheme &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;나는 개인적으로 데스크탑용, 모바일용 토글 버튼이 하나씩 있어서 커스텀 훅을 구현하고 컨텍스트를 사용해서 상태와 함수를 컴포넌트에 전달했지만, 필요 없다면 토글 버튼에 상태 관리 로직을 직접 구현해도 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;DefaultLayout&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; theme&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; toggleTheme &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useDarkMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;DarkModeContext.Provider&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        theme&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        toggleTheme&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Navbar&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;PageContents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;PageContents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Footer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;DarkModeContext.Provider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;DarkmodeToggleButton&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; theme&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; toggleTheme &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DarkModeContext&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    theme &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ToggleButton&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;toggleTheme&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ToggleTrack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;moon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;sun&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ToggleThumb&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;themeName&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;theme&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ToggleTrack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ToggleButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;특히 토글 버튼에서 &lt;code class=&quot;language-text&quot;&gt;theme&lt;/code&gt; 값이 truthy한 값이어야 렌더링 되도록 조건을 달았는데, 저렇게 하지 않으면 토글 버튼이 처음부터 브라우저에 저장된 테마가 적용되지 않는다. 대신 토글 버튼의 &lt;code class=&quot;language-text&quot;&gt;themeName&lt;/code&gt; props가 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;일 때  표시되는 값에서 저장된 값으로 전환되는 깜빡임 현상이 발생한다. 저 부분은 버튼이 화면에 나타나기까지 딜레이가 조금 발생하더라도 어쩔 수 없다. &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;이렇게 테마 분리, 시스템 테마 반영 및 탐지, 컬러 테마 변경 UI까지 구현이 끝났다. 참고한 자료들은 다음과 같다.&lt;/p&gt;
&lt;h2 id=&quot;참고-자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/amaltapalov/how-i-set-dark-mode-for-gatsby-website-4ni0&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;🌙 How I set Dark Mode for Gatsby website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/gaearon/overreacted.io/blob/master/src/html.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/gaearon/overreacted.io/blob/master/src/html.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://styled-components.com/docs/api#themeprovider&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;styled-components: API Reference - ThemeProvider&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[dynamic import로 줄여본 next.js 앱의 최초 로딩 시간]]></title><description><![CDATA[dynamic import Next.js는 ES2020의  dynamic import  문법을 지원한다. dynamic import를 사용하면 모듈을 빌드 타임이 아닌 런타임에 불러오도록 한다. 이를 통해 번들 파일을  분리하고  퍼포먼스 향상을 기대할 수 있다. 앱…]]></description><link>https://blog.rhostem.com//posts/2020-05-10-nextjs-and-dynamic-import</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-05-10-nextjs-and-dynamic-import</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 10 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;dynamic-import&quot;&gt;&lt;a href=&quot;#dynamic-import&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;dynamic import&lt;/h2&gt;
&lt;p&gt;Next.js는 ES2020의 &lt;a href=&quot;https://github.com/tc39/proposal-dynamic-import&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;dynamic import&lt;/a&gt; 문법을 지원한다. dynamic import를 사용하면 모듈을 빌드 타임이 아닌 런타임에 불러오도록 한다. 이를 통해 번들 파일을  분리하고  퍼포먼스 향상을 기대할 수 있다. 앱에는 초기 로딩부터 사용하지 않는 부분이 존재할 수 있으며, 또 그 부분의 사이즈가 생각보다 클 수 있기 때문이다. 그리고 사용자 언어 등 런타임에서만 알 수 있는 정보에 기반해서 모듈을 선택해야 하는 케이스도 생길 수 있다. &lt;/p&gt;
&lt;p&gt;dynamic import의 공식 문법에서는 &lt;code class=&quot;language-text&quot;&gt;import&lt;/code&gt; 호출이 promise를 리턴하는 것처럼 코드를 작성한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// this code resides in add.js file&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; add

&lt;span class=&quot;token comment&quot;&gt;// in index.js file&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./add.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;module &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// returns 11&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// log error);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만  next.js에서는 &lt;a href=&quot;https://nextjs.org/docs/advanced-features/dynamic-import&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;dynamic&lt;/a&gt;이라는 모듈을 제공해서 promise resolve 과정 없이도 변수에 할당할 수 있도록 해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dynamic &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;next/dynamic&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; DynamicComponent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;../components/hello&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;nextjs-프로젝트에-적용하기&quot;&gt;&lt;a href=&quot;#nextjs-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;next.js 프로젝트에 적용하기&lt;/h2&gt;
&lt;p&gt;작업 중인 프로젝트에서는 각각의 라우트에 매칭되는 루트 컴포넌트를 하나씩 만들었다. 라우트의 컨텐츠는 루트 컴포넌트 안에 들어간다. 따라서 특정 라우트에만 사용되는 컴포넌트가 여럿 존재하게 된다. 그리고 next.js로 작성된 앱은 서버 렌더링을 하기 때문에 라우트에 매칭되는 컴포넌트가 아닌 다른 루트 컴포넌트들을 모두 불러올 필요가 없다. 그래서 &lt;code class=&quot;language-text&quot;&gt;next/dynamic&lt;/code&gt; 모듈을 사용해서 그 루트 컴포넌트들을 불러오도록 했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// index.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dynamic &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;next/dynamic&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// index 라우트에서 사용할 루트 컴포넌트(HomePage)를 dynamic import로 불러온다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Home &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;components/pages/HomePage&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Home&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Home&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; index&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;dynamic-import-적용-전의-빌드-결과&quot;&gt;&lt;a href=&quot;#dynamic-import-%EC%A0%81%EC%9A%A9-%EC%A0%84%EC%9D%98-%EB%B9%8C%EB%93%9C-%EA%B2%B0%EA%B3%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;dynamic import 적용 전의 빌드 결과&lt;/h3&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/k0tpxnZQechpfAgFa82ga/f7e8641e47928d71de02e258d2e5cc39/before_dynamic_import_page_components.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 57.598784194528875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAXCAMAAABODP0nAAAMWWlDQ1BpY2MAAHjalZcHWJNXF4DvNzJJwgggICPsJcomgIwQVgQBmYKohCSQMEJMCCJuaqmCeyAKLrQqomi1AlIn4qoWxW0dxYFKpRatuFD5b0hAa///+Z/e5znffXPuueeec3K/cQHQ7uTLZHmoDgD50kJ5fEQIa2JqGov0COCABmUk0OULFDJOXFw0gG2o/3t7fR0gqv6Ki8oX+HdNTyhSCABA0iFnChWCfMjHAcBLBTJ5IQDEUKi3nl4oU7EYsr4cBgh5loqz1bxcxZlq3jZokxjPhdwMAJnG58uzAWC0QT2rSJAN/TAeQXaVCiVSALT1IQcKxHwh5ETIo/LzC1Q8D7IDtJdB3gmZnfmFz+y/+c8c9s/nZw+zOq/BRg6VKGR5/Bn/sjT/v+XnKYfWsINCE8sj41X5wxrezC2IUjENco80MyZWVWvIbyVCdd0BQKliZWSS2h41FSi4sH7AELKrkB8aBdkUcrg0LyZao8/MkoTzIMPdghZLCnmJmrkLRYqwBI3PGnlBfOwQZ8m5HM3cBr58cF2VfZsyN4mj8X9TLOIN+X9VIk5MgUwFAKMWSZJjIDMg6ytyE6LUNphViZgbM2QjV8ar4reBzBZJI0LU/rH0LHl4vMZelq8YyhcrE0t4MRquKhQnRqrrg+0S8AfjN4LcKJJykob8iBQTo4dyEYpCw9S5Y+0iaZImX+yerDAkXjO3V5YXp7HHyaK8CJXeCrKJoihBMxcfWwg3p9o/Hi0rjEtUx4ln5PDHxanjwYtANOCCUMACSiiZoADkAEl7T1MP/KUeCQd8IAfZQARcNJqhGSmDI1J4TQAl4A9IIqAYnhcyOCoCRVD/cVirvrqArMHRosEZueAx5HwQBfLgb+XgLOnwasngEdRI/rG6AMaaB0U19k8dB2qiNRrlkF+W9pAlMYwYSowkhhMdcRM8EPfHo+E1GIo7zsZ9h6L9bE94TOggPCBcI3QSbk2VlMq/imU86IT+wzUZZ36ZMW4HfXrhIXgA9A4944a4CXDBPeE6HDwIruwFtVxN3KrcWf8lz+EMvqi5xo7iSkEpIyjBFIevZzKcGF7DXlQV/bI+6lgzh6vKHR75en3uF3UWwj7qa0tsIXYAO4OdwM5hh7EmwMKOYc3YBeyIiof30KPBPTS0WvxgPLnQj+Qf6/E1a6oqqXCtd+12/aAZA4Wi4kLVDcYtkM2QS7LFhSwOfAuIWDypYPQolruruysAqneK+jHVe3HwXYEY637WzUsFYKzJwMDAoc+6GE8ADjbB2/zxZ50DfPYwxgBwdolAKS9S63DVhQCfBtrwjjIG5sAaOMCM3IE38AfBIAyMA7EgEaSCKbDOYrif5WA6mAXmgzJQAZaDNWA92AS2gp1gD9gPmsBhcAKcBufBJXAN3Ib7pws8A73gNehHEISE0BEmYoxYILaIM+KOsJFAJAyJRuKRVCQDyUakiBKZhXyDVCArkfXIFqQO+QE5hJxAziEdyC3kPtKNvETeoxhKQ/VRM9QOHYOyUQ4ahSaik9FsdBpagi5Al6JVaC26G21ET6Dn0WtoJ/oM7cMApoUZYpaYC8bGuFgsloZlYXJsDlaOVWK1WAPWAv/pK1gn1oO9w4k4E2fhLnAPR+JJuACfhs/BF+Pr8Z14I96GX8Hv4734JwKdYEpwJvgReISJhGzCdEIZoZKwnXCQcAreTV2E10Qi0ZBoT/SBd2MqMYc4k7iYuIG4l3ic2EF8SOwjkUjGJGdSACmWxCcVkspI60i7ScdIl0ldpLdkLbIF2Z0cTk4jS8ml5EryLvJR8mXyE3I/RYdiS/GjxFKElBmUZZRtlBbKRUoXpZ+qS7WnBlATqTnU+dQqagP1FPUO9S8tLS0rLV+tCVoSrXlaVVr7tM5q3dd6R9OjOdG4tHSakraUtoN2nHaL9hedTrejB9PT6IX0pfQ6+kn6PfpbBpMxmsFjCBlzGdWMRsZlxnNtiratNkd7inaJdqX2Ae2L2j06FB07Ha4OX2eOTrXOIZ0bOn26TF033VjdfN3Furt0z+k+1SPp2emF6Qn1Fuht1Tup95CJMa2ZXKaA+Q1zG/MUs0ufqG+vz9PP0a/Q36Pfrt9roGfgaZBsUGxQbXDEoNMQM7Qz5BnmGS4z3G943fD9CLMRnBGiEYtGNIy4POKN0UijYCORUbnRXqNrRu+NWcZhxrnGK4ybjO+a4CZOJhNMpptsNDll0jNSf6T/SMHI8pH7R/5qipo6mcabzjTdanrBtM/M3CzCTGa2zuykWY+5oXmweY75avOj5t0WTItAC4nFaotjFr+zDFgcVh6ritXG6rU0tYy0VFpusWy37Leyt0qyKrXaa3XXmmrNts6yXm3dat1rY2Ez3maWTb3Nr7YUW7at2Hat7RnbN3b2dil239k12T21N7Ln2ZfY19vfcaA7BDlMc6h1uOpIdGQ75jpucLzkhDp5OYmdqp0uOqPO3s4S5w3OHaMIo3xHSUfVjrrhQnPhuBS51LvcH204Onp06eim0c/H2IxJG7NizJkxn1y9XPNct7nedtNzG+dW6tbi9tLdyV3gXu1+1YPuEe4x16PZ44Wns6fIc6PnTS+m13iv77xavT56+3jLvRu8u31sfDJ8anxusPXZcezF7LO+BN8Q37m+h33f+Xn7Ffrt9/vT38U/13+X/9Ox9mNFY7eNfRhgFcAP2BLQGcgKzAjcHNgZZBnED6oNehBsHSwM3h78hOPIyeHs5jwPcQ2RhxwMecP1487mHg/FQiNCy0Pbw/TCksLWh90LtwrPDq8P743wipgZcTySEBkVuSLyBs+MJ+DV8XrH+YybPa4tihaVELU+6kG0U7Q8umU8On7c+FXj78TYxkhjmmJBLC92VezdOPu4aXE/TSBOiJtQPeFxvFv8rPgzCcyEqQm7El4nhiQuS7yd5JCkTGpN1k5OT65LfpMSmrIypXPimImzJ55PNUmVpDankdKS07an9U0Km7RmUle6V3pZ+vXJ9pOLJ5+bYjIlb8qRqdpT+VMPZBAyUjJ2ZXzgx/Jr+X2ZvMyazF4BV7BW8EwYLFwt7BYFiFaKnmQFZK3MepodkL0qu1scJK4U90i4kvWSFzmROZty3uTG5u7IHchLydubT87PyD8k1ZPmStsKzAuKCzpkzrIyWec0v2lrpvXKo+TbFYhisqK5UB9+vF9QOii/Vd4vCiyqLno7PXn6gWLdYmnxhRlOMxbNeFISXvL9THymYGbrLMtZ82fdn82ZvWUOMidzTutc67kL5nbNi5i3cz51fu78X0pdS1eWvvom5ZuWBWYL5i14+G3Et/VljDJ52Y3v/L/btBBfKFnYvshj0bpFn8qF5T9XuFZUVnxYLFj88xK3JVVLBpZmLW1f5r1s43Licuny6yuCVuxcqbuyZOXDVeNXNa5mrS5f/WrN1DXnKj0rN62lrlWu7ayKrmpeZ7Nu+boP68Xrr1WHVO+tMa1ZVPNmg3DD5Y3BGxs2mW2q2PR+s2TzzS0RWxpr7WortxK3Fm19vC1525nv2d/XbTfZXrH94w7pjs6d8Tvb6nzq6naZ7lpWj9Yr67t3p+++tCd0T3ODS8OWvYZ7K/aBfcp9v/+Q8cP1/VH7Ww+wDzT8aPtjzUHmwfJGpHFGY2+TuKmzObW549C4Q60t/i0Hfxr9047DloerjxgcWXaUenTB0YFjJcf6jsuO95zIPvGwdWrr7ZMTT15tm9DWfirq1NnT4adPnuGcOXY24Ozhc37nDv3M/rnpvPf5xgteFw7+4vXLwXbv9saLPhebL/leaukY23H0ctDlE1dCr5y+yrt6/lrMtY7rSddv3ki/0XlTePPprbxbL34t+rX/9rw7hDvld3XuVt4zvVf7m+Nvezu9O4/cD71/4UHCg9sPBQ+fPVI8+tC14DH9ceUTiyd1T92fHu4O7770+6Tfu57JnvX3lP2h+0fNc4fnP/4Z/OeF3om9XS/kLwZeLv7L+K8drzxftfbF9d17nf+6/035W+O3O9+x3515n/L+Sf/0D6QPVR8dP7Z8ivp0ZyB/YEDGl/MHPwUwKGhWFgAvdwBAh98UzEvw+2GS+sw32BD1OXWQwP9i9blwsHkD0AA71ec6F55J90Gxg0IPBiAWSmIwQD08hkXTFFke7mpfjHoASJYDAy8LAKBA+RAxMNAfNzDwsQYGexWAo0/VZ01VI8KzwWY3FV22qC/++pynPod+kePXPVBF4Am+7v8Db6KHwV9AchwAAAHXUExURSMaLCIYKyQaLSQaLCAWKR8VKDoyQj01RUM7SjYtPTgvPzUsPTkxQTgwQDoxQTsyQkA4SEY/TSkfMSsiMyYcLiQbLSgfMSUcLiceMCUbLjw0RElBUCMZLC4lNlNMWjkwQCYcLyshMzYuPkM1REczQlpHUy0kNSogMiwiNEY+TTwoLkQtME80MicdL0pDUS8mNzAnOC4lNy8mODYtPjolLkMvL042MkQ8Sz83Rz00REhBTz83RisiNDQrPDsfLkEhL08mMz42RS8lNykgMjcdLUAgLzszQzMqOygeMC0jNTcuP0EhMEA4R0E5SUI6SSwjNSUbLTkeLkQiMD42RkdATjcoLUAwLk49MT01RD41RUA3RzIpOishK0xDODkwMC4lKyYdKSQaKSkfKkE5SDkwQS0kNjEoOSwiMjw0PDcuPEE5RUU9TEc/TjoxQk1GVFFKWFBJV01FU1RNW0U+TElCUEM7S0xEUjwzQ0tEUiIZKy4kNkpCUEhAT09IVkpCUUlBTzUtPUM8SzcvPyohMj82RkhATiIYKjszQkE4SCcySCYvRSUrQSUqPyMkOSUsQiYwRiYuRCYuQy4oOkI7SjUsPDAoOTIqOkxEUygqPiYtQicwRiYtQy0xRDUXZ4AAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAAHdElNRQfmDBsPHh3yOnubAAAB9klEQVQoz2NgYGRkZGJmAVIsQMCKEzCwsXNwcnHz8PKx8QsICuFRKCyCU05UjJFVXEJCEqydQUpaBpdCWTl5BUUlZRVVsEI1dXVZHE7T0FTiYmXV0tYBK5TVZdfTVzMwNDTCUKjGZczBympiagZWqG9kbiFlaWXNx6Fvg6ZQWMqWh5XVzt4BqlBK05rd0lHWwMkZTaGYJRsbK6uLK0Shk5G1m6a1uwcWN3p6cQGttvOGKJQ18vH18w/A5hlnLy+gwsAgiEI1o2A/flvsvg5REmNlDQ0LByuUiPCJjIo2wKYwJjYunpU1ITEJrJDFIDk5hSPZJiI1DV1hekZmFiIKWWWzc3Lz8gsKizQ5is05Sko1/cp8yt0FK4y5PcuzK5EUVnFks1UXWEmVaArWaNZmV3PUJYfURGfXN0jZcPAmIyv0a5S3zNF0M/apNlKHRyaQwaBoYYusUM2a3dfCkr+2OE0NLXkINzUhK2wGKmyxSubUM+flRVXo0crNiqQQZhMQtLHiBgyystlu7Ywd7IaRbDzZEbapLRHJ7lHRnV3dPV1dvbx9/T0T+vondk5iZfCU5bDmYZrsZ+EWrG9tHszNn+I4JVhzqo+tlJGblIGjpizMxHbZGiVWqEK94Chf42nB/GnTZW1qcqP8ZsycNbu7fw4zkhsJAwAkxG6GeEsYGwAAABJ0RVh0ZXhpZjpFeGlmT2Zmc2V0ADc4ydR7JwAAABl0RVh0ZXhpZjpQaXhlbFhEaW1lbnNpb24AMTMxNjWg2qkAAAAYdEVYdGV4aWY6UGl4ZWxZRGltZW5zaW9uADc1OKwcod4AAABcdEVYdGV4aWY6VXNlckNvbW1lbnQANjUsIDgzLCA2NywgNzMsIDczLCAwLCAwLCAwLCA4MywgOTksIDExNCwgMTAxLCAxMDEsIDExMCwgMTE1LCAxMDQsIDExMSwgMTE2QLgfcgAAACh0RVh0aWNjOmNvcHlyaWdodABDb3B5cmlnaHQgQXBwbGUgSW5jLiwgMjAyMAq63rAAAAAXdEVYdGljYzpkZXNjcmlwdGlvbgBEaXNwbGF5FxuVuAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;before_dynamic_import_page_components&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/k0tpxnZQechpfAgFa82ga/f7e8641e47928d71de02e258d2e5cc39/before_dynamic_import_page_components.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/k0tpxnZQechpfAgFa82ga/f7e8641e47928d71de02e258d2e5cc39/before_dynamic_import_page_components.png?w=329 329w,
https://images.ctfassets.net/rpmifyuylbfw/k0tpxnZQechpfAgFa82ga/f7e8641e47928d71de02e258d2e5cc39/before_dynamic_import_page_components.png?w=658 658w,
https://images.ctfassets.net/rpmifyuylbfw/k0tpxnZQechpfAgFa82ga/f7e8641e47928d71de02e258d2e5cc39/before_dynamic_import_page_components.png?w=1316 1316w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;h3 id=&quot;dynamic-import-적용-후의-빌드-결과&quot;&gt;&lt;a href=&quot;#dynamic-import-%EC%A0%81%EC%9A%A9-%ED%9B%84%EC%9D%98-%EB%B9%8C%EB%93%9C-%EA%B2%B0%EA%B3%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;dynamic import 적용 후의 빌드 결과&lt;/h3&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1lSXmE0alsjDA4UVvhr6I0/bd2451e87358ce2ba69f03c3332b3ffd/after_dynamic_import_page_components.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 67.07410236822001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;after_dynamic_import_page_components&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1lSXmE0alsjDA4UVvhr6I0/bd2451e87358ce2ba69f03c3332b3ffd/after_dynamic_import_page_components.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1lSXmE0alsjDA4UVvhr6I0/bd2451e87358ce2ba69f03c3332b3ffd/after_dynamic_import_page_components.png?w=327 327w,
https://images.ctfassets.net/rpmifyuylbfw/1lSXmE0alsjDA4UVvhr6I0/bd2451e87358ce2ba69f03c3332b3ffd/after_dynamic_import_page_components.png?w=655 655w,
https://images.ctfassets.net/rpmifyuylbfw/1lSXmE0alsjDA4UVvhr6I0/bd2451e87358ce2ba69f03c3332b3ffd/after_dynamic_import_page_components.png?w=1309 1309w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;모든 라우트에 dynamic import를 적용하고 빌드를 해 보니 first load 사이즈가 크게 줄었다. 페이지별 번들 파일의 사이즈가 300kb대에서 100kb 단위로 줄어든 것을 확인할 수 있다. 하지만 share by all 항목에 있는 파일의 수가 더 증가했다.&lt;/p&gt;
&lt;h2 id=&quot;dynamic-import-전후의-로딩-시간-비교&quot;&gt;&lt;a href=&quot;#dynamic-import-%EC%A0%84%ED%9B%84%EC%9D%98-%EB%A1%9C%EB%94%A9-%EC%8B%9C%EA%B0%84-%EB%B9%84%EA%B5%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;dynamic import 전후의 로딩 시간 비교&lt;/h2&gt;
&lt;h3 id=&quot;다이나믹-임포트-전의-로딩-워터폴-그래프&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%9D%B4%EB%82%98%EB%AF%B9-%EC%9E%84%ED%8F%AC%ED%8A%B8-%EC%A0%84%EC%9D%98-%EB%A1%9C%EB%94%A9-%EC%9B%8C%ED%84%B0%ED%8F%B4-%EA%B7%B8%EB%9E%98%ED%94%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다이나믹 임포트 전의 로딩 워터폴 그래프&lt;/h3&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3LYcmyTteP2WlatdN7H525/97b9b5db97617d409e9c9ec666710368/before_dynamic_-_file_load_waterfall.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 53.51213282247765%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;before_dynamic_-_file_load_waterfall&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3LYcmyTteP2WlatdN7H525/97b9b5db97617d409e9c9ec666710368/before_dynamic_-_file_load_waterfall.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3LYcmyTteP2WlatdN7H525/97b9b5db97617d409e9c9ec666710368/before_dynamic_-_file_load_waterfall.png?w=392 392w,
https://images.ctfassets.net/rpmifyuylbfw/3LYcmyTteP2WlatdN7H525/97b9b5db97617d409e9c9ec666710368/before_dynamic_-_file_load_waterfall.png?w=783 783w,
https://images.ctfassets.net/rpmifyuylbfw/3LYcmyTteP2WlatdN7H525/97b9b5db97617d409e9c9ec666710368/before_dynamic_-_file_load_waterfall.png?w=1566 1566w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;자바스크립트 파일의 로딩은 200ms 전후로 모두 끝이 난다.&lt;/p&gt;
&lt;h3 id=&quot;다이나믹-임포트-후의-로딩-워터폴-그래프&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%9D%B4%EB%82%98%EB%AF%B9-%EC%9E%84%ED%8F%AC%ED%8A%B8-%ED%9B%84%EC%9D%98-%EB%A1%9C%EB%94%A9-%EC%9B%8C%ED%84%B0%ED%8F%B4-%EA%B7%B8%EB%9E%98%ED%94%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다이나믹 임포트 후의 로딩 워터폴 그래프&lt;/h3&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/584nohpIyKv2MHRDiFZYAc/2f4bda4e7f707f6bb1fbbc53b9579e8a/after_dynamic_-_file_load_waterfall.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 48.755656108597286%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;after_dynamic_-_file_load_waterfall&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/584nohpIyKv2MHRDiFZYAc/2f4bda4e7f707f6bb1fbbc53b9579e8a/after_dynamic_-_file_load_waterfall.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/584nohpIyKv2MHRDiFZYAc/2f4bda4e7f707f6bb1fbbc53b9579e8a/after_dynamic_-_file_load_waterfall.png?w=442 442w,
https://images.ctfassets.net/rpmifyuylbfw/584nohpIyKv2MHRDiFZYAc/2f4bda4e7f707f6bb1fbbc53b9579e8a/after_dynamic_-_file_load_waterfall.png?w=884 884w,
https://images.ctfassets.net/rpmifyuylbfw/584nohpIyKv2MHRDiFZYAc/2f4bda4e7f707f6bb1fbbc53b9579e8a/after_dynamic_-_file_load_waterfall.png?w=1768 1768w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;번들 사이즈가 줄어들어서 200ms 훨씬 전에 로딩이 끝난다&lt;/p&gt;
&lt;h2 id=&quot;정리&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;테스트에 사용한 엡은 라우트가 6개밖에 되지 않으므로 사이즈가 작은 앱이라고 할 수 있다. 만약 앱의 규모가 이보다 크다면 dynamic import로 얻을 수 있는 효과도 더 커질 것으로 보인다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[프론트엔드 면접 핸드북 - 자바스크립트(3)]]></title><description><![CDATA[이 인터뷰 핸드북은  front-end-interview-handbook/javascript-questions 을 기반으로 정리한 것입니다. 41. ES6의 클래스와 ES5의 생성자 함수의 차이점 한줄 답변: 둘 다 생성자 역할을 하지만 상속을 구현하는 방법에서 큰 …]]></description><link>https://blog.rhostem.com//posts/2020-04-14-fe-interview-handbook-js-3</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-04-14-fe-interview-handbook-js-3</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Tue, 14 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 인터뷰 핸드북은 &lt;a href=&quot;https://github.com/yangshun/front-end-interview-handbook/blob/master/questions/javascript-questions.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;front-end-interview-handbook/javascript-questions&lt;/a&gt;을 기반으로 정리한 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;41-es6의-클래스와-es5의-생성자-함수의-차이점&quot;&gt;&lt;a href=&quot;#41-es6%EC%9D%98-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-es5%EC%9D%98-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;41. ES6의 클래스와 ES5의 생성자 함수의 차이점&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 둘 다 생성자 역할을 하지만 상속을 구현하는 방법에서 큰 차이가 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;간단한 생성자 함수와 클래스&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ES5 Function Constructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ES6 Class&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래는 상속을 구현한 코드. ES5 문법이 훨씬 더 길고 복잡하며 클래스 문법을 사용하면 간단하다.&lt;/p&gt;
&lt;p&gt;ES5에서는 생성자 함수를 상속받기 위해서 prototype에 새 객체를 복사하는 등 여러가지 작업이 필요하지만 클래스는 그런 과정이 필요없다. &lt;code class=&quot;language-text&quot;&gt;extends&lt;/code&gt; 키워드로 상속받을 함수를 명시하고, &lt;code class=&quot;language-text&quot;&gt;constructor&lt;/code&gt; 메소드에서 &lt;code class=&quot;language-text&quot;&gt;super(this)&lt;/code&gt; 만 추가하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ES5 Function Constructor&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Student&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; studentId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Call constructor of superclass to initialize superclass-derived members.&lt;/span&gt;
  Person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Initialize subclass&apos;s own members.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;studentId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; studentId&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

Student&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Student&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constructor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Student&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ES6 Class&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Student&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; studentId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;studentId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; studentId&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;42-화살표-함수를-어떻게-사용하면-좋은지-기존-함수와의-차이점은-무엇인지&quot;&gt;&lt;a href=&quot;#42-%ED%99%94%EC%82%B4%ED%91%9C-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%A2%8B%EC%9D%80%EC%A7%80-%EA%B8%B0%EC%A1%B4-%ED%95%A8%EC%88%98%EC%99%80%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EC%A7%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;42. 화살표 함수를 어떻게 사용하면 좋은지, 기존 함수와의 차이점은 무엇인지&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 간단한 문법, 함수 안의 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 선언된 scope의 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 가리키는 lexical scoping&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;우선 &lt;code class=&quot;language-text&quot;&gt;function&lt;/code&gt; 키워드가 필요없어서 함수 선언 문법이 훨씬 단순하다는 명백한 장점이 있다. 그리고 화살표 함수 안에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 는 화살표 함수를 포함한 스쿠프의 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 가리킨다. &lt;code class=&quot;language-text&quot;&gt;function&lt;/code&gt; 으로 선언된 함수 안에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 그 함수를 호출한 객체에 의해 결정된다.&lt;/p&gt;
&lt;p&gt;이러한 lexical-scope(문법적인 스쿠프)는 더 직관적이며 React 컴포넌트에서 콜백을 호출할 때 특히 유용하다. (컴포넌트 메소드 안에서 this가 메소드가 선언된 컴포넌트 인스턴스가 아니라 다른 객체 인스턴스를 가리킬 수 있기 때문이다. 컴포넌트 메소드를 화살표 함수로 선언하면 저런 문제가 사라진다.)&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;43-생성자-함수에서-메소드-선언에-화살표-함수를-선언하는-것의-장점은&quot;&gt;&lt;a href=&quot;#43-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%84%A0%EC%96%B8%EC%97%90-%ED%99%94%EC%82%B4%ED%91%9C-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%84%A0%EC%96%B8%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%98-%EC%9E%A5%EC%A0%90%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;43. 생성자 함수에서 메소드 선언에 화살표 함수를 선언하는 것의 장점은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 바뀌지 않는다. 항상 같은 객체를 가리킨다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;생성자 안에서 메소드를 화살표 함수로 선언하는 것의 가장 큰 장점은 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 가리키는 값이 함수 생성 시점에 결정된 후에 바뀌지 않는다는 것이다. 그러므로 생성자 함수가 새 객체를 만들면 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 는 언제나 그 객체를 가리킨다.&lt;/p&gt;
&lt;p&gt;아래의 예제에서 첫번째 메소드는 &lt;code class=&quot;language-text&quot;&gt;function&lt;/code&gt; 키워드를 사용해서, 두번째 메소드는 화살표 함수로 선언되어 있고, 둘 다 함 수 안에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 키워드를 참조한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Person&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; firstName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 일반 함수&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;sayName1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 화살표 함수&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;sayName2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; john &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;John&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dave &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Dave&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sayName1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// John&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 일반 함수의 this는 바뀔 수 있다. 하지만 화살표 함수에서는 바뀌지 않는다.&lt;/span&gt;
john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sayName1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Dave (call로 인해 this는 이제 dave 객체다)&lt;/span&gt;
john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sayName2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// John (call을 사용했지만 this는 여전히 john 객체다)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// apply도 call을 사용한 예제와 마찬가지로 바뀌지 않는다&lt;/span&gt;
john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sayName1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Dave&lt;/span&gt;
john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sayName2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// John&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// bind를 사용해도 마찬가지로 바뀌지 않는다&lt;/span&gt;
john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sayName1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Dave&lt;/span&gt;
john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sayName2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// John&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sayNameFromWindow1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sayName1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sayNameFromWindow1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// sayNameFromWindow1 변수는 전역 스쿠프에 속하며, 전역 스쿠프에서 this는 window 객체다.&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sayNameFromWindow2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; john&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sayName2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sayNameFromWindow2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// John. this는 변하지 않는다.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이처럼 화살표 함수 안에서 this는 바뀌지 않는다. 그러므로 화살표 함수를 어플리케이션의 어디에 가져다 둬도 함수 안에서 컨텍스트가 바뀔 걱정을 하지 않아도 된다.&lt;/p&gt;
&lt;p&gt;이는 React 클래스 컴포넌트에서 특히 유용하다. 만약 클릭 이벤트 핸들러로 사용할 컴포넌트 메소드를 일반 함수로 선언하고, 그 메소드를 자식 컴포넌트의 props로 전달해서 사용한다고 가정하자. 자식 컴포넌트에서 그 메소드가 실행될 때 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 자식 컴포넌트 인스턴스 객체를 가리키게 된다. 만약 메소드 안에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 사용하려 한다면 의도한 것과는 다른 결과가 발생할 것이다(&lt;code class=&quot;language-text&quot;&gt;Uncaught TypeError: undefined is not a function&lt;/code&gt; 같은 오류가 흔히 발생한다.)&lt;/p&gt;
&lt;p&gt;이를 방지하기 위해서는 상위 컴포넌트에서 메소드에 this를 bind해줘야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
     &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ChildComponent onChange&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 화살표 함수를 사용하면 저럴 필요가 없다. 메소드 안에서 this는 항상 그 메소드가 선언되어 있는 컴포넌트 객체를 가리킬 것이기 때문이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;44-고차-함수higher-order-function란&quot;&gt;&lt;a href=&quot;#44-%EA%B3%A0%EC%B0%A8-%ED%95%A8%EC%88%98higher-order-function%EB%9E%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;44. 고차 함수(higher-order function)란?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 파라미터로 함수를 받거나, 함수를 리턴하는 함수&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;고차 함수는 함수를 파라미터로 받거나 함수를 리턴하는 함수를 말한다. 반복적으로 실행되는 어떤 작업을 추상화시키는 수단으로 사용한다.&lt;/p&gt;
&lt;p&gt;고차 함수의 고전적인 예제는 &lt;code class=&quot;language-text&quot;&gt;Array.prototype.map&lt;/code&gt; 함수다. 배열을 기반으로 새로운 배열을 만들 때 사용하는데, 이런저런 과정을 생략하고 맵핑 로직만 전달하면 되기 때문에 코드가 무척 간결해진다.&lt;/p&gt;
&lt;p&gt;배열에 문자열로 구성되어 있고, 모든 요소에 &lt;code class=&quot;language-text&quot;&gt;toUpperCase&lt;/code&gt;를 적용해서 새 배열을 만들어야 한다고 하자. map 없이 구현하려면 아래와 같지만&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; names &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;irish&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;daisy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;anna&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;transformNamesToUppercase&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; results &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; names&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    results&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; results&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;transformNamesToUppercase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&apos;IRISH&apos;, &apos;DAISY&apos;, &apos;ANNA&apos;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;.map(transformerFn)&lt;/code&gt; 함수를 사용한다면 무척 간단해진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;transformNamesToUppercase&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; names&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;transformNamesToUppercase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;names&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&apos;IRISH&apos;, &apos;DAISY&apos;, &apos;ANNA&apos;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Array의 &lt;code class=&quot;language-text&quot;&gt;forEach&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;reduce&lt;/code&gt; 등도 모두 고차 함수다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hackernoon.com/effective-functional-javascript-first-class-and-higher-order-functions-713fde8df50a&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://hackernoon.com/effective-functional-javascript-first-class-and-higher-order-functions-713fde8df50a&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eloquentjavascript.net/05_higher_order.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://eloquentjavascript.net/05&lt;em&gt;higher&lt;/em&gt;order.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;45-구조-분해-할당destructuring-예제를-보여줄-수-있는가&quot;&gt;&lt;a href=&quot;#45-%EA%B5%AC%EC%A1%B0-%EB%B6%84%ED%95%B4-%ED%95%A0%EB%8B%B9destructuring-%EC%98%88%EC%A0%9C%EB%A5%BC-%EB%B3%B4%EC%97%AC%EC%A4%84-%EC%88%98-%EC%9E%88%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;45. 구조 분해 할당(destructuring) 예제를 보여줄 수 있는가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: ES6에서 도입된 객체, 배열의 멤버를 간단하게 추출할 수 있는 문법&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;구조 분해 할당은 ES6에 도입된 문법으로 객체와 배열의 값을 추출해서 변수에 바로 할당할 수 있는 간결하고 편리한 방법이다.&lt;/p&gt;
&lt;h3 id=&quot;배열-destructuring&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%EC%97%B4-destructuring&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배열 destructuring&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 예제 배열 선언&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;three&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;one&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; two&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; three&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 배열 내부 순서대로 변수에 할당된다.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;one&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;one&quot;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;two&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;two&quot;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;three&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;three&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 변수 교환&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 3&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;객체-destructuring&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4-destructuring&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체 destructuring&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 예제 변수 선언&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; o &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; q&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; q &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 객체의 필드 이름을 그대로 사용해야 한다&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 42&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;q&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; q&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; qValue &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 새로운 변수명을 제공할 수도 있다.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 42&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;qValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-1&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ponyfoo.com/articles/es6-destructuring-in-depth&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://ponyfoo.com/articles/es6-destructuring-in-depth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;46-문자열-생성에-있어-큰-유연성을-제공하는-템플릿-리터럴의-예제를-보여달라&quot;&gt;&lt;a href=&quot;#46-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%83%9D%EC%84%B1%EC%97%90-%EC%9E%88%EC%96%B4-%ED%81%B0-%EC%9C%A0%EC%97%B0%EC%84%B1%EC%9D%84-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A6%AC%ED%84%B0%EB%9F%B4%EC%9D%98-%EC%98%88%EC%A0%9C%EB%A5%BC-%EB%B3%B4%EC%97%AC%EB%8B%AC%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;46. 문자열 생성에 있어 큰 유연성을 제공하는 템플릿 리터럴의 예제를 보여달라&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: &lt;code class=&quot;language-text&quot;&gt;result is ${data}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;템플릿 리터럴은 문자열 삽입, 문자열 안에 변수 넣기 등을 간단하게 할 수 있도록 한다. ES6 이전에는 아래와 같이 해야 했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tyler&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;Hi, my name is &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; and I am &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; years old!&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// &apos;Hi, my name is Tyler and I am 28 years old!&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 템플릿 리터럴이 있으면 훨씬 간단해진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tyler&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`Hi, my name is &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; and I am &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; years old!`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// &apos;Hi, my name is Tyler and I am 28 years old!&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;템플릿 리터럴임을 명시하고 &lt;code class=&quot;language-text&quot;&gt;${}&lt;/code&gt; 같은 표현식을 사용하기 위해서는 홑따옴표(quote)가 아니라 backtick을 사용해야 한다.&lt;/p&gt;
&lt;p&gt;두번째로 유용한 점은 여러 줄로 구성된 문자열을 표현하기 더 쉽다는 것이다. ES6 전에는 개행 문자를 사용해서 표현해야 했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is line one.\nThis is line two.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This is line one.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This is line two.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 화면을 넘어가는 긴 문자열을 에디터에서 보기 좋게 작성하려면 아래와 같이 문자열을 분리할 필요도 있었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;This is line one.\n&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;This is line two.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This is line one.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This is line two.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 템플릿 리터럴을 사용하면 개행 문자를 사용하지 않더라도 여러 줄로 문자열을 표현할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`This is line one.
    This is line two.`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This is line one.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This is line two.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 다른 사용 방법은 문자열에 간단한 변수를 삽입해서 템플릿 라이브러리(ex. React)를 대체하는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tyler&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; age&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
      &amp;lt;div&gt;
        &amp;lt;p&gt;Name: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/p&gt;
        &amp;lt;p&gt;Name: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;age&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/p&gt;
      &amp;lt;/div&gt;
    `&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;innerHTML&lt;/code&gt;을 사용할 때 주의할 점은 &lt;strong&gt;XSS(cross site scripting 을 사용한 해킹)에 노출&lt;/strong&gt;될 수 있다는 사실이다. 사용자로부터 제공된 데이터(ex. 자기소개, 제품 후기 등)라면 사용하기 전에 항상 문자열을 &lt;strong&gt;sanitize&lt;/strong&gt;해야 한다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-2&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-2&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;47-curry-함수의-예제-그리고-curry-함수가-제공하는-장점은&quot;&gt;&lt;a href=&quot;#47-curry-%ED%95%A8%EC%88%98%EC%9D%98-%EC%98%88%EC%A0%9C-%EA%B7%B8%EB%A6%AC%EA%B3%A0-curry-%ED%95%A8%EC%88%98%EA%B0%80-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-%EC%9E%A5%EC%A0%90%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;47. curry 함수의 예제, 그리고 curry 함수가 제공하는 장점은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 함수 파라미터가 n개라면, 총 n번 호출해야 실제로 실행되는 함수를 만든다. 함수형 프로그래밍에 유용하다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;커링(curryin)은 파라미터가 1개 이상은 함수를 여러개로 분리하고, 연속으로 호출하면서 파라미터를 누적시킨 후 필요한 파라미터가 모두 제공되면 실제 함수를 호출하는 패턴이다.&lt;/p&gt;
&lt;p&gt;이 방법은 함수형으로 작성된 코드를 더 읽기 쉽고 조합하기 쉽도록 하는데 유용하다. 함수를 커링할 때 &lt;strong&gt;처음에 1개의 함수를 제공해야 하고, 파라미터를 1개씩 제공받는 함수로 쪼개진다&lt;/strong&gt;. 예를 들어 어떤 함수의 파라미터가 3개라면, 커링된 함수는 총 3번 호출해야 처음에 제공된 함수가 호출된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 커링에는 재귀 패턴을 사용한다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Function.prototype.length는 파라미터의 수&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; fn&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 커링 로직 재귀 함수&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// depth = 분리할 갯수(최대 재귀 호출 횟수), args = 현재까지 누적된 파라미터 배열&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_curried&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;depth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newArgument&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;depth &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 필요한 파라미터가 모두 쌓였다면 처음 제공된 함수 호출&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newArgument&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// 파라미터가 전부 쌓이지 않았다면, 커링 로직을 다시 호출한다.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_curried&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;depth &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newArgument&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_curried&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// add는 2개의 파라미터가 필요하다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; curriedAdd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;add&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// add 함수를 커링시킨다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; addFive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curriedAdd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 첫번째 파라미터에 5가 들어간 새로운 함수를 만든다.&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addFive&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [5, 6, 7, 8, 9, 10]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-3&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-3&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hackernoon.com/currying-in-js-d9ddc64f162e&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://hackernoon.com/currying-in-js-d9ddc64f162e&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;48-spread-문법과-장점과-rest-문법과의-차이점은&quot;&gt;&lt;a href=&quot;#48-spread-%EB%AC%B8%EB%B2%95%EA%B3%BC-%EC%9E%A5%EC%A0%90%EA%B3%BC-rest-%EB%AC%B8%EB%B2%95%EA%B3%BC%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;48. spread 문법과 장점과 rest 문법과의 차이점은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: spread ⇒ 데이터를 풀어놓는다, rest ⇒ 전달받은 데이터를 배열, 객체 안에 채워넣는다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ES6의 전개(spread) 문법은 함수형 패러다임으로 코드를 작성할 때 매우 유용하다. 배열이나 객체의 복제본을 &lt;code class=&quot;language-text&quot;&gt;Object.create&lt;/code&gt; 나 &lt;code class=&quot;language-text&quot;&gt;Array.prototype.slice&lt;/code&gt; , 또는 라이브러리 함수를 사용하지 않고도 간단하게 만들 수 있기 때문이다. 이 문법은 Redux나 RxJS 를 사용하는 프로젝트에서 자주 사용된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;putDookieInAnyArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dookie&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;putDookieInAnyArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;I&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;really&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;don&apos;t&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;like&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&quot;I&quot;, &quot;really&quot;, &quot;don&apos;t&quot;, &quot;like&quot;, &quot;dookie&quot;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Todd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  age&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;29&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; copyOfTodd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;person &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ES6의 rest 문법은 함수에 전달된 파라미터를 배열로 만들 수 있는 간단한 문법이다. 이것은 마치 spread 문법을 반대로 해놓은 것 같다. spread가 배열 안에 있는 데이터를 펼쳐놓는다면, rest 문법은 데이터를 받아서 배열에 채워넣기 때문이다.&lt;/p&gt;
&lt;p&gt;이 문법은 함수 파라미터, 배열와 객체 destructuring에도 사용할 수 있다. destructuring에서 배열 데이터는 새로운 배열로, 객체 데이터는 새로운 객체로 만들어진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addFiveToABunchOfNumbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;numbers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; numbers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addFiveToABunchOfNumbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [9, 10, 11, 12, 13, 14, 15]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;rest&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// a: 1, b: 2, rest: [3, 4]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;others &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  f&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  g&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  h&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// e: 1, f: 2, others: { g: 3, h: 4 }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-4&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;49-파일-사이에서-코드를-어떻게-공유하는가&quot;&gt;&lt;a href=&quot;#49-%ED%8C%8C%EC%9D%BC-%EC%82%AC%EC%9D%B4%EC%97%90%EC%84%9C-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B3%B5%EC%9C%A0%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;49. 파일 사이에서 코드를 어떻게 공유하는가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: CommonJS, AMD, ES modules 를 사용한다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;자바스크립트 환경에 따라 다르다&lt;/p&gt;
&lt;p&gt;클라이언트(브라우저 환경)에서는 변수, 함수가 global 스쿠프에 선언되기 때문에 모든 스크립트에서 서로 참조할 수 있다. 또는 RequireJS로 AMD(Asynchronous Module Definition)를 사용해서 모듈화시킬 수도 있다.&lt;/p&gt;
&lt;p&gt;서버(node.js)에서는 CommonJS를 사용한다. 각각의 파일은 모듈로 취급되며 &lt;code class=&quot;language-text&quot;&gt;module.exports&lt;/code&gt; 문법을 사용해서 변수, 함수를 내보낼 수 있다.&lt;/p&gt;
&lt;p&gt;ES2015는 AMD와 CommonJS를 대체할 수 있는 모듈 문법을 정의했다. 이것은 브라우저와 Node 환경에서 모두 사용할 수 있다. (&lt;code class=&quot;language-text&quot;&gt;import&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;export&lt;/code&gt;)&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-5&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://requirejs.org/docs/whyamd.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://requirejs.org/docs/whyamd.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nodejs.org/docs/latest/api/modules.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://nodejs.org/docs/latest/api/modules.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://2ality.com/2014/09/es6-modules-final.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://2ality.com/2014/09/es6-modules-final.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;50-static-클래스-멤버를-사용하는-이유는&quot;&gt;&lt;a href=&quot;#50-static-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%A9%A4%EB%B2%84%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EB%8A%94&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;50. static 클래스 멤버를 사용하는 이유는?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 객체 인스턴스의 영향 없음. 설정 값, 유틸리티 함수로 활용&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;static 클래스 멤버(속성, 메소드)는 특정한 클래스 인스턴스에 구속되지 않으며 어떤 인스턴스에서 참조하더라도 같은 값을 가진다. static 속성은 주로 설정값으로 사용하며, 메소드는 인스턴스의 상태값에 영향을 받지 않는 순수한 유틸리티 함수로 사용한다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-6&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-6&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/21155438/when-to-use-static-variables-methods-and-when-to-use-instance-variables-methods&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/21155438/when-to-use-static-variables-methods-and-when-to-use-instance-variables-methods&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;51-code-classlanguage-textletcode-code-classlanguage-textvarcode-code-classlanguage-textconstcode-를-사용해-선언된-변수의-차이점&quot;&gt;&lt;a href=&quot;#51-code-classlanguage-textletcode-code-classlanguage-textvarcode-code-classlanguage-textconstcode-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EC%84%A0%EC%96%B8%EB%90%9C-%EB%B3%80%EC%88%98%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;51. &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; 를 사용해 선언된 변수의 차이점&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: var는 함수 레벨 스쿠프. let, const는 블럭 레벨 스쿠프. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt; 키워드로 선언한 변수는 그 변수를 포함한 함수 스쿠프에 속한다. 만약 변수를 선언한 곳이 어떤 함수 안도 아니라면 전역 스쿠프에 속한다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; 는 함수 스쿠프가 아닌 블럭 스쿠프(함수, &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt;~&lt;code class=&quot;language-text&quot;&gt;else&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; 루프 등)에 속한다. 이는 가장 가까운 중괄호(&lt;code class=&quot;language-text&quot;&gt;{}&lt;/code&gt; )로 둘러싸인 영역 안에서만 접근 가능하다는 의미다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 모든 변수는 이 함수 안에서만 접근 가능하다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; bar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; qux &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;qux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// bar&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// baz&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;qux&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// qux&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ReferenceError: bar is not defined&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ReferenceError: baz is not defined&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;qux&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ReferenceError: qux is not defined&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; bar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; qux &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;qux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// var 키워드로 선언된 변수는 함수 스쿠프 어디에서든 접근 가능하다.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// bar&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// let, const 키워드로 선언된 변수를 중괄호 바깥에서는 접근 불가능하다.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ReferenceError: baz is not defined&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;qux&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ReferenceError: qux is not defined&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;호이스팅&quot;&gt;&lt;a href=&quot;#%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;호이스팅&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;는 호이스팅(hoisting)되지만 &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt;는 그렇지 않다. let, const로 선언된 변수를 선언되기 전에 참조하려 한다면 오류를 발생시킬 것이다. 대신 호이스팅된 변수는 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;로 나온다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ReferenceError: can&apos;t access lexical declaration &apos;baz&apos; before initialization&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ReferenceError: can&apos;t access lexical declaration &apos;bar&apos; before initialization&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;재선언&quot;&gt;&lt;a href=&quot;#%EC%9E%AC%EC%84%A0%EC%96%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;재선언&lt;/h3&gt;
&lt;p&gt;var 키워드로 같은 이름의 변수를 다시 선언하는 것은 가능하지만, let, const는 에러를 발생시킨다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;bar&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;qux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Uncaught SyntaxError: Identifier &apos;baz&apos; has already been declared&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;재할당&quot;&gt;&lt;a href=&quot;#%EC%9E%AC%ED%95%A0%EB%8B%B9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;재할당&lt;/h3&gt;
&lt;p&gt;let은 변수에 값을 재할당하는 것이 가능하지만, const는 불가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 아래 코드는 오류를 발생시킨다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
baz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;qux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-7&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-7&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[프론트엔드 면접 핸드북 - 자바스크립트(2)]]></title><description><![CDATA[이 인터뷰 핸드북은  front-end-interview-handbook/javascript-questions 을 기반으로 정리한 것입니다. 21. 이벤트 버블링에 대해서 설명하라 한줄 답변: DOM 이벤트가 상위 요소(element)로 전파되는 현상 DOM 요소에서…]]></description><link>https://blog.rhostem.com//posts/2020-04-13-fe-interview-handbook-js-2</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-04-13-fe-interview-handbook-js-2</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 13 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 인터뷰 핸드북은 &lt;a href=&quot;https://github.com/yangshun/front-end-interview-handbook/blob/master/contents/en/javascript-questions.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;front-end-interview-handbook/javascript-questions&lt;/a&gt;을 기반으로 정리한 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;21-이벤트-버블링에-대해서-설명하라&quot;&gt;&lt;a href=&quot;#21-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%84%A4%EB%AA%85%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;21. 이벤트 버블링에 대해서 설명하라&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: DOM 이벤트가 상위 요소(element)로 전파되는 현상&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;DOM 요소에서 이벤트가 발생했을 때, 거기에 등록된 이벤트 리스너 함수가 있으면 실행을 시도한다. 그리고 이벤트는 부모 요소를 통해 DOM 트리 위쪽으로 전파(bubbled up)되며 거쳐가는 DOM 요소마다 같은 일(이벤트 리스너 확인 및 실행)이 반복된다. 이 버블링은 최상위 요소인 &lt;code class=&quot;language-text&quot;&gt;document&lt;/code&gt;까지 진행된다. 이벤트 버블링은 이벤트 위임(delegation) 메카니즘 안에 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;22-attribute와-property의-차이점이-무엇인가&quot;&gt;&lt;a href=&quot;#22-attribute%EC%99%80-property%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%B4-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;22. attribute와 property의 차이점이 무엇인가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: attribute도 DOM property 중 하나다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;attribute는 HTML 마크업에 정의되어 있지만 property는 DOM 객체에 정의되어 있다. 차이점을 설명하기 위해, HTML 안에 다음의 텍스트 필드가 있다고 가정해보자&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Hello&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;DOM 트리에 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;HTMLInputElement&lt;/code&gt;&lt;/a&gt; 객체가 생성된다. 이 객체는 수많은 property를 가지고 있다. (accept, accessKey, align, alt, attributes, autofocus, baseURI, checked, childElementCount, childNodes, children, classList, className, clientHeight, etc.) attribute는 DOM 객체의 &lt;code class=&quot;language-text&quot;&gt;attributes&lt;/code&gt; property가 가진 요소들이다.&lt;/p&gt;
&lt;p&gt;DOM node가 생성되면 많은 수의 property가 attributes에 있는 요소와 같거나, 비슷한 이름을 가지고 있다. 하지만 모두 1대1 대응을 하지는 않는다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;the-input&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Name:&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;id property는 id attribute 값을 반영한다. id property를 바꾸면 id attribute도 변경을 반영한다.&lt;/p&gt;
&lt;p&gt;하지만, &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt; property는 그렇지 않다. value는 input 엘레멘트의 현재 입력값을 반영한다. 사용자가 입력하면 value property는 바뀌지만, value attribute는 초기값을 그대로 유지한다. 만약 사용자가 input 필드가 John을 입력했다면 아래와 같은 결과를 얻을 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; theInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;the-input&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
theInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// returns &quot;John&quot;&lt;/span&gt;
theInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// returns &quot;Name:&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;value property는 바뀌었지만 value attribute는 초기값인 &lt;code class=&quot;language-text&quot;&gt;Name:&lt;/code&gt; 그대로다. attribute와 property의 관계는 몇 가지로 나뉠 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;attribute를 바로 반영하고 이름까지 같은&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;rel&lt;/code&gt;,&lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;attribute 변경 반영하지만 이름이 조금 다른&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;htmlFor&lt;/code&gt;(property) - &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt;(attribute) , &lt;code class=&quot;language-text&quot;&gt;className&lt;/code&gt; - &lt;code class=&quot;language-text&quot;&gt;class&lt;/code&gt; 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;attribute 값을 반영하지만 수정에는 영향이 없는&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;href&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;disabled&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;multiple&lt;/code&gt; 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;관련 상세 스펙은 아래 링크에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflect&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflect&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;관련-자료&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/6003819/properties-and-attributes-in-html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/6003819/properties-and-attributes-in-html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;23-javascript-내장-객체를-확장하는-것이-왜-좋은-방법이-아닌가&quot;&gt;&lt;a href=&quot;#23-javascript-%EB%82%B4%EC%9E%A5-%EA%B0%9D%EC%B2%B4%EB%A5%BC-%ED%99%95%EC%9E%A5%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B4-%EC%99%9C-%EC%A2%8B%EC%9D%80-%EB%B0%A9%EB%B2%95%EC%9D%B4-%EC%95%84%EB%8B%8C%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;23. Javascript 내장 객체를 확장하는 것이 왜 좋은 방법이 아닌가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 나만 확장한 것이 아니라 다른 사람도 확장했다면? 충돌은?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;내장(built-in) 객체를 확장하는 것은 property/function을 prototype에 추가한다는 말이다. 처음에는 좋은 아이디어처럼 보일 지 몰라도, 실제로는 위험한 방법이다. 당신이 코드에서 2개의 라이브러리를 사용하는데, 둘 다 Array.prototype을 확장해 &lt;code class=&quot;language-text&quot;&gt;contain&lt;/code&gt;이라는 메소드를 추가해서 구현한 것이라고 가정해 보자. 두 라이브러리를 모두 불러오면, 마지막에 불러온 코드가 다른 라이브러리의 &lt;code class=&quot;language-text&quot;&gt;contain&lt;/code&gt; 메소드를 덮어쓰게 된다. 두 라이브러리가 &lt;code class=&quot;language-text&quot;&gt;contain&lt;/code&gt;을 같은 결과를 만들도록 구현했다면 문제가 없겠지만, 아니라면 어느 한 라이브러리는 문제를 일으킬 것이다.&lt;/p&gt;
&lt;p&gt;기본 객체를 확장해야 하는 경우는 대체 코드(polyfill)을 만들어야 할 때 뿐이다. 예를 들어 IE 브라우저가 지원하지 않는 &lt;code class=&quot;language-text&quot;&gt;Array.prototype.includes&lt;/code&gt;를 사용하려 할때, &lt;code class=&quot;language-text&quot;&gt;Array.prototype&lt;/code&gt;에 &lt;code class=&quot;language-text&quot;&gt;includes&lt;/code&gt; 메소드를 추가해야 할 필요가 생긴다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-1&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://lucybain.com/blog/2014/js-extending-built-in-objects/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://lucybain.com/blog/2014/js-extending-built-in-objects/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;24-document의-load-이벤트와-domcontentloaded의-차이점은&quot;&gt;&lt;a href=&quot;#24-document%EC%9D%98-load-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EC%99%80-domcontentloaded%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;24. document의 load 이벤트와 DOMContentLoaded의 차이점은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 실행 시점이 페이지에 포함된 리소스가 모두 로딩된 후인지 아닌지의 차이&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DOMContentLoaded&lt;/code&gt; 이벤트는 HTML 도큐멘트가 최초로 로딩되고 파싱되었을 때 발생한다. 스타일시트, 이미지, 서브프레임의 완전한 로딩은 기다리지 않는다. 그에 비해 &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt;의 &lt;code class=&quot;language-text&quot;&gt;load&lt;/code&gt; 이벤트는 DOM과 페이지가 포함한 모든 리소스가 로딩되었을 때 발생한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;25--과--연산자의-차이는&quot;&gt;&lt;a href=&quot;#25--%EA%B3%BC--%EC%97%B0%EC%82%B0%EC%9E%90%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%8A%94&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;25. === 과 == 연산자의 차이는?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: loose, strict equality&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;==&lt;/code&gt; 추상적인 등가 연산자, &lt;code class=&quot;language-text&quot;&gt;===&lt;/code&gt;는 엄격한(strict) 등가 연산자다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;==&lt;/code&gt;는 필요하다면 타입 변환을 하면서 비교를 하고, &lt;code class=&quot;language-text&quot;&gt;===&lt;/code&gt;는 있는 그대로 비교한다. 그래서 몇몇 케이스에서 두 연산자가 리턴하는 값이 다를 수 있다. &lt;code class=&quot;language-text&quot;&gt;==&lt;/code&gt;는 loose equality 연산자라고도 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;==&lt;/code&gt;는 사용하지 말아야 한다. 예외가 있다면 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;를 비교할 때는 편리한 측면도 있다. 물론 두 값이 가진 의미는 다르다. &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;은 명시적으로 이 값이 없음을, &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;는 변수에 아직 값이 할당되지 않았음을 의미한다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-2&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-2&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/359494/which-equals-operator-vs-should-be-used-in-javascript-comparisons&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/359494/which-equals-operator-vs-should-be-used-in-javascript-comparisons&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;26-자바스크립트와-관련한-same-origin-정책에-대해-설명&quot;&gt;&lt;a href=&quot;#26-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%99%80-%EA%B4%80%EB%A0%A8%ED%95%9C-same-origin-%EC%A0%95%EC%B1%85%EC%97%90-%EB%8C%80%ED%95%B4-%EC%84%A4%EB%AA%85&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;26. 자바스크립트와 관련한 same-origin 정책에 대해 설명&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 다른 도메인의 스크립트를 사용한 해킹 방지&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;동일 출처 원칙(same-origin policy)는 자바스크립트가 서로 다른 도메인 사이에 리퀘스트를 날리는 것을 방지한다. origin은 URI 스키마, 호스트네임, 그리고 포트 번호로 정의된다. 이 정책은 다른 도메인의 악의적인 스크립트가 타겟 웹페이지에서 DOM을 통해 민감한 정보를 가져가는 것을 방지한다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-3&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-3&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Same-origin_policy&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://en.wikipedia.org/wiki/Same-origin_policy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;27-다음의-함수를-구현하기&quot;&gt;&lt;a href=&quot;#27-%EB%8B%A4%EC%9D%8C%EC%9D%98-%ED%95%A8%EC%88%98%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;27. 다음의 함수를 구현하기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: Array.prototype.concat VS spread oprator&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;duplicate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [1,2,3,4,5,1,2,3,4,5]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// concat 사용&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;duplicate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ES6 문법 활용:&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;duplicate&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;28-왜-ternary-표현식이라-불리며-ternary이라는-단어가-가리키는-것은&quot;&gt;&lt;a href=&quot;#28-%EC%99%9C-ternary-%ED%91%9C%ED%98%84%EC%8B%9D%EC%9D%B4%EB%9D%BC-%EB%B6%88%EB%A6%AC%EB%A9%B0-ternary%EC%9D%B4%EB%9D%BC%EB%8A%94-%EB%8B%A8%EC%96%B4%EA%B0%80-%EA%B0%80%EB%A6%AC%ED%82%A4%EB%8A%94-%EA%B2%83%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;28. 왜 Ternary 표현식이라 불리며 “Ternary”이라는 단어가 가리키는 것은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 삼항 연산자 표현식. &lt;code class=&quot;language-text&quot;&gt;condition ? then : else&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;“Ternary”라는 단어는 “셋”을 의미한다. 그리고 Ternary 표현식은 3개의 연산자를 가진다.&lt;/p&gt;
&lt;p&gt;첫째로 조건문, 두번째로 then 표현식, 세번째로 else 표현식을 가진다. 삼항 표현식은 자바스크립트에서만 사용되는 것은 아니며 다른 언어에도 존재한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;condition &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; exprIfTrue &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; exprIfFalse&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-4&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;29-use-strict가-무엇인가-이것을-사용하는것의-장점과-단점은&quot;&gt;&lt;a href=&quot;#29-use-strict%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-%EC%9D%B4%EA%B2%83%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B2%83%EC%9D%98-%EC%9E%A5%EC%A0%90%EA%B3%BC-%EB%8B%A8%EC%A0%90%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;29. “use strict”가 무엇인가? 이것을 사용하는것의 장점과 단점은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 일부 기능을 제한한다는 의미. 오류 방지 목적을 가지며 단점보다는 장점이 많음&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;전체 코드나 함수에 strict mode를 적용하기 위해 사용한다. strict 모드의 선언은 자바스크립트 변형의 제한에 동의한다는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;장점&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;의도하지 않은 전역 변수가 선언되지 못하도록 한다&lt;/li&gt;
&lt;li&gt;삭제할 수 없는 속성(property)를 삭제하려고 시도하면 오류를 발생시킨다&lt;/li&gt;
&lt;li&gt;함수의 파라미터 이름은 서로 달라야 한다&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 는 전역 컨텍스트에서 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;다&lt;/li&gt;
&lt;li&gt;몇몇 일반적인 코딩 실수를 잡아서 예외 처리(throw exception)시킨다 (예를 들어 전역 객체에 접근하려고 하는 것)&lt;/li&gt;
&lt;li&gt;자바스크립트에서 개발자에게 혼란을 주거나, 잘못 만든 것으로 보이는 여러 기능의 사용을 금지한다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;못쓰게 되는 많은 기능들이 어떤 개발자에게는 필요한 기능일 수 있다&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;function.caller&lt;/code&gt; 그리고 &lt;code class=&quot;language-text&quot;&gt;function.arguments&lt;/code&gt; 에 접근할 수 없다&lt;/li&gt;
&lt;li&gt;서로 다른 strict mode로 작성된 코드를 연결했을 때 오류가 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;전체적으로, 단점보다는 장점이 더 많은 것으로 보인다. 그리고 나는 strict 모드에서 사용하지 못하게 하는 기능이 그렇게 필요했던 적이 없다. 나는 strict 모드를 사용할 것을 추천한다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-5&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://2ality.com/2011/10/strict-mode-hatred.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://2ality.com/2011/10/strict-mode-hatred.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://lucybain.com/blog/2014/js-use-strict/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://lucybain.com/blog/2014/js-use-strict/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;30-웹사이트의-전역-scope를-건드리지-않고-그대로-두는-것이-좋은-이유는&quot;&gt;&lt;a href=&quot;#30-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8%EC%9D%98-%EC%A0%84%EC%97%AD-scope%EB%A5%BC-%EA%B1%B4%EB%93%9C%EB%A6%AC%EC%A7%80-%EC%95%8A%EA%B3%A0-%EA%B7%B8%EB%8C%80%EB%A1%9C-%EB%91%90%EB%8A%94-%EA%B2%83%EC%9D%B4-%EC%A2%8B%EC%9D%80-%EC%9D%B4%EC%9C%A0%EB%8A%94&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;30. 웹사이트의 전역 scope를 건드리지 않고 그대로 두는 것이 좋은 이유는?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 변수와 함수 이름의 충돌을 방지하기 위해&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;모든 스크립트는 전역 스쿠프에 접근할 수 있다. 만약 모든 사람이 변수 선언에 전역 네임스페이스를 사용한다면 충돌이 매우 많이 발생할 것이다.&lt;/p&gt;
&lt;p&gt;모듈 패턴(IIFE 등)으로 직접 선언한 변수는 로컬 네임스페이스에 포함되도록 해야 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;31-spa-앱이-무엇인지-그리고-seo는-어떻게-해야-하는지&quot;&gt;&lt;a href=&quot;#31-spa-%EC%95%B1%EC%9D%B4-%EB%AC%B4%EC%97%87%EC%9D%B8%EC%A7%80-%EA%B7%B8%EB%A6%AC%EA%B3%A0-seo%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EC%A7%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;31. SPA 앱이 무엇인지, 그리고 SEO는 어떻게 해야 하는지&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 클라이언트 사이드 렌더링 웹사이트&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;최근 웹 개발자들은 그들의 제품을 웹 사이트보다는 “웹 앱”으로 부른다. 저 두 용어에 엄격한 차이는 없지만, 웹 앱은 더 상호작용성이 뛰어나고 동적이다. 유저들은 동작을 입력하는 즉시 반응을 얻을 수 있다. 전통적으로 웹사이트는 브라우저를 통해 서버에서 HTML을 받아서 렌더링하는 방식이었다. 사용자가 다른 URL로 이동하면 페이지 전체가 새로고침되고 서버가 이동할 URL에 맞는 새 HTML을 전달해준다. 이것을 서버 사이드 렌더링이라고 부른다.&lt;/p&gt;
&lt;p&gt;하지만 최신의 SPA에서는 클라이언트 사이드 렌더링을 사용한다. 브라우저는 서버에서 초기 페이지와 함께 앱에 필요한 스크립트(프레임워크, 라이브러리, 앱 코드 등을 포함), 스타일시트를 내려받는다. 사용자가 다른 페이지로 이동하면 페이지 새로고침은 발생하지 않는다. 페이지의 URL은 HTML5 History API를 통해 기록된다. 새 페이지에 필요한 새 데이터는 브라우저에서 AJAX 리퀘스트를 통해 서버에서 보통 JSON 포맷으로 가져온다. SPA는초기 로딩때 가져온 자바스크립트를 사용해 데이터와 페이지를 동적으로 업데이트한다. 이 모델은 모바일 네이티브 앱이 동작하는 방식과 유사하다.&lt;/p&gt;
&lt;h3 id=&quot;장점-1&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;사용자에게 앱이 보다 반응성있게 느껴지고 페이지 전환 사이에 화면 깜빡임과 새로고침 딜레이를 보지 않아도 된다.&lt;/li&gt;
&lt;li&gt;서버에 더 적은 수의 HTTP 리퀘스트가 간다. 이미지같은 요소가 페이지마다 로딩될 필요가 없기 때문&lt;/li&gt;
&lt;li&gt;서버와 클라이언트 사이의 확실한 역할 구분이 이뤄진다. 그렇기에 서버 코드의 수정 없이 다른 플랫폼(모바일, 챗봇, 스마트 시계 등)의 새로운 클라이언트를 쉽게 만들 수 있다. 또 API 스펙을 변경하지 않으면서 클라이언트와 서버의 기술 스택을 서로 독립적으로 변경할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점-1&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;스크립트, 스타일시트 등 필요한 리소스가 많아서 초기 로딩이 더 무거워진다.&lt;/li&gt;
&lt;li&gt;서버에서 모든 라우트로의 요청을 1개의 시작 라우트로 리다이렉트 되도록 설정해야 하고, 클라이언트 사이드에서도 라우팅을 직접 처리해야 한다.&lt;/li&gt;
&lt;li&gt;SPA는 컨텐츠의 렌더링을 자바스크립트에 의존한다. 하지만 모든 검색 엔진이 크롤링 중에 자바스크립트를 실행하지는 않는다. 이것은 검색 엔진 최적화(SEO)에 장애물이 된다. 하지만 대부분의 경우 앱을 만들 때 SEO는 가장 중요한 요소가 아니다. 그리고 모든 컨텐츠가 검색 엔진에 의해 인덱싱될 필요도 없다. SEO가 필요하다면 서버사이드 렌더링을 사용하거나 &lt;a href=&quot;https://prerender.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Prerender&lt;/a&gt;같은 서비스를 사용해 “자바스크립트로 브라우저에서 렌더링하고, HTML을 저장하고, 크롤러에게 제공”하도록 할 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;관련-자료-6&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-6&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/grab/front-end-guide#single-page-apps-spas&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/grab/front-end-guide#single-page-apps-spas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://stackoverflow.com/questions/21862054/single-page-app-advantages-and-disadvantages&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://stackoverflow.com/questions/21862054/single-page-app-advantages-and-disadvantages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.isquaredsoftware.com/presentations/2016-10-revolution-of-web-dev/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://blog.isquaredsoftware.com/presentations/2016-10-revolution-of-web-dev/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.freecodecamp.com/heres-why-client-side-rendering-won-46a349fadb52&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://medium.freecodecamp.com/heres-why-client-side-rendering-won-46a349fadb52&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;32-promise와-그것의-polyfill에-대해서는-얼만큼의-경험을-가지고-있는가&quot;&gt;&lt;a href=&quot;#32-promise%EC%99%80-%EA%B7%B8%EA%B2%83%EC%9D%98-polyfill%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C%EB%8A%94-%EC%96%BC%EB%A7%8C%ED%81%BC%EC%9D%98-%EA%B2%BD%ED%97%98%EC%9D%84-%EA%B0%80%EC%A7%80%EA%B3%A0-%EC%9E%88%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;32. Promise와 그것의 polyfill에 대해서는 얼만큼의 경험을 가지고 있는가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: Promise는 비동기적으로 값을 리턴하는 객체다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Promise(프로미스)는 지금이 아닌 나중에, 비동기적으로 값을 리턴할 수 있는 객체다. resolve된 값이나, 또는 reject된 이유(예를 들어 네트워크 오류가 발생했다던가)를 받을 수 있다.&lt;/p&gt;
&lt;p&gt;프로미스는 &lt;strong&gt;fulfilled&lt;/strong&gt;, &lt;strong&gt;rejected&lt;/strong&gt;, &lt;strong&gt;pending&lt;/strong&gt; 3가지의 중 하나의 상태를 가진다. 프로미스를 사용할 때 fulfilled, reject 상태로 변경될 때 콜백을 전달 할 수 있다.&lt;/p&gt;
&lt;p&gt;잘 알려진 polyfill로는 jquery &lt;code class=&quot;language-text&quot;&gt;$.deferred&lt;/code&gt; , Q, Bluebird가 있지만 전부 표준 스펙을 만족시키진 못한다. ES2015는 프로미스를 지원하며 이제 polyfill은 필요하지 않다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-7&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-7&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;33-콜백대신-프로미스를-사용하는-것의-장점과-단점은&quot;&gt;&lt;a href=&quot;#33-%EC%BD%9C%EB%B0%B1%EB%8C%80%EC%8B%A0-%ED%94%84%EB%A1%9C%EB%AF%B8%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%98-%EC%9E%A5%EC%A0%90%EA%B3%BC-%EB%8B%A8%EC%A0%90%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;33. 콜백대신 프로미스를 사용하는 것의 장점과 단점은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 비동기 작업을 더 쉽게 구현할 수 있다는 장점. 단점은 딱히..?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;장점-2&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90-2&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;가독성 떨어지는 콜백 지옥에서의 탈출&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;.then()&lt;/code&gt;을 사용해서 순차적인 비동기 작업을 가독성 있게 작성할 수 있다&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Promise.all()&lt;/code&gt;을 사용해서 병렬로 실행되는 비동기 작업을 쉽게 작성할 수 있다&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;프로미스가 있으면 콜백만 사용하는 코딩에서 발생하는 아래와 같은 일들이 발생하지 않는다&lt;/p&gt;
&lt;ul&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;li&gt;확인해야 하는 에러, 예외가 숨어버린다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점-2&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90-2&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;조금 더 복잡한 코드&lt;/li&gt;
&lt;li&gt;ES2015를 지원하지 않는 구형 브라우저에서는 사용할 수 없으며 폴리필이나 Babel을 통한 컴파일이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;34-자바스크립트로-컴파일되는-언어를-사용하는-것의-장단점&quot;&gt;&lt;a href=&quot;#34-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A1%9C-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%90%98%EB%8A%94-%EC%96%B8%EC%96%B4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%98-%EC%9E%A5%EB%8B%A8%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;34. 자바스크립트로 컴파일되는 언어를 사용하는 것의 장/단점&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 정적 타입 지원 등 자바스크립트의 한계를 보완해준다. 하지만 자바스크립트도 ES2015 이후 무척 좋아짐&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CoffeeScript, Elm, ClojureScript, PureScript, and TypeScript 등의 언어를 사용하는 것을 말한다.&lt;/p&gt;
&lt;h3 id=&quot;장점-3&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90-3&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;자바스크립트가 가지고 있는 오랜 문제점의 해결, 자바스크립트가 가진 안티 패턴(anti-patterns, 객체 확장, 전역 스쿠프 사용, 적절하지 않은 방법의 truthy, falsey 값 확인 등)을 사용하지 않도록 만드는 것.&lt;/li&gt;
&lt;li&gt;더 짧은 코드의 사용, 자바스크립트에 기반한 편리한 문법(syntactic sugar) 제공. (ES2015에서 많이 개선됨)&lt;/li&gt;
&lt;li&gt;(typescript를 사용하면) 오랜 기간 유지될 프로젝트에 정적 타입을 사용할 수 있어서 무척 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점-3&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90-3&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;브라우저만 자바스크립트만 지원하기 때문에 작성한 코드가 자바스크립트로 컴파일하는 과정이 추가로 필요하다&lt;/li&gt;
&lt;li&gt;소스 맵이 pre-compile된 소스에 제대로 매핑되지 않아서 디버깅이 무척 힘들 수 있다&lt;/li&gt;
&lt;li&gt;대부분의 개발자가 저들 언어에 익숙하지 않으며 학습이 필요하다. 결과적으로 당신의 프로젝트 관리를 위한 비용이 증가할 수 있다.&lt;/li&gt;
&lt;li&gt;관련 커뮤니티가 작은 언어는 그만큼 관련 리소스, 튜토리얼, 라이브러리, 툴이 부족한다.&lt;/li&gt;
&lt;li&gt;IDE/editor 지원이 빈약할 수 있다(TypeScript 는 VSCode가 제대로 지원함. Flow 페이스북의 Nuclide 프로젝트가 있었지만 현재 retire함)&lt;/li&gt;
&lt;li&gt;이들 언어는 항상 최신 자바스크립트 표준보다 뒤쳐져 있다.&lt;/li&gt;
&lt;li&gt;개발자들은 자기가 작성한 코드가 무엇으로 컴파일되는지 알아야 한다 - 왜냐면 실제로 동작하는 것은 그 컴파일 코드고, 결국에는 그것이 중요한 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ES2015는 대단한 개선이 이뤄져서 코드를 작성하기에 무척 좋다. 요즘에는 CoffeeScript에 대한 수요를 거의 보지 못했다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-8&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.htmlgoodies.com/beyond/javascript/three-javascript-anti-patterns-and-how-to-avoid-them.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Three JavaScript Anti-Patterns and How To Avoid Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://softwareengineering.stackexchange.com/questions/72569/what-are-the-pros-and-cons-of-coffeescript&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://softwareengineering.stackexchange.com/questions/72569/what-are-the-pros-and-cons-of-coffeescript&lt;/a&gt;ㅋ&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;35-자바스크립트-개발에-어떤-디버깅-툴을-사용하는지&quot;&gt;&lt;a href=&quot;#35-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%97%90-%EC%96%B4%EB%96%A4-%EB%94%94%EB%B2%84%EA%B9%85-%ED%88%B4%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EC%A7%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;35. 자바스크립트 개발에 어떤 디버깅 툴을 사용하는지?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: Chrome devtools, debugger, console.log, React, Redux Devtools&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;36-객체-속성-배열-항목-반복iterate에-어떤-방법을-사용하는가&quot;&gt;&lt;a href=&quot;#36-%EA%B0%9D%EC%B2%B4-%EC%86%8D%EC%84%B1-%EB%B0%B0%EC%97%B4-%ED%95%AD%EB%AA%A9-%EB%B0%98%EB%B3%B5iterate%EC%97%90-%EC%96%B4%EB%96%A4-%EB%B0%A9%EB%B2%95%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;36. 객체 속성, 배열 항목 반복(iterate)에 어떤 방법을 사용하는가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 객체 - &lt;code class=&quot;language-text&quot;&gt;for-in&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;hasOwnProperty&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Object.keys&lt;/code&gt;, 베열 - &lt;code class=&quot;language-text&quot;&gt;for-of&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;1-객체&quot;&gt;&lt;a href=&quot;#1-%EA%B0%9D%EC%B2%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 객체&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;for-in 루프&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;for (var property in obj) { console.log(property); }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;하지만 상속받은 속성도 포함한다. 그래서 &lt;code class=&quot;language-text&quot;&gt;obj.hasOwnproperty(property)&lt;/code&gt; 를 사용해서 검사해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Object.keys(obj).forEach(property ⇒ { ... })&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Object.keys&lt;/code&gt; 는 static 메서드이며 객체가 가진 열거 가능한(enumerable) 모든 속성을 배열로 만들어서 리턴한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Object.getOwnPropertyNames()&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Object.getOwnPropertyNames(obj).forEach(property =&amp;gt; { ... })&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;getOwnPropertyNames&lt;/code&gt;는 객체가 가진 모든 열거 가능한, 열거 불가능한(non-enumerable) 값을 배열에 담아 리턴한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;참조-enumarable-non-enumerable의-차이&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EC%A1%B0-enumarable-non-enumerable%EC%9D%98-%EC%B0%A8%EC%9D%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참조) enumarable, non-enumerable의 차이&lt;/h4&gt;
&lt;p&gt;enumerable 속성이란 내부적으로 enumerable 플래그가 &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt; 값으로 지정되어 있는 속성이다. 그 값은 &lt;code class=&quot;language-text&quot;&gt;Object.getOwnPropertyDescriptor&lt;/code&gt; 메소드로 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOwnPropertyDescriptor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; writable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; enumerable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; configurable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;enumerable 속성은 &lt;code class=&quot;language-text&quot;&gt;for ... in&lt;/code&gt; 루프를 실행했을 때 나타난다.&lt;/p&gt;
&lt;h3 id=&quot;2-배열&quot;&gt;&lt;a href=&quot;#2-%EB%B0%B0%EC%97%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 배열&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; 루프&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;for (var i = 0; i &amp;lt; arr.length; i++)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ES2015에서는 변수명 충돌을 방지하기 위해 var 대신 블럭 스쿠프 변수 키워드 &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; 을 사용하는 것을 추천한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;forEach&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;arr.forEach(function (el, index) { ... })&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;이 방법은 index 변수가 필요없다면 굳이 선언하지 않아도, 코드에서 생략해도 되기 때문에 더 편리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;for - of&lt;/code&gt; 루프&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;for (let elem of arr) { ... }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ES6는 새로운 반복문을 제공한다. &lt;code class=&quot;language-text&quot;&gt;for - of&lt;/code&gt; 문법은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterable_protocol&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Iteration protocols&lt;/a&gt;을 따르는 객체 (String, Array, Map, Set 등)로 반복문을 만들 수 있다. for 루프와 forEach 메소드의 장점을 조합한 것이라 할 수 있다. for 루프는 반복문을 중간에 멈추게 할 수 있고, forEach는 index 값이 필요없기 때문에 for 루프보다 더 간결한 문법을 가진다. for - of 문법은 둘의 장점을모두 가지고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;for 루프보다는 forEach 메소드를 더 많이 사용한다. 하지만 이제 ES6를 사용할 수 있으므로, for - of 문법을 더 많이 활용한다.&lt;/p&gt;
&lt;p&gt;만약 for - of 루프를 사용하면서 배열의 값과 인덱스가 모두 필요하다면, &lt;code class=&quot;language-text&quot;&gt;Array.prototype.entries&lt;/code&gt; 메소드와 구조분해할당(destructuring)을 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; arr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; elem&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; arr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;: &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; elem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-9&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://2ality.com/2015/08/getting-started-es6.html#from-for-to-foreach-to-for-of&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://2ality.com/2015/08/getting-started-es6.html#from-for-to-foreach-to-for-of&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Enumerability_and_ownership_of_properties&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Enumerability and ownership of properties - JavaScript | MDN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;37-mutable-immutable-객체의-차이점&quot;&gt;&lt;a href=&quot;#37-mutable-immutable-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;37. mutable, immutable 객체의 차이점&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 이미 선언된 값의 수정 가능, 불가능 여부&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;mutability = 가변성, immutability = 불변성&lt;/li&gt;
&lt;li&gt;mutable = 가변의, immutable = 불변의&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;불변성은 함수형 프로그래밍에서 핵심 개념이다. 그리고 객체 지향 프로그래밍에도 많은 것을 제공한다. 가변(mutable) 객체는 만들어진 후에 그 값이 변경될 수 있는 객체다. 불변(immutable) 객체는 만들어진 후 변경될 수 없다.&lt;/p&gt;
&lt;h3 id=&quot;자바스크립트에서-불변-객체의-예제&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-%EB%B6%88%EB%B3%80-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%98%88%EC%A0%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자바스크립트에서 불변 객체의 예제&lt;/h3&gt;
&lt;p&gt;자바스크립트에서 몇몇 내장 타입(숫자, 문자열)은 불변이다. 하지만 커스텀 객체는 일반적으로 가변적이다. 불변 객체로는 &lt;code class=&quot;language-text&quot;&gt;Math&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Date&lt;/code&gt; 가 있다.&lt;/p&gt;
&lt;h3 id=&quot;객체-상수-속성&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4-%EC%83%81%EC%88%98-%EC%86%8D%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체 상수 속성&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;writable: false&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;configurable: false&lt;/code&gt;를 조합해서 객체 속성을 상수(변경, 삭제, 재할당이 불가능함)로 만들 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; myObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defineProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myObject&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;number&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  writable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  configurable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myObject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 42&lt;/span&gt;
myObject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;43&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myObject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 42&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;객체-확장-방지&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4-%ED%99%95%EC%9E%A5-%EB%B0%A9%EC%A7%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체 확장 방지&lt;/h3&gt;
&lt;p&gt;객체에 새로운 속성이 추가되는 것을 방지하려면, &lt;code class=&quot;language-text&quot;&gt;Object.preventExtensions&lt;/code&gt; 메소드를 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; myObject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  a&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventExtensions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myObject&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

myObject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
myObject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;strict 모드에서는 속성 &lt;code class=&quot;language-text&quot;&gt;b&lt;/code&gt;를 할당하는 과정에서 &lt;code class=&quot;language-text&quot;&gt;TypeError&lt;/code&gt; 오류가 발생한다.&lt;/p&gt;
&lt;h3 id=&quot;seal&quot;&gt;&lt;a href=&quot;#seal&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Seal&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Object.seal&lt;/code&gt; 메소드는 봉인된(sealed) 객체를 만든다. 새로운 속성을 추가할 수 없고, 속성을 제거하지도 새로운 값을 할당하지도 못한다. &lt;code class=&quot;language-text&quot;&gt;Object.preventExtension&lt;/code&gt; 메소드에 모든 속성의 &lt;code class=&quot;language-text&quot;&gt;configurable&lt;/code&gt; 값을 false로 만드는 기능을 더한 것이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;configurable&lt;/code&gt;
이 속성의 값을 변경할 수 있고, 대상 객체에서 삭제할 수도 있다면 &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;. 기본값은 &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;freeze&quot;&gt;&lt;a href=&quot;#freeze&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Freeze&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Object.freeze&lt;/code&gt; 메소드는 동결된(freezed) 객체를 만든다. 동결된 객체는 속성의 추가, 제거, 값 변경이 불가능하다. 그리고 동결된 객체는 프로토타입이 변경되는 것도 방지한다. 이 메소드는 자바스크립트 객체에 불병성을 부여할 수 있는 방법 중 가장 강력한 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; immutable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;freeze&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Object.freeze&lt;/code&gt;는 동결된 버전의 새 객체를 만드는 것이 아니라, 파라미터에 전달된 객체를 동결된 상태로 만들어서 그대로 반환한다.&lt;/p&gt;
&lt;h3 id=&quot;장점-4&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90-4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;변경 탐지를 더 쉽게 할 수 있다. 객체 등가 비교가 레퍼런스 비교를 통해서 쉽고 빠르게 가능하다. 이는 React와 Redux에서 객체 변경을 확인하는 데 유용하다.&lt;/li&gt;
&lt;li&gt;불변 객체를 사용한 프로그램은 파악하는데 덜 복잡하다. 왜냐하면 객체가 시간이 지나면서 바뀌는 상황을 고려할 필요가 없기 때문이다.&lt;/li&gt;
&lt;li&gt;함수에 불변 객체를 파라미터로 전달하거나, 리턴값으로 받는다면 객체의 방어적 복사본(defensive copy)은 더 이상 필요하지 않다. 왜냐하면 전달한 객체가 함수 안에서 변경될 걱정이 없기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;defensive copy. 원본과 동일한 복사본을 만드는 것. 함수에 원본의 레퍼런스를 전달하는 대신, 복사본의 레퍼런스를 전달한다. 함수를 실행 후에도 원본 값에는 영향을 주지 않기 위한 목적으로 만든다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;객체를 캐싱해서 여러 번 재사용할 수 있다.&lt;/li&gt;
&lt;li&gt;스레드에서의 안전성 - 여러 스레드가 동시에 실행되는 환경에서도 값이 변경될 위험이 없으므로 안전하다.&lt;/li&gt;
&lt;li&gt;ImmutableJS 같은 라이브러리를 사용하면 여러 객체가 비슷한 구조를 공유하기 때문에 메모리를 더 적게 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점-4&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90-4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;매번 새로운 객체를 만드는 식으로 불변 데이터 구조를 가볍게 구현하면 퍼포먼스가 무척 나빠진다. 효율적인 불변 데이터 활용을 위해서는 라이브러리를 사용하는 것이 추천된다.&lt;/li&gt;
&lt;li&gt;이미 있는 객체의 일부를 수정하지 않고 많은 작은 객체를 할당(allocation), 그리고 할당 해제(deallocation)한다면 성능에 문제를 야기할 수 있다. 할당자(allocator)와 가비지 컬렉터(garbage collector)의 복잡성은 보통 heap 메모리 영역에 있는 객체의 수에 따라 다르다.&lt;/li&gt;
&lt;li&gt;그래프 같은 순환 데이터 구조는 만들기 어렵다. 만약 수정, 초기화도 될 수 없는 두 객체가 있다고 한다면 두 객체가 어떻게 서로를 참조하도록 할 것인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;관련-자료-10&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-10&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1863515/pros-cons-of-immutability-vs-mutability&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/1863515/pros-cons-of-immutability-vs-mutability&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;38-코드를-작성할-때-불변성을-어떻게-구현하는지&quot;&gt;&lt;a href=&quot;#38-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%A0-%EB%95%8C-%EB%B6%88%EB%B3%80%EC%84%B1%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B5%AC%ED%98%84%ED%95%98%EB%8A%94%EC%A7%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;38. 코드를 작성할 때 불변성을 어떻게 구현하는지&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt;, spread opeartor, &lt;code class=&quot;language-text&quot;&gt;Object.assign&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Array.prototype.concat&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;우선 immutable.js, mori, immer같은 라이브러리를 사용하는 방법이 있다.&lt;/p&gt;
&lt;p&gt;대안으로는 &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; 를 사용한 변수의 선언과 객체 생성에 앞서 언급한 방법들을 조합하는 방법이 있다. 객체를 수정(mutation)을 위해서는 전개 연산자(spread operator), &lt;code class=&quot;language-text&quot;&gt;Object.assign&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Array.prototype.concat&lt;/code&gt; 등의 메소드를 사용해서 원본 객체를 수정하는 대신 새 객체를 만든다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Array Example&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; arr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newArr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [1, 2, 3, 4]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Object Example&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; human &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;freeze&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; race&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;human&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; john &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;human&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;John&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// {race: &quot;human&quot;, name: &quot;John&quot;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; alienJohn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;john&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; race&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;alien&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// {race: &quot;alien&quot;, name: &quot;John&quot;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-11&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-11&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.sitepoint.com/immutability-javascript/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://www.sitepoint.com/immutability-javascript/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wecodetheweb.com/2016/02/12/immutable-javascript-using-es6-and-beyond/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://wecodetheweb.com/2016/02/12/immutable-javascript-using-es6-and-beyond/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;39-동기-비동기-함수의-차이점&quot;&gt;&lt;a href=&quot;#39-%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;39. 동기, 비동기 함수의 차이점&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 코드의 순차적인 실행 vs 콜백에 전달된 코드를 나중에 실행&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;동기 함수는 블로킹(blocking)이 발생하지만, 비동기 함수는 그렇지 않다.&lt;/p&gt;
&lt;p&gt;동기 함수는 앞에 있는 코드의 실행이 완료되어야 다음으로 넘어간다. 실행 순서가 보장되지만, 어떤 코드의 실행 시간이 길어진다면 프로그램이 멈춘 것처럼 보이게 될 것이다.&lt;/p&gt;
&lt;p&gt;비동기 함수는 보통 콜백 함수를 파라미터로 받고 실행 즉시 다음 라인에 있는 코드로 넘어간다. 콜백은 비동기 작업이 완료되고 콜 스택이 비었을 때 실행된다. 웹 서버로부터 데이터를 불러오거나 데이터베이스 쿼리를 실행하는 것 같은 무거운 작업은 비동기적으로 실행되어야 한다. 그래야 메인 스레드가 그 긴 작업 시간동안 멈추지 않고 진행될 수 있다. 브라우저에서 저런 작업을 비동기로 하지 않는다면 UI가 사용자의 입력에 반응하지 않고 멈춰버릴 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;40-이벤트-루프란-무엇인가-콜-스택과-태스크-큐의-차이점은&quot;&gt;&lt;a href=&quot;#40-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-%EC%BD%9C-%EC%8A%A4%ED%83%9D%EA%B3%BC-%ED%83%9C%EC%8A%A4%ED%81%AC-%ED%81%90%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;40. 이벤트 루프란 무엇인가? 콜 스택과 태스크 큐의 차이점은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 동기 함수는 스택에 즉시 추가, 비동기 콜백은 태스크 큐에 대기하고 있다가 이벤트 루프가 스택에 추가&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이벤트 루프는 싱글 스레드 루프(loop)로 콜 스택을 모니터링하면서 태스크 큐에 실행해야 할 작업이 있는지 확인한다. 만약 콜 스택이 비어있고 태스크 큐에 콜백 함수가 있다면, 함수는 큐에서 제거되고 실행을 위해 콜 스택에 추가(push)된다.&lt;/p&gt;
&lt;p&gt;이벤트 루프에 대해서 더 자세한 내용은 유튜브에서 많은 조회수를 기록한 &lt;a href=&quot;https://www.youtube.com/watch?v=8aGhZQkoFbQ&amp;#x26;feature=emb_title&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;What the heck is the event loop anyway?&lt;/a&gt; 동영상을 보길 권한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5eDtzPLYSBHbzypfyHkgrZ/22ac6cafe98f5642011b2c45d7b0d155/_2020-03-25__10.19.34_.jpg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 55.21327014218009%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;이벤트 루프 동영상 스크린샷&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5eDtzPLYSBHbzypfyHkgrZ/22ac6cafe98f5642011b2c45d7b0d155/_2020-03-25__10.19.34_.jpg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5eDtzPLYSBHbzypfyHkgrZ/22ac6cafe98f5642011b2c45d7b0d155/_2020-03-25__10.19.34_.jpg?w=317 317w,
https://images.ctfassets.net/rpmifyuylbfw/5eDtzPLYSBHbzypfyHkgrZ/22ac6cafe98f5642011b2c45d7b0d155/_2020-03-25__10.19.34_.jpg?w=633 633w,
https://images.ctfassets.net/rpmifyuylbfw/5eDtzPLYSBHbzypfyHkgrZ/22ac6cafe98f5642011b2c45d7b0d155/_2020-03-25__10.19.34_.jpg?w=1266 1266w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;동영상 스크린샷&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; 같은 동기 함수는 스택에 바로 들어가고 실행된다. 하지만 비동기 web api인 setTimeout은 스택에 들어간 후 바로 지워지고, 대신 웹브라우저에서 타이머를 실행시킨다. 타이머가 끝나면, 콜백(&lt;code class=&quot;language-text&quot;&gt;cb&lt;/code&gt;)을 태스크 큐에 추가하고, 모니터링하고 있던 이벤트 루프가 콜백을 스택에 추가해서 실행한다. 콜백 안에 비동기 함수가 있다면 비슷한 과정이 반복될 것이다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-12&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-12&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://theproactiveprogrammer.com/javascript/the-javascript-event-loop-a-stack-and-a-queue/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://theproactiveprogrammer.com/javascript/the-javascript-event-loop-a-stack-and-a-queue/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[프론트엔드 면접 핸드북 - 자바스크립트(1)]]></title><description><![CDATA[이 인터뷰 핸드북은  front-end-interview-handbook/javascript-questions 을 기반으로 정리한 것입니다. 1. 이벤트 위임(event delegation)에 대해서 설명하라 한줄 답변: 부모 요소에 이벤트 리스너를 붙이는 것 이벤트…]]></description><link>https://blog.rhostem.com//posts/2020-04-12-fe-interview-handbook-js-1</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-04-12-fe-interview-handbook-js-1</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 12 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 인터뷰 핸드북은 &lt;a href=&quot;https://github.com/yangshun/front-end-interview-handbook/blob/master/contents/en/javascript-questions.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;front-end-interview-handbook/javascript-questions&lt;/a&gt;을 기반으로 정리한 것입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-이벤트-위임event-delegation에-대해서-설명하라&quot;&gt;&lt;a href=&quot;#1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%9C%84%EC%9E%84event-delegation%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%84%A4%EB%AA%85%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 이벤트 위임(event delegation)에 대해서 설명하라&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 부모 요소에 이벤트 리스너를 붙이는 것&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이벤트 위임이란 &lt;strong&gt;이벤트 리스너를 부모 요소(element)에 붙이는 것&lt;/strong&gt;을 말한다. 하위 요소에 이벤트가 발생하면 &lt;strong&gt;이벤트 버블링&lt;/strong&gt; 때문에 부모 요소에 연결된 리스너가 실행된다. 이 테크닉의 장점은&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용하는 메모리의 양(&lt;a href=&quot;https://www.google.com/url?sa=t&amp;#x26;rct=j&amp;#x26;q=&amp;#x26;esrc=s&amp;#x26;source=web&amp;#x26;cd=3&amp;#x26;cad=rja&amp;#x26;uact=8&amp;#x26;ved=2ahUKEwi04K-d-InoAhWEHXAKHUyWABAQFjACegQIEhAG&amp;#x26;url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FMemory_footprint&amp;#x26;usg=AOvVaw2kEPjndmdUxQ9RGivs2S2j&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;memory footprint&lt;/a&gt;)이 감소한다. 이벤트가 발생하는 모든 요소마다 리스너를 추가할 필요가 없고 부모에 하나만 추가하면 되기 때문이다.&lt;/li&gt;
&lt;li&gt;이벤트 발생하는 요소가 추가되고 제거될때마다 리스너를 추가하고 제거할 필요가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;관련-자료&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://davidwalsh.name/event-delegate&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://davidwalsh.name/event-delegate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-this-키워드는-어떻게-동작하는지-설명하라&quot;&gt;&lt;a href=&quot;#2-this-%ED%82%A4%EC%9B%8C%EB%93%9C%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94%EC%A7%80-%EC%84%A4%EB%AA%85%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. this 키워드는 어떻게 동작하는지 설명하라&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 가리키는 것은 코드의 실행 위치, 실행 방법에 따라 달라진다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;에 대한 간단한 설명은 없다. 자바스크립트에서 가장 헷갈리는 개념 중 하나라고 할 수 있다. 그래도 설명하자면 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;의 값은 &lt;strong&gt;함수가 어떻게 실행되느냐에 따라&lt;/strong&gt; 달라진다. 그리고 가장 깔끔하다고 생각하는 &lt;a href=&quot;https://medium.com/@arnav_aggarwalhttps://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3https://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Arnav Aggrawal의 글&lt;/a&gt;에 의하면 다음의 규칙들로 정리할 수 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;함수 실행에 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드를 사용하면, 그 함수 안에서 this 키워드는 새로운 객체를 가리킨다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;apply&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;bind&lt;/code&gt;를 사용해 함수를 호출/생성 했다면, 함수 안에서 this 키워드는 apply, call, bind 호출시 전달된 객체를 가리킨다.&lt;/li&gt;
&lt;li&gt;함수가 객체의 메소드로서 호출되었다면, &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 그 함수를 속성(property)으로 추가한 객체를 가리킨다.&lt;/li&gt;
&lt;li&gt;함수가 단순히 실행되었다면, 다시말해 위의 3가지 사례를 제외한 방식으로 호출되었다면, &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 전역 객체를 가리킨다. 브라우저에서는 &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt;, node에서는 &lt;code class=&quot;language-text&quot;&gt;global&lt;/code&gt;이 된다. 만약 strict 모드(‘use strict’)에서는 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 전역 객체 대신 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;가 된다.&lt;/li&gt;
&lt;li&gt;만약 위의 4가지 조건이 중첩된다면, 먼저 제시한 규칙이 적용된다. (1번 규칙부터 우선함)&lt;/li&gt;
&lt;li&gt;함수가 ES2015의 화살표 함수라면, 위의 규칙을 모두 무시하고, 함수가 만들어진 시점에서 그 함수를 둘러싼 scope의 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 가리킨다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;관련-자료-1&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Arrow function expressions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/3127440/1751946&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/a/3127440/1751946&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-프로토타입-상속이-어떻게-동작하는지-설명하라&quot;&gt;&lt;a href=&quot;#3-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85-%EC%83%81%EC%86%8D%EC%9D%B4-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94%EC%A7%80-%EC%84%A4%EB%AA%85%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 프로토타입 상속이 어떻게 동작하는지 설명하라&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 어떤 속성이 내 객체에 있으면 사용하고, 없으면 프로토타입 객체에 있는 것을 사용한다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;자바스크립트 인터뷰에서 무척 자주 나오는 질문이다. 모든 자바스크립트 객체는 &lt;code class=&quot;language-text&quot;&gt;__proto__&lt;/code&gt;라는 속성을 가지고 있다. 그것은 다른 객체를 가리키는 레퍼런스며, 객체의 “prototype”이라고 불린다. 객체에서 어떤 속성(property)을 사용하려 하는데 그 객체에 없다면 자바스크립트 엔진은 &lt;code class=&quot;language-text&quot;&gt;__proto__&lt;/code&gt; 객체에 그 속성이 있는지 확인한다. 만약 거기에도 없다면 다시 &lt;code class=&quot;language-text&quot;&gt;__proto__&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;__proto__&lt;/code&gt; 객체에 있는지 확인하는 식으로 반복 확인하면서 프로토타입 체인의 끝까지 탐색한다. 이 동작은 전통적인 상속(inheritance)처럼 보이지만 &lt;a href=&quot;https://davidwalsh.name/javascript-objects&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;상속 보다는 위임&lt;/a&gt;에 더 가깝다.&lt;/p&gt;
&lt;h3 id=&quot;예제&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%A0%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예제&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/create&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Object.create&lt;/a&gt;(지정된 프로토타입 객체 및 속성을 갖는 새 객체를 생성)의 polyfill 코드.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;function&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Tmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    Tmp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Parent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Parent&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Parent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;greet&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello from Parent&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; child &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Parent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

child&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;cry&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;waaaaaahhhh!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

child&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Outputs: waaaaaahhhh!&lt;/span&gt;

child&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Outputs: hello from Parent&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-2&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-2&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://dmitrysoshnikov.com/ecmascript/javascript-the-core/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://dmitrysoshnikov.com/ecmascript/javascript-the-core/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.quora.com/What-is-prototypal-inheritance/answer/Kyle-Simpson&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://www.quora.com/What-is-prototypal-inheritance/answer/Kyle-Simpson&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://davidwalsh.name/javascript-objects&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://davidwalsh.name/javascript-objects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://crockford.com/javascript/prototypal.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://crockford.com/javascript/prototypal.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance&lt;em&gt;and&lt;/em&gt;the&lt;em&gt;prototype&lt;/em&gt;chain&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-amd-vs-commonjs에-대해-어떻게-생각하는가&quot;&gt;&lt;a href=&quot;#4-amd-vs-commonjs%EC%97%90-%EB%8C%80%ED%95%B4-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%83%9D%EA%B0%81%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. AMD vs CommonJS에 대해 어떻게 생각하는가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 비동기 vs 동기 모듈. 이제는 Javascript module에 의해 대통합.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;둘다 모듈 시스템을 구현하는 방식이다. ES2015 전까지 자바스크립트는 모듈 시스템을 직접 지원하지 않았다. CommonJS는 동기적인데 비해 AMD는 이름 그대로(Asynchronous Module Definition) 비동기적이다. CommonJS는 서버사이드 개발에 맞춰 디자인되었지만 AMD는 비동기적인 모듈 로딩을 지원하므로 보다 브라우저를 위한 시스템이라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;나는 AMD의 문법이 다소 장황하며 CommonJS의 문법이 다른 언어에서 사용하는 import 문법과 비슷하다고 본다. 현재 사용하고 있는 개발 시스템에서 나는 AMD가 필요없어 보인다. 왜냐면 모든 소스가 babel로 컴파일되고 하나의 파일로 번들링된 후 앱이 시작하기 때문이다. 여기서는 비동기 로딩이라는 AMD의 장점을 취할 수 없다. 그리고 CommonJS는 Node의 모듈 작성 방식과 비슷하기 때문에 클라이언트 사이드와 서버 사이드 개발 사이에 모듈 작성 방식의 차이에서 발생하는 비용이 크지 않다.&lt;/p&gt;
&lt;p&gt;나는 ES2015의 모듈이 만족스럽다. 동기적, 비동기적 모듈 로딩을 모두 지원한다. 물론 ES2015가 브라우저와 노드에 완전히 탑재되지는 않았다. 하지만 Babel이라는 트랜스파일러가 있으므로 우리는 드디어 하나의 방법만 사용할 수 있게 되었다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-3&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-3&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://auth0.com/blog/javascript-module-systems-showdown/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://auth0.com/blog/javascript-module-systems-showdown/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/16521471/relation-between-commonjs-amd-and-requirejs&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/16521471/relation-between-commonjs-amd-and-requirejs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;5-다음의-코드-code-classlanguage-textfunction-foo-code는-왜-iife처럼-실행되지-않는가-iife가-되게-하려면-어떤-부분을-수정해야-하나&quot;&gt;&lt;a href=&quot;#5-%EB%8B%A4%EC%9D%8C%EC%9D%98-%EC%BD%94%EB%93%9C-code-classlanguage-textfunction-foo-code%EB%8A%94-%EC%99%9C-iife%EC%B2%98%EB%9F%BC-%EC%8B%A4%ED%96%89%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94%EA%B0%80-iife%EA%B0%80-%EB%90%98%EA%B2%8C-%ED%95%98%EB%A0%A4%EB%A9%B4-%EC%96%B4%EB%96%A4-%EB%B6%80%EB%B6%84%EC%9D%84-%EC%88%98%EC%A0%95%ED%95%B4%EC%95%BC-%ED%95%98%EB%82%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 다음의 코드 &lt;code class=&quot;language-text&quot;&gt;function foo(){ }();&lt;/code&gt;는 왜 IIFE처럼 실행되지 않는가? IIFE가 되게 하려면 어떤 부분을 수정해야 하나?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 함수 선언부를 소괄호로 둘러싸야 즉시 호출이 가능하다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;IIFE 는 &lt;strong&gt;Immediately Invoked Function Expressions&lt;/strong&gt; 의 약자다.&lt;/p&gt;
&lt;p&gt;자바스트립트 엔진은 &lt;code class=&quot;language-text&quot;&gt;function foo(){ }();&lt;/code&gt; 을 &lt;code class=&quot;language-text&quot;&gt;function foo(){}&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;();&lt;/code&gt;으로 읽는다. 첫번째는 함수 선언이고, 두번째는 함수 호출이다. 하지만 호출하려는 함수 이름이 없어서 &lt;code class=&quot;language-text&quot;&gt;Uncaught SyntaxError: Unexpected token )&lt;/code&gt; 에러가 발생한다.&lt;/p&gt;
&lt;p&gt;IIFE처럼 작동하게 하려면 아래처럼 작성해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;function&lt;/code&gt; 키워드로 시작하면 &lt;strong&gt;함수 선언&lt;/strong&gt;으로 인식하기 때문이다. 하지만 소괄호로 둘러싸면 &lt;strong&gt;함수 표현식으로 인식&lt;/strong&gt;하고 뒤따르는 &lt;code class=&quot;language-text&quot;&gt;()&lt;/code&gt;로 실행할 수 있다. 저렇게 실행한 함수는 전역 스쿠프에 노출되지 않으며 필요하지 않다면 함수 이름도 생략해서 작성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;function &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;console.log&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;소괄호로 둘러싸는 대신 &lt;code class=&quot;language-text&quot;&gt;void&lt;/code&gt; 키워드를 사용할 수도 있다. 하지만 리턴값을 받을 수는 없다. &lt;code class=&quot;language-text&quot;&gt;void&lt;/code&gt; 키워드는 항상 undefined를 리턴하기 때문이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; const foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; void &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; bar&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; console.log&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;foo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; console.log&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
undefined&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-4&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://lucybain.com/blog/2014/immediately-invoked-function-expression/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://lucybain.com/blog/2014/immediately-invoked-function-expression/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;6-code-classlanguage-textnullcode-code-classlanguage-textundefinedcode-그리고-선언되지-않은-변수에는-어떤-차이가-있는가&quot;&gt;&lt;a href=&quot;#6-code-classlanguage-textnullcode-code-classlanguage-textundefinedcode-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%84%A0%EC%96%B8%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EB%B3%80%EC%88%98%EC%97%90%EB%8A%94-%EC%96%B4%EB%96%A4-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EC%9E%88%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;, 그리고 선언되지 않은 변수에는 어떤 차이가 있는가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;은 명시적인 값, &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;는 문자 그대로의 의미, 그리고 선언하지 않은 변수는 쓰지 말아야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;선언되지-않은-변수undeclared-variable&quot;&gt;&lt;a href=&quot;#%EC%84%A0%EC%96%B8%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EB%B3%80%EC%88%98undeclared-variable&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;선언되지 않은 변수(undeclared variable)&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt;를 붙여서 선언하지 않은 변수를 코드에서 사용하는 것을 말한다. strict 모드에서는 레퍼렌스 에러가 발생한다. strict 모드가 아니면 전역에 자동으로 추가된다. 사용하지 말아야 한다.&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-textundefinedcode-변수&quot;&gt;&lt;a href=&quot;#code-classlanguage-textundefinedcode-%EB%B3%80%EC%88%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; 변수&lt;/h3&gt;
&lt;p&gt;선언은 되었지만 값이 할당되지 않은 상태. 함수가 값을 리턴하지 않는다면 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;를 반환한다. falsy한 값이기 때문에 확실히 undefined인지 확인하기 위해서는 strict equality 연산자(&lt;code class=&quot;language-text&quot;&gt;===&lt;/code&gt;)를 사용해야 한다. loose equality(&lt;code class=&quot;language-text&quot;&gt;==&lt;/code&gt;)를 사용하면 undefined와 null 이 같다고 할 수 있으니 주의.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 값 할당 안함&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; undefined&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 리턴값 없음&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// strict equality&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;undefined &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;undefined &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;나는 변수를 undeclared 또는 unassigned 상태로 두지 않으려 한다. 당분간 사용하지 않을 변수라면 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 값을 명시적으로 할당해 둔다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-5&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/15985875/effect-of-declared-and-undeclared-variables&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/15985875/effect-of-declared-and-undeclared-variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/undefined&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/undefined&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;7-클로져closure란-무엇이며-어떻게왜-사용하는가&quot;&gt;&lt;a href=&quot;#7-%ED%81%B4%EB%A1%9C%EC%A0%B8closure%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EC%96%B4%EB%96%BB%EA%B2%8C%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. 클로져(closure)란 무엇이며 어떻게/왜 사용하는가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 접근 불가능한 영역에 통로를 만드는 것&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;클로져는 함수의 실행이 끝난 뒤에도 함수에 선언된 변수의 값을 접근할 수 있도록 만든 함수를 말한다.&lt;/p&gt;
&lt;p&gt;함수는 블럭으로 둘러싸여 있고 하나의 스쿠프를 형성한다. 함수 안에 선언된 변수는 함수 바깥에서 참조할 수 없으며, 함수가 실행된 후에는 더 이상 사용할 수 없다.&lt;/p&gt;
&lt;p&gt;하지만 자바스크립트에서는 함수 리턴값을 통해 함수 안에 선언된 변수를 노출시킴으로서 클로져를 형성할 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;자바스크립트에서 스쿠프(Scope)는 현재 코드 특정 변수에 대한 접근 가능 여부를 결정하는 환경(context)를 가리킨다.&lt;/p&gt;
&lt;p&gt;스쿠프는 두가지 타입이 있다. 지역(local)과 전역(global)이다. 전역 변수는 어떤 블럭에도 속하지 않은 위치에서 선언된 것이며, 로컬 변수는 블럭 안에서 선언된 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;사용하는-이유&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사용하는 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;데이터 프라이버시 / &lt;a href=&quot;https://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;모듈 패턴&lt;/a&gt;에서 private 메소드를 구현하기 위해 사용됨.&lt;/li&gt;
&lt;li&gt;함수형 프로그래밍에서 partial application, currying 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;예제-1&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%A0%9C-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예제&lt;/h3&gt;
&lt;h4 id=&quot;함수-안의-변수-업데이트&quot;&gt;&lt;a href=&quot;#%ED%95%A8%EC%88%98-%EC%95%88%EC%9D%98-%EB%B3%80%EC%88%98-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;함수 안의 변수 업데이트&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;increase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    num &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; num&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; increase&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;간단한-curry&quot;&gt;&lt;a href=&quot;#%EA%B0%84%EB%8B%A8%ED%95%9C-curry&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;간단한 curry&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; add3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; num2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; num1 &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; num2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;add3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 7&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-6&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-6&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;8-foreach와-map의-차이점이-무엇이며-어떤-메소드를-사용해야-하는가&quot;&gt;&lt;a href=&quot;#8-foreach%EC%99%80-map%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%B4-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EC%96%B4%EB%96%A4-%EB%A9%94%EC%86%8C%EB%93%9C%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. forEach와 map의 차이점이 무엇이며, 어떤 메소드를 사용해야 하는가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 콜백 리턴값으로 맵핑을 하느냐, 하지 않느냐&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;code-classlanguage-textarrayprototypeforeachcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textarrayprototypeforeachcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;Array.prototype.forEach&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;배열의 요소을 순환한다&lt;/li&gt;
&lt;li&gt;각각의 요소를 파라미터로 전달받는 콜백 함수가 실행된다.&lt;/li&gt;
&lt;li&gt;값을 리턴하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-classlanguage-textarrayprototypemapcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textarrayprototypemapcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;Array.prototype.map&lt;/code&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;배열의 요소을 순환한다&lt;/li&gt;
&lt;li&gt;새로운 배열을 리턴한다. 그리고 각각의 요소마다 콜백함수를 실행시키며, 콜백의 리턴값을 새로운 배열의 요소로 mapping(사상)한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;차이점과-선택&quot;&gt;&lt;a href=&quot;#%EC%B0%A8%EC%9D%B4%EC%A0%90%EA%B3%BC-%EC%84%A0%ED%83%9D&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;차이점과 선택&lt;/h3&gt;
&lt;p&gt;두 메소드 모두 원본 배열의 값을 바꾸지 않는다. 차이점은 forEach는 값을 리턴하지 않고, map은 값을 리턴한다는 것이다. 결과가 필요하다면 map을, 아니라면 forEach를 사용하면 된다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-7&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-7&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://codeburst.io/javascript-map-vs-foreach-f38111822c0f&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://codeburst.io/javascript-map-vs-foreach-f38111822c0f&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;9-익명-함수를-사용하는-사례에는-어떤-것들이-있나&quot;&gt;&lt;a href=&quot;#9-%EC%9D%B5%EB%AA%85-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%82%AC%EB%A1%80%EC%97%90%EB%8A%94-%EC%96%B4%EB%96%A4-%EA%B2%83%EB%93%A4%EC%9D%B4-%EC%9E%88%EB%82%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9. 익명 함수를 사용하는 사례에는 어떤 것들이 있나?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: IIFE, callback ⇒ 다른 곳에서 참조할 일이 없는 것들&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;iife&quot;&gt;&lt;a href=&quot;#iife&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;IIFE&lt;/h3&gt;
&lt;p&gt;코드를 둘러싸서 실행 코드와 변수를 전역 스쿠프에 노출하지 않을 목적으로 사용한다. 다른 곳에서 사용하지 않을 것이므로 IIFE 패턴에 사용할 함수는 이름이 필요 없다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 실행할 코드 작성&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;콜백-함수&quot;&gt;&lt;a href=&quot;#%EC%BD%9C%EB%B0%B1-%ED%95%A8%EC%88%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;콜백 함수&lt;/h3&gt;
&lt;p&gt;콜백으로서의 함수는 재사용하지 않는 경우가 대부분이다. 콜백이 사용되는 위치에 직접 선언하는 편이 더 가독성이 높다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSeconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// vs&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onEveryTick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSeconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onEveryTick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-8&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.quora.com/What-is-a-typical-usecase-for-anonymous-functions&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://www.quora.com/What-is-a-typical-usecase-for-anonymous-functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/10273185/what-are-the-benefits-to-using-anonymous-functions-instead-of-named-functions-fo&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/10273185/what-are-the-benefits-to-using-anonymous-functions-instead-of-named-functions-fo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;10-호스트-객체와-네이티브-객체의-차이점&quot;&gt;&lt;a href=&quot;#10-%ED%98%B8%EC%8A%A4%ED%8A%B8-%EA%B0%9D%EC%B2%B4%EC%99%80-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10. 호스트 객체와 네이티브 객체의 차이점&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 표준 객체와 런타임 환경 객체&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;네이티브 객체는 자바스크립트 언어의 일부로서 ECMAScript 표준에 정의된 것이다. 예를 들면 String, Math, RegExp, Object, Function 등이다.&lt;/p&gt;
&lt;p&gt;호스트(Host) 객체는 런타임 환경(브라우저, Node)에서 제공하는 객체로서 &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;XMLHTTPRequest&lt;/code&gt; 등이 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;11-code-classlanguage-textfunction-personcode이-있을-때-code-classlanguage-textvar-person--personcode과-code-classlanguage-textvar-person--new-personcode의-차이점은&quot;&gt;&lt;a href=&quot;#11-code-classlanguage-textfunction-personcode%EC%9D%B4-%EC%9E%88%EC%9D%84-%EB%95%8C-code-classlanguage-textvar-person--personcode%EA%B3%BC-code-classlanguage-textvar-person--new-personcode%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;11. &lt;code class=&quot;language-text&quot;&gt;function Person(){}&lt;/code&gt;이 있을 때, &lt;code class=&quot;language-text&quot;&gt;var person = Person()&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;var person = new Person()&lt;/code&gt;의 차이점은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 함수를 생성자로 사용하느냐, 일반 호출로 사용하느냐의 차이.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 질문은 다소 모호하다. 내 생각에 이 질문의 의도는 생성자(constructor)에 대해서 묻고 있는 것으로 보인다. 기술적으로 말해서 &lt;code class=&quot;language-text&quot;&gt;function Person(){}&lt;/code&gt;은 그냥 일반적인 함수 선언이다. 함수의 이름으로 파스칼 케이스(PascalCase)를 사용하는 것은 이 함수가 생성자 함수임을 나타내기 위한 규칙(convention)이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;var person = Person()&lt;/code&gt;은 &lt;code class=&quot;language-text&quot;&gt;Person&lt;/code&gt;을 생성자가 아닌 함수로서 호출한다. 생성자 역할을 해야 하는 함수를 저렇게 호출하는 것은 흔히 볼 수 있는 실수 중 하나다. 일반적으로 생성자는 아무것도 리턴하지 않는다. 인스턴스를 리턴받으려는 의도였겠지만 실제로는 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; 값이 할당될 뿐이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;var person = new Person()&lt;/code&gt; 은 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 연산자를 사용해 &lt;code class=&quot;language-text&quot;&gt;Person&lt;/code&gt; 객체의 인스턴스를 만든다. 그 인스턴스는 &lt;code class=&quot;language-text&quot;&gt;Person.prototype&lt;/code&gt;을 상속한다. 프로토타입을 상속하는 다른 방법에는 &lt;code class=&quot;language-text&quot;&gt;Object.create&lt;/code&gt;가 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;정리하면 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 생성자 함수&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;John&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Uncaught TypeError: Cannot read property &apos;name&apos; of undefined&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; person &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Person&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;John&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Person { name: &quot;John&quot; }&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;person&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;john&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-9&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;12-code-classlanguage-textfunctionprototypecallcode과-code-classlanguage-textfunctionprototypeapplycode-의-차이점은&quot;&gt;&lt;a href=&quot;#12-code-classlanguage-textfunctionprototypecallcode%EA%B3%BC-code-classlanguage-textfunctionprototypeapplycode-%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;12. &lt;code class=&quot;language-text&quot;&gt;Function.prototype.call&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;Function.prototype.apply&lt;/code&gt; 의 차이점은?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 객체를 지정한다는 것은 같지만, 함수 파라미터 전달방식에 차이가 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;apply&lt;/code&gt; 모두 함수를 호출하는데 사용하며, 첫번째 파라미터는 함수 안에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 값으로 사용된다. 두 번째 파라미터부터는 함수의 파라미터로 전달된다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt; 은 콤마로 구분된 파라미터를 사용하지만, &lt;code class=&quot;language-text&quot;&gt;apply&lt;/code&gt;는 배열을 사용한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt;의 &lt;strong&gt;c&lt;/strong&gt; ⇒ comma separated arguments&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;apply&lt;/code&gt;의 &lt;strong&gt;a&lt;/strong&gt; ⇒ array of arguments&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;add&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 3&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;add&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&quot;13-code-classlanguage-textfunctionprototypebindcode에-대해-설명하라&quot;&gt;&lt;a href=&quot;#13-code-classlanguage-textfunctionprototypebindcode%EC%97%90-%EB%8C%80%ED%95%B4-%EC%84%A4%EB%AA%85%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;13. &lt;code class=&quot;language-text&quot;&gt;Function.prototype.bind&lt;/code&gt;에 대해 설명하라&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 키워드가 지정된 새 함수를 만든다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;bind&lt;/code&gt; 메소드는 새로운 함수를 만든다. 새로 만들어진 함수 안에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 키워드의 값은 &lt;code class=&quot;language-text&quot;&gt;bind&lt;/code&gt;를 호출할 때 첫 번째로 전달된 값이 된다. &lt;code class=&quot;language-text&quot;&gt;Function.prototype.call&lt;/code&gt;처럼 두번째 파라미터부터 차례대로 원래 함수의 파라미터로 전달된다. (Taken word-for-word from &lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MDN&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;React에서 컴포넌트 클래스의 메소드에 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 바인딩하는 것이 유용한 사용 방법 중 하나다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;클래스의 메소드가 비동기적으로 실행되거나, DOM의 이벤트 핸들러(ex. &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt;)로 할당되면 this가 유지되지 않고 사라진다. &lt;code class=&quot;language-text&quot;&gt;bind&lt;/code&gt;를 사용해서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 명시적으로 바인딩하거나 화살표 함수를 사용해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;code-classlanguage-textbindcode를-사용하는-예제&quot;&gt;&lt;a href=&quot;#code-classlanguage-textbindcode%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%98%88%EC%A0%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;bind&lt;/code&gt;를 사용하는 예제&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;setTimeout&lt;/code&gt; 은 비동기적으로 실행되므로 함수가 호출될 때 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 유지하지 않는다. 그래서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 바인딩하지 않으면 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 클래스 인스턴스가 아닌 다른 값(전역 객체)을 가리키게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; to&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fullName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;speak&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;My name is&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fullName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;speak&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;debounce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;speak&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

foo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// My name is undefined&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// My name is Bar&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;메소드를 선언할 때 화살표 함수를 사용하면 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 바인딩하지 않아도 된다. 화살표 함수는 자신의 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 바인딩하지 않으며, &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 lexical하게 바인딩하기 때문이다. 즉 다른 규칙 다 무시하고, 화살표 함수 코드가 작성된 곳의 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 사용한다. 개인적으로는 이것이 화살표 함수의 탄생 목적 중 하나로 보이기도 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fullName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function-variable function&quot;&gt;speak&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;My name is&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fullName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-10&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-10&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/news/this-is-why-we-need-to-bind-event-handlers-in-class-components-in-react-f7ea1a6f93eb/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;This is why we need to bind event handlers in Class Components in React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/42556083/what-does-bindthis-in-constructor-do-in-reactjs&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/42556083/what-does-bindthis-in-constructor-do-in-reactjs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;14-code-classlanguage-textdocumentwritecode를-사용하겠는가&quot;&gt;&lt;a href=&quot;#14-code-classlanguage-textdocumentwritecode%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B2%A0%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;14. &lt;code class=&quot;language-text&quot;&gt;document.write&lt;/code&gt;를 사용하겠는가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 아니오. 기존의 html, body 모두 날아감&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;document.write&lt;/code&gt;는 문자열을 document stream으로 작성한다. 웹페이지가 로딩된 후에 &lt;code class=&quot;language-text&quot;&gt;document.write&lt;/code&gt;가 호출되면 그것은 &lt;code class=&quot;language-text&quot;&gt;document.open&lt;/code&gt;을 호출하고, 로딩된 페이지가 모두 지워진다. (&lt;code class=&quot;language-text&quot;&gt;head&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt; 태그가 제거된다!) 그리고 컨텐츠를 전달된 파라미터로 교체한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;write example&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;
      &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;h1&gt;Out with the old - in with the new!&amp;lt;/h1&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onload&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;newContent();&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Some original document content.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 코드를 실행하면 html이 아래처럼 바뀐다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3.us-west-2.amazonaws.com/secure.notion-static.com/7af4fbaa-124f-4a93-8b5f-0d97249acaa0/_2020-03-12__1.42.55.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;#x26;X-Amz-Credential=AKIAT73L2G45O3KS52Y5%2F20200412%2Fus-west-2%2Fs3%2Faws4_request&amp;#x26;X-Amz-Date=20200412T081747Z&amp;#x26;X-Amz-Expires=86400&amp;#x26;X-Amz-Signature=5ba08f380f2270e80f9d28fda2c7dec0c6970330174920caccdfcdffed7e20b8&amp;#x26;X-Amz-SignedHeaders=host&amp;#x26;response-content-disposition=filename%20%3D%22_2020-03-12__1.42.55.png%22&quot; alt=&quot;document.write 실행 결과&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;document.write&lt;/code&gt;가 애널리틱스 코드에서 사용되거나, 자바스크립트가 허용된 환경에서만 스타일을 추가하고 싶을때 사용된다는 설명이 웹상에 존재한다. 심지어 HTML5 bolilerplate 코드에서 스크립트를 병렬적으로 로딩하고 실행 순서를 유지하는데 사용된다고 하기도 한다.&lt;/p&gt;
&lt;p&gt;하지만, 나는 저런 테크닉들이 요즘에 와서는 다소 철이 지난 오래된 것들이라 생각한다. 그리고 &lt;code class=&quot;language-text&quot;&gt;document.write&lt;/code&gt; 를 사용하지 않아도 구현할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;15-feature-detection-feature-inference-그리고-ua-문자열에-대해서-설명하라&quot;&gt;&lt;a href=&quot;#15-feature-detection-feature-inference-%EA%B7%B8%EB%A6%AC%EA%B3%A0-ua-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%84%A4%EB%AA%85%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;15. feature detection, feature inference, 그리고 UA 문자열에 대해서 설명하라&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 브라우저가 어떤 기능을 지원하는지 확인하기 위한 테크닉들이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;feature-detection&quot;&gt;&lt;a href=&quot;#feature-detection&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Feature Detection&lt;/h3&gt;
&lt;p&gt;기능 탐지(feature detection)는 브라우저에서 어떤 기능을 포함한 코드 블럭을 사용할 수 있는지 확인하는 것, 그리고 지원 여부에 따라 다른 코드를 실행해서 오류를 발생하지 않게 하여 일관적인 사용자 경험을 제공하는 것을 포함한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;geolocation&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Can use navigator.geolocation&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Handle lack of feature&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://modernizr.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Modernizr&lt;/a&gt; 는 feature detection을 처리하는데 사용할 수 있는 훌륭한 라이브러리다.&lt;/p&gt;
&lt;h3 id=&quot;feature-inference&quot;&gt;&lt;a href=&quot;#feature-inference&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Feature inference&lt;/h3&gt;
&lt;p&gt;기능 추론(Feature inference)은 feature detection처럼 기능을 확인하지만, 그 기능은 사용하지 않고 다른 기능을 사용한다. 탐지한 기능이 있으면 관련된 다른 기능도 있을 것이라 추정하는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getElementsByTagName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  element &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 이런 코드는 별로 권장하지 않는다. Feature detection이 더 확실한 방법이다.&lt;/p&gt;
&lt;h3 id=&quot;ua-문자열&quot;&gt;&lt;a href=&quot;#ua-%EB%AC%B8%EC%9E%90%EC%97%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UA 문자열&lt;/h3&gt;
&lt;p&gt;브라우저가 제공하는 문자열. UA를 통해 네트워크 프로토콜이 리퀘스트하는 대상의 어플리케이션 타입, 운영체제, 소프트웨어 벤더(vendor), 소프트웨어 버전을 확인할 수 있다. 브라우저에서 &lt;code class=&quot;language-text&quot;&gt;navigator.userAgent&lt;/code&gt; 로 가져올 수 있다. 하지만 이 문자열은 파싱하기가 까다롭고 잘못되었을 수도 있다. 예를 들어 macOS에서 실행한 Chrome 브라우저는 “Chrome”과 “Safari”라는 문자열을 모두 포함한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// chrome에서 확인한 userAgent.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 뒤쪽에 보면 Safari도 포함하고 있다.&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그래서 Safari임을 확인하려면 Safari 문자열이 있는지, 그리고 Chrome 문자열이 없는지도 확인해야 한다. 이 방법은 사용하지 말아야 한다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-11&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-11&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Learn/Tools&lt;em&gt;and&lt;/em&gt;testing/Cross&lt;em&gt;browser&lt;/em&gt;testing/Feature_detection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser&lt;em&gt;detection&lt;/em&gt;using&lt;em&gt;the&lt;/em&gt;user_agent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;16-ajax에-대해-가능한-자세하게-설명하라&quot;&gt;&lt;a href=&quot;#16-ajax%EC%97%90-%EB%8C%80%ED%95%B4-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9E%90%EC%84%B8%ED%95%98%EA%B2%8C-%EC%84%A4%EB%AA%85%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;16. Ajax에 대해 가능한 자세하게 설명하라&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 비동기 데이터 통신, 동적인 웹 컨텐츠 제공을 위한 기술&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ajax(asynchronous JavaScript and XML)는 비동기적인 웹 어플리케이션을 만들기 위해 클라이언트 사이드에서 사용하는 웹 개발 기술의 모음이다.&lt;/p&gt;
&lt;p&gt;Ajax를 사용하면 웹 어플리케이션은 현재 표시중인 있는 화면에 영향을 주지 않고 서버에 비동기적으로(백그라운드에서) 데이터를 보내고 받을 수 있다. 데이터 교환 계층과 프레젠테이션 레이어를 분리함으로 Ajax는 웹 어플리케이션이 화면 새로고침 없이도 동적으로 컨텐츠를 변경하는 것이 가능하도록 만들었다.&lt;/p&gt;
&lt;p&gt;실제로는 XML을 JSON으로 대체해서 구현하고 있다. 왜냐하면 자바스크립트 엔진에 JSON 처리 기능이 탑재되어 있어 데이터를 바로 사용할 수 있다는 이점이 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;XMLHttpRequest&lt;/code&gt; API는 비동기 통신에 많이 사용되는데, 최근에는 &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt; API를 많이 사용한다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-12&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-12&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Ajax_(programming)&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://en.wikipedia.org/wiki/Ajax_(programming)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/AJAX&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/AJAX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;17-ajax의-장점과-단점은-무엇인가&quot;&gt;&lt;a href=&quot;#17-ajax%EC%9D%98-%EC%9E%A5%EC%A0%90%EA%B3%BC-%EB%8B%A8%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;17. Ajax의 장점과 단점은 무엇인가?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: SPA의 그것과 거의 같다. 동적인 웹 구현을 가능하게 하지만 그만큼 댓가도 따른다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;장점&quot;&gt;&lt;a href=&quot;#%EC%9E%A5%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;개선된 상호작용. 서버에서 가져온 컨텐츠를 페이지 새로고침 없이 보여줄 수 있다.&lt;/li&gt;
&lt;li&gt;어플리케이션 스크립트와 스타일시트를 한번만 가져와도 된다.&lt;/li&gt;
&lt;li&gt;페이지에서 상태를 유지할 수 있다. 페이지 새로고침이 되지 않을 것이기 때문에 자바스크립트 변수와 DOM 상태가 초기화되지 않는다.&lt;/li&gt;
&lt;li&gt;기본적으로 SPA(single page application)가 가지는 대부분의 장점과 일치한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;단점&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;동적인 웹페이지는 북마크하기 더 어렵다.&lt;/li&gt;
&lt;li&gt;자바스크립트가 허용되지 않은 페이지에서는 동작하지 않는다.&lt;/li&gt;
&lt;li&gt;어떤 웹 크롤러는 자바스크립트를 실행하지 않기 때문에 동적으로 로딩되는 컨텐츠가 표시되지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;Ajax를 이용해 데이터 가져오는 웹페이지는 클라이언트 사이드에서 템플릿과 원격 데이터를 조합해 DOM을 업데이트해야 한다. 이를 위해선 자바스크립트가 클라이언트 사이드에서 파싱되고 실행되어야 한다. 느린 접속 속도와 낮은 스펙을 가진 모바일 기기에서는 페이지 표시에 어려움을 겪을 수 있다.&lt;/li&gt;
&lt;li&gt;기본적으로 SPA가 가지는 대부분의 단점과 일치한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;18-jsonp가-어떻게-동작하는지-설명&quot;&gt;&lt;a href=&quot;#18-jsonp%EA%B0%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94%EC%A7%80-%EC%84%A4%EB%AA%85&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;18. JSONP가 어떻게 동작하는지 설명&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 다른 도메인에 있는 서버에서 클라이언트의 전역 함수를 호출하는 테크닉이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;JSONP(JSON with Padding)는 크로스 도메인 정책을 우회하기 위해서 주로 사용된다. 웹 브라우저는 Ajax 리퀘스트를 다른 도메인에 요청하는 것을 허용하지 않기 때문이다.&lt;/p&gt;
&lt;p&gt;JSONP는 &lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt; 태그로 리퀘스트를 보내며 보통 쿼리 파라미터에 &lt;code class=&quot;language-text&quot;&gt;callback&lt;/code&gt; 을 붙인다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;plaintext&quot;&gt;&lt;pre class=&quot;language-plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;https://example.com?callback=printData&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서버에서는 데이터를 &lt;code class=&quot;language-text&quot;&gt;callback&lt;/code&gt; 파라미터로 전달된 &lt;code class=&quot;language-text&quot;&gt;printData&lt;/code&gt; 라는 함수에 담아서 실행한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- https://mydomain.com --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`My name is &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://example.com?callback=printData&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// File loaded from https://example.com?callback=printData&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;printData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;user name&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;클라이언트에는 &lt;code class=&quot;language-text&quot;&gt;printData&lt;/code&gt;라는 함수가 전역 scope에 선언되어 있어야 한다. 다른 도메인의 스크립트가 클라이언트에서 실행되면서 클라이언트의 함수를 실행하고, 파라미터까지 전달할 수 있다.&lt;/p&gt;
&lt;p&gt;JSONP 구현을 위해서는 클라이언트와 서버가 &lt;code class=&quot;language-text&quot;&gt;printData&lt;/code&gt; 라는 이름을 가진 함수를 사용할 것이고 어떻게 데이터를 보낼지 사전에 조율되어야 한다.&lt;/p&gt;
&lt;p&gt;JSONP는 안전하지 않으며 보안에 문제를 가져올 수 있다. JSONP는 자바스크립트이기에 자바스크립트로 할 수 있는 다른 모든 것들을 할 수 있다. 그렇기에 JSONP를 통해 데이터를 제공하는 상대를 신뢰해야만 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;이제 JSONP는 해킹으로 간주되며, &lt;a href=&quot;https://en.wikipedia.org/wiki/Cross-origin_resource_sharing&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CORS&lt;/a&gt;를 사용한 구현이 더 권장된다.&lt;/p&gt;
&lt;h3 id=&quot;관련-자료-13&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-13&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/a/2067584/1751946&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/a/2067584/1751946&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;19-자바스크립트-templating을-사용해-본-적이-있는가-어떤-라이브러리로&quot;&gt;&lt;a href=&quot;#19-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-templating%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4-%EB%B3%B8-%EC%A0%81%EC%9D%B4-%EC%9E%88%EB%8A%94%EA%B0%80-%EC%96%B4%EB%96%A4-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EB%A1%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;19. 자바스크립트 templating을 사용해 본 적이 있는가? 어떤 라이브러리로?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: Angular, React, template literal(XSS에 유의)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Angular, React를 사용한다. Lodash도 &lt;code class=&quot;language-text&quot;&gt;_.template&lt;/code&gt; 메소드로 템플릿 작업을 할 수 있지만 거의 사용하지 않았다.&lt;/p&gt;
&lt;p&gt;React로 JSX를 가장 많이 사용했으며 좋아한다. 자바스크립트에 가깝고 JSX를 위해 별도로 공부할 것이 거의 없기 때문이다. 주의해야 할 점이라면 DOM attribute 키 값을 JSX에서는 모두 camel 케이스로 사용해야 한다는 것 정도가 있다.&lt;/p&gt;
&lt;p&gt;최근에는 ES2015에서 표준이 된 template literal을 사용해 라이브러리 도움 없이도 템플릿을 만들 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; template &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&amp;lt;div&gt;My name is: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/div&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 템플릿 라이브러리의 도움 없이 template literal을 사용할 때는 XSS(cross site scripting) 공격을 염두에 두고 필요하면 문자열을 모두 이스케이프 처리해야 한다.&lt;/p&gt;
&lt;p&gt;React에서는 동적으로 HTML을 추가하기 위한 attribute로 &lt;code class=&quot;language-text&quot;&gt;dangerouslySetInnerHTML&lt;/code&gt; 을 제공한다. 그 이름에서 HTML을 직접 추가할 때는 XSS에 주의해야 한다는 사실을 상기시키고 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;20-hoisting에-대해서-설명하라&quot;&gt;&lt;a href=&quot;#20-hoisting%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%84%A4%EB%AA%85%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;20. hoisting에 대해서 설명하라&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;한줄 답변: 같은 스쿠프라면 아래쪽 줄에 있는 변수를 윗줄에서 참조 가능하도록 하는 자바스크립트의 특징. 하지만 변수에 할당된 값은 알려주지 않는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hoisting(이하 호이스팅)은 자바스크립트 변수 선언의 동작 방식을 설명하기 위한 용어다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt; 키워드로 선언되거나 초기화된 변수는 그 선언이 자바스크립트 엔진에 의해 모듈/함수 스쿠프의 최상단으로 &lt;strong&gt;옮겨진다&lt;/strong&gt;. 이것을 호이스팅이라고 부른다. 하지만 선언(declaration)만 호이스팅될 뿐 값 할당(assignment)은 여전히 거기에 머무른다. 다시말해 값이 할당된 변수라도 호이스팅을 통 통해 접근하면 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; 값을 얻게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;선언이 실제로 옮겨진것이 아니라는 것임을 주지해야 한다. 자바스크립트 엔진은 컴파일 과정에서 코드를 파싱해서 변수 선언을 인지하고 어느 스쿠프에 속하는지 알게 된다. 스쿠프 맨 위로 호이스팅되는 것은 시각화를 통해 더 쉽게 이해할 수 있다.&lt;/p&gt;
&lt;p&gt;본체(body)가 있는 함수 선언은 호이스팅되지만 함수 표현식(변수에 할당하는 식으로 선언한 함수)은 호이스팅되지 않는다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 함수 선언&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [Function: foo]&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;FOOOOO&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;FOOOOO&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [Function: foo]&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 함수 표현식&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Uncaught TypeError: bar is not a function&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;bar&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;BARRRR&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [Function: bar]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; 로 선언된 변수도 호이스팅된다. 하지만 코드에서 선언한 부분 위에서 접근하면 &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;와는 다르게 값이 할당되어 있지 않으면 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;가 출력되는 것이 아니라, &lt;code class=&quot;language-text&quot;&gt;ReferenceError&lt;/code&gt; exception 오류를 발생시킨다. 그런 변수는 코드 시작점부터 변수 선언부까지 ”&lt;strong&gt;temporal dead zone(TDZ)&lt;/strong&gt;“을 가진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// -- 변수 y의 TDZ 시작&lt;/span&gt;
x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// undefined.&lt;/span&gt;
y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Reference error: y is not defined&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;local&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;local&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// -- 변수 y의 TDZ 종료&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;관련-자료-14&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A0%A8-%EC%9E%90%EB%A3%8C-14&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관련 자료&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_Types#Variable_hoisting&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar&lt;em&gt;and&lt;/em&gt;Types#Variable_hoisting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6/31222689#31222689&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6/31222689#31222689&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Redux Toolkit을 활용한 React 상태 관리]]></title><description><![CDATA[Redux Toolkit의 특징 Redux Toolkit 은 Redux의 공식 개발 도구다. Redux의 액션 생성자, 리듀서 자체는 단순한 함수인 데다 미들웨어를 추가할 수 있어서 관련 라이브러리와 도구가 매우 많고 그만큼 개발 방식이 다양하다. 처음 접하는 사람들…]]></description><link>https://blog.rhostem.com//posts/2020-03-04-redux-toolkits</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-03-04-redux-toolkits</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Wed, 04 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;redux-toolkit의-특징&quot;&gt;&lt;a href=&quot;#redux-toolkit%EC%9D%98-%ED%8A%B9%EC%A7%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redux Toolkit의 특징&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://redux-toolkit.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Redux Toolkit&lt;/a&gt;은 Redux의 공식 개발 도구다. Redux의 액션 생성자, 리듀서 자체는 단순한 함수인 데다 미들웨어를 추가할 수 있어서 관련 라이브러리와 도구가 매우 많고 그만큼 개발 방식이 다양하다. 처음 접하는 사람들은 어떤 방식을 실무 개발에 적용하면 좋을지 시간을 들여서 조사해 봐야 한다. 하지만 그만큼 시행착오가 많이 발생하며, 사용하다 보면 Redux가 좋긴 한데 액션 및 리듀서를 관리하기 위한 코드의 양이 너무 늘어난다는 불만도 자연스레 생긴다. 그러면서 자연스레 더 간단한 코드로 상태 관리를 할 수 있는 mobx같은 다른 라이브러리로 눈이 돌아가기도 한다.&lt;/p&gt;
&lt;p&gt;그래서 Redux에서 직접 개발 도구를 만들어서 공개한 모양이다. 그들은 이 도구는 결코 표준이 아니며, 그들이 생각하기에 효율적인 개발 방법이라고 말하고 있다. 하지만 직접 API를 살펴보고 코드를 작성해 본 결과 나름 코드의 양을 줄일 수 있는 데다 생각의 전환까지 가져올 수 있었다. Redux를 사용하고 있고 뭔가 아쉬움을 느끼고 있는 사람이라면 한 번 살펴봐도 좋을 것 같다.&lt;/p&gt;
&lt;h2 id=&quot;외부-라이브러리의-도입&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%9D%98-%EB%8F%84%EC%9E%85&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부 라이브러리의 도입&lt;/h2&gt;
&lt;p&gt;먼저 알려 둘 사실은 Redux Toolkit에서는 이름있는 Redux 관련 라이브러리를 내부적으로 도입해서 사용하고 있다는 점이다. 리듀서 생성 API에는 &lt;a href=&quot;https://github.com/immerjs/immer&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;immer&lt;/a&gt;를, 셀렉터 생성 API에는 &lt;a href=&quot;https://github.com/reduxjs/reselect&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;reselect&lt;/a&gt;를 사용하고 있다. 저 라이브러리에 익숙한 사람이라면 Redux Toolkit에서 제안하는 상태 관리 방식을 더 자연스럽게 받아들일 수 있을 것이다.&lt;/p&gt;
&lt;h2 id=&quot;action&quot;&gt;&lt;a href=&quot;#action&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;action&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://redux-toolkit.js.org/api/createAction&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;createAction&lt;/code&gt;&lt;/a&gt; API를 사용해서 액션 생성자 함수를 만든다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; increment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;counter/increment&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; action &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// returns { type: &apos;counter/increment&apos; }&lt;/span&gt;

action &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// returns { type: &apos;counter/increment&apos;, payload: 3 }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 코드에서 볼 수 있듯이 &lt;code class=&quot;language-text&quot;&gt;createAction&lt;/code&gt;에는 기본적으로 타입 문자열만 제공하면 된다. 그리고 만들어진 액션 생성자의 파라미터는 그대로 &lt;code class=&quot;language-text&quot;&gt;payload&lt;/code&gt; 필드에 들어간다.&lt;/p&gt;
&lt;p&gt;만약 리턴되는 액션 객체에 더 손을 보고 싶다면, 콜백 함수를 &lt;code class=&quot;language-text&quot;&gt;createAction&lt;/code&gt;의 두 번째 파라미터로 전달하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; addTodo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;todos/add&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;prepare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      text&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      createdAt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toISOString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;addTodo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Write more docs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 아래의 객체를 리턴한다.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/**
 * {
 *   type: &apos;todos/add&apos;,
 *   payload: {
 *     text: &apos;Write more docs&apos;,
 *     createdAt: &apos;2019-10-03T07:53:36.581Z&apos;
 *   }
 * }
 **/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;콜백 함수 안에서는 액션 생성자 함수의 파라미터로 전달받지 않은 데이터를 추가할 수 있다. 이때 리턴되는 객체는 반드시 FSA 형태를 따라야 한다.&lt;/p&gt;
&lt;h3 id=&quot;flux-standard-action&quot;&gt;&lt;a href=&quot;#flux-standard-action&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Flux Standard Action&lt;/h3&gt;
&lt;p&gt;Redux Toolkit에서는 &lt;strong&gt;액션 객체의 형태로 FSA(&lt;a href=&quot;https://github.com/redux-utilities/flux-standard-action&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Flux Standard Action&lt;/a&gt;)를 강제한다&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;number/increment&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    amount&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;객체는 액션을 구분할 고유한 문자열을 가진 &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; 필드가 반드시 있으며, &lt;code class=&quot;language-text&quot;&gt;payload&lt;/code&gt; 필드에 데이터를 담아 전달한다. 그 외에 &lt;code class=&quot;language-text&quot;&gt;meta&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;error&lt;/code&gt; 필드를 가질 수도 있다. (더 자세한 사항은 Flux 홈페이지 참조)&lt;/p&gt;
&lt;h2 id=&quot;reducer&quot;&gt;&lt;a href=&quot;#reducer&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;reducer&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;createAction&lt;/code&gt;만 보면 조금 심드렁할 수도 있다. 하지만 reducer부터 조금씩 차이가 드러나기 시작한다. Redux Toolkit에서 리듀서 작성은 &lt;a href=&quot;https://redux-toolkit.js.org/api/createReducer&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;createReducer&lt;/code&gt;&lt;/a&gt; API를 사용한다. 그리고 앞서 소개한 &lt;code class=&quot;language-text&quot;&gt;createAction&lt;/code&gt;으로 만든 액션 생성자도 함께 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; increment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;increment&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; decrement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;decrement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; counterReducer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createReducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;increment&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;decrement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;코드에서도 확인할 수 있겠지만, &lt;strong&gt;reducer 함수에서 흔히 보이던 switch 문이 사라졌다&lt;/strong&gt;! 이제 더는 쓸데없는 &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; 케이스를 작성할 필요가 없다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;createReducer&lt;/code&gt; 함수는 첫 번째 파라미터로 초기 상태 값 객체(initialState)를, 두 번째 파라미터로 리듀서 맵 객체를 요구한다. switch 문의 case 문자열이 리듀서 맵의 필드 값이 된 형태다. 리듀서 함수를 구현할 때 사용하는 switch 문법에 대해서 논쟁이 있었는데, 이 라이브러리에서는 완전히 없애버렸다.&lt;/p&gt;
&lt;p&gt;그리고 리듀서 맵의 필드에 액션 생성자에 전달한 문자열을 넣어도 되지만, 액션 생성자 함수를 직접 넣어도 된다. 이것이 가능한 이유는 &lt;code class=&quot;language-text&quot;&gt;createAction&lt;/code&gt;이 리턴하는 액션 생성자 함수의 &lt;code class=&quot;language-text&quot;&gt;toString&lt;/code&gt;(&lt;code class=&quot;language-text&quot;&gt;Object.prototype.toString&lt;/code&gt;) 메소드를 오버라이딩했기 때문이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;increment&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;increment&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 방식 덕분에 액션의 타입 문자열을 할당한 변수를 별도로 관리할 필요가 없다. 타입 문자열을 전달할 필요 없이 액션 생성자를 직접 전달하면 되기 때문이다. &lt;code class=&quot;language-text&quot;&gt;createReducer&lt;/code&gt; 함수는 리듀서 맵으로 코드의 양을 줄였고, 타입 변수를 관리할 필요를 없애서 또 코드의 양을 줄인 셈이다.&lt;/p&gt;
&lt;h3 id=&quot;immer를-도입한-redux-toolkit&quot;&gt;&lt;a href=&quot;#immer%EB%A5%BC-%EB%8F%84%EC%9E%85%ED%95%9C-redux-toolkit&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;immer를 도입한 Redux Toolkit&lt;/h3&gt;
&lt;p&gt;리듀서 함수는 내부적으로 immer의 &lt;a href=&quot;https://immerjs.github.io/immer/docs/produce&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;produce&lt;/a&gt;를 사용한다. 그래서 리듀서 함수에서 새로운 상태(state) 객체를 리턴할 필요가 없다. 대신 상태 값을 직접 변경하는 방식으로 코드를 작성하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoReducer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createReducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;addTodo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;불변성을 유지하기 위해 &lt;code class=&quot;language-text&quot;&gt;Object.assign&lt;/code&gt; 으로 새 객체를 만들거나, &lt;code class=&quot;language-text&quot;&gt;Array.prototype.map&lt;/code&gt; 등으로 새 배열을 만들거나, 심지어 immutable.js 같은 라이브러리로 상태 객체를 래핑하는 등의 작업을 할 필요가 없다. 저 함수 안에서만 저런 문법이 가능하다는 사실을 항상 인지하고 있기만 하면 된다.&lt;/p&gt;
&lt;h2 id=&quot;action--reducer-→-slice&quot;&gt;&lt;a href=&quot;#action--reducer-%E2%86%92-slice&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;action + reducer → slice&lt;/h2&gt;
&lt;p&gt;Redux Toolkit은 액션이나 리듀서 외에도 다른 방식으로 상태를 관리할 수 있는 도구를 제공한다. 그것은 &lt;a href=&quot;https://redux-toolkit.js.org/api/createSlice&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;createSlice&lt;/code&gt;&lt;/a&gt; API로 액션, 리듀서를 한 번에 만드는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; todoId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todosSlice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createSlice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 액션 타입 문자열의 prefix로 들어간다. ex) &quot;todos/addTodo&quot;&lt;/span&gt;
  name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;todos&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 초기값&lt;/span&gt;
  initialState&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 리듀서 맵&lt;/span&gt;
  reducers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 리듀서와 액션 생성자가 분리되어 있다.&lt;/span&gt;
    addTodo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// 리듀서 함수&lt;/span&gt;
      reducer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// createAction 함수의 두번째 파라미터인 콜백 함수에 해당한다.&lt;/span&gt;
      prepare&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; todoId&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          text
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 리듀서와 액션 생성자 함수가 분리되어 있지 않다.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// removeTodo 액션은 파라미터가 payload에 바로 할당된다.&lt;/span&gt;
    removeTodo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;splice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;createSlice&lt;/code&gt;는 초기값, 리듀서, 액션을 하나의 객체에 담아 전달받는다. 그렇게 만든 슬라이스 객체에서 액션과 리듀서는 아래와 같이 가져올 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; addTodo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; removeTodo &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; todosSlice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;actions&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; reducer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; todosSlice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;createSlice&lt;/code&gt;를 사용하면 액션을 생성하고, 그 액션을 리듀서 맵에 전달할 필요도 없이 한 번에 만들 수 있다. 별도로 작성하는 것보다 가독성은 조금 떨어지긴 하지만 코드의 양을 더 줄이면서 간단하게 리덕스 상태 관리를 구현할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;create-slice&quot;&gt;&lt;a href=&quot;#create-slice&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;create “slice”?&lt;/h3&gt;
&lt;p&gt;slice는 상태(state) 트리 구조에서 리듀서 함수 1개를 가리킨다고 한다. 그런데 왜 slice, ‘얇은 조각’이라는 이름을 붙였을까?&lt;/p&gt;
&lt;p&gt;앱의 규모가 커지면 리듀서 액션의 덩치도 자연스럽게 커지고 리듀서 함수 1개의 길이가 수백 라인을 넘어가 버린다. 코드의 복잡도가 높아져 테스트 코드가 없으면 건드리기 불안할 정도가 되곤 한다. 소프트웨어 개발에 있어 사실 그런 일은 불가피하긴 하다. slice라는 단어를 사용한 이유는 그래도 앱의 로직을 최대한 분리하고 작은 덩치로 유지하자는 철학을 담으려는 목적이 아닌가 한다.&lt;/p&gt;
&lt;p&gt;보통 아래에 있는 트리 구조와 같이 유형별로 소스를 구분하는 방법을 많이 사용할 것이다. 컴포넌트는 &lt;code class=&quot;language-text&quot;&gt;components&lt;/code&gt; 폴더 아래에, 리덕스 관련 코드는 모두 store 폴더에 아래에 두는 식이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;.
├── api (API 모듈)
├── components (일반 켬포넌트)
├── constant (상수)
├── models (데이터 모델 변수, 타입)
├── pages (페이지 컴포넌트)
├── store  (Redux 관련 코드)
├── styles (스타일시트)
└── utils (유틸리티 모듈)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 Redux Toolkit 개발팀에서 공개한 공식 템플릿(&lt;a href=&quot;https://github.com/reduxjs/cra-template-redux&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;cra-template-redux&lt;/a&gt;)에서는 독특한 방식의 폴더 구조를 제안하고 있다. features 라는 폴더 안에 기능별로 폴더를 만든 후 그 안에 React 컴포넌트와 스타일시트, Redux 구현 등 관련 소스를 모두 같은 곳에 위치시키는 방법이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;.
├── api
│   └── githubAPI.js
├── components
│   └── Heading.js
├── features
│   ├── counter
│   │   ├── Counter.css
│   │   ├── Counter.js
│   │   └── counterReducer.js
│   ├── github
│   │   ├── RepoDetail.js
│   │   └── repoDetailSlice.js
│   └── todos
│       ├── Todos.js
│       └── todosSlice.js
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;components&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;styles&lt;/code&gt; 폴더에는 공용 컴포넌트, 글로벌 스타일, 믹스인처럼 유틸리티 성격을 가진 모듈을 두고, 실제 기능과 관련된 소스는 모두 &lt;code class=&quot;language-text&quot;&gt;features&lt;/code&gt; 폴더 아래에 주제별로 모으는 식이다. 이를 보면 slice라는 단어는 리듀서 뿐만 아니라 기능 또한 작은 덩치로 가볍게 유지하자는 의미를 담고 있을 수 있겠다는 생각이 든다.&lt;/p&gt;
&lt;p&gt;좋은 방식으로 보이지만, 앱의 규모가 커졌을 때 무엇이 더 좋은 방식일지는 역시 직접 경험해 봐야 알 것 같다. 리듀서의 수가 너무 많아져서 더 복잡하게 느껴질 것 같기도 하고, 관심사 분리 원칙을 더 확실하게 지켜서 사이드 이펙트가 덜 발생하고 레거시 코드 분석이 그나마 더 쉬워질 수도 있을 것 같기도 하다. 역시 코드 관리 방식은 팀과 개인의 선택 문제다.&lt;/p&gt;
&lt;h2 id=&quot;selector&quot;&gt;&lt;a href=&quot;#selector&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;selector&lt;/h2&gt;
&lt;p&gt;셀렉터는 리덕스에서 state를 기반으로 새로운 값을 리턴하는 함수를 말한다. 예를 들어 할 일 목록에서 현재 상태가 ‘완료’로 표시된 데이터만 필터링해서 리턴하는 것을 말한다.&lt;/p&gt;
&lt;p&gt;Redux Toolkit의 &lt;a href=&quot;https://redux-toolkit.js.org/api/createSelector&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;createSelector&lt;/code&gt;&lt;/a&gt; API는 &lt;a href=&quot;https://github.com/reduxjs/reselect&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;reselect&lt;/a&gt; 라이브러리의 그것과 같다. 이 API는 새로운 상태 추출에 사용하는 값이 변경되지 않으면 다시 실행되지 않는다. 즉 메모이제이션(memoization&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;)을 지원한다.&lt;/p&gt;
&lt;p&gt;아래의 코드는 현재 활성화된 탭에 맞는 데이터 배열을 리턴하는 셀렉터 코드다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeListSelector &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createSelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;live&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 상태 1 리턴 함수&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;closed&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 상태 2 리턴 함수&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tab&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 상태 3 리턴 함수&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 상태 1, 2, 3이 차례로 들어간다&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;liveEvents&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; closedEvents&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tab&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tab&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;LIVE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; liveEvents&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CLOSED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; closedEvents&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;createSelector&lt;/code&gt;를 호출할 때 파라미터의 수에는 제한이 없지만, 마지막에는 상태 객체를 리턴할 콜백 함수가 있어야 한다. 마지막 콜백 함수의 파라미터에는 앞선 파라미터에서 리턴한 객체가 차례대로 위치한다. &lt;code class=&quot;language-text&quot;&gt;activeListSelector&lt;/code&gt;는 &lt;code class=&quot;language-text&quot;&gt;state.events.live&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;state.events.closed&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;state.events.tab&lt;/code&gt; 중 어느 하나의 값이 바뀌어야 다시 실행되어서 새로운 값이 적용되며, 그렇지 않으면 다시 실행되지 않는다. 이렇게 만든 셀렉터는 React 컴포넌트에서 아래와 같은 방식으로 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useSelector &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react-redux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;EventList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeTabEvents &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useSelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeTabEventsSelector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; activeTabEvents&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;eventData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;span key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;eventData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;store&quot;&gt;&lt;a href=&quot;#store&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;store&lt;/h2&gt;
&lt;p&gt;액션, 리듀서를 만들었으니 이제 스토어 객체를 만들어서 React 컴포넌트 트리에 제공해야 한다. Redux Toolkit은 Redux의 &lt;a href=&quot;https://redux.js.org/api/createstore/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;createStore&lt;/code&gt;&lt;/a&gt;를 활용한 &lt;a href=&quot;https://redux-toolkit.js.org/api/configureStore&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;configureStore&lt;/code&gt;&lt;/a&gt; API를 제공한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  configureStore&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  combineReducers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// redux의 그것과 같다.&lt;/span&gt;
  getDefaultMiddleware
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@reduxjs/toolkit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; todosSlices &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/features/todos/todosSlice&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; counter &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/features/counter/counterReducer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rootReducer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;combineReducers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  counter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// createReducer로 만든 리듀서 객체&lt;/span&gt;
  todos&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; todosSlices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reducer &lt;span class=&quot;token comment&quot;&gt;// createSlice로 만든 slice 객체가 가진 reducer&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;configureStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  reducer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rootReducer
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;combineReducers&lt;/code&gt;는 Redux에서 사용하고 있는 것과 같다. &lt;code class=&quot;language-text&quot;&gt;configureStore&lt;/code&gt; 함수에 필수적으로 필요한 값은 &lt;code class=&quot;language-text&quot;&gt;reducer&lt;/code&gt;필드다. &lt;code class=&quot;language-text&quot;&gt;middleware&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;preloadedState&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;enhancers&lt;/code&gt; 등의 사용 방법은 문서를 참조하면 될 것이다.&lt;/p&gt;
&lt;p&gt;그리고 &lt;code class=&quot;language-text&quot;&gt;configureStore&lt;/code&gt;에 &lt;code class=&quot;language-text&quot;&gt;middleware&lt;/code&gt; 필드를 전달하지 않으면 &lt;strong&gt;기본적으로 제공되는 미들웨어가 있다&lt;/strong&gt;. 그 미들웨어들을 가져오는 API가 &lt;code class=&quot;language-text&quot;&gt;getDefaultMiddleware&lt;/code&gt;다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; middleware &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getDefaultMiddleware&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;middleware&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  thunk&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  immutableStateInvariant&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// development 모드에서만 사용됨&lt;/span&gt;
  serializableStateInvariant &lt;span class=&quot;token comment&quot;&gt;// development 모드에서만 사용됨&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;기본 미들웨어로는 &lt;a href=&quot;https://github.com/reduxjs/redux-thunk&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;redux-thunk&lt;/code&gt;&lt;/a&gt;를 사용하며 개발 모드에서만 redux-immutable-state-invariant, serializable-state-invariant-middleware 미들웨어가 추가된다. 만약 비동기 액션 처리에 redux-thunk를 사용하지 않거나 redux-saga 등의 다른 미들웨어가 필요하다면 아래와 같이 설정을 수정해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; logger &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;redux-logger&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;configureStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  reducer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rootReducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  middleware&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDefaultMiddleware&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; logger&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;typescript-설정&quot;&gt;&lt;a href=&quot;#typescript-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TypeScript 설정&lt;/h2&gt;
&lt;p&gt;Redux Toolkit은 타입스크립트를 지원한다. 그리고 타입을 사용하면 액션을 호출하고 상태를 가져올 때 버그를 발생할 가능성을 줄여준다.&lt;/p&gt;
&lt;h3 id=&quot;slice&quot;&gt;&lt;a href=&quot;#slice&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;slice&lt;/h3&gt;
&lt;p&gt;initialState에 타이핑을 추가한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ITodo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  text&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  isDone&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; initialState&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ITodo&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todosSlice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createSlice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  initialState&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;슬라이스에서는 리듀서의 리턴 타입으로 payload에 어떤 데이터를 넣어야 하는지 지정할 수 있다. redux-toolkit에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;PayloadAction&lt;/code&gt; 타입을 사용하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todosSlice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createSlice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	reducers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    removeTodo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; payload &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PayloadAction&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// ... &lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;root-reducer&quot;&gt;&lt;a href=&quot;#root-reducer&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;root reducer&lt;/h3&gt;
&lt;p&gt;선언한 슬라이스를 기반으로 전체 상태 트리 타입을 가져올 수 있다. 타입스트립트의 유틸리티 타입인 &lt;code class=&quot;language-text&quot;&gt;ReturnType&lt;/code&gt;을 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; todosSlices &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;features/todo/todoSlice&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rootReducer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;combineReducers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  todo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; todosSlices&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; global &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; IRootState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ReturnType&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;typeof&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rootReducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
}

export default rootReducer&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;더미-스토어-인스턴스로-스토어-관련-타입-선언&quot;&gt;&lt;a href=&quot;#%EB%8D%94%EB%AF%B8-%EC%8A%A4%ED%86%A0%EC%96%B4-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%EB%A1%9C-%EC%8A%A4%ED%86%A0%EC%96%B4-%EA%B4%80%EB%A0%A8-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더미 스토어 인스턴스로 스토어 관련 타입 선언&lt;/h3&gt;
&lt;p&gt;스토어 인스턴스, &lt;code class=&quot;language-text&quot;&gt;dispatch&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;getState&lt;/code&gt; 함수의 타입을 만들기 위해서 &lt;code class=&quot;language-text&quot;&gt;Provider&lt;/code&gt; 에 전달하는 인스턴스 대신 더미 인스턴스를 만들어서 타입을 선언했다. &lt;code class=&quot;language-text&quot;&gt;createStore&lt;/code&gt;  모듈이 별도로 있어서 사용한 트릭인데, 다른 방법을 사용해도 상관없다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 타이핑을 위한 더미 스토어 객체. 실제로 사용하지는 않는다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dummyStore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;configureStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  reducer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rootReducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt; global &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; IStore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; dummyStore
  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; IAppDispatch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; dummyStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dispatch
  &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; IGetState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; dummyStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getState
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sampleSelector &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createSelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; IRootState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; getState&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; IGetState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;code-classlanguage-textuseselectorcode-훅을-대신할-code-classlanguage-textusetypedselectorcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textuseselectorcode-%ED%9B%85%EC%9D%84-%EB%8C%80%EC%8B%A0%ED%95%A0-code-classlanguage-textusetypedselectorcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;useSelector&lt;/code&gt; 훅을 대신할 &lt;code class=&quot;language-text&quot;&gt;useTypedSelector&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;함수형 컴포넌트 안에서 Redux state 객체를 가져오는 &lt;code class=&quot;language-text&quot;&gt;useSelector&lt;/code&gt; 훅의 파라미터는 root state다. 거기에 아래처럼 일일이 &lt;code class=&quot;language-text&quot;&gt;IRootState&lt;/code&gt;로 파라미터를 붙여도 되지만, 그렇게 하지 않아도 되도록 react-redux 에서 헬퍼 타입을 제공한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; TypedUseSelectorHook&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; useSelector &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-redux&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// useSelector hook 대신 사용. useSelector 함수의 파라미터에 타입을 지정하지 않아도 된다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; useTypedSelector&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; TypedUseSelectorHook&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IRootState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; = useSelector&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;예제-프로젝트&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예제 프로젝트&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rhostem/startkits/tree/master/next-redux-ts-starter&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;startkits/next-redux-ts-starter at master · rhostem/startkits&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고-자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://redux-toolkit.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Redux Toolkit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redux.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Redux - A predictable state container for JavaScript apps.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/reduxjs/cra-template-redux&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;reduxjs/cra-template-redux: The official Redux+JS template for Create React App&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;메모이제이션은 컴퓨터 프로그램이 같은 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 같은 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술이다. 동적 계획법의 핵심이 되는 기술이다. 메모아이제이션이라고도 한다.(출처 - 위키백과)&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Git subtree를 활용한 코드 공유]]></title><description><![CDATA[서비스를 개발하다 보면 코드를 공유해야 하는 일이 생긴다. 백엔드를 마이크로 서비스 구조로 개발하면 API 리스펀스, 각종 데이터 모델에 같은 타입을 사용해서 일관성을 유지해야 한다. 그리고 프론트엔드 웹을 데스크탑, 모바일로 분리해서 개발한다면 데이터 모델뿐만 아니…]]></description><link>https://blog.rhostem.com//posts/2020-01-03-code-sharing-with-git-subtree</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2020-01-03-code-sharing-with-git-subtree</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Fri, 03 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;서비스를 개발하다 보면 코드를 공유해야 하는 일이 생긴다. 백엔드를 마이크로 서비스 구조로 개발하면 API 리스펀스, 각종 데이터 모델에 같은 타입을 사용해서 일관성을 유지해야 한다. 그리고 프론트엔드 웹을 데스크탑, 모바일로 분리해서 개발한다면 데이터 모델뿐만 아니라 컴포넌트 및 다양한 유틸리티 모듈을 함께 사용할 필요가 생긴다. 만약 백엔드가 node.js로 만들어져 프론트엔드 웹과 Javascript라는 같은 언어를 사용한다면 코드 공유의 필요성은 더욱 클 것이다.&lt;/p&gt;
&lt;p&gt;코드 공유를 위한 방법은 여러 가지가 있다. &lt;a href=&quot;https://www.npmjs.com/products&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;NPM private 레지스트리&lt;/a&gt;, &lt;a href=&quot;https://bit.dev/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Bit&lt;/a&gt;, &lt;a href=&quot;https://git-scm.com/book/ko/v2/Git-%EB%8F%84%EA%B5%AC-%EC%84%9C%EB%B8%8C%EB%AA%A8%EB%93%88&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Git submodule&lt;/a&gt;, 그리고 전통적이고 간단한 “복붙”(copy and paste)같은 방법이 있다. 이 글에서는 &lt;a href=&quot;https://github.com/git/git/blob/master/contrib/subtree/git-subtree.txt&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Git subtree&lt;/a&gt;로 코드를 공유하는 방법을 소개한다.&lt;/p&gt;
&lt;h2 id=&quot;git-submodule의-단점&quot;&gt;&lt;a href=&quot;#git-submodule%EC%9D%98-%EB%8B%A8%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git Submodule의 단점&lt;/h2&gt;
&lt;p&gt;Git Submodule(이하 서브모듈)은 Git 저장소에 다른 Git 저장소를 추가하는 방법이다. 서브모듈로 추가된 저장소(repository)는 상위 저장소에서 1개의 폴더로 취급되며 일반적인 파일처럼 접근해서 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;그런데 문제가 있다. 상위 저장소는 서브모듈의 정보 중에서 체크아웃한 커밋의 SHA 값만 기록한다. 그런데 만약 서브모듈의 원격에 새로운 내용이 푸시되어서 서브모듈을 업데이트해야 하는 상황이 생긴다면 어떻게 될까? 아래와 같은 시나리오를 가정해보자.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;서브모듈 B를 추가한 저장소 A에서 서브모듈을 직접 수정한 후 커밋을 했다.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;서브모듈 B의 원격에 새로운 내용이 푸시되었다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;저장소 A에 있는 서브모듈의 내용과 원격에 있는 서브모듈의 내용은 서로 다르다. 충돌이 발생할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;서브모듈의 업데이트를 해야 한다. &lt;code class=&quot;language-text&quot;&gt;git submodule update&lt;/code&gt; 명령어로 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위와 같은 시나리오에서 서브모듈의 병합의 제대로 이뤄질까? 대답은 “아니오”다. 왜냐면 상위 저장소에서 서브모듈을 SHA 값, 하나의 바이너리처럼 취급하기 때문이다. SHA 값이 다르므로 그냥 최신 커밋의 내용으로 교체해 버린다. 저장소 A에서 작업한 중요한 변경사항은 그냥 사라져 버릴 것이다. 만약 병합을 제대로 하고 싶다면 사용자가 직접 서브모듈에 새로운 브랜치를 만들어서 커밋하고. 서브모듈 업데이트를 한 후 직접 병합하고 충돌을 해결한 후 푸시해야 한다.&lt;/p&gt;
&lt;p&gt;그러면 서브모듈로 추가한 소스는 건드리지 않는다는 원칙을 세우고 작업을 하면 되지 않나? 라고 생각할 수도 있다. 하지만 작업을 하다 보면 이것이 서브모듈인지 상위 저장소의 소스인지 헷갈릴 수 있어서 실수로 수정하고 커밋을 할 가능성은 얼마든지 있다.&lt;/p&gt;
&lt;h2 id=&quot;git-subtree&quot;&gt;&lt;a href=&quot;#git-subtree&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git Subtree&lt;/h2&gt;
&lt;p&gt;Git Subtree(이하 서브트리)는 SHA 값만 저장하는 서브모듈과 달리 상위 저장소에 파일을 직접 추가하고 트래킹한다. 자연히 서브트리의 파일 및 변경사항도 상위 저장소에 기록된다. 그리고 서브트리의 원격에 있는 소스와 서브트리를 추가한 저장소의 소스가 서로 달라도 “subtree merge” 기능을 사용해서 양쪽의 변경사항을 모두 반영할 수 있다.&lt;/p&gt;
&lt;p&gt;이처럼 &lt;strong&gt;상위 저장소에서 서브트리를 직접 수정하고 원격에 푸시할 수 있다는 것&lt;/strong&gt;이 서브모듈과 큰 차이점이다. 예를 들어 서브트리의 컴포넌트를 상위 저장소의 프로젝트에서 사용할 때 버그를 발견했다면, 즉시 수정해서 커밋한 후 버그를 해결한 상태로 서브트리의 원격에 반영할 수 있다는 편리함이 있다.&lt;/p&gt;
&lt;p&gt;물론 단점도 있다. 서브트리를 추가한 모든 사용자가 서브트리의 내용을 자유롭게 변경해서 원격에 푸시할 수도 있다는 점이다. 프로젝트를 공동으로 진행할 때 어떤 사람이 작업한 feature A 브랜치에서 작동하는 서브트리의 소스를 푸시했는데, 그것을 내려받은 다른 사람의 feature B 브랜치에서는 오류가 발생할 수도 있다. 소규모 팀에서 작업할 때는 쉽게 대응이 가능하겠지만, 만약 수십 명이 프로젝트에 참여하는 상황이라면 위와 같은 문제가 발생할 가능성이 더 커지고 커뮤니케이션도 원활히 되지 않을 수 있다. 대규모 팀이라면 자유도가 높은 서브트리의 사용보다 프라이빗 NPM 레지스트리를 사용하면서 공용 소스 코드의 수정에 엄격한 기준을 세우는 등 다른 방법을 사용하는 편이 나을 수도 있다.&lt;/p&gt;
&lt;h2 id=&quot;git-서브트리-사용법&quot;&gt;&lt;a href=&quot;#git-%EC%84%9C%EB%B8%8C%ED%8A%B8%EB%A6%AC-%EC%82%AC%EC%9A%A9%EB%B2%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git 서브트리 사용법&lt;/h2&gt;
&lt;p&gt;이번 글을 위해 예제를 위한 샘플 저장소를 생성한 후 진행했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서브트리를 사용할 상위 저장소: &lt;a href=&quot;https://github.com/rhostem/gitsubtree-consumer&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;rhostem/gitsubtree-consumer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;서브트리로 추가될 저장소: &lt;a href=&quot;https://github.com/rhostem/gitsubtree-lib&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;rhostem/gitsubtree-lib&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;1-서브트리로-사용할-원격-저장소-추가&quot;&gt;&lt;a href=&quot;#1-%EC%84%9C%EB%B8%8C%ED%8A%B8%EB%A6%AC%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%A0-%EC%9B%90%EA%B2%A9-%EC%A0%80%EC%9E%A5%EC%86%8C-%EC%B6%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 서브트리로 사용할 원격 저장소 추가&lt;/h3&gt;
&lt;p&gt;다른 저장소를 서브트리로 바로 추가할 수도 있다. 하지만 서브트리를 fetch, pull, push도 할 것이므로 서브트리로 사용할 Git 저장소를 상위 저장소의 새로운 원격 저장소로서 추가한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; remote add gitsubtree-lib git@github.com:rhostem/gitsubtree-lib.git
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; remote add &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;원격 저장소의 이름&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;원격 저장소의 주소&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;원격 저장소의 이름으로 &lt;code class=&quot;language-text&quot;&gt;subtree&lt;/code&gt;를 사용했다. 기본 저장소 외에 하나가 더 추가되었음을 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; remote ⮐
origin
gitsubtree-lib&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;2-새로운-원격-저장소의-브랜치를-서브트리로-추가&quot;&gt;&lt;a href=&quot;#2-%EC%83%88%EB%A1%9C%EC%9A%B4-%EC%9B%90%EA%B2%A9-%EC%A0%80%EC%9E%A5%EC%86%8C%EC%9D%98-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%EC%84%9C%EB%B8%8C%ED%8A%B8%EB%A6%AC%EB%A1%9C-%EC%B6%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 새로운 원격 저장소의 브랜치를 서브트리로 추가.&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; subtree add --prefix lib gitsubtree-lib master
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; subtree add --prefix &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;클론할 폴더&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;원격 저장소의 이름&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;브랜치 이름&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;--prefix&lt;/code&gt; 옵션으로 서브트리를 클론할 폴더를 지정한다. 그리고 원격 저장소의 이름, 체크아웃할 브랜치 이름을 지정하면 서브트리로 gitsubtree-lib 저장소가 추가된다. 브랜치는 &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt; 를 사용하도록 했다.&lt;/p&gt;
&lt;p&gt;서브트리로 추가한 직후 폴더 구조는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.&lt;/span&gt;
├── README.md &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gitsubtree-consumer의 파일&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
└── lib
    └── README.md &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gitsubtree-lib의 파일&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;3-서브트리를-원격에서-내려받기pull&quot;&gt;&lt;a href=&quot;#3-%EC%84%9C%EB%B8%8C%ED%8A%B8%EB%A6%AC%EB%A5%BC-%EC%9B%90%EA%B2%A9%EC%97%90%EC%84%9C-%EB%82%B4%EB%A0%A4%EB%B0%9B%EA%B8%B0pull&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 서브트리를 원격에서 내려받기(pull)&lt;/h3&gt;
&lt;p&gt;서브트리의 원격에 올라온 커밋을 내려받는다. 서브트리를 추가하는 명령어와 큰 차이가 없으며, 자동으로 병합(merge)이 진행된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; subtree pull --prefix lib gitsubtree-lib master&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;커맨드라인에서 실행 후 병합이 완료되면 에디터가 열리면서 커밋 메시지의 저장을 요구할 수도 있다.&lt;/p&gt;
&lt;h3 id=&quot;4-서브트리를-원격에-올리기push&quot;&gt;&lt;a href=&quot;#4-%EC%84%9C%EB%B8%8C%ED%8A%B8%EB%A6%AC%EB%A5%BC-%EC%9B%90%EA%B2%A9%EC%97%90-%EC%98%AC%EB%A6%AC%EA%B8%B0push&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 서브트리를 원격에 올리기(push)&lt;/h3&gt;
&lt;p&gt;상위 저장소에서 서브트리의 소스를 직접 수정 후 변경사항을 커밋하고 원격에 푸시할 수 있다. 역시 명령어는 내려받기와 큰 차이가 없다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; subtree push --prefix lib gitsubtree-lib master&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;명령어가 짧지 않으므로 쉘 스크립트나 NPM 스크립트로 작성해서 사용하는 편이 좋다. 그리고 서브트리 명령어는 항상 상위 저장소의 최상위 폴더에서 실행해야 한다. 서브트리의 소스가 있는 폴더에서 &lt;code class=&quot;language-text&quot;&gt;git subtree&lt;/code&gt;명령어를 사용하면 동작하지 않는다.&lt;/p&gt;
&lt;h2 id=&quot;참고-자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.atlassian.com/git/tutorials/git-subtree&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Git subtree: the alternative to Git submodule | Atlassian Git Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/apenwarr/git-subtree&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;apenwarr/git-subtree: An experimental alternative to the git-submodule command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Why your company shouldn’t use Git submodules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[PM2로 Node.js 앱이 무중단 상태가 되도록 관리하기]]></title><description><![CDATA[PM2의 클러스터 모드를 통한 로드 밸런싱 PM2 를  클러스터 모드 로 사용하면 Node.js 서버 앱을 동시에 여러 개 실행하면서 1개의 포트를 공유할 수 있다. 굳이 똑같은 앱을 많은 메모리를 사용해가며 실행해야 하는 이유는  Node.js가 싱글 스레드여서 1…]]></description><link>https://blog.rhostem.com//posts/2019-10-23-pm2-graceful-reload</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2019-10-23-pm2-graceful-reload</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Wed, 23 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;pm2의-클러스터-모드를-통한-로드-밸런싱&quot;&gt;&lt;a href=&quot;#pm2%EC%9D%98-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EB%AA%A8%EB%93%9C%EB%A5%BC-%ED%86%B5%ED%95%9C-%EB%A1%9C%EB%93%9C-%EB%B0%B8%EB%9F%B0%EC%8B%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PM2의 클러스터 모드를 통한 로드 밸런싱&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://pm2.keymetrics.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;PM2&lt;/a&gt;를 &lt;a href=&quot;http://pm2.keymetrics.io/docs/usage/cluster-mode/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;클러스터 모드&lt;/a&gt;로 사용하면 Node.js 서버 앱을 동시에 여러 개 실행하면서 1개의 포트를 공유할 수 있다. 굳이 똑같은 앱을 많은 메모리를 사용해가며 실행해야 하는 이유는 &lt;strong&gt;Node.js가 싱글 스레드여서 1개의 CPU만 사용하기 때문&lt;/strong&gt;이다. PM2는 Node.js의 클러스터 모듈을 사용해서 원하는 수 만큼 프로세스를 쉽게 늘리고 줄일 수 있도록 도와준다. 그리고 자연히 1개의 프로세스만 실행할 때보다 성능과 안정성을 높일 수 있다.&lt;/p&gt;
&lt;p&gt;하지만 PM2는 무중단 재시작(graceful reload)를 &lt;em&gt;무조건&lt;/em&gt; 보장하지 않는다. PM2로 관리 중이던 앱을 재시작하면 &lt;strong&gt;예전 프로세스를 새 프로세스로 교체하는 과정이 발생&lt;/strong&gt;하는데, 거기서 이슈가 발생할 수 있다. 예를 들면&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;새 프로세스가 시작은 되었지만 앱이 완전히 준비되지 않은 상태에서(=&lt;code class=&quot;language-text&quot;&gt;server.listen&lt;/code&gt; 메소드의 콜백이 실행되지 않은 상태에서)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PM2가 네트워크 요청을 새 프로세스로 보낼 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예전 프로세스가 네트워크 요청을 처리중인데도&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PM2가 무시하고 프로세스를 강제로 죽여버렸을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 두 가지 모두 서비스가 일시적으로 죽은 것처럼 보이게 만들 수 있다.&lt;/p&gt;
&lt;p&gt;PM2는 프로세스를 교체할 때 앱의 구동과 종료에 필요한 시간에 여유를 주고 있다. &lt;strong&gt;다만 그 여유 시간이 지나면 앱이 준비가 되었든 말았든 프로세스가 강제로 교체된다&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;PM2는 재시작 명령어(&lt;code class=&quot;language-text&quot;&gt;pm2 reload&lt;/code&gt;)를 받아서 새 프로세스를 생성한 후 Node.js 앱이 준비될때까지(&lt;code class=&quot;language-text&quot;&gt;listen_timeout&lt;/code&gt;) 8초, 예전 프로세스가 종료될때까지(&lt;code class=&quot;language-text&quot;&gt;kill_timeout&lt;/code&gt;) 1.6초 동안 대기한다. 그 설정에 따라&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;새 프로세스를 시작한 후 8초가 지나면&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PM2는 예전 프로세스에 새 프로세스가 준비되었다는 신호(&lt;code class=&quot;language-text&quot;&gt;SIGINT&lt;/code&gt;)를 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;예전 프로세스가 새 프로세스가 시작되었다는 신호(&lt;code class=&quot;language-text&quot;&gt;SIGINT&lt;/code&gt;)를 받은 후 1.6초가 지나면&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PM2는 예전 프로세스를 강제로 종료시키고 새 프로세스로 교체한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;하지만 문제는 모든 앱이 PM2가 기본 설정한 그 시간 안에 시작되고, 종료되지는 않는다는 점이다. 그렇다고 해서 대기 시간을 어림짐작으로 30초, 1분 이런 식으로 늘려주는 방법은 현명하지 못하다. 예외라는 것은 언제든지 발생 할 수 있기 때문이다. 무중단 재시작 보장을 위해서는 &lt;strong&gt;Node.js 앱 코드에서 PM2와 신호를 주고받아야 한다&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;pm2와-nodejs-사이의-신호-교환&quot;&gt;&lt;a href=&quot;#pm2%EC%99%80-nodejs-%EC%82%AC%EC%9D%B4%EC%9D%98-%EC%8B%A0%ED%98%B8-%EA%B5%90%ED%99%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PM2와 Node.js 사이의 신호 교환&lt;/h2&gt;
&lt;h3 id=&quot;pm2-설정-파일&quot;&gt;&lt;a href=&quot;#pm2-%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PM2 설정 파일&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  apps&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      script&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;server.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      instances&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;      exec_mode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cluster&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 실행 모드. cluster로 명시하지 않으면 기본 fork 모드가 된다.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;      wait_ready&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Node.js 앱으로부터 앱이 실행되었다는 신호를 직접 받겠다는 의미&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;      listen_timeout&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 앱 실행 신호까지 기다릴 최대 시간. ms 단위.&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;      kill_timeout&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 새로운 프로세스 실행이 완료된 후 예전 프로세스를 교체하기까지 기다릴 시간&lt;/span&gt;&lt;/span&gt;      max_memory_restart&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;500M&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      env&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;development&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      env_production&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;production&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;8081&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://doc.pm2.io/en/runtime/reference/ecosystem-file/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;PM2 Ecosystem File Reference&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;서버-앱의-실행-준비가-완료되었다는-신호를-전달&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B2%84-%EC%95%B1%EC%9D%98-%EC%8B%A4%ED%96%89-%EC%A4%80%EB%B9%84%EA%B0%80-%EC%99%84%EB%A3%8C%EB%90%98%EC%97%88%EB%8B%A4%EB%8A%94-%EC%8B%A0%ED%98%B8%EB%A5%BC-%EC%A0%84%EB%8B%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서버 앱의 실행 준비가 완료되었다는 신호를 전달&lt;/h3&gt;
&lt;p&gt;설정에서 &lt;code class=&quot;language-text&quot;&gt;wait_ready&lt;/code&gt; 옵션을 &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;로 설정했으므로 Node.js 앱에서 구동이 완료되었다는 신호를 PM2에게 직접 전달해야 한다. express.js를 서버로 사용한다면 &lt;code class=&quot;language-text&quot;&gt;listen&lt;/code&gt; 메소드의 콜백에서 시작 신호를 보내면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; listeningServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&gt; Ready on http://localhost:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;port&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// PM2가 스크립트를 실행하지 않았다면 process.send 메소드가 undefined일 수 있다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// PM2에게 앱 구동이 완료되었음을 전달한다&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;    process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ready&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;실제로 구현된 앱이 실행되기까지 평균적으로 얼마나 걸리는지 확인을 해보고, 그보다 더 여유있는 시간을 &lt;code class=&quot;language-text&quot;&gt;listen_timeout&lt;/code&gt;에 설정하면 된다.&lt;/p&gt;
&lt;h3 id=&quot;새로운-프로세스가-준비되었다는-신호-수신&quot;&gt;&lt;a href=&quot;#%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EA%B0%80-%EC%A4%80%EB%B9%84%EB%90%98%EC%97%88%EB%8B%A4%EB%8A%94-%EC%8B%A0%ED%98%B8-%EC%88%98%EC%8B%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;새로운 프로세스가 준비되었다는 신호 수신&lt;/h3&gt;
&lt;p&gt;PM2는 &lt;code class=&quot;language-text&quot;&gt;listen_timeout&lt;/code&gt;에 설정된 시간이 경과하거나 &lt;code class=&quot;language-text&quot;&gt;process.send(&amp;#39;ready&amp;#39;)&lt;/code&gt; 호출을 통해 시작 신호를 수신받으면 예전 프로세스에게 새 앱이 준비되었으니 앱을 종료시키라는 의미로 &lt;code class=&quot;language-text&quot;&gt;SIGINT&lt;/code&gt; 시그널을 보낸다. Node.js 앱은 이 신호를 받아서 앱을 종료시키면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; isAppGoingToBeClosed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// HTTP 연결을 종료시킬 미들웨어에서 사용할 변수&lt;/span&gt;

&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;SIGINT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// SIGINT 신호가 수신되었을 때&lt;/span&gt;&lt;/span&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&gt; received SIGNIT signal&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  isAppGoingToBeClosed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// pm2 재시작 신호가 들어오면 서버를 종료시킨다.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// listeningServer: server.listen 메소드가 리턴하는 서버 인스턴스를 할당한 변수&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  listeningServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;server closed&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 예제 코드에서는 로그를 출력하는 정도의 작업만 했지만 서버 앱이 사용중인 데이터베이스같은 다른 서비스의 종료도 처리할 수 있을 것이다. 그리고 앱의 완전한 종료까지 시간이 많이 걸릴 것 같다면 &lt;code class=&quot;language-text&quot;&gt;kill_timeout&lt;/code&gt;을 더 여유 있게 설정해줘야 한다.&lt;/p&gt;
&lt;p&gt;그리고 &lt;code class=&quot;language-text&quot;&gt;SIGINT&lt;/code&gt; 수신 후에 HTTP 연결을 종료시키기 위해 네트워크 응답에 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Connection&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Connection&lt;/a&gt; 헤더를 &lt;code class=&quot;language-text&quot;&gt;close&lt;/code&gt;로 설정하는 미들웨어를 작성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 프로세스 종료 예정이라면 연결을 종료한다&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isAppGoingToBeClosed&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Connection&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;close&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 하면 프로세스가 교체되기 전에 Node.js 서버 앱이 먼저 종료되고, 네트워크 요청도 더이상 처리하지 않을 수 있다.&lt;/p&gt;
&lt;p&gt;본문에 있는 코드는 글 작성을 위해 만든 예제 앱에서 부분적으로 발췌한 것이며, 완전한 소스는 아래 링크에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rhostem/nextjs-pm2-cd&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/rhostem/nextjs-pm2-cd&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://pm2.keymetrics.io/docs/usage/signals-clean-restart/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;PM2 - Graceful Start/Shutdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://engineering.linecorp.com/ko/blog/pm2-nodejs/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;PM2를 활용한 Node.js 무중단 서비스하기 - LINE ENGINEERING&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[React Final Form을 사용한 폼 제어]]></title><description><![CDATA[React Final Form 은   Final Form 을 React 컴포넌트로 래핑한 라이브러리다. 다른 라이브러리에 대한 의존성 없이 최소 2개의 컴포넌트와 render props 패턴을 가지고 간단히 폼을 제어할 수 있다. 최소한의 기능을 구현하기 위해   요…]]></description><link>https://blog.rhostem.com//posts/2019-10-14-react-final-form</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2019-10-14-react-final-form</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 14 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://final-form.org/react&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React Final Form&lt;/a&gt;은  &lt;a href=&quot;https://final-form.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Final Form&lt;/a&gt;을 React 컴포넌트로 래핑한 라이브러리다. 다른 라이브러리에 대한 의존성 없이 최소 2개의 컴포넌트와 render props 패턴을 가지고 간단히 폼을 제어할 수 있다.&lt;/p&gt;
&lt;p&gt;최소한의 기능을 구현하기 위해 &lt;code class=&quot;language-text&quot;&gt;form&lt;/code&gt; 요소, 텍스트 입력을 위한 &lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt; 요소 하나, 그리고 form 요소의 onSubmit 이벤트를 호출하기 위한 &lt;code class=&quot;language-text&quot;&gt;button&lt;/code&gt; 요소만 있는 예제를 작성한다면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codesandbox.io/s/react-final-form-simple-example-6xfr2?fontsize=14&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React Final Form - Only 1 field - CodeSandbox&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react-dom&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Form&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Field &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react-final-form&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onSubmit&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; values &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;values&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Form&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onSubmit&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onSubmit&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;initialValues&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; handleSubmit &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onSubmit&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;handleSubmit&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Field&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; input &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onChange&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Submit&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;무척 간단하다. Final Form이 내부적으로 &lt;strong&gt;옵저버 패턴을 사용&lt;/strong&gt;하고 있어서 &lt;strong&gt;입력값이 변경되면 그 값을 사용하는 모든 내부 상태값과 동작에 즉시 반영&lt;/strong&gt;된다. 예를 들면 어떤 필드의 값이 변경되면 초기값과 현재 값이 달라졌는지 여부가 업데이트되며, 그 필드의 유효성 검사도 다시 실행되는 식이다. 라이브러리를 사용하는 개발자 입장에서는 최소한의 정보만 제공하면 되어 무척 편리하다. 폼을 직접 구현하기 위해 필드의 값을 직접 컴포넌트에 state에 할당하고, 업데이트하는 함수를 선언하고, 유효성 검사 이벤트를 연결하고… 이런 반복 작업이 모두 사라진다.&lt;/p&gt;
&lt;p&gt;물론 &lt;code class=&quot;language-text&quot;&gt;Form&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Field&lt;/code&gt;, 그리고 render props에는 위의 예제에서 작성한 것보다 훨씬 많은 값들이 제공된다. 그리고 위의 예제에서는 폼 데이터 제어에서 필수 요소인 유효성 검사도 빠져 있다.&lt;/p&gt;
&lt;h2 id=&quot;react-final-form-api&quot;&gt;&lt;a href=&quot;#react-final-form-api&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;React Final Form API&lt;/h2&gt;
&lt;h3 id=&quot;form&quot;&gt;&lt;a href=&quot;#form&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Form&lt;/h3&gt;
&lt;p&gt;폼 제어를 위한 최상위 컴포넌트. Field는 Form 컴포넌트의 render props에 전달한 컴포넌트 안에 있어야 기능한다. Form 컴포넌트는 마운팅될 때 Final Form 인스턴스를 생성하며 Form 상태와 내부 Field들의 상태를 관리한다. 그리고 상태가 업데이트되면 Form 안에 있는 모든 Field에 변경 사항을 알려준다(publish). Field 컴포넌트는 Form으로부터 상태값을 전달받는다(subscribe).&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textonsubmitcode-함수&quot;&gt;&lt;a href=&quot;#code-classlanguage-textonsubmitcode-%ED%95%A8%EC%88%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;onSubmit&lt;/code&gt; 함수&lt;/h4&gt;
&lt;p&gt;Form 컴포넌트에는 &lt;code class=&quot;language-text&quot;&gt;onSubmit&lt;/code&gt; props를 꼭 전달해야 한다. &lt;code class=&quot;language-text&quot;&gt;onSubmit&lt;/code&gt;은 폼이 제출(=form 요소의 onSubmit 이벤트가 발생)되고 모든 유효성 검사에 문제가 없을 때 호출되며 &lt;strong&gt;모든 필드의 데이터를 포함한 객체&lt;/strong&gt;를 첫번째 파라미터로 받는다. 객체의 필드들의 이름은 Field 컴포넌트의 &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; props로 전달된 문자열과 일치한다. 예를 들어 첫번째 예제 코드의 onSubmit 함수의 파라미터는 아래와 같은 형태가 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onSubmit&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;formValues&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;폼-초기값-설정&quot;&gt;&lt;a href=&quot;#%ED%8F%BC-%EC%B4%88%EA%B8%B0%EA%B0%92-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;폼 초기값 설정&lt;/h4&gt;
&lt;p&gt;Form 안에 있는 Field들의 초기값은 Field 컴포넌트에 1개씩 할당해도 되지만, 필드의 수가 많으면 Form 컴포넌트의 &lt;code class=&quot;language-text&quot;&gt;initialValues&lt;/code&gt; props에 1개의 객체로 한꺼번에 전달하는 편이 더 간편하다. &lt;code class=&quot;language-text&quot;&gt;initialValues&lt;/code&gt;가 받는 데이터는 객체이며, 객체의 필드는 Field 컴포넌트의 &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; props와 일치해야 한다. 다만 &lt;code class=&quot;language-text&quot;&gt;initialValues&lt;/code&gt;에는 모든 필드의 초기값을 전달하지 않아도 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Form
  initialValues&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; userId&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;이름&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  render&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Field&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;userId&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* userId 초기값이 적용된다. */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;field&quot;&gt;&lt;a href=&quot;#field&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Field&lt;/h3&gt;
&lt;p&gt;Form 컴포넌트 내부에 위치하며 render props 패턴을 통해 Form 컴포넌트로부터 Field의 상태를 전달받는다. Field의 props로 필드의 이름, 렌더링할 컴포넌트를 전달한다. Field 안에 들어갈 컴포넌트 렌더링에는 입력을 받을 input 같은 요소 외의 다른 어떤 요소가 들어가도 상관없다. 단 &lt;strong&gt;Form 컴포넌트에 입력값을 연결하기 위해 &lt;code class=&quot;language-text&quot;&gt;props.input.onChange&lt;/code&gt; 콜백을 반드시 사용&lt;/strong&gt;해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Field&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;userId&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;User Id&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &amp;lt;input
        &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* props로 전달된 input.onChange를 사용해서 데이터를 Form 컴포넌트에 연결한다. */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        onChange=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onChange&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        value=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      /&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;필드-유효성-검사&quot;&gt;&lt;a href=&quot;#%ED%95%84%EB%93%9C-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%82%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;필드 유효성 검사&lt;/h4&gt;
&lt;p&gt;Final Form에서 유효성 검사는 단순 함수로 가능하다. 필드의 유효성 검사 함수에는 현재 필드의 값이 전달되고, 함수가 문자열을 리턴하면 필드에 오류가 있음을 의미하며, &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;가 리턴되면 필드에 문제가 없음을 의미한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// undefined, null, 빈 문자열이면 오류&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;v &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;이 값은 필수입니다&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; undefined&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 값이 반드시 숫자&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;mustBeNumber&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;number&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;이 값은 숫자여야 합니다&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; undefined&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 복수의 유효성 검사를 조합해서 사용하는 것이 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;composeValidators&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;validators&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
  validators&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; validator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; error &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; undefined&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 조합 함수는 팩토리 함수 패턴과 &lt;code class=&quot;language-text&quot;&gt;Array.prototype.reduce&lt;/code&gt; 함수를 사용했다. reduce 함수에서는 초기값을 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;(=오류 없음)로 두고 &lt;code class=&quot;language-text&quot;&gt;error&lt;/code&gt;(현재 값)에 유효한 값이 없으면 유효성 검사를 호출하고, 아니면 더 이상 실행하지 않는 방식이다. 아래와 같은 방식으로 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Field
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* 예를 들어 나이를 입력받는 필드 */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;age&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* 이 필드는 undefined, null이 아니며 값이 숫자여야 한다. 그렇지 않으면 onSubmit이 호출되지 않는다.*/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  validate&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;composeValidators&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mustBeNumber&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Field&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;age&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;유효성 검사 함수가 리턴한 문자열은 Field의 render props에서 &lt;code class=&quot;language-text&quot;&gt;meta.error&lt;/code&gt; 필드로 전달되어 오류 메시지로 사용한다. 그리고 다른 메타데이터와 조합하면 오류 메시지를 표시할 조건을 구체적으로 결정할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Field&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;age&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;composeValidators&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mustBeNumber&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; meta &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* 에러는 meta props가 가지고 있다. */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* 폼 제출에 실패했고 에러 메시지가 있을 때 메시지를 표시하도록 한다 */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;meta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;submitFailed &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; meta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;meta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;보다 상세한 필드 유효성 검사 예제는 아래에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://final-form.org/docs/react-final-form/examples/field-level-validation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Final Form Docs – Field-Level Validation&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Elastic Beanstalk로 Next.js 앱 배포하기]]></title><description><![CDATA[AWS  Elastic Beanstalk 는 웹 애플리케이션을 배포할 수 있는 클라우드 플랫폼으로 인스턴스 성능 조절, 로드 밸런싱, 상태 모니터링, 버전 관리 등이 가능하다. 사용자에게 웹 애플리케이션 배포를 위한 AWS의 여러 서비스를 통합 제공하는 서비스라고 볼…]]></description><link>https://blog.rhostem.com//posts/2019-09-01-deploy-next-app-to-eb</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2019-09-01-deploy-next-app-to-eb</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 01 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;AWS &lt;a href=&quot;https://aws.amazon.com/ko/elasticbeanstalk&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Elastic Beanstalk&lt;/a&gt;는 웹 애플리케이션을 배포할 수 있는 클라우드 플랫폼으로 인스턴스 성능 조절, 로드 밸런싱, 상태 모니터링, 버전 관리 등이 가능하다. 사용자에게 웹 애플리케이션 배포를 위한 AWS의 여러 서비스를 통합 제공하는 서비스라고 볼 수 있다. 특히 변화하는 네트워크 트래픽에 대응할 수 있도록 자동 스케일링을 지원한다. 그리고 복수의 환경을 구성해서 스테이징 버전을 충분히 테스트한 후 프로덕션 버전을 서비스 중단 없이 교체하는 등 서비스 안정성 보장을 위한 다양한 기능이 있다.&lt;/p&gt;
&lt;p&gt;공식 문서에는 &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/create_deploy_nodejs_express.html?shortFooter=true&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Node.js 플랫폼에서 express 애플리케이션을 배포&lt;/a&gt;하는 방법을 제공한다. 이 글에서는 별도의 빌드 과정이 필요한 &lt;a href=&quot;https://nextjs.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Next&lt;/a&gt; 앱을 배포하는 과정을 정리했다.&lt;/p&gt;
&lt;h2 id=&quot;elastic-beanstalk-용어&quot;&gt;&lt;a href=&quot;#elastic-beanstalk-%EC%9A%A9%EC%96%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Elastic Beanstalk 용어&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;애플리케이션&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;복수의 환경으로 구성된 작업 공간.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;환경&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;앱이 실행되는 배포 환경. 각각의 환경은 서로 다른 ID와 접속 URL을 가진다. 개발, 릴리즈, 프로덕션 등 목적에 맞게 추가해서 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;인스턴스&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1개의 환경은 복수의 AWS EC2 인스턴스를 가질 수 있다. 인스턴스 유형은 필요한 성능에 맞춰 선택할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;플랫폼&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;운영체제, 웹 서버, 애플리케이션 서버 등 웹 애플리케이션 실행을 위한 구성 요소를 포함한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;용량&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;환경의 인스턴스가 1개 이상이면 자동으로 로드 밸런싱이 작동한다. 환경은 최소 1개에서 최대 10000개의 인스턴스를 가질 수 있다. 실제로 사용되는 인스턴스의 수는 사용자가 설정한 최소, 최대 인스턴스 수와 트리거에 기반을 둬서 자동으로 조절된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;롤링 업데이트&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;배포를 하면 기본적으로 모든 인스턴스를 한 번에 업데이트한다. 그런데 Elastic Beanstalk는 드롭인(drop-in) 업그레이드 프로세스를 사용하기 때문에 애플리케이션이 몇 초 정도 가동이 중지될 수 있다. 그래서 프로덕션 환경에서는 인스턴스를 나눠서 업데이트하는 방법이 권장된다. 한 번에 업데이트할 인스턴스의 수와 비율을 사용자가 직접 지정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;배포-과정&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%ED%8F%AC-%EA%B3%BC%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배포 과정&lt;/h2&gt;
&lt;p&gt;아래의 배포 과정은 macOS에서 진행할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;1-eb-cli-설치&quot;&gt;&lt;a href=&quot;#1-eb-cli-%EC%84%A4%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. EB CLI 설치&lt;/h3&gt;
&lt;p&gt;설치 스크립트가 있는 Git repository를 클론한 후 실행한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/aws/aws-elastic-beanstalk-cli-setup.git

./aws-elastic-beanstalk-cli-setup/scripts/bundled_installer&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;시스템에 파이썬이 설치되어 있지 않다면 &lt;code class=&quot;language-text&quot;&gt;bundled_installer&lt;/code&gt; 실행 중에 오류가 발생한다. 그럴 때는 scripts 폴더에 있는 &lt;code class=&quot;language-text&quot;&gt;python_installer&lt;/code&gt;로 파이썬을 설치한 후 진행하면 된다. 설치 성공 후에는 CLI 실행 파일을 PATH에 추가하는 명령어가 제공되므로 그것도 실행해준다.&lt;/p&gt;
&lt;h3 id=&quot;2-git-저장소-초기화&quot;&gt;&lt;a href=&quot;#2-git-%EC%A0%80%EC%9E%A5%EC%86%8C-%EC%B4%88%EA%B8%B0%ED%99%94&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Git 저장소 초기화&lt;/h3&gt;
&lt;p&gt;Elastic Beanstalk는 Git 저장소에서 마지막으로 커밋된 버전을 압축해서 S3에 올린다. 소스를 수정했더라도 커밋을 하지 않았다면 해당 내용은 배포되지 않으니 주의해야 한다.&lt;/p&gt;
&lt;p&gt;그리고 현재 브랜치의 커밋이 원격에 푸시되지 않아도 배포할 수 있다. 그래서 공동 작업을 한다면 CI 도구를 사용해서 원격에 올라간 작업 내용만 Elastic Beastalk에 배포되게 보장해야 할 것이다.&lt;/p&gt;
&lt;h3 id=&quot;3-nextjs-앱-설치&quot;&gt;&lt;a href=&quot;#3-nextjs-%EC%95%B1-%EC%84%A4%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Next.js 앱 설치&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://nextjs.org/docs&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Next.js 문서&lt;/a&gt;에 있는 설치 과정을 참조해서 빌드와 실행이 가능하도록 프로젝트를 소스를 구성한다.&lt;/p&gt;
&lt;h3 id=&quot;4-code-classlanguage-texteb-initcode&quot;&gt;&lt;a href=&quot;#4-code-classlanguage-texteb-initcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. &lt;code class=&quot;language-text&quot;&gt;eb init&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Elastic Beanstalk 애플리케이션을 생성하거나 선택할 수 있도록 초기 설정을 진행한다. 자기에게 맞는 &lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/using-regions-availability-zones.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;리전&lt;/a&gt;, 플랫폼 등을 선택한다.&lt;/p&gt;
&lt;h3 id=&quot;5-code-classlanguage-texteb-createcode&quot;&gt;&lt;a href=&quot;#5-code-classlanguage-texteb-createcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. &lt;code class=&quot;language-text&quot;&gt;eb create&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;배포할 환경을 만든다. 이름을 입력하고 로드 밸런서 타입을 선택한다. 로드 밸런서는 보통 기본 타입인 application을 선택한다.&lt;/p&gt;
&lt;p&gt;init, create 과정을 거치면 프로젝트 폴더에 .elasticbeanstalk/config.yml 파일이 만들어진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;branch-defaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;develop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;    &lt;span class=&quot;token key atrule&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; eb&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;dev &lt;span class=&quot;token comment&quot;&gt;# develop 브랜치에서 배포할 환경. eb use 명령어로 수정 가능하다. &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token key atrule&quot;&gt;environment-defaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;eb-next-app-dev&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null important&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null important&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token key atrule&quot;&gt;application_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; eb&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;next&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;app &lt;span class=&quot;token comment&quot;&gt;# 애플리케이션 이름&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token key atrule&quot;&gt;default_ec2_keyname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;name &lt;span class=&quot;token comment&quot;&gt;# 배포에 ssh를 사용하려면 AWS IAM에서 엑세스 키를 만들어야 한다.&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token key atrule&quot;&gt;default_platform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;elasticbeanstalk&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;ap&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;northeast&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;platform/Node.js running on 64bit Amazon Linux/4.10.1 &lt;span class=&quot;token comment&quot;&gt;# 선택한 플랫폼&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;default_region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ap&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;northeast&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2 &lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;# Seoul 리전&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;include_git_submodules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;instance_profile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null important&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;platform_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null important&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;platform_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null important&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; eb&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cli
  &lt;span class=&quot;token key atrule&quot;&gt;sc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; git
  &lt;span class=&quot;token key atrule&quot;&gt;workspace_type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Application&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 글에서 진행하는 배포 과정에서는 config.yml 파일을 직접 수정해야 할 필요가 없다.&lt;/p&gt;
&lt;h3 id=&quot;6-ebignore-파일-추가&quot;&gt;&lt;a href=&quot;#6-ebignore-%ED%8C%8C%EC%9D%BC-%EC%B6%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. .ebignore 파일 추가&lt;/h3&gt;
&lt;p&gt;배포는 Git 저장소 전체를 S3에 업로드하는 방식이다. .gitignore 설정 파일이 있다면 등록된 파일을 제외하고 배포를 한다. 그런데 .gitignore에는 보통 .next, node_modules처럼 앱 실행에 필요한 파일과 폴더가 지정되어 있다. 그런 상태에서 배포를 해서 앱을 실행하면 자연히 필요한 파일을 찾지 못하는 오류가 생긴다. 그래서 &lt;strong&gt;.ebignore 파일에 배포를 할 때 제외시킬 파일을 지정&lt;/strong&gt;해야 한다. .ebignore에는 아래와 같은 내용이 들어갈 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;.git
.gitignore&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Git 저장소 관련 파일만 제외하고 모두 배포하도록 했다. EB CLI는 .ebignore가 있으면 .gitignore를 무시한다. &lt;/p&gt;
&lt;h3 id=&quot;7-nextjs-앱의-실행-포트를-8081로-변경&quot;&gt;&lt;a href=&quot;#7-nextjs-%EC%95%B1%EC%9D%98-%EC%8B%A4%ED%96%89-%ED%8F%AC%ED%8A%B8%EB%A5%BC-8081%EB%A1%9C-%EB%B3%80%EA%B2%BD&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. Next.js 앱의 실행 포트를 8081로 변경&lt;/h3&gt;
&lt;p&gt;Elastic Beanstalk의 node.js 플랫폼은 기본적으로 웹 서버로 Nginx를 사용하며 &lt;strong&gt;리버스 프록시 설정을 통해 외부 접근을 8081 포트로 보낸다&lt;/strong&gt;. 그런데 &lt;strong&gt;Next 앱은 기본적으로 서버 포트로 3000을 사용&lt;/strong&gt;한다. 리버스 프록시 설정을 직접 수정할 수도 있지만 Next 앱이 프로덕션 모드로 실행할 때 8081 포트를 사용하도록 package.json 파일을 수정하는 편이 더 간단하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;next start -p 8081&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;8-npmrc-설정-생략-가능&quot;&gt;&lt;a href=&quot;#8-npmrc-%EC%84%A4%EC%A0%95-%EC%83%9D%EB%9E%B5-%EA%B0%80%EB%8A%A5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. .npmrc 설정 (생략 가능)&lt;/h3&gt;
&lt;p&gt;Elastic Beanstalk에서 node.js 앱을 실행하기 위한 기본 명령어는 &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt;로 지정되어 있다(콘솔에서 수정 가능). 만약 저 스크립트에 &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;이 포함되어 있다면 배포 과정에서 오류가 발생할 수 있다.&lt;/p&gt;
&lt;p&gt;npm install은 root 계정으로 실행되지만 node-gyp 프로세스가 일부 패키지를 기본 계정인 ec2-user로 실행한다. 그 결과 node-gyp 프로세스가 root 계정이 만든 파일에 접근할 수 없는 오류를 만든다. 이를 방지하기 위해서는 npm이 node-gyp 프로세스를 강제로 root 계정을 통해 실행하도록 .npmrc 파일을 추가해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;unsafe-perm&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;true&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;9-code-classlanguage-texteb-deploycode&quot;&gt;&lt;a href=&quot;#9-code-classlanguage-texteb-deploycode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9. &lt;code class=&quot;language-text&quot;&gt;eb deploy&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;터미널에서 &lt;code class=&quot;language-text&quot;&gt;eb deploy &amp;lt;환경 이름&amp;gt;&lt;/code&gt;을 실행하면 해당 환경으로 브랜치의 최근 커밋이 배포된다.&lt;/p&gt;
&lt;p&gt;빌드 후 배포가 진행되도록 npm script를 수정한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;next dev&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;next start -p 8081&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run clean &amp;amp;&amp;amp; next build&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;clean&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rm -rf .next&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;deploy-dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm install &amp;amp;&amp;amp; npm run build &amp;amp;&amp;amp; eb deploy eb-next-app-dev&quot;&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run deploy-dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;10-code-classlanguage-texteb-opencode&quot;&gt;&lt;a href=&quot;#10-code-classlanguage-texteb-opencode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10. &lt;code class=&quot;language-text&quot;&gt;eb open&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;배포에 성공했다면 &lt;code class=&quot;language-text&quot;&gt;eb open&lt;/code&gt; 명령어를 사용해서 기본 브라우저에 배포된 웹페이지를 열어본다. 접속 주소는 콘솔에서도 확인할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;참고-문서&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EB%AC%B8%EC%84%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 문서&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/platforms-glossary.html?shortFooter=true&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;AWS Elastic Beanstalk 플랫폼 용어집 - AWS Elastic Beanstalk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/troubleshooting-deployments.html?shortFooter=true&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;배포 - AWS Elastic Beanstalk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/nodejs-platform-proxy.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;프록시 서버 구성 - AWS Elastic Beanstalk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/46001516/beanstalk-node-js-deployment-node-gyp-fails-due-to-permission-denied&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Beanstalk: Node.js deployment - node-gyp fails due to permission denied - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;샘플-앱-저장소&quot;&gt;&lt;a href=&quot;#%EC%83%98%ED%94%8C-%EC%95%B1-%EC%A0%80%EC%9E%A5%EC%86%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;샘플 앱 저장소&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rhostem/eb-next-app&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;rhostem/eb-next-app: deploy next app with elastic beanstalk&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[React Hooks API가 도입된 이유]]></title><description><![CDATA[React Hooks(이하 리액트 훅)은 함수형 컴포넌트로 클래스 컴포넌트를 대체하기 위한 목적으로 도입되었다. 훅이 있기 전의 함수형 컴포넌트는 자체 state(상태)를 가질 수 없었고 상위 컴포넌트로부터 props(값)을 전달받아 표현하는 컴포넌트(presenta…]]></description><link>https://blog.rhostem.com//posts/2019-08-18-reason-whey-react-hooks-opt-in</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2019-08-18-reason-whey-react-hooks-opt-in</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 18 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;React Hooks(이하 리액트 훅)은 함수형 컴포넌트로 클래스 컴포넌트를 대체하기 위한 목적으로 도입되었다. 훅이 있기 전의 함수형 컴포넌트는 자체 state(상태)를 가질 수 없었고 상위 컴포넌트로부터 props(값)을 전달받아 표현하는 컴포넌트(presentational or dumb component)로 사용되고 있었다. 구분하자면 클래스 컴포넌트는 컨트롤러, 함수형 컴포넌트는 유닛이었다.&lt;/p&gt;
&lt;p&gt;하지만 React 16.8 이후 리액트 훅이 도입되면서 그런 구분이 사라졌다. 새로운 API를 통해 함수형 컴포넌트도 자체 상태를 가질 수 있으며 &lt;a href=&quot;https://reactjs.org/docs/hooks-faq.html#how-do-lifecycle-methods-correspond-to-hooks&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;라이프사이클 메소드를 대체할 수 있는 방법&lt;/a&gt;이 생겼다.&lt;/p&gt;
&lt;p&gt;React가 등장한 후 5년여 동안 많은 변화가 있었지만 컴포넌트를 작성하는 API에는 큰 변화가 없었다. 그런데 왜 갑자기 기존의 방법론을 뒤엎는 새로운 길을 제시한 것일까?&lt;/p&gt;
&lt;h2 id=&quot;컴포넌트-로직-공유의-복잡함&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A1%9C%EC%A7%81-%EA%B3%B5%EC%9C%A0%EC%9D%98-%EB%B3%B5%EC%9E%A1%ED%95%A8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트 로직 공유의 복잡함&lt;/h2&gt;
&lt;p&gt;리액트에서 컴포넌트 상태 관리 로직을 공유하기 위한 방법으로는 &lt;a href=&quot;https://ko.reactjs.org/docs/higher-order-components.html#___gatsby&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;hoc(Higher-Order Components)&lt;/a&gt;와 &lt;a href=&quot;https://ko.reactjs.org/docs/render-props.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;render props&lt;/a&gt;가 있다. 하지만 두 패턴 모두 처음보면 다소 이해하기 어려우며 실무 활용을 위해서는 여러가지 예제 코드를 분석하고 직접 만들어보는 학습 과정이 필요하다. 특히 자바스크립트에서는 함수가 일급 객체여서 함수(리액트 컴포넌트)를 함수의 파라미터로 전달해서 활용하는 패턴이 익숙하지 않을 수 있다. 그리고 타입스크립트로 리액트를 사용한 사람이라면 hoc의 리턴 타입을 선언하는 일이 무척 까다로워서 고생했던 경험을 가진 사람이 많을 것이다.&lt;/p&gt;
&lt;p&gt;하지만 이제 함수형 컴포넌트에서 커스텀 훅을 사용하면 hoc와 render props을 대체할 수 있다. 그리고 커스텀 훅은 특별한 것이 아니라 그냥 ‘함수’다. 예를 들어 &lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt; 요소의 상태를 관리하는 기능을 커스텀 훅으로 분리하면 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; useEffect &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useChangeInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; initialValue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onChange&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setValue&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;handleChange&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;initialValue&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  
   
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;    value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;    handleChange&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;/span&gt;&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 커스텀 훅을 사용할 컴포넌트&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SampleInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    handleChange&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;gatsby-highlight-code-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useChangeInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// custom hook을 호출해서 상태 관리에 필요한 값을 가져온다&lt;/span&gt;&lt;/span&gt;    initialValue&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;input
      type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;text&quot;&lt;/span&gt;
      value&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      onChange&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;handleChange&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;useChangeInput&lt;/code&gt; 커스텀 훅은 일반적인 함수면서 내부에 &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt;같은 훅 API를 사용하고 있다. 커스텀 훅을 사용할 때는 hoc처럼 컴포넌트를 함수로 래핑하는 과정이 필요없고 단순히 호출을 통해 필요한 상태와 액션을 컴포넌트에 연결(hook)만 하면 된다. &lt;/p&gt;
&lt;h2 id=&quot;커스텀-훅이-redux를-대체할-수-있다&quot;&gt;&lt;a href=&quot;#%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85%EC%9D%B4-redux%EB%A5%BC-%EB%8C%80%EC%B2%B4%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;커스텀 훅이 Redux를 대체할 수 있다?&lt;/h2&gt;
&lt;p&gt;커스텀 훅의 사용 방식은 마치 Redux로 앱 상태 관리자를 따로 두면서 필요한 컴포넌트에 action과 state를 연결하는 방식과(&lt;code class=&quot;language-text&quot;&gt;connect&lt;/code&gt; hoc와 &lt;code class=&quot;language-text&quot;&gt;mapStateToProps&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;mapDispatchToProps&lt;/code&gt;) 유사하다. 게다가 리액트에서 &lt;code class=&quot;language-text&quot;&gt;useReducer&lt;/code&gt; API를 제공하고 있으니 리액트가 Redux가 제공하는 기능을 대체하려는 의도가 있는 것인가? 라고 생각할 수도 있다. 하지만 커스텀 훅은 싱글 인스턴스를 만들지 않으며 클로져처럼 동작한다. 커스텀 훅을 사용하는 컴포넌트마다 서로 다른 상태 값을 참조하게 되는 것이다. 여러 컴포넌트에서 상태를 공유하기 위해서는 Redux나 Context API, 컴포넌트 트리를 통해 props 전달하기 같은 방식을 여전히 사용해야 한다. &lt;/p&gt;
&lt;p&gt;그리고 Redux는 관련 라이브러리 생태계가 오랜 기간 유지되어 왔으며 미들웨어를 통해 확장할 수 있는 기능이 많다. 또 react-redux는 최신 버전에서 &lt;code class=&quot;language-text&quot;&gt;useSelector&lt;/code&gt;같은 함수형 컴포넌트를 위한 API도 제공하고 있다. 역할을 구분하자면 커스텀 훅은 UI 상호작용과 관련된 로컬 컴포넌트 상태 관리에, Redux는 앱의 핵심 상태를 관리하는 비지니스 로직을 관리하는 일로 나눌 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;훅을 사용할 케이스&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UI의 상태를 네트워크 또는 로컬 스토리지 등을 통해서 별도로 저장하거나 가져오지 않음&lt;/li&gt;
&lt;li&gt;child가 아닌 다른 컴포넌트와 상태를 공유하지 않음&lt;/li&gt;
&lt;li&gt;계속 유지되지 않고, 일시적으로 존재할 상태(ex. input 필드의 입력)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;redux를 사용할 케이스&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;네트워크나 디바이스 API를 사용한 I/O&lt;/li&gt;
&lt;li&gt;상태의 저장, 불러오기&lt;/li&gt;
&lt;li&gt;child가 아닌 컴포넌트와 상태 공유&lt;/li&gt;
&lt;li&gt;앱의 다른 파트와 공유해야 하는 비즈니스 로직, 데이터 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;사이드-이펙트와-버그를-유발할-수-있는-라이프사이클-메소드&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%9D%B4%EB%93%9C-%EC%9D%B4%ED%8E%99%ED%8A%B8%EC%99%80-%EB%B2%84%EA%B7%B8%EB%A5%BC-%EC%9C%A0%EB%B0%9C%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4-%EB%A9%94%EC%86%8C%EB%93%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사이드 이펙트와 버그를 유발할 수 있는 라이프사이클 메소드&lt;/h2&gt;
&lt;p&gt;클래스 컴포넌트에는 &lt;a href=&quot;https://reactjs.org/docs/glossary.html#lifecycle-methods&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;라이프사이클 메소드&lt;/a&gt;가 제공된다. 대표적으로 &lt;code class=&quot;language-text&quot;&gt;componentDidMount&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;render&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;componentDidUpdate&lt;/code&gt; 메소드가 있으며 각각 서로 다른 시점에 호출되며 서로 다른 목적을 가진다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/52br3RQchocQ7Lf4PUgPwI/34920c8920c29103dc202c752f5387ad/react_lifecycle_diagram.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 63.82608695652174%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;react lifecycle diagram&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/52br3RQchocQ7Lf4PUgPwI/34920c8920c29103dc202c752f5387ad/react_lifecycle_diagram.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/52br3RQchocQ7Lf4PUgPwI/34920c8920c29103dc202c752f5387ad/react_lifecycle_diagram.png?w=575 575w,
https://images.ctfassets.net/rpmifyuylbfw/52br3RQchocQ7Lf4PUgPwI/34920c8920c29103dc202c752f5387ad/react_lifecycle_diagram.png?w=1150 1150w,
https://images.ctfassets.net/rpmifyuylbfw/52br3RQchocQ7Lf4PUgPwI/34920c8920c29103dc202c752f5387ad/react_lifecycle_diagram.png?w=2300 2300w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;&lt;a href=&quot;http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram&lt;/a&gt;&lt;/em&gt; &lt;/p&gt;
&lt;p&gt;라이프사이클 메소드는 러닝 커브를 높이는 역할을 함은 물론 의도하지 않은 사이드 이펙트와 버그를 발생할 가능성도 함께 높인다. 예를 들어 렌더링 여부를 결정하는 &lt;code class=&quot;language-text&quot;&gt;shouldComponentUpdate&lt;/code&gt; 라이프사이클은 최적화를 위해서 많이 사용되지만, 데이터 업데이트에 제대로 반응하지 않는 먹통 컴포넌트를 종종 만들어내는 원인이 되기도 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;shouldComponentUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prevProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prevState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isDeepEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prevProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 예제에서는 업데이트된 &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt; props의 내용이 같다면 렌더링을 추가로 실행하지 않는다. 의도하지 않은 버그를 방지하기 위해서는 저 컴포넌트의 복잡도를 낮춰서 컴포넌트가 업데이트되는 경우의 수를 가능한 한 적게 유지해야 할 것이다.&lt;/p&gt;
&lt;p&gt;그리고 props를 기반으로 컴포넌트를 업데이트하려면 &lt;code class=&quot;language-text&quot;&gt;componentDidUpdate&lt;/code&gt; 라이프사이클을 사용하게 된다.
컴포넌트 사이즈가 조금만 커지면 저 라이프사이클 메소드에 props를 확인하는 if 문과 로직이 계속 추가되고 클래스의 메소드도 계속해서 추가된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;componentDidUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prevProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prevState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; prevProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initialDate &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; prevProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;initialDate&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        date&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; initialDate
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 또 다른 사이드 이펙트 ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;사이드 이펙트가 많아지면 자연스럽게 컴포넌트의 덩치가 커지고, render 메소드만으로는 이 컴포넌트가 어떻게 동작하는지 파악하기 어려워진다. 하지만 &lt;code class=&quot;language-text&quot;&gt;componentDidUpdate&lt;/code&gt; 들어갈 사이드 이펙트를 커스텀 훅으로 분리하면 컴포넌트의 복잡도를 낮출 수 있다. 그리고 라이프사이클을 사용하는 컴포넌트에 비해서 테스트코드 작성도 더 쉬워진다.&lt;/p&gt;
&lt;h2 id=&quot;props와-state가-바뀌면-다시-렌더링-된다는-1개의-규칙&quot;&gt;&lt;a href=&quot;#props%EC%99%80-state%EA%B0%80-%EB%B0%94%EB%80%8C%EB%A9%B4-%EB%8B%A4%EC%8B%9C-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%90%9C%EB%8B%A4%EB%8A%94-1%EA%B0%9C%EC%9D%98-%EA%B7%9C%EC%B9%99&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;props와 state가 바뀌면 다시 렌더링 된다는 1개의 규칙&lt;/h2&gt;
&lt;p&gt;함수형 컴포넌트에서는 라이프사이클 메소드가 없으니 렌더링 규칙은 명확하다. 상위 컴포넌트에서 전달받는 props가 달라지거나 컴포넌트 안에서 &lt;code class=&quot;language-text&quot;&gt;useState&lt;/code&gt;로 관리하는 상태 값이 업데이트되면 컴포넌트가 다시 렌더링 된다. 그리고 특정 값이 변경되었을 때 사이드 이펙트를 발생시키는 &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt;, 캐싱을 지원하는 &lt;code class=&quot;language-text&quot;&gt;useMemo&lt;/code&gt;, 함수의 레퍼런스를 유지시켜주는 &lt;code class=&quot;language-text&quot;&gt;useCallback&lt;/code&gt; 등의 API를 사용해서 컴포넌트 렌더링을 제어 할 수 있다. &lt;/p&gt;
&lt;p&gt;처음 사용하면 생각지 못하게 무한 렌더링에 빠지는 등 클래스 컴포넌트와는 다른 구현 방식에 익숙해지는 데 시간이 걸릴 수 있다. 하지만 어느 정도 적응이 되면 클래스 컴포넌트보다 간단한 문법, 값이 바뀌고 레퍼런스가 바뀌면 컴포넌트가 다시 렌더링 된다는 일관적인 규칙의 단순함이 좋아서 굳이 클래스 컴포넌트로 돌아가고 싶은 마음이 들지 않게 될 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;참고 문서&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-intro.html#motivation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Introducing Hooks – Motivation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/do-react-hooks-replace-redux-210bab340672&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Do React Hooks Replace Redux?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://overreacted.io/a-complete-guide-to-useeffect/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;A Complete Guide to useEffect&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[mobx-react와 React Hooks API 함께 사용하기]]></title><description><![CDATA[React hooks와 mobx-react v4 React 16.8에서 도입된 React Hooks는 함수형 컴포넌트에서 상태를 관리할 수 있는 새로운 방법을 제공한다. Hooks API를 사용하면 함수형 컴포넌트에서도 자체 상태값을 가질 수 있으며, 클래스 컴포넌트…]]></description><link>https://blog.rhostem.com//posts/2019-07-22-mobx-v6-and-react-v16-8</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2019-07-22-mobx-v6-and-react-v16-8</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 22 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;react-hooks와-mobx-react-v4&quot;&gt;&lt;a href=&quot;#react-hooks%EC%99%80-mobx-react-v4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;React hooks와 mobx-react v4&lt;/h2&gt;
&lt;p&gt;React 16.8에서 도입된 React Hooks는 함수형 컴포넌트에서 상태를 관리할 수 있는 새로운 방법을 제공한다. Hooks API를 사용하면 함수형 컴포넌트에서도 자체 상태값을 가질 수 있으며, 클래스 컴포넌트의 라이프사이클 메소드도 대체할 수 있다. 물론 클래스 컴포넌트가 더 좋은지 함수형이 더 좋은지는 논쟁의 여지가 있다. 그리고 컴포넌트가 커지면 복잡도가 높아지는 것은 클래스나 함수형이나 마찬가지다. 하지만 개인적으로는 적절한 크기의 컴포넌트라면 확실히 Hooks를 사용했을 때 코드가 더 간결하고 직관적이라는 인상을 받았다.&lt;/p&gt;
&lt;p&gt;그런데 mobx-react가 제공하는 &lt;code class=&quot;language-text&quot;&gt;observer&lt;/code&gt;로 래핑된 함수형 컴포넌트에서 &lt;code class=&quot;language-text&quot;&gt;useState&lt;/code&gt;같은 훅 API를 사용하려고 하면 React는 “훅은 함수형 컴포넌트에서만 사용할 수 있다”는 메시지와 함께 오류를 발생시킨다. mobx-react v4의 &lt;code class=&quot;language-text&quot;&gt;observer&lt;/code&gt; API는 &lt;a href=&quot;https://github.com/mobxjs/mobx-react/blob/mobx4/src/observer.js#L307&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;클래스 컴포넌트를 리턴하는 hoc&lt;/a&gt;(higher order component)이기 때문이다. 훅을 사용하면서 mobx-react의 store에서 데이터를 가져오려면 mobx-react v6 또는 &lt;a href=&quot;https://github.com/mobxjs/mobx-react-lite&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;mobx-react-lite&lt;/a&gt;를 사용해야 한다.&lt;/p&gt;
&lt;h2 id=&quot;mobx-react-v6&quot;&gt;&lt;a href=&quot;#mobx-react-v6&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;mobx-react v6&lt;/h2&gt;
&lt;p&gt;mobx-react-lite는 React 16.8과 훅을 지원하기 위해 함수형 컴포넌트에서만 사용할 수 있는 API만 제공하고 있다. 특히 mobx-react-lite는 &lt;a href=&quot;https://ko.reactjs.org/docs/legacy-context.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React legacy Context API&lt;/a&gt;를 사용하는 &lt;code class=&quot;language-text&quot;&gt;Provider&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;inject&lt;/code&gt; API를 제공하지 않는다. 대신 React.createContext API를 사용해서 store를 가져오는 방법을 제안한다.&lt;/p&gt;
&lt;p&gt;mobx-react v4, v5를 사용해서 앱을 개발하고 있었다면 mobx-react-lite의 React Hooks를 위한 API를 포함하고 있는 mobx-react v6로 마이그레이션 하면 된다. 마이그레이션이라 해도 이미 구현되어 있는 클래스 컴포넌트는 수정할 필요가 없어서 크게 부담이 없다. 물론 공식 문서에서는 store를 새로운 API(=&lt;a href=&quot;https://mobx-react.js.org/state-how#creating-a-state&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;useLocalStore&lt;/a&gt;)로 구현하는 방법을 권하고 있다. 하지만 Hook API가 제공된다고 해서 클래스 컴포넌트를 버려야 할 이유는 없으므로 어떻게 사용할지는 이 도구를 사용하는 사람의 선택에 달렸다고 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;함수형-컴포넌트에서-code-classlanguage-textinjectcode-를-대체할-code-classlanguage-textusestorecode-함수&quot;&gt;&lt;a href=&quot;#%ED%95%A8%EC%88%98%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%97%90%EC%84%9C-code-classlanguage-textinjectcode-%EB%A5%BC-%EB%8C%80%EC%B2%B4%ED%95%A0-code-classlanguage-textusestorecode-%ED%95%A8%EC%88%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;함수형 컴포넌트에서 &lt;code class=&quot;language-text&quot;&gt;inject&lt;/code&gt; 를 대체할 &lt;code class=&quot;language-text&quot;&gt;useStore&lt;/code&gt; 함수&lt;/h3&gt;
&lt;p&gt;앞서 언급했듯이 마이그레이션을 위해서 기존의 코드를 수정해야 할 필요는 없다. 다만 훅을 사용하는 함수형 컴포넌트를 위한 &lt;code class=&quot;language-text&quot;&gt;inject&lt;/code&gt; hoc는 별도로 제공하지 않으므로 store를 가져오기 위한 헬퍼 함수를 만들어야 한다. 간단하게 구현 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; MobXProviderContext &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mobx-react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * React hooks를 사용하는 컴포넌트에서 store를 가져올 때 사용한다.
 * 참조) https://mobx-react.js.org/recipes-migration#hooks-for-the-rescue
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useStores&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;MobXProviderContext&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; useStores&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;React.useContext&lt;/code&gt; API는 파라미터로 전달된 Context의 현재 값을 반환한다. 거기에 &lt;code class=&quot;language-text&quot;&gt;MobXProviderContext&lt;/code&gt; 를 사용하면 mobx-react의 &lt;code class=&quot;language-text&quot;&gt;Provider&lt;/code&gt; 가 제공하는 store 객체를 가져올 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; observer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mobx-react&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useStores &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../useStores&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; UserInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;observer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useStores&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;컴포넌트를 &lt;code class=&quot;language-text&quot;&gt;observer&lt;/code&gt; hoc로 래핑하는 과정은 동일하다. 대신 클래스 컴포넌트의 &lt;code class=&quot;language-text&quot;&gt;inject&lt;/code&gt; hoc를 사용하는 대신 &lt;code class=&quot;language-text&quot;&gt;useStores&lt;/code&gt; 헬퍼 함수를 사용해서 store 객체를 가져올 수 있다. &lt;/p&gt;
&lt;p&gt;개발을 진행하면서 특정 데이터를 사용하는 패턴이 보인다면 커스텀 훅을 만드는 것처럼 커스텀 useStores 함수를 만들 수도 있을 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useObserver &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mobx-react&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useUserData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; login &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useStores&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// useObserver를 사용해서 리턴하는 값의 업데이트를 계속 반영한다&lt;/span&gt;
    userName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    isLoggedIn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; login&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isLoggedIn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;UserInfo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; username&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isLoggedIn &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useUserData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; is &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isLoggedIn &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;on&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;off&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;useUserData&lt;/code&gt;가 리턴하는 객체가 &lt;code class=&quot;language-text&quot;&gt;useObserver&lt;/code&gt;로 래핑되어 있기에 컴포넌트를 &lt;code class=&quot;language-text&quot;&gt;observer&lt;/code&gt;로 래핑하지 않아도 동작한다. 하지만 컴포넌트에 다른 observable을 사용한다면 래핑이 필요하다.&lt;/p&gt;
&lt;h3 id=&quot;함수형-컴포넌트를-위한-injector-hoc&quot;&gt;&lt;a href=&quot;#%ED%95%A8%EC%88%98%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-injector-hoc&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;함수형 컴포넌트를 위한 injector hoc&lt;/h3&gt;
&lt;p&gt;함수형 컴포넌트에서는 &lt;code class=&quot;language-text&quot;&gt;useStores&lt;/code&gt;를 사용하면 된다. 하지만 클래스 컴포넌트에서 사용하던 스타일로 &lt;code class=&quot;language-text&quot;&gt;inject&lt;/code&gt; hoc를 사용하고 싶다면 직접 구현할 수 있다. 공식 문서에서도 &lt;a href=&quot;https://mobx-react.js.org/recipes-migration#make-your-own-inject&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;간단한 구현을 제공&lt;/a&gt;하고 있다. 하지만 개인적으로는 &lt;code class=&quot;language-text&quot;&gt;useStores&lt;/code&gt;를 사용하는 편이 더 간단하고 훅 API에도 어울려 보인다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; observer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mobx-react&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useStores &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../useStores&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; baseComponent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;component&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ownProps &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useStores&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;baseComponent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ownProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  component&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;displayName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; baseComponent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; component
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[[번역] 탄력적인 컴포넌트 작성하기]]></title><description><![CDATA[(이 글은  React 의 코어 컨트리뷰터인   Dan Abramov  의  Writing Resilient Components 를 번역한 글입니다.) React를 배우기 시작하는 사람들은 보통 스타일 가이드를 찾아본다. 프로젝트 전반에 적용되는 일관적인 규칙을 가지…]]></description><link>https://blog.rhostem.com//posts/2019-07-14-writing-resilient-components</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2019-07-14-writing-resilient-components</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 14 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;(이 글은 &lt;a href=&quot;https://github.com/facebook/react&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React&lt;/a&gt;의 코어 컨트리뷰터인  &lt;a href=&quot;https://mobile.twitter.com/dan_abramov&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Dan Abramov&lt;/a&gt; 의 &lt;a href=&quot;https://overreacted.io/writing-resilient-components/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Writing Resilient Components&lt;/a&gt;를 번역한 글입니다.)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;React를 배우기 시작하는 사람들은 보통 스타일 가이드를 찾아본다. 프로젝트 전반에 적용되는 일관적인 규칙을 가지려는 자세는 좋으나 대부분은 각자가 정한 임의의 규칙에 따르게 된다. 그래서 React는 어떤 규칙을 지키라고 특별히 강요하지 않고 있다.&lt;/p&gt;
&lt;p&gt;사람마다 다른 타입 시스템을 사용할 수 있고, 함수 선언 또는 화살표 함수 중 하나를 더 선호할 수 있으며, 나중에 찾기 쉬우려고 props를 알파벳 순으로 정렬해 둘 수도 있다.&lt;/p&gt;
&lt;p&gt;이런 유연함은 React를 이미 규칙(convention)이 존재하고 있는 &lt;a href=&quot;https://reactjs.org/docs/add-react-to-a-website.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;프로젝트에 도입해서 사용하는 일&lt;/a&gt;을 가능하게 한다. 하지만 끝없는 논쟁도 불러일으키기도 한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;React에는 모든 컴포넌트가 지키기 위해 노력해야 할 중요한 디자인 원칙들이 있다. 그런데 내 생각엔 스타일 가이드들이 이 원칙들을 잘 받아들여서 만들어져 있지 않은 것 같다. 먼저 스타일 가이드에 대해 살펴보고, 그 후에 &lt;a href=&quot;https://overreacted.io/writing-resilient-components/#writing-resilient-components&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;정말로 유용한 원칙들&lt;/a&gt;에 대해 얘기하려고 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;상상-속의-문제에-주의를-빼앗기지-마라&quot;&gt;&lt;a href=&quot;#%EC%83%81%EC%83%81-%EC%86%8D%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%97%90-%EC%A3%BC%EC%9D%98%EB%A5%BC-%EB%B9%BC%EC%95%97%EA%B8%B0%EC%A7%80-%EB%A7%88%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상상 속의 문제에 주의를 빼앗기지 마라&lt;/h2&gt;
&lt;p&gt;컴포넌트 디자인 원칙을 이야기하기 전에, 나는 스타일 가이드에 대해 몇 마디를 하고 싶다. 이건 사람들이 좋아할 만한 얘기는 아니지만, 누군가는 해야 한다!&lt;/p&gt;
&lt;p&gt;자바스크립트 커뮤니티에서는 린터&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;가 강요하는 매우 편항적인 스타일이 몇몇 존재한다. 내가 보기에는 어떤 이들은 그 스타일을 가지고 필요 이상으로 다른 사람들과 마찰을 일으키곤 한다. 나는 아무런 문제가 없는 코드를 내게 보여주면서 “React가 이 코드에 문제가 있다고 하네요?”라고 말하는 사람을 도대체 몇 명이나 봤는지 셀 수가 없을 정도다. React가 아니라 그들이 사용하고 있는 린터의 규칙이 그렇게 말하고 있는 것이란 말이다! 이는 세 가지 이슈로 이어진다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사람들은 린터가 개발에 유용한 도구 역할을 하기보다 &lt;strong&gt;지나치게 시끄러운 감시인&lt;/strong&gt; 역할을 하는 것에 익숙해져 있다. 코딩 스타일의 깔끔함을 유지하기 위해 검사기가 발생시키는 너무나 많은 메시지 때문에 유용한 경고문들은 다 묻혀버린다. 결과적으로 사람들은 디버깅할 때 린터의 메시지를 살펴보지 않게 되며 유용한 팁도 놓치게 된다. 게다가 자바스크립트 작성에 익숙하지 않은 사람들(예를 들어 디자이너)은 코드를 작성하는데 더 어려운 시간을 보내게 된다.&lt;/li&gt;
&lt;li&gt;사람들은 특정 패턴이 맞는 것인지 틀린 것인지 구분할 수 없게 된다. 예를 들어 인기 있는 규칙 중에 &lt;code class=&quot;language-text&quot;&gt;setState&lt;/code&gt;를 &lt;code class=&quot;language-text&quot;&gt;componentDidMount&lt;/code&gt;에서는 호출하지 말라는 것(&lt;a href=&quot;https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;react/no-did-mount-set-state&lt;/a&gt;)이 있다. 하지만 만약 저것이 항상 “나쁜” 패턴이었다면 React는 애초에 그런 코드를 작성할 수 있게 만들지도 않았을 것이다! 저 패턴의 적법한 사용 사례에는 DOM 노드의 레이아웃을 확인하는 것 – 예를 들면 툴팁의 위치 결정 – 이 있다. 나는 저 규칙에 걸리지 않으려고 &lt;code class=&quot;language-text&quot;&gt;setTimeout&lt;/code&gt;을 추가하는 사례를 많이 봤다. 완전히 요점을 놓쳐버린 것이다.&lt;/li&gt;
&lt;li&gt;결국 사람들은 린터 덕분에 “강압적인 마인드”를 가지게 되고 &lt;strong&gt;별다른 차이도 없으면서&lt;/strong&gt; 코드에서 확인하기 쉬운 규칙을 지키려 하게 된다. “당신은 함수 선언에 익숙하다고 했지만, 우리 프로젝트에서는 화살표 함수를 사용할 것입니다.” 나는 저런 규칙을 강요한다는 느낌이 들 때마다 내가 감정적인 소모를 한다는 것을 알게 되어서, 이제는 그냥 놓아버리려고 노력한다. 그것들은 내 코드의 품질을 높여주지는 않고 단지 스타일을 지키면서 잘못된 성취감을 느껴보라고 어르고 있는 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그렇다고 내가 린터를 사용하지 말라고 하는 건가? 전혀 그렇지 않다!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;좋은 규칙과 함께라면 린터는 버그가 일어나기 전에 잡아줄 수 있는 아주 훌륭한 도구다&lt;/strong&gt;. 하지만 &lt;em&gt;스타일&lt;/em&gt;에 너무 치중해서 개발에 오히려 방해되고 있다는 말이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;필요한-문법-규칙만-남기고-정리하자&quot;&gt;&lt;a href=&quot;#%ED%95%84%EC%9A%94%ED%95%9C-%EB%AC%B8%EB%B2%95-%EA%B7%9C%EC%B9%99%EB%A7%8C-%EB%82%A8%EA%B8%B0%EA%B3%A0-%EC%A0%95%EB%A6%AC%ED%95%98%EC%9E%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;필요한 문법 규칙만 남기고 정리하자&lt;/h2&gt;
&lt;p&gt;당장 다음 주 월요일에 할 수 있는 일을 제안하려고 한다. 팀원들과 함께 30분만 투자해서 프로젝트에 설정된 문법 규칙을 하나씩 살펴보기 바란다. “이 규칙이 정말로 우리가 버그를 잡는 데 도움이 줬던가?”라고 물어보자. 만약 아니라면 그냥 꺼 버려라.(또는 스타일 규칙이 없는 깔끔한 &lt;a href=&quot;https://www.npmjs.com/package/eslint-config-react-app&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;eslint-config-react-app&lt;/a&gt;에서 시작해도 된다)&lt;/p&gt;
&lt;p&gt;최소한 저런 규칙 때문에 팀 내부에서 생길 수 있는 잡음을 일찌감치 제거하는 과정이 필요하다. 당신이나 누군가가 수년 전에 린터에 추가해 둔 어떤 규칙도 “모범 사례”라고 여기지 않았으면 한다. 사용 중인 규칙에 대한 의구심을 가지고 정답을 찾아보자. 그 누구도 당신이 린터 규칙을 결정할 정도로 똑똑하지 않다고 말할 순 없다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;그런데 코드 포매팅은 어떻게 할까?&lt;/strong&gt; &lt;a href=&quot;https://prettier.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Prettier&lt;/a&gt;를 사용하고 “스타일의 깔끔함”에 대해서는 잊어버리자. 코드에서 빠진 세미콜론이나 빈칸을 알아서 추가해 줄 수 있는 도구(=Prettier)를 이미 사용하고 있는데도 다른 도구가 잘못된 곳이 있다고 소리치도록 둘 필요는 없다. 린터는 버그를 찾기 위해서 사용해야지, 우리에게 코드의 &lt;em&gt;심미성&lt;/em&gt;을 강요하게 만들어선 안 된다.&lt;/p&gt;
&lt;p&gt;물론 포매팅과 직접적인 관계는 없지만, 프로젝트 전반에 일관적으로 적용되지 않으면 신경을 거슬리게 하는 코딩 스타일이 있긴 하다.&lt;/p&gt;
&lt;p&gt;그러나, 그런 것들은 대부분 린터의 규칙으로 잡아내기에는 너무나 미묘하다. 그래서 동료들 간에 &lt;strong&gt;신뢰를 구축&lt;/strong&gt;하는 일과 본인이 공부한 내용을 위키 형식이나 짧은 디자인 가이드 형태로 공유하는 일이 중요하다.&lt;/p&gt;
&lt;p&gt;모든 것을 자동화할 필요는 없다! 동료가 시간을 들여 작성한 가이드를 읽으며 얻는 통찰은 그저 단순히 “규칙”을 따르는 것보다 훨씬 더 가치 있는 일이다. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;그런데 엄격한 스타일 가이드를 따르는 것이 방해된다면, 무엇이 정말로 중요한가?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그것이 이 포스트의 주제다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;탄력적인-컴포넌트-작성하기&quot;&gt;&lt;a href=&quot;#%ED%83%84%EB%A0%A5%EC%A0%81%EC%9D%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;탄력적인 컴포넌트 작성하기&lt;/h2&gt;
&lt;p&gt;들여쓰기를 맞추는 일이나 모듈 import를 알파벳 순으로 정리하는 일이 잘못된 컴포넌트 디자인을 바로잡아 주지 않는다. 그래서 코드가 어떻게 &lt;em&gt;보이는지&lt;/em&gt;에 초점을 맞추는 대신 나는 어떻게 &lt;em&gt;동작하는지&lt;/em&gt;에 초점을 맞추려고 한다. 내가 도움된다고 여기는 몇 개의 컴포넌트 디자인 원칙들이 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;데이터 흐름을 멈추지 마라&lt;/li&gt;
&lt;li&gt;언제라도 렌더링을 할 준비가 되어 있게 하라&lt;/li&gt;
&lt;li&gt;싱글턴(singletone)인 컴포넌트는 없다.&lt;/li&gt;
&lt;li&gt;로컬 state를 독립된 상태로 유지하라&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;만약 당신이 React를 사용하지 않는다고 하더라도, 단방향(unidirectional) 데이터 흐름 모델을 사용하는 UI 라이브러리를 사용하다 보면 이와 같은 규칙을 찾아내게 될 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙-1-데이터-흐름을-멈추지-마라&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%99-1-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84%EC%9D%84-%EB%A9%88%EC%B6%94%EC%A7%80-%EB%A7%88%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙 1: 데이터 흐름을 멈추지 마라&lt;/h2&gt;
&lt;h3 id=&quot;렌더링에서-데이터-흐름을-멈추지-마라&quot;&gt;&lt;a href=&quot;#%EB%A0%8C%EB%8D%94%EB%A7%81%EC%97%90%EC%84%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84%EC%9D%84-%EB%A9%88%EC%B6%94%EC%A7%80-%EB%A7%88%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;렌더링에서 데이터 흐름을 멈추지 마라&lt;/h3&gt;
&lt;p&gt;다른 사람이 당신이 만든 컴포넌트를 사용할 때, 그들은 다른 값을 가진 props를 계속 전달할 수 있고 컴포넌트에 그 변화가 반영될 것이라 기대한다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// isOk 는 상태 값에 기반을 두며 언제든지 변경될 수 있다&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Button color&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isOk &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`blue`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`red`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;일반적으로 이것이 React가 기본적으로 동작하는 방식이다. 당신이 &lt;code class=&quot;language-text&quot;&gt;color&lt;/code&gt; prop을 &lt;code class=&quot;language-text&quot;&gt;Button&lt;/code&gt; 컴포넌트에 전달한다면 위의 렌더링 과정에서 전달된 값을 확인할 수 있다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ✅ `color` 값은 언제나 신선!&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Button-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 React를 배우면서 하게 되는 흔한 실수는 props를 state로 복사하는 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; color &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 🔴 `color`는 오래됨!&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Button-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이는 클래스를 React 밖에서 사용한 경험이 있다면 더 직관적이었을 것이다. 하지만 어떤 props를 state에 복사한다면 당신은 Button에 오는 모든 업데이트를 무시하게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 🔴 color를 state에 복사한 구현으로는 더는 제대로 작동하지 않는다.&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Button color&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isOk &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`blue`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`red`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이런 구현을 의도적으로 하는 경우가 아주 드물게 있긴 하다. &lt;code class=&quot;language-text&quot;&gt;initialColor&lt;/code&gt;또는 &lt;code class=&quot;language-text&quot;&gt;defaultColor&lt;/code&gt; 값을 받아서 그 값들의 업데이트를 무시하겠다고 확실히 하는 것이다.&lt;/p&gt;
&lt;p&gt;하지만 일반적으로 당신은 &lt;strong&gt;props를 컴포넌트에서 직접 읽을 수 있길 바랄 것&lt;/strong&gt;이며 props(그리고 props로부터 계산된 어떤 값이라도)를 state에 복사하는 일은 피하고 싶을 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ✅ `color` 값은 언제나 최신!&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Button-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;p&gt;계산된 값&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;은 사람들이 이따금 props를 state로 복사하게 되는 원인 중 하나다. 예를 들어 버튼 텍스트 컬러를 결정하는데  &lt;code class=&quot;language-text&quot;&gt;color&lt;/code&gt;를 파라미터로 받으면서 결과를 얻는데 시간과 자원이 많이 소모되는 함수를 사용한다고 생각해보자. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    textColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slowlyCalculateTextColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos;Button-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos; Button-text-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textColor &lt;span class=&quot;token comment&quot;&gt;// 🔴 `color` prop 업데이트를 반영하지 않는다&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 컴포넌트는 &lt;code class=&quot;language-text&quot;&gt;color&lt;/code&gt; prop이 변경되었을 때  &lt;code class=&quot;language-text&quot;&gt;this.state.textColor&lt;/code&gt;를 다시 계산하지 않으므로 버그가 있다고 할 수 있다. 가장 쉬운 해법은 &lt;code class=&quot;language-text&quot;&gt;textColor&lt;/code&gt; 계산을 &lt;code class=&quot;language-text&quot;&gt;render&lt;/code&gt; 메소드 안에서 하고  &lt;code class=&quot;language-text&quot;&gt;PureComponent&lt;/code&gt;를 사용하는 방법이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PureComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; textColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slowlyCalculateTextColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos;Button-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos; Button-text-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; textColor &lt;span class=&quot;token comment&quot;&gt;// ✅ 언제나 최신 &lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;문제가 해결되었다! 이제 props가 변경되면 &lt;code class=&quot;language-text&quot;&gt;textColor&lt;/code&gt;를 다시 계산한다.&lt;code class=&quot;language-text&quot;&gt;PureComponent&lt;/code&gt;를 사용했으니 같은 prop이 넘어오면 무시하므로 비싼 계산을 다시 하지도 않는다.&lt;/p&gt;
&lt;p&gt;하지만 우리는 더 최적화를 하고 싶다. 만약 &lt;code class=&quot;language-text&quot;&gt;children&lt;/code&gt; prop이 바뀐다면 어떻게 되나? 렌더링을 할 때마다 그 &lt;code class=&quot;language-text&quot;&gt;textColor&lt;/code&gt;를 계산하는 무거운 함수(&lt;code class=&quot;language-text&quot;&gt;slowlyCalculateTextColor&lt;/code&gt;)를 다시 실행하게 될 것이다. 이를 해결하기 위한 솔루션은 아마도 &lt;code class=&quot;language-text&quot;&gt;componentDidUpdate&lt;/code&gt;에서 &lt;code class=&quot;language-text&quot;&gt;textColor&lt;/code&gt;를 계산하는 방법이 될 것이다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    textColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slowlyCalculateTextColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;componentDidUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prevProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prevProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// 😔 color가 업데이트 될 때마다 추가적인 렌더링이 들어간다.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        textColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slowlyCalculateTextColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos;Button-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos; Button-text-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textColor &lt;span class=&quot;token comment&quot;&gt;// ✅ 최종 렌더링에서는 prop 업데이트를 반영한다&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 이는 우리의 컴포넌트가 prop이 바뀔때마다 한 번 더 렌더링하게 될 것을 의미한다. 우리가 최적화를 시도한 상황이라면 이건 이상적인 방법이 아니다.&lt;/p&gt;
&lt;p&gt;이제는 레거시가 된 &lt;code class=&quot;language-text&quot;&gt;componentWillReceiveProps&lt;/code&gt; 라이프사이클 메소드를 사용할 수도 있다. 하지만 사람들은 보통 거기에 사이드 이펙트도 함께 집어넣는다. 그리고 그것은 앞으로 React에서 구현될 동시 렌더링(Concurrent Rendering) 기능과 관련된 &lt;a href=&quot;https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-16.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;시분할과 지연(Time slicing and Suspense)&lt;/a&gt; 문제를 야기한다. 그리고 “안전한” &lt;code class=&quot;language-text&quot;&gt;getDerivedStateFromProps&lt;/code&gt; 라이프사이클 메소드는 이제 좀 구식이다.&lt;/p&gt;
&lt;p&gt;두 번째로 돌아가 보자. 사실 우리는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Memoization&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;메모이제이션(memoization)&lt;/a&gt;을 원한다. 어떤 입력값이 있고, 우리는 그 입력값이 바뀌지 않는 한 다시 계산하지 않길 바란다.&lt;/p&gt;
&lt;p&gt;클래스를 사용한다면 메모이제이션을 위한 &lt;a href=&quot;https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#what-about-memoization&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;헬퍼&lt;/a&gt;를 사용할 수도 있다. 하지만 한 단계 더 나가서 비싼 계산을 메모이제이션 할 수 있는 기능을 제공하는 React의 Hooks를 사용해보자. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; textColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMemo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slowlyCalculateTextColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ✅ `color`가 바뀔 때만 실행하라는 의미&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Button-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; color &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; Button-text-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; textColor&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이게 당신이 필요한 코드 전부다! &lt;/p&gt;
&lt;p&gt;클래스 컴포넌트에서는 &lt;a href=&quot;https://github.com/alexreardon/memoize-one&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;memoize-one&lt;/a&gt; 같은 헬퍼를 사용할 수 있다. 함수형 컴포넌트에서는 &lt;code class=&quot;language-text&quot;&gt;useMemo&lt;/code&gt; 훅(Hook)이 비슷한 기능을 제공한다.&lt;/p&gt;
&lt;p&gt;이제 우리는 비싼 계산을 최적화하는 과정에서도 props를 state에 복사하는 것은 좋은 방법이 아니라는 사실을 확인했다. 우리의 렌더링 결과는 props의 변경을 계속 반영해야 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;사이드-이펙트-안에서-데이터-흐름을-멈추게-하지-말라&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%9D%B4%EB%93%9C-%EC%9D%B4%ED%8E%99%ED%8A%B8-%EC%95%88%EC%97%90%EC%84%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84%EC%9D%84-%EB%A9%88%EC%B6%94%EA%B2%8C-%ED%95%98%EC%A7%80-%EB%A7%90%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사이드 이펙트 안에서 데이터 흐름을 멈추게 하지 말라&lt;/h3&gt;
&lt;p&gt;지금까지 얘기한 내용은 prop 변경이 있을 때 렌더링 결과를 일관적으로 유지하는 방법에 관한 것이다.  props를 state에 복사하지 않는 것이 거기에 포함된다. 그런데 한편으로는, 사이드 이펙트 또한 데이터 흐름의 일부라는 것도 중요하다. &lt;/p&gt;
&lt;p&gt;아래의 React 컴포넌트를 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SearchResults&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;componentDidMount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;fetchResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFetchUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 원격에서 데이터 가져오기 ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;getFetchUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;http://myapi/results?query&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아주 많은 React 컴포넌트가 이와 비슷한 형태다. 하지만 조금만 더 가까이서 보면, 우리는 버그를 발견할 수 있다. &lt;code class=&quot;language-text&quot;&gt;fetchResults&lt;/code&gt; 메소드는 데이터를 가져올 때 &lt;code class=&quot;language-text&quot;&gt;query&lt;/code&gt; prop을 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;getFetchUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;http://myapi/results?query&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 &lt;code class=&quot;language-text&quot;&gt;query&lt;/code&gt; prop이 바뀐다면? 위의 컴포넌트에서는 아무런 일도 일어나지 않는다. 즉 우리가 작성한 컴포넌트의 사이드 이펙트가 props의 변경을 고려하지 않고 있다는 말이다. 이런 코드는 React 앱에서 발생하는 아주 흔한 버그 중 하나다. &lt;/p&gt;
&lt;p&gt;이 컴포넌트를 고치기 위해서 해야 할 일은 다음과 같다.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;componentDidMount&lt;/code&gt; 안에서 호출되는 모든 메소드를 살펴본다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;예제에서는 &lt;code class=&quot;language-text&quot;&gt;fetchResults&lt;/code&gt; 와  &lt;code class=&quot;language-text&quot;&gt;getFetchUrl&lt;/code&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그 메소드들이 사용하고 있는 모든 props와 state를 정리한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;예제에서는 &lt;code class=&quot;language-text&quot;&gt;this.props.query&lt;/code&gt; 하나다. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그 props들이  변경될 때마다 사이드 이펙트가 확실하게 실행되도록 만들어야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;componentDidUpdate&lt;/code&gt; 메소드를 추가함으로써 구현할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SearchResults&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;componentDidMount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;componentDidUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prevProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prevProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ✅ query가  변경되었을 때 fetchResults 실행&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;fetchResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFetchUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 원격에서 데이터 가져오기 ... &lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;getFetchUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;http://myapi/results?query&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ✅ 업데이트가 반영됨&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;만약 이런 실수를 자동으로 잡아줄 수 있다면 더 좋지 않을까?&lt;/strong&gt; 린터 같은 도구가 우리를 도와줄 수 있진 있을까?&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;안타깝게도 클래스 컴포넌트의 일관성을 자동으로 검사하는 것은 몹시 어려운 일이다. 모든 메소드는 다른 모든 메소드 안에서 호출될 수 있다.  &lt;code class=&quot;language-text&quot;&gt;componentDidMount&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;componentDidUpdate&lt;/code&gt; 에서의 함수 호출을 정적으로 분석하는 것은 잘못된 긍정(false positives)이 있어서 곤란하다. &lt;/p&gt;
&lt;p&gt;하지만, React에 일관성을 확인하기 위해 정적으로 분석될 수 있는 API를 설계할 수 있었다.  &lt;a href=&quot;https://overreacted.io/a-complete-guide-to-useeffect/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; Hook&lt;/a&gt;이 그런 API다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SearchResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; query &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setData&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;currentPage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setCurrentPage&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getFetchUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// 원격에서 데이터 가져오기 ... &lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getFetchUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos;http://myapi/results?query&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; query &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos;&amp;amp;page=&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; currentPage
      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;fetchResults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;currentPage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; query&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ✅ 변경되었을 때 데이터를 다시 가져오기&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우리는 effect &lt;em&gt;안에&lt;/em&gt; 로직을 넣어뒀기에, &lt;em&gt;React 데이터 흐름에서 어떤 값&lt;/em&gt;들에 의존하고 있는지 더 쉽게 확인할 수 있다. 이 예제에서는 &lt;code class=&quot;language-text&quot;&gt;[currentPage, query]&lt;/code&gt;다. &lt;/p&gt;
&lt;p&gt;이 “effect 의존성(dependencies)” 배열이 새로운 개념이 아니라는 사실에 유의해야 한다. 클래스에서는 함수가 호출될 때마다 매번 이 “의존성”을 확인했었다. &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; API는 단지 같은 개념을 더 명시적으로 만들었을 뿐이다.&lt;/p&gt;
&lt;p&gt;그리고 이 API를 사용하면 린터가 자동으로 의존성 검사를 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://overreacted.io/useeffect-bc7a074c528f3b0be1b7e509b6a8683b.gif&quot; alt=&quot;https://overreacted.io/useeffect-bc7a074c528f3b0be1b7e509b6a8683b.gif&quot;&gt;&lt;/p&gt;
&lt;p&gt;(새로운 추천 규칙인 &lt;em&gt;exhaustive-deps&lt;/em&gt;을 사용하는 예제다. 이 규칙은 &lt;em&gt;eslint-plugin-react-hooks&lt;/em&gt;에 포함되어 있다. 그리고 곧 &lt;em&gt;Create React App&lt;/em&gt;에 포함될 것이다)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;당신이 컴포넌트 작성에 클래스를 사용하든 함수를 사용하든, 사이드 이펙트에는 모든 prop과 state의 변경을 고려해야 한다는 점이 중요하다는 것을 꼭 알아야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;만약 클래스 API를 사용한다면 당신은 렌더링 일관성에 대해 스스로 생각해야 하며 &lt;code class=&quot;language-text&quot;&gt;componentDidUpdate&lt;/code&gt; 메소드 안에서 관련된 모든 prop또는 state의 변경을 직접 확인해야 한다. 그러지 않으면 당신의 컴포넌트는 prop과 state의 변경에 탄력적일 수 없다. 이것은 단지 React에 국한된 문제는 아니다. 컴포넌트의 “생성”과 “업데이트”를 분리해서 제어할 수 있는 모든 UI 라이브러리에 적용된다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; API는 일관성에 중심을 두고 기존의 구현을 뒤집었다. 이건 &lt;a href=&quot;https://overreacted.io/a-complete-guide-to-useeffect/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;처음에는 익숙하지 않아서 어색하게 느껴질 수도 있다&lt;/a&gt;. 하지만 결국에는 당신의 컴포넌트가 로직 안의 데이터 변경에 더 탄력성이 있도록 만든다. 그리고 “의존성”이 이제는 명시적이기 때문에, 린터의 규칙을 사용해서 effect가 일관성이 있고 문제가 없는지 검증할 수 있다. 우리가 사용하는 린터가 버그를 찾아주는 것이다!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;최적화-과정에서-데이터-흐름이-멈추게-하지-마라&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%A0%81%ED%99%94-%EA%B3%BC%EC%A0%95%EC%97%90%EC%84%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84%EC%9D%B4-%EB%A9%88%EC%B6%94%EA%B2%8C-%ED%95%98%EC%A7%80-%EB%A7%88%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최적화 과정에서 데이터 흐름이 멈추게 하지 마라&lt;/h3&gt;
&lt;p&gt;의도치 않게 props의 변경을 무시하게 되는 사례가 하나 더 있다. 이것은 컴포넌트를 최적화를 직접 시도할 때 발생할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;PureComponent&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;React.memo&lt;/code&gt;를 사용한 얕은(shallow) 동등성(equality) 검사를 사용하는 기본적인 비교에 기반을 둔 최적화 시도는 안전하다는 사실을 기억해 두자. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;하지만 만약 당신이 비교 구문을 직접 작성해서 “최적화”를 시도한다면, 함수 props의 완벽한 검사를 실수로 놓치게 될 가능성이 있다.&lt;/strong&gt; &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;shouldComponentUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prevProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 🔴 this.props.onClick은 비교하지 않는다&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; prevProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onClick&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 🔴 업데이트를 반영하지 않는다&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; textColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slowlyCalculateTextColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button
        onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onClick&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Button-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; Button-text-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; textColor&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;클래스를 사용하면 이런 실수를 하기 쉽다. &lt;code class=&quot;language-text&quot;&gt;Button&lt;/code&gt; 컴포넌트에 &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; prop으로 메소드를 내려보내면, 그 메소드는 어찌 되었던 동일성(identity)을 가지게 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyForm&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;handleClick&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ✅ 처음부터 끝까지 항상 같은 함수&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Do something&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Hello&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Button color&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;green&apos;&lt;/span&gt; onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleClick&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          Press me
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;우리의 최적화는 바로 망가지진 않는다. 하지만, &lt;code class=&quot;language-text&quot;&gt;Button&lt;/code&gt; 컴포넌트는 전달되는 값이 변경되더라도 오래된 &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; 값을 계속 “바라보고” 있게 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyForm&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    isEnabled&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;handleClick&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; isEnabled&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Do something&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Hello&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Button color&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;green&apos;&lt;/span&gt; onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// 🔴 onClick이 null로 바뀌더라도 Button 컴포넌트는 무시한다. &lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// 🔴 shouldComponentUpdate에서 비교를 안하기 때문이다.&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEnabled &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleClick &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          Press me
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 예제에서는 버튼을 한 번 클릭한 후에 버튼이 더 이상 동작하지 않아야 한다(&lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; prop이 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;로 바뀌어야 하므로). 하지만 버튼은 &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; prop의 모든 업데이트를 무시하기에 기대했던 대로 동작하지 않는다.&lt;/p&gt;
&lt;p&gt;이것은 함수의 동일성이 계속해서 바뀔 수 있는 값에 의존하고 있으면 더욱 혼란스러워진다. 아래 코드의 &lt;code class=&quot;language-text&quot;&gt;draft.content&lt;/code&gt;가 그러하다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;drafts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;draft &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Button
      color&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;blue&apos;&lt;/span&gt;
      key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;draft&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 🔴 버튼은 onClick props의 업데이트를 무시한다&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handlePublish&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; draft&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      Publish
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;draft.content&lt;/code&gt;는 값이 계속 바뀔 가능성이 있지만, 우리의 &lt;code class=&quot;language-text&quot;&gt;Button&lt;/code&gt; 컴포넌트는  &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; prop의 변화를 무시하게 되어 있어서 최초의 &lt;code class=&quot;language-text&quot;&gt;draft.content&lt;/code&gt;가 바인딩된 “첫번째 버전”의 &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; 메소드를 계속 바라보게 된다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;그러면, 어떻게 하면 이 문제를 피할 수 있을까?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;나는 &lt;code class=&quot;language-text&quot;&gt;shouldComponentUpdate&lt;/code&gt; 메소드를 직접 작성하지 않는 것을 권장하고 싶으며, &lt;code class=&quot;language-text&quot;&gt;React.memo&lt;/code&gt;에 커스텀 비교 구문을 전달하는 것도 피하라고 하고 싶다. &lt;code class=&quot;language-text&quot;&gt;React.memo&lt;/code&gt;에 구현된 비교는 함수 동일성(identity)의 변화를 감지할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; onClick&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; textColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;slowlyCalculateTextColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button
      onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onClick&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Button-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; color &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; Button-text-&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; textColor&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;memo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Button&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ✅ 얕은(shallow) 비교를 사용한다&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;클래스에서는, &lt;code class=&quot;language-text&quot;&gt;PureComponent&lt;/code&gt;가 같은 역할을 한다.&lt;/p&gt;
&lt;p&gt;이는 다른 함수를 prop으로 보내면 항상 업데이트가 반영됨을 보장한다.&lt;/p&gt;
&lt;p&gt;만약 당신이 커스텀 비교를 하고 싶다면, &lt;strong&gt;함수를 빠뜨리지 않도록 확실하게 구현해야 한다&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token function&quot;&gt;shouldComponentUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prevProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ✅ this.props.onClick을 비교한다&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; prevProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onClick &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; prevProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onClick
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 언급한 것처럼, 클래스 컴포넌트에서는 이 문제를 놓치기 쉽다. 왜냐하면, 메소드의 동일성은 보통 안정적이기 때문이다(하지만 항상 그렇지는 않다 - 그렇기에 이 문제로 인한 버그는 고치기 어렵다). Hooks를 사용한다면, 얘기는 조금 다르다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;함수를 렌더링 할 때마다 달라지기 때문에 이 문제를 &lt;a href=&quot;https://github.com/facebook/react/issues/14972#issuecomment-468280039&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;바로 찾아낼 수 있다&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;useCallback&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;useContext&lt;/code&gt;를 사용하면, &lt;a href=&quot;https://reactjs.org/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;함수를 컴포넌트 트리 깊은 곳에 전달하는 문제를 전부 해결&lt;/a&gt;할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;이 섹션을 한마디로 요약한다면, &lt;strong&gt;데이터 흐름을 멈추지 마라!&lt;/strong&gt;라고 할 수 있겠다.&lt;/p&gt;
&lt;p&gt;props와 state를 사용할 때마다, 그 값들이 바뀌면 어떤 일이 일어나야 하는지 생각해보자. 거의 모든 사례에서 컴포넌트는 최초의 렌더링에 머무르지 말고 계속 업데이트되어야 한다. 그것이 로직의 변화에 탄력을 준다.&lt;/p&gt;
&lt;p&gt;클래스를 사용하면 라이프사이클 메소드 안에서 props와 state를 사용할 때 업데이트에 대해 잊어버리기 쉽다. Hooks는 당신이 제대로 된 방법을 사용할 수 있도록 유도해준다. 하지만 지금 사용하고 있지 않다면, 컴포넌트를 사고하는 방식의 조정 기간이 조금 필요하다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙-2-언제라도-렌더링할-준비가-되어-있게-하라&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%99-2-%EC%96%B8%EC%A0%9C%EB%9D%BC%EB%8F%84-%EB%A0%8C%EB%8D%94%EB%A7%81%ED%95%A0-%EC%A4%80%EB%B9%84%EA%B0%80-%EB%90%98%EC%96%B4-%EC%9E%88%EA%B2%8C-%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙 2: 언제라도 렌더링할 준비가 되어 있게 하라.&lt;/h2&gt;
&lt;p&gt;React를 사용하면 컴포넌트 렌더링에 걸리는 시간은 크게 신경 쓰지 않고 코드를 작성할 수 있다. 당신은 컴포넌트가 어떤 시점에 어떻게 보일지 묘사&lt;em&gt;해야&lt;/em&gt; 하며, React는 그것이 가능하게 한다. 이 모델의 장점을 최대한 활용하자!&lt;/p&gt;
&lt;p&gt;컴포넌트가 어떤 타이밍에 어떻게 동작할지 추측을 하지 말아야 한다. 당신이 작성한 컴포넌트는 어떤 순간에도 다시 렌더링(re-render)될 준비가 되어 있어야 한다. &lt;/p&gt;
&lt;p&gt;이 원칙을 어떻게 어기게 될까? React는 쉽게 어길 수 없게 하지만 – 만약 레거시 라이프사이클 메소드 &lt;code class=&quot;language-text&quot;&gt;componentWillReceiveProps&lt;/code&gt;를 사용한다면 그럴 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TextInput&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;``&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 🔴 부모 컴포넌트로부터 props를 받을 때마다 로컬 state `value`를 재설정한다 &lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;componentWillReceiveProps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nextProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nextProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;handleChange&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;input
        value&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        onChange&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleChange&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 예제에서 우리는 &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt;를 로컬 state에 보관한다. 그런데 &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt;를 props를 통해 받기도 한다. “새로운 값을 받을 때마다” 컴포넌트는 &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt;를 state에 재설정한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이 패턴의 문제는 업데이트를 완전히 제어할 수 없는 타이밍에 의존하고 있다는 점이다.&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;오늘은 이 컴포넌트의 부모가 업데이트를 거의 안할 수도 있어서 &lt;code class=&quot;language-text&quot;&gt;TextInput&lt;/code&gt; 컴포넌트가 어떤 중요한 상황에서만 props를 전달받을 수도 있다. 예를 들면 폼 데이터를 저장하는 것과 같은 상황에서 말이다.&lt;/p&gt;
&lt;p&gt;하지만 내일은 &lt;code class=&quot;language-text&quot;&gt;TextInput&lt;/code&gt;의 부모 컴포넌트에 어떤 애니메이션 효과를 추가할 수도 있다. 만약 그 부모 컴포넌트가 더 자주 렌더링을 한다면 계속해서 자식 컴포넌트의 state를 &lt;a href=&quot;https://codesandbox.io/s/m3w9zn1z8x&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“날려버리게”&lt;/a&gt; 될 것이다!&lt;sup id=&quot;fnref-3&quot;&gt;&lt;a href=&quot;#fn-3&quot; class=&quot;footnote-ref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; 이 문제에 대해서는 &lt;a href=&quot;https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“당신은 아마 Derived State가 필요없을 것이다”&lt;/a&gt;에서 더 자세하게 읽어볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;그러면 어떻게 이 문제를 고칠 수 있을까?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;무엇보다도, 우리는 머릿속의 디자인 모델을 바로잡아야 한다. “props를 전달받는 것”을 “렌더링”과 다른 별개의 이벤트라고 생각하는 걸 그만둬야 한다. 부모 컴포넌트의 리렌더링에 의해 일어나는 자식 컴포넌트의 리렌더링은 자식 컴포넌트가 가진 로컬 state의 업데이트로 발생하는 리렌더링과 다르게 동작해서는 안 된다. &lt;strong&gt;컴포넌트는 많고 적을 수 있는 렌더링 횟수에 탄력적이어야 한다. 그렇지 않으면 그 컴포넌트는 특정 부모 컴포넌트에 너무 강하게 연결된 것이기 때문이다.&lt;/strong&gt;&lt;sup id=&quot;fnref-4&quot;&gt;&lt;a href=&quot;#fn-4&quot; class=&quot;footnote-ref&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;(이 &lt;a href=&quot;https://codesandbox.io/s/m3w9zn1z8x&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;데모&lt;/a&gt;는 부모의 리렌더링이 어떻게 자식 컴포넌트의 state를 망가뜨리는지 보여준다.)&lt;/p&gt;
&lt;p&gt;당신이 정말로 props를 통해 생성된 state(derived state)의 사용이 필요할 때 쓸 수 있는 &lt;a href=&quot;https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#preferred-solutions&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;다른&lt;/a&gt; &lt;a href=&quot;https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;솔루션&lt;/a&gt;들이 있긴 하지만, 보통은 부모 컴포넌트가 완전히 제어하는 컴포넌트(&lt;a href=&quot;https://reactjs.org/docs/forms.html#controlled-components&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;controlled component&lt;/a&gt;)를 사용해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 옵션 1: 로컬 state가 없는 완전히 제어되는 컴포넌트. &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TextInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onChange &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;input
      value&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      onChange&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onChange&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아니면 제어되지 않는 컴포넌트(&lt;a href=&quot;https://reactjs.org/docs/uncontrolled-components.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;uncontrolled component&lt;/a&gt;)를 사용하면서 부모에서 필요할 때 자식 컴포넌트를 렌더링할 수 있도록 키를 부여할 수도 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 옵션 2: 로컬 state를 가진 제어되지 않는 컴포넌트&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TextInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setValue&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;input
      value&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      onChange&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;e &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// key 값을 바꿈으로써 내부 state를 리셋할 수 있다.&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TextInput key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;formId&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 섹션의 요점은 당신의 컴포넌트가 상태가 부모 컴포넌트가 더 자주 리렌더링 된다고 해도 망가지지 않아야 한다는 것이다. React API 디자인은 레거시 &lt;code class=&quot;language-text&quot;&gt;componentWillReceiveProps&lt;/code&gt; 라이프사이클 메소드를 사용하지 않는다면 그것을 매우 쉽게 구현할 수 있도록 해 두었다.&lt;/p&gt;
&lt;p&gt;컴포넌트 스트레스 테스트를 위해서는 임의로 아래의 코드를 부모 컴포넌트에 추가해볼 수 있다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;componentDidMount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 테스트 후에는 바로 제거해야 한다!&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;setInterval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forceUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;테스트를 위한 것이므로 이 코드를 남겨둬서는 안 된다. 이 코드를 부모 컴포넌트가 예상한 것보다 자주 렌더링이 일어날 때 어떤 일이 발생하는지 확인할 수 있는 빠른 방법이다. 저렇게 해도 자식 컴포넌트를 망가뜨려선 안 된다!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;당신은 이렇게 생각할 수도 있다: “나는 props가 변경될 때 state를 계속 다시 설정할 것이다. 하지만 불필요한 리렌더링은 &lt;code class=&quot;language-text&quot;&gt;PureComponent&lt;/code&gt;를 사용해서 피할 것이다.”&lt;/p&gt;
&lt;p&gt;그렇다면 아래의 코드는 제대로 동작해야 한다. 맞을까?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 🤔 불필요한 리렌더링을 방지해야 하는데... 그렇게 될까?&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TextInput&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PureComponent&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 🔴 부모 컴포넌트가 렌더링 할때마다 state를 설정한다&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;componentWillReceiveProps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nextProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nextProps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;handleChange&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;input
        value&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        onChange&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleChange&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;처음에는 부모 컴포넌트의 리렌더링이 state를 “날려버리는” 문제를 해결할 것처럼 보일 수 있다. 어쨌든, props가 같다면 업데이트를 건너뛴다 – 그리고 &lt;code class=&quot;language-text&quot;&gt;componentWillReceiveProps&lt;/code&gt;도 호출되지 않는다.&lt;/p&gt;
&lt;p&gt;하지만 이는 우리에게 보안에 관한 잘못된 인상을 부여한다. &lt;strong&gt;이 컴포넌트는 여전히 prop의 변경에 탄력적이지 않다&lt;/strong&gt;. 예를 들어, 만약 컴포넌트에 자주 변경되는 또 다른 값(애니메이션이 적용된 스타일 같은 것)을 prop으로 전달한다면, 컴포넌트의 로컬 state를 여전히 “잃어버리게” 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TextInput
  style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;opacity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; someValueFromState&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  value&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 🔴 TextInput 컴포넌트의 componentWillReceiveProps는&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// style이 바뀔 때마다 value를 재설정하게 될 것이다.&lt;/span&gt;
    value
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러므로 이 접근법에도 문제가 있다. &lt;code class=&quot;language-text&quot;&gt;PureComponent&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;shouldComponentUpdate&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;React.memo&lt;/code&gt;같은 다양한 최적화 방법을 컴포넌트의 &lt;strong&gt;반응&lt;/strong&gt;을 제어하는 데 사용해서는 안 된다. 대신 최적화에 도움이 될 때만 사용해야 한다. 최적화를 제거했다고 컴포넌트가 제대로 동작하지 않는다면, 그건 애초에 쓰기도 어려운 것이다.&lt;/p&gt;
&lt;p&gt;이 해결책은 우리가 앞서 살펴본 것과 같다. “props를 받는 것”을 특별한 일로 여기지 말아야 한다. props와 state의 “동기화”를 하지 말자. 대부분은 모든 값은 완전히 제어되거나(props를 통해서만 업데이트) 그 반대여야 한다(로컬 state만 사용). 그리고 &lt;a href=&quot;https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#preferred-solutions&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;할 수만 있다면&lt;/a&gt; 파생된 state(derived state)의 사용은 피해야 한다. 그리고 언제나 렌더링할 준비를 해두자!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙-3-싱글턴인-컴포넌트는-없다&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%99-3-%EC%8B%B1%EA%B8%80%ED%84%B4%EC%9D%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%8A%94-%EC%97%86%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙 3: 싱글턴인 컴포넌트는 없다.&lt;/h2&gt;
&lt;p&gt;가끔 우리는 어떤 컴포넌트가 딱 한 번만 표시된다고 생각된다. 예를 들면 내비게이션 바 같은 것이다. 어떤 경우에는 맞는 말일 수 있다. 하지만 이 가정은 한참 후에야 마주치게 되는 디자인 문제를 종종 일으킨다. &lt;/p&gt;
&lt;p&gt;예를 들어, 라우트가 변경될 때 2개의 &lt;code class=&quot;language-text&quot;&gt;Page&lt;/code&gt; 컴포넌트 – 이전 &lt;code class=&quot;language-text&quot;&gt;Page&lt;/code&gt;와 다음 &lt;code class=&quot;language-text&quot;&gt;Page&lt;/code&gt; – 사이에 전환 효과를 주는 애니메이션을 구현할 필요가 생겼다고 가정해보자. 두 페이지 모두 애니메이션이 진행될 때 마운팅 된 상태여야 한다. 하지만 당신은 각각의 컴포넌트가 화면에 마운팅되는 오직 1개의 &lt;code class=&quot;language-text&quot;&gt;Page&lt;/code&gt; 컴포넌트로 생각하고 만들어졌다는 사실을 뒤늦게 발견하게 된다.&lt;/p&gt;
&lt;p&gt;이 문제를 확인하는 방법은 매우 간단하다. 재미삼아 당신의 앱을 동시에 2개 렌더링해 보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;ReactDOM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 앱의 루트 컴포넌트를 2번 렌더링&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MyApp &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; 
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MyApp &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`root`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여기저기 클릭해보자. (아마 이 실험을 위해서 CSS를 조금 조작해야 할 수도 있다)&lt;/p&gt;
&lt;p&gt;당신의 앱이 여전히 기대했던 대로 동작하는가? 아니면 이상하게 깨지거나 오류 메시지가 발생하는가? 복잡한 컴포넌트에 이런 스트레스 테스트를 한 번씩 해보는 것은 좋은 아이디어다. 그리고 같은 컴포넌트를 여러 개 동시에 렌더링해도 서로에게 영향을 미치지 말아야 한다. &lt;/p&gt;
&lt;p&gt;내가 직접 작성했던 컴포넌트에서 가끔 문제가 되었던 패턴은 &lt;code class=&quot;language-text&quot;&gt;componentWillUnmount&lt;/code&gt;에서 전역 state를 “초기화” 시키는 것이었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;componentWillUnmount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Redux 스토어에 있는 어떤 값을 초기화&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;당연하겠지만 페이지에 저런 컴포넌트가 2개 있는 상태에서 하나를 없애면 다른 하나에 문제를 일으키게 된다. “전역” state를 컴포넌트 마운팅 시점에 초기화하는 것도 마찬가지다. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;componentDidMount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Redux 스토어에 있는 어떤 값을 초기화&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;두 번째로 마운팅되는 컴포넌트가 첫번째 컴포넌트가 사용하고 있던 Redux 스토어의 값을 초기화시켜버리게 될 것이다(입력된 폼 데이터를 날려버린다든지).&lt;/p&gt;
&lt;p&gt;이러한 패턴들은 우리가 작성한 컴포넌트가 오류가 발생하기 쉬운지 아닌지를 판단할 수 있는 좋은 지표가 된다. &lt;strong&gt;컴포넌트 트리를 보여주고 숨기는 과정이 다른 트리에 있는 컴포넌트를 망가뜨려서는 안 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;컴포넌트를 두 번 렌더링할 계획이 있든 없든, 이런 문제를 해결하는 것은 장기적으로 이득이 된다. 그리고 당신을 더욱 탄력적인 컴포넌트 디자인으로 이끌어줄 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;원칙-4-로컬-state를-독립된-상태로-유지하라&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EC%B9%99-4-%EB%A1%9C%EC%BB%AC-state%EB%A5%BC-%EB%8F%85%EB%A6%BD%EB%90%9C-%EC%83%81%ED%83%9C%EB%A1%9C-%EC%9C%A0%EC%A7%80%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원칙 4: 로컬 State를 독립된 상태로 유지하라&lt;/h2&gt;
&lt;p&gt;소셜 미디어의 &lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt; 컴포넌트를 생각해보자. 그것은 &lt;code class=&quot;language-text&quot;&gt;Comment&lt;/code&gt; 스레드(확장될 수 있는)를 가지고 있으며 &lt;code class=&quot;language-text&quot;&gt;NewComment&lt;/code&gt; 입력을 가지고 있다.&lt;/p&gt;
&lt;p&gt;React 컴포넌트는 로컬 state를 가질 수 있다. 하지만 어떤 state가 정말로 ‘로컬’이라고 할 수 있는가? 포스트 콘텐츠 자체는 로컬인가 아닌가? 코멘트 목록은? 어떤 코멘트의 스레드가 열려 있었는지에 관한 기록은? 코멘트 입력은?&lt;/p&gt;
&lt;p&gt;당신이 만약 모든 것을 “상태 관리자”&lt;sup id=&quot;fnref-5&quot;&gt;&lt;a href=&quot;#fn-5&quot; class=&quot;footnote-ref&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;에 집어넣는 일에 익숙하다면 다소 대답하기 어려운 질문이 될 수 있다. 여기에 경계를 나누기 위한 간단한 방법이 있다.&lt;/p&gt;
&lt;p&gt;만약 어떤 state가 로컬이어야 하는지 아닌지 확실치 않다면, 이렇게 자문해보라: “만약 이 컴포넌트가 두 번 렌더링된다면, 이런 값들의 변경이 다른 카피에 반영되어야 하는가?” 대답이 “아니다”라면 로컬 state가 맞다.&lt;/p&gt;
&lt;p&gt;예를 들며, 어떤 &lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt; 컴포넌트를 두 번 렌더링했다고 가정하자. 그리고 이 컴포넌트 안에서 바뀔 수 있는 값들을 생각해보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;포스트 내용&lt;/em&gt;. 포스트를 수정한 후에 다른 위치에 있는 컴포넌트에서 업데이트하고 싶을 수 있다. 그러므로 수정된 포스트 내용은 아마도 &lt;code class=&quot;language-text&quot;&gt;Post&lt;/code&gt; 컴포넌트의 &lt;strong&gt;로컬 state에 있어서는 안될 것이다&lt;/strong&gt;.(그러는 대신, 포스트 내용은 Apollo, Relay, 또는 Redux같은 저장소에서 유지될 것이다)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;코멘트 목록&lt;/em&gt;. 이것은 포스트 컨텐츠와 비슷하다. 코멘트를 추가하면 다른 곳에도 반영되어야 한다. 그래서 이상적으로는 코멘트 목록을 위한 저장소를 사용해야 할 것이고 Post 컴포넌트의 &lt;strong&gt;로컬 state에 있어서는 안된다&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;어떤 코멘트 스레드가 확장되어 있는지&lt;/em&gt;. 어떤 코멘트의 스레드를 클릭해서 열었는데 다른 곳에서도 열린다면 뭔가 잘못된 것처럼 보일 것이다. 이 경우에서 우리는 추상적인 “코멘트 개체”를 다루는 것이 아니라 특정 &lt;code class=&quot;language-text&quot;&gt;Comment&lt;/code&gt; UI를 조작하고 있다고 봐야 한다. 그러므로 “열림” 플래그는 &lt;code class=&quot;language-text&quot;&gt;Comment&lt;/code&gt; 컴포넌트의 &lt;strong&gt;로컬 state에 있어야 한다&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;새로운 코멘트 입력값&lt;/em&gt;. 코멘트를 입력하고 있는데 다른 코멘트 입력창에 추가된다면 역시 이상하게 보일 것이다. 복수의 입력창이 명확하게 같은 값이라고 라벨이 붙어있지 않다면, 사람들은 보통 각각의 입력을 독립된 값으로 여긴다. 그러므로 입력값은 &lt;code class=&quot;language-text&quot;&gt;NewComment&lt;/code&gt; 컴포넌트의 &lt;strong&gt;로컬 state에 있어야 한다&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;나는 지금 제안하고 있는 이 규칙들을 무조건 지켜야 한다고 말하는 것이 아니다. 물론, 간단한 앱에서 당신은 저 “저장소”를 포함한 모든 것을 로컬 state에서 관리하고 싶을 수도 있다. 나는 단지  &lt;a href=&quot;https://overreacted.io/the-elements-of-ui-engineering/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;첫번째 원칙&lt;/a&gt; 에 있는 이상적인 사용자 경험에 관해서 이야기하고 있을 뿐이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;확실히 로컬한 state를 전역 값으로 만드는 것은 피해라&lt;/strong&gt;. 이것은 우리가 얘기하고 있는 “탄력성” 구현에 도움을 준다: 컴포넌트 사이의 놀라운 동기화는 많이 없다. 보너스로, 이것은 또 아주 넓은 범주의 성능 이슈를 해결하기도 한다. “과다 렌더링”은 당신의 state가 알맞은 위치에 있다면 그렇게 큰 이슈가 되지 않는다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;정리&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;지금까지 얘기한 원칙들을 정리하자면:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;데이터 흐름을 멈추지 마라.&lt;/strong&gt; Props와 state는 바뀔 수 있다. 그리고 컴포넌트는 그 변화를 계속해서 처리할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;언제라도 렌더링할 준비가 되어 있게 하라.&lt;/strong&gt; 컴포넌트는 렌더링 빈도에 상관없이 제대로 동작해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;싱글턴인 컴포넌트는 없다.&lt;/strong&gt; 컴포넌트가 한 번만 렌더링 된다 하더라도, 두 번 렌더링 되었을 때 문제가 없게 한다면 당신의 디자인은 더 좋아질 것이다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;로컬 state를 독립된 상태로 유지하라.&lt;/strong&gt; 각각의 UI에 어떤 state가 로컬이어야 할지 생각해보라 – 그리고 필요 이상으로 state를 높은 곳으로 끌어올리지 마라.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 원칙들은 &lt;a href=&quot;https://overreacted.io/optimized-for-change/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;변화에 최적화된&lt;/a&gt; 컴포넌트를 작성하는 데 도움을 준다. 그런 컴포넌트는 컴포넌트 트리에 추가하기 쉽고, 바꾸기 쉽고, 삭제하기 쉽다.&lt;/p&gt;
&lt;p&gt;그리고 가장 중요한 점으로, 작성하는 컴포넌트가 탄력적이게 되면, props가 알파벳 순으로 정렬되어야 하는지 아닌지를 고민하는 것과 같은 어려운 딜레마로 돌아갈 수 있다.&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;린터(linter) 또는 린트(lint). 소스 코드를 분석하여 프로그램 오류, 버그, 스타일 오류에 표지(flag)를 달아놓기 위한 도구. 자바스크립트 린터에는 eslint, tslint가 있다&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;computed values. 이미 존재하는 값으로부터 유래된 값. React 컴포넌트에서 어떤 계산된 값을 state를 사용해서 만들었다면, state가 업데이트 되면 그 계산된 값도 업데이트를 반영한다.&lt;/p&gt;
&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;사용자가 직접 입력한 값을 this.state.value에 업데이트하고 있다. 그런데 부모 컴포넌트로부터 새로운 value 값이나 다른 prop이 전달된다면 사용자가 지금까지 입력했던 값은 날아가버리게 될 것이다.&lt;/p&gt;
&lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;특정 컴포넌트가 부모일 때만 제대로 동작한다면 재사용성이 떨어질 것이다.&lt;/p&gt;
&lt;a href=&quot;#fnref-4&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-5&quot;&gt;
&lt;p&gt;Redux, Mobx, etc&lt;/p&gt;
&lt;a href=&quot;#fnref-5&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[UMD(Universal Module Definition) 패턴]]></title><description><![CDATA[모듈이란 1개 이상의 값(객체, 함수, 변수 등)을 내보내는 자바스크립트 파일을 말한다. 모던 웹 개발에서는 자바스크립트가 차지하는 비중이 무척 높아졌기 때문에 모듈을 통한 기능과 관심사의 분리는 필수적이다. 자바스크립트 모듈 선언 포맷에는 AMD와 CommonJS	…]]></description><link>https://blog.rhostem.com//posts/2019-06-23-universal-module-definition-pattern</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2019-06-23-universal-module-definition-pattern</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 23 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;모듈이란 1개 이상의 값(객체, 함수, 변수 등)을 내보내는 자바스크립트 파일을 말한다. 모던 웹 개발에서는 자바스크립트가 차지하는 비중이 무척 높아졌기 때문에 모듈을 통한 기능과 관심사의 분리는 필수적이다. 자바스크립트 모듈 선언 포맷에는 AMD와 CommonJS	가 있다.&lt;/p&gt;
&lt;h2 id=&quot;amd&quot;&gt;&lt;a href=&quot;#amd&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AMD&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Asynchronous_module_definition&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;AMD(Asynchronous Module Definition)&lt;/a&gt;는 코드 모듈을 선언하면서 그것이 의존하고 있는 모듈도 함께 명시한다. 그리고 필요에 따라 비동기적으로 의존 모듈을 불러온다. AMD는 아래와 같은 형식으로 모듈을 선언한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;jquery&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 의존 모듈로 jQuery를 선언&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// 함수 선언	&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withjQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	
  &lt;span class=&quot;token comment&quot;&gt;// 외부에 함수를 노출시켜 사용할 수 있도록 한다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; withjQuery&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;AMD를 사용하면 실행할 코드가 필요한 파일만 가져오기 때문에 성능 향상을 기대할 수 있다. 그리고 의존하고 있는 모듈을 확실히 가져온 다음에 코드를 실행할 수 있기 때문에 오류도 줄어든다. AMD 포맷을 구현한 대표적인 라이브러리에는 Require.js, Dojo Toolkit 등이 있다.&lt;/p&gt;
&lt;h2 id=&quot;commonjs&quot;&gt;&lt;a href=&quot;#commonjs&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CommonJS&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/CommonJS&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CommonJS&lt;/a&gt;는 웹 브라우저가 아닌 환경에서의 자바스크립트 모듈 포맷을 확립하기 위한 프로젝트다. 이 프로젝트가 시작된 가장 큰 이유는 서로 다른 개발 환경(웹 서버, 네이티브 데스크탑 앱 등)에서 만들어진 자바스크립트 코드를 공유하기 위한 포맷이 없었기 때문이었다.  2009년 ServerJS라는 이름의 프로젝트로 시작되었고 이후 CommonJS로 이름이 바뀌게 된다. 자바스크립트의 표준이 되는 ECMAScript를 정의하는 ECMA 국제 그룹 TC39와 직접적인 관계는 없지만, TC39의 멤버 중 일부는 CommonJS 프로젝트에 참여하기도 했다.&lt;/p&gt;
&lt;p&gt;CommonJS 포맷은 Node.js에서 구현해 뒀기 때문에 AMD보다 접해본 사람이 더 많을 것이다.  앞서 작성한 &lt;code class=&quot;language-text&quot;&gt;withJquery&lt;/code&gt; 모듈을 CommonJS 형식으로 작성하면 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 의존 모듈 선언 &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; $ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;jquery&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 함수 선언	&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withjQuery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 외부에 함수를 노출시킨다.&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; withjQuery&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;umd&quot;&gt;&lt;a href=&quot;#umd&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;UMD&lt;/h2&gt;
&lt;p&gt;앞에서 제시한 예제 코드를 통해 AMD와 CommonJS 포맷 모두 ‘의존 모듈을 정의하고 새로운 모듈을 내보낸다’는 같은 목적을 가지고 있다는 것을 알 수 있다. 다만 그 방식이 다를 뿐이다.&lt;/p&gt;
&lt;p&gt;그런데 자바스크립트로 개발을 하다 보면 두 방식을 모두 지원해야 하는 상황이 생긴다. 대표적으로 오픈소스 라이브러리 모듈은 다양한 환경을 지원해야 할 필요가 있을 것이다. 그럴 때는 &lt;a href=&quot;https://github.com/umdjs/umd&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;UMD(Universal Module Definition)&lt;/a&gt;를 사용해서 모듈을 선언할 수 있다.&lt;/p&gt;
&lt;p&gt;UMD는 CommonJS처럼 스펙이 아닌 코드 작성 패턴이라고 보면 되며, 다양한 패턴이 존재한다. 아래의 패턴은 모듈을 AMD, CommonJS, 또는 브라우저 전역 변수로 할당해서 사용할 수 있도록 하는 &lt;a href=&quot;https://github.com/umdjs/umd/blob/master/templates/returnExports.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;returnExports.js&lt;/a&gt; 패턴이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 모듈 선언을 위해 IIFE 패턴을 사용하고 있다.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 이 예제에서는 의존 모듈을 표현하기 위해 임의로 모듈 &apos;b&apos;를 사용한다&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// root 파라미터는 실행 환경에 따라 브라우저의 window 또는 node.js의 global을 가리킨다.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// factory 파라미터는 모듈 코드를 감싸고 있는 함수를 가리킨다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; factory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	
 	&lt;span class=&quot;token comment&quot;&gt;// AMD에서 사용하는 define 함수를 사용할 수 있는지 확인한다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; define &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;function&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; define&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;amd&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// AMD 포맷에 따라 모듈을 선언한다. 의존 모듈은 b가 된다.&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; factory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// 의존 모듈이 없다면 아래와 같이 작성한다&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// define([], factory);&lt;/span&gt;
    
	&lt;span class=&quot;token comment&quot;&gt;// AMD를 지원하지 않는다면 CommonJS 모듈을 사용할 수 있는지 확인한다.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; module &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;object&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// CommonJS 포맷에 따라 모듈을 선언한다. &lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 의존 모듈을 factory 함수의 파라미터로 전달해준다.&lt;/span&gt;
    module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;b&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// 의존 모듈이 없다면 아래와 같이 작성한다&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// module.exports = factory();&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// AMD도 CommonJS도 아니라면 브라우저라고 판단한다.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 여기서 root는 window가 된다. returnExports는 전역에서 모듈의 이름이다.&lt;/span&gt;
    root&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;returnExports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// 의존 모듈이 없다면 아래와 같이 작성한다&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// root.returnExports = factory();&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
&lt;span class=&quot;token comment&quot;&gt;// 함수를 즉시 실행한다. &lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 첫번째 파라미터는 전역 변수. &lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// (여기서는 Web Worker 지원을 위해 self 변수를 확인하지만 그럴 필요가 없다면 this만 전달해도 된다)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 두번째 파라미터는 factory에 해당한다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; self &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;undefined&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; self &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 의존 모듈 b&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// return하는 값이 이 모듈이 내보내는 값이 된다(이 예제에서는 객체).&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;코멘트를 제거하고 정리한 간략한 버전은 아래와 같다. &lt;/p&gt;
&lt;p&gt;아래의 샘플은 현재 실행 환경을 확인하기위해 &lt;code class=&quot;language-text&quot;&gt;NODE_ENV&lt;/code&gt; 환경변수가 &lt;code class=&quot;language-text&quot;&gt;development&lt;/code&gt; 라면 &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;, 아니면 &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;를 반환하는 기능을 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; factory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; define &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;function&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; define&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;amd&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// AMD&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; factory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; module &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;object&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// CommonJS&lt;/span&gt;
    module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// browser&lt;/span&gt;
    root&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDev &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;development&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Github &lt;a href=&quot;https://github.com/umdjs/umd&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;umdjs/umd&lt;/a&gt; 저장소에는 이 외에도 몇가지 패턴들을 더 제공하고 있으니 필요한 패턴을 참조해서 사용하면 될 것이다. 그리고 소스 파일을 UMD 패턴으로 변환시켜주는 도구(ex. &lt;a href=&quot;https://github.com/eduardolundgren/gulp-umd&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;gulp-umd&lt;/a&gt;)도 존재한다. &lt;/p&gt;</content:encoded></item><item><title><![CDATA[Gatsby + Contentful + CircleCI로 블로그 마이그레이션]]></title><description><![CDATA[자동 빌드가 안되는 현재의 블로그 블로그는  Gatsby 로 만들었고 콘텐츠는 소스 폴더와 함께 git 저장소에 포함되어 있었다. 배포를 위해서는 로컬에서 직접 빌드를 한 후 서버에 업로드하는 방식을 사용했다.  하지만 콘텐츠가 소스에 포함되어 있으니 사소한 오타 수…]]></description><link>https://blog.rhostem.com//posts/2019-01-12-blog-with-gatsby-contentful-circleci</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2019-01-12-blog-with-gatsby-contentful-circleci</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 12 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;자동-빌드가-안되는-현재의-블로그&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%8F%99-%EB%B9%8C%EB%93%9C%EA%B0%80-%EC%95%88%EB%90%98%EB%8A%94-%ED%98%84%EC%9E%AC%EC%9D%98-%EB%B8%94%EB%A1%9C%EA%B7%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자동 빌드가 안되는 현재의 블로그&lt;/h2&gt;
&lt;p&gt;블로그는 &lt;a href=&quot;https://www.gatsbyjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Gatsby&lt;/a&gt;로 만들었고 콘텐츠는 소스 폴더와 함께 git 저장소에 포함되어 있었다. 배포를 위해서는 로컬에서 직접 빌드를 한 후 서버에 업로드하는 방식을 사용했다. &lt;/p&gt;
&lt;p&gt;하지만 콘텐츠가 소스에 포함되어 있으니 사소한 오타 수정도 커밋 로그에 남겨야 하고, 터미널을 띄워서 배포 명령어를 직접 입력해야 한다. 자동화가 전혀 되어있지 않는 데다 아무리 텍스트 에디터가 마크다운 문법을 잘 지원한다 하더라도 콘텐츠 관리 측면에서 좋은 점수를 줄 수 없었다. &lt;/p&gt;
&lt;p&gt;그래서 이번 기회에 콘텐츠도 소스에서 분리해서 CMS로 따리 관리하고, 코드 커밋과 콘텐츠 업데이트 시점에 사이트를 자동으로 재배포되도록 블로그 시스템을 업그레이드하기로 했다.&lt;/p&gt;
&lt;h2 id=&quot;웹사이트-개발관리-도구&quot;&gt;&lt;a href=&quot;#%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EA%B4%80%EB%A6%AC-%EB%8F%84%EA%B5%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹사이트 개발/관리 도구&lt;/h2&gt;
&lt;p&gt;시스템 마이그레이션을 위해서는 도구가 필요하다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정적 웹사이트 프레임워크&lt;/li&gt;
&lt;li&gt;콘텐츠 관리 시스템&lt;/li&gt;
&lt;li&gt;CI/CD 도구&lt;/li&gt;
&lt;li&gt;웹 서버&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;정적-웹사이트-프레임워크&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EC%A0%81-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정적 웹사이트 프레임워크&lt;/h3&gt;
&lt;p&gt;사용하고 있던 &lt;strong&gt;Gatsby&lt;/strong&gt;를 그대로 사용하기로 했다. 프레임워크의 메이저 버전이 v2로 올라간지 몇달이 지난 상태였기에 이번 참에 함께 업데이트 하기로 했다. v2로 올려야 React 16, Babel 7, Webpack 4를 사용할 수 있고 빌드 시간 단축, 번들 사이즈 감소 등의  혜택을 누릴 수 있다.  하지만 긴 마이그레이션 가이드 문서가 보여주듯 플러그인 버전도 모두 변경해야 하고 수정된 스펙도 제법 있다. 그래서 시간은 제법 걸리겠지만, 최대 2년이 넘은 기존 코드도 리뷰도 할 겸 Gatsby v2로 새 프로젝트를 만든 후 기존 코드를 옮기는 방식을 사용하기로 했다.  &lt;/p&gt;
&lt;h3 id=&quot;콘텐츠-관리-시스템cms&quot;&gt;&lt;a href=&quot;#%EC%BD%98%ED%85%90%EC%B8%A0-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9Ccms&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;콘텐츠 관리 시스템(CMS)&lt;/h3&gt;
&lt;p&gt;유명한 CMS로는 Wordpress가 있다. CMS를 사용하면 웹사이트를 쉽게 만들고 배포할 수 있지만 ‘사용하기 편리하다 == 커스터마이징이 어렵다’는 공식이 성립하기 때문에 마음대로 만들고 싶은 나 같은 개발자에게는 별로 선호되는 도구는 아니다. 그럴 바에는 더 편하게 블로그 전용 웹서비스를 사용하는 편이 낫지 않을까 하는 생각을 하고 있다.&lt;/p&gt;
&lt;p&gt;그래서 Gatsby 같은 도구와 사용하기에는  Wordpress처럼 프론트엔드 레이어까지 제공하는 것이 아니라 데이터의 관리만 제공하는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Headless_content_management_system&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Headless CMS&lt;/a&gt;가 적합하다. 그리고 Gatsby는 플러그인을 통해 다양한 CMS에서 콘텐츠를 가져올 수 있도록 도와준다. 오픈 소스 CMS인 &lt;a href=&quot;https://www.drupal.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Drupal&lt;/a&gt;을 포함해서 Wordpress,  Contentful,  ButterCMS 같은 CMS는 물론 MongoDB, MySQL같은 데이터베이스, 그리고 서드 파티 API를 통해서도 콘텐츠를 가져올 수 있다. &lt;/p&gt;
&lt;p&gt;Gatsby에서 플러그인을 제공하는 몇 가지 CMS를 둘러본 후 &lt;a href=&quot;https://www.contentful.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Contentful&lt;/strong&gt;&lt;/a&gt;을 사용하기로 했다. 무료 플랜은 콘텐츠와 미디어를 합해서 5000개까지 업로드할 수 있다. 지난 근 2년간 작성한 글의 수와 이미지의 수를 합치면 200개가 되지 않으니 개인 블로그로 사용하기에는 충분해 보였다. 인터페이스도 깔끔하며 웹훅 설정 등도 간편하게 제공하는 등 다양한 기능이 잘 준비되어 있다. 다만 해외 서비스라서 웹사이트 속도는 조금 느린 편이다. 관리 도구에서 콘텐츠를 불러올 때 0.5초~1.5초 정도 걸려서 국내 웹사이트와 비교하면 상대적으로 느리게 느껴질 수 있다. 하지만 실제 배포된 웹사이트는 콘텐츠를 모두 포함하고 있으니 CMS API 호출 속도는 큰 문제가 되지 않는다.&lt;/p&gt;
&lt;h3 id=&quot;cicd-도구&quot;&gt;&lt;a href=&quot;#cicd-%EB%8F%84%EA%B5%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CI/CD 도구&lt;/h3&gt;
&lt;p&gt;웹사이트는 아래 상황에서 자동 배포가 되어야 한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;master 브랜치에 커밋이 푸시되었을 때&lt;/li&gt;
&lt;li&gt;콘텐츠가 생성, 수정, 삭제되었을 때&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;자동 빌드 도구는 &lt;strong&gt;CircleCI&lt;/strong&gt;를 사용하기로 했다. 다른 사이드 프로젝트를 하면서 사용해 봤는데 무료 플랜의 빌드 성능도 괜찮고 1달에 1000분까지의 빌드 시간을 제공하므로 역시 개인이 사용하기에는 문제가 없을 것이다. &lt;/p&gt;
&lt;p&gt;CircleCI는 Github 계정으로 로그인 후 사용할 저장소를 선택하면 기본적인 웹훅&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; 설정은 자동으로 해준다. 사용자는 설정 파일에 원하는 명령어를 추가하면 된다. CircleCI 설정 파일에 배포 프로세스를 추가하면 1번의 조건은 만족하게 된다.&lt;/p&gt;
&lt;p&gt;그리고 2번을 만족하기 위해서는 CMS인 Contentful에서 웹훅 설정을 해야 한다. Contenful은 고맙게도 웹훅을 위한 전용 메뉴까지 제공하며 다양한 서비스를 위한 템플릿도 제공한다. CircleCI는 물론 Netlify, Heroku, TravisCI, Gitlab 등의 서비스를 쉽게 사용할 수 있다. &lt;/p&gt;
&lt;p&gt;Contentful에서 CircleCI에 설정된 빌드를 실행하기 위해서는 웹훅 템플릿을 선택한 후 Git 저장소 이름, 서비스 접속 키 등 꼭 필요한 정보만 입력하면 된다. 이렇게 하면 콘텐츠 업데이트 시 웹사이트 자동 배포가 가능하다.&lt;/p&gt;
&lt;h3 id=&quot;웹-서버&quot;&gt;&lt;a href=&quot;#%EC%9B%B9-%EC%84%9C%EB%B2%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹 서버&lt;/h3&gt;
&lt;p&gt;처음에는 Github Pages를 사용하다가 지금은 AWS Lightsail의 우분투 인스턴스에 nginx 웹 서버를 설치해서 배포하고 있다. 이번 마이그레이션 과정에서 &lt;a href=&quot;https://www.netlify.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Netlify&lt;/a&gt;로 옮기는 것도 고려하고 있어서 테스트해 봤는데, 엄청나게 간편해서 놀랬다. Github 저장소를 선택한 후 브랜치, 빌드 스크립트, Public 폴더, 빌드에 필요한 환경 변수 등만 저장하면 끝이다. CircleCI 처럼 docker 기반의 빌드 도구를 가지고 있어서 사용자가 별도로 할 일은 없다. &lt;/p&gt;
&lt;p&gt;Netlify의 간단함에 배포가 되는 순간 옮길까? 라는 마음이 들긴 했지만 AWS보다는 역시 느린 속도&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;가 조금 걸렸다. 그리고 CircleCI에서 빌드 프로세스를 모두 관리하고 싶어서 CircleCI에서 AWS의 &lt;strong&gt;nginx 웹 서버&lt;/strong&gt;에 직접 배포하는 방식을 사용하기로 했다.&lt;/p&gt;
&lt;h2 id=&quot;정리&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EB%A6%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정리&lt;/h2&gt;
&lt;p&gt;이렇게 웹 서버까지 시스템 마이그레이션 위한 도구가 모두 정해졌다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정적 웹사이트 빌드 도구: &lt;strong&gt;Gatsby&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;콘텐츠 관리 시스템: &lt;strong&gt;Contentful&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;CI/CD 도구: &lt;strong&gt;CircleCI&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;웹 서버: &lt;strong&gt;nginx&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;현재 이 블로그는 위의 도구들을 사용한 &lt;a href=&quot;https://github.com/rhostem/blog&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;마이그레이션이 완료된 상태&lt;/a&gt;다. 비용은 AWS Lightsail 인스턴스에 들어가는 월 5천원 정도가 된다. Netlify나 Google Firebase 호스팅 등의 서비스를 사용한다면 그 비용도 0이 된다.&lt;/p&gt;
&lt;p&gt;Contentful에서 글을 작성한 후 ‘published’ 상태로 전환만 하면 사이트가 다시 빌드되어서 배포되니 무척 편리하다. Gatsby에서 Contentful의 소스를 가져와서 콘텐츠별로 페이지를 만들기, CircleCI 설정 등 마이그레이션에 대한 자세한 내용은 다른 글에서 다룰 예정이다. &lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;webhook, 제 3자가 웹서비스의 콘텐츠를 수정할 수 있도록 하는 것. 오픈 API를 호출해서 채팅방에 알림을 보내거나, 서버에서 빌드를 실행시키거나 하는 작업이 가능하다&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;1.5MB 정도 되는 페이지의 최초 로드 시간이 1~2초정도 차이가 난다. 웹 폰트처럼 용량이 큰 파일은 CDN을 사용하지 않는다면 더 큰 차이가 난다.&lt;/p&gt;
&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[[번역] 개발자를 위한 면접 지침]]></title><description><![CDATA[이 글은  A developer’s guide to interviewing 의 번역입니다. 기본적으로 구직자로서 면접관을 대상으로 할 수 있는 질문들을 정리한 글입니다. 글쓴이가 거주하고 있는 것으로 보이는 미국과 한국의 기업 환경에는 차이가 있기에 조심스럽게 해야 …]]></description><link>https://blog.rhostem.com//posts/2019-01-05-developer-guide-for-interview</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2019-01-05-developer-guide-for-interview</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 05 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://medium.freecodecamp.org/how-to-interview-as-a-developer-candidate-b666734f12dd&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;A developer’s guide to interviewing&lt;/a&gt;의 번역입니다. 기본적으로 구직자로서 면접관을 대상으로 할 수 있는 질문들을 정리한 글입니다. 글쓴이가 거주하고 있는 것으로 보이는 미국과 한국의 기업 환경에는 차이가 있기에 조심스럽게 해야 할 질문도 있겠습니다. 하지만 경력이 쌓이면 자연스럽게 면접관의 역할을 하게 될 것이므로 개발자로서, 관리자로서, 그리고 경영진으로서 가져야 할 업무 능력, 지식, 가치관에 대한 조언이라 생각하고 읽어도 좋을 것 같습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;구직을 위해 면접을 본 적이 있다면, 면접관으로부터 테이블 너머에서 “더 궁금한 것이 있으신가요?”라는 질문을 받은 적이 있을 것이다. 당신이 만약 “음, 아니오. 없습니다”라고 대답했다면 면접은 단방향이라는 관점을 가지고 있을 가능성이 크다.&lt;/p&gt;
&lt;p&gt;구직자로서의 당신은 결과를 만드는 일에 집중하는 것이 당연하다. 하지만 면접은 단방향이 아니다. 회사가 당신을 면접하는 일에 집중하는 것처럼 당신도 &lt;strong&gt;회사를 면접하는 일&lt;/strong&gt;에 집중해야 한다.&lt;/p&gt;
&lt;p&gt;그런데 무슨 질문을 해야 하나?&lt;/p&gt;
&lt;p&gt;이직을 준비하는 많은 개발자는 나에게 이 질문을 한다. 나는 지난 15년간 7개의 회사에서 일했고(2번의 인턴쉽과 스타트업에서의 6개월 포함) 그동안 많은 사람과 면접을 진행했다. 그리고 다른 이들에게 도움이 되길 바라는 마음에 지금까지 면접에서 했던 질문들을 정리하기로 했다.&lt;/p&gt;
&lt;p&gt;나는 이 글이 계속 업데이트되길 바란다. 만약 당신이 제안하고 싶은 내용이 있다면 &lt;a href=&quot;https://twitter.com/djsmith42&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;트위터를 통해 알려주길 바란다&lt;/a&gt;. 모두에게 도움이 되기 위해 기꺼이 함께할 것이다.&lt;/p&gt;
&lt;h2 id=&quot;누구와-얘기를-하게-될까&quot;&gt;&lt;a href=&quot;#%EB%88%84%EA%B5%AC%EC%99%80-%EC%96%98%EA%B8%B0%EB%A5%BC-%ED%95%98%EA%B2%8C-%EB%90%A0%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;누구와 얘기를 하게 될까?&lt;/h2&gt;
&lt;p&gt;면접을 보는 동안 만나게 되는 사람들의 역할은 보통 세 종류로 나뉜다. 회사의 규모에 따라 한 사람이 &lt;strong&gt;세 가지&lt;/strong&gt; 역할을 모두 할 수도 있고, 많은 사람이 그 역할을 나눠 맡을 수도 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;개발자&lt;/li&gt;
&lt;li&gt;관리자 (기술 책임, 중간 관리자, 기술 감독)&lt;/li&gt;
&lt;li&gt;경영진 (VP, CTO, CEO, 부서장)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;나는 저들에게 할 수 있는 서로 다른 질문이 있으며, 이 글에서 하나씩 언급할 것이다. 그리고 답변들을 비교하기 위해 때로는 서로 다른 대상에게 같은 질문을 하기도 한다는 사실을 알고 있었으면 한다.&lt;/p&gt;
&lt;p&gt;이 글은 제법 길다. 쭉 읽어낼 수 있는 글이라기보다는 참고서에 가깝다. 만약 내가 오늘 면접이 있다면 이 글을 가져가서 조심스럽게 면접을 진행하는 동안 (조심스럽게) 참조할 것이다.&lt;/p&gt;
&lt;p&gt;질문 대부분은 “옳고” “그른” 대답이 없다. 질문들은 당신이 회사의 문화, 업무 프로세스, 그리고 조직에 대해 파악할 수 있도록 도와줄 것이다. 그리고 면접을 보는 동안 당신의 머리가 멍해졌을 때 대화를 이어나가기 위한 도구로서도 요긴하게 쓰일 수 있다.&lt;/p&gt;
&lt;p&gt;예를 들어, 나는 면접을 시작할 때 면접관들에게 몇 가지 질문을 해도 되느냐고 물어보곤 한다. 이는 면접관들이 일정에 맞게 면접을 진행하는 데 도움을 줄 것이다. 보통은 면접이 끝날 때 질문을 할 기회를 주기 때문에 질문을 하고 싶다는 당신의 의도를 면접관들에게 일찍 전달해야 한다. 하나의 질문이 끝나고 나면 멈춘 후 다른 질문을 계속해도 되는지, 그들이 시간을 얼마나 더 가졌는지 물어봐야 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;개발자를-위한-질문&quot;&gt;&lt;a href=&quot;#%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%A7%88%EB%AC%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;개발자를 위한 질문&lt;/h2&gt;
&lt;h3 id=&quot;1-오늘-해야-할-일을-어떻게-파악하십니까&quot;&gt;&lt;a href=&quot;#1-%EC%98%A4%EB%8A%98-%ED%95%B4%EC%95%BC-%ED%95%A0-%EC%9D%BC%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%8C%8C%EC%95%85%ED%95%98%EC%8B%AD%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 오늘 해야 할 일을 어떻게 파악하십니까?&lt;/h3&gt;
&lt;p&gt;이 질문의 목적은 업무 프로세스가 제대로 구축되어 있는지 확인하기 위한 것이다. 나는 2~3명의 개발자에게 질문의 답변을 들었으면 한다. 운영진들이 특정 프로세스를 따른다고 말하더라도 개발자들이 아무런 얘기를 하지 않는다면 그건 부정적인 신호다. 개발자들이 서로 다른 얘기를 한다면, 그 또한 부정적인 신호로 볼 수 있다.&lt;/p&gt;
&lt;p&gt;높은 수준의 팀에서는 이 질문에 대해 일관적인 대답을 들을 수 있다. 그것은 모든 개발자가 업무 프로세스를 잘 알고 있으며, 프로세스가 단순해서 개발자들에게 부담되지 않고 도움이 되고 있음을 의미한다.&lt;/p&gt;
&lt;p&gt;좋은 대답의 예: “우리는 새로운 기능과 버그를 고치기 위한 (몇) 주 단위의 스프린트를 진행하며 매일 서로에게 했던 일을 공유합니다. 우리와 함께 일하는 제품 관리자는 고객 대응에 임하면서 개발자들에게 새 기능과 버그 수정의 우선순위를 잘 조정해줍니다.”&lt;/p&gt;
&lt;p&gt;나쁜 대답의 예: “회사에 오면 어떤 문제가 터졌는지 살펴봅니다. 거의 매일 비상사태입니다.”&lt;/p&gt;
&lt;p&gt;내가 “스크럼”이나 다른 특정한 방법론을 언급한 것이 아님을 분명히 했으면 좋겠다. 나는 회사가 사용하는 개발 프로세스의 이름에는 관심이 없으며 그들이 매일매일 “어떻게 업무를 진행하는지”를 더 중요하게 본다.&lt;/p&gt;
&lt;h3 id=&quot;2-버전-관리-도구로는-무엇을-사용합니까&quot;&gt;&lt;a href=&quot;#2-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC-%EB%8F%84%EA%B5%AC%EB%A1%9C%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A9%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 버전 관리 도구로는 무엇을 사용합니까?&lt;/h3&gt;
&lt;p&gt;좋은 도구의 사용은 좋은 팀의 지표가 된다. 어떤 팀이 구시대적인 버전 관리 도구를 사용하고 있다면, 아마 그것 말고도 철 지난 도구를 많이 사용하고 있을 것이다. 게다가 그들은 좋은 도구를 사용함으로써 얻는 효율을 그다지 중요하게 여기지 않을 것이다.&lt;/p&gt;
&lt;p&gt;이 질문 다음으로는 업무 진행 방식에 대해 질문을 하면 좋다. “분기점(branch)을 사용하나요?” “재배치(rebase)와 병합(merge) 중 무엇을 선호하십니까?” 이런 질문은 그들이 사용하는 도구에 얼마나 전문적인 지식을 가졌는지, 그리고 그들에게 무엇을 기대할 수 있는지를 알 수 있다. 예를 들어 그 회사에 가면 리누스 토르발즈&lt;sup id=&quot;fnref-1&quot;&gt;&lt;a href=&quot;#fn-1&quot; class=&quot;footnote-ref&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;처럼 뛰어난 사람을 만날 수 있다는 확신을 얻을 수도 있지만, 오히려 내가 “지역 Git 전문가” 역할을 하게 될 것 같다는 예감을 하게 될 수도 있다.&lt;/p&gt;
&lt;p&gt;이 질문은 개발 도구에 대한 전반적인 토론을 시작할 수 있도록 있도록 하며, 종종 당신에게 좋은 통찰을 가져다줄 것이다.&lt;/p&gt;
&lt;h3 id=&quot;3-여기서-일하면-어떤-점이-좋습니까&quot;&gt;&lt;a href=&quot;#3-%EC%97%AC%EA%B8%B0%EC%84%9C-%EC%9D%BC%ED%95%98%EB%A9%B4-%EC%96%B4%EB%96%A4-%EC%A0%90%EC%9D%B4-%EC%A2%8B%EC%8A%B5%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 여기서 일하면 어떤 점이 좋습니까?&lt;/h3&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;긍정적 대답은 많을수록 좋다. 면접을 보는 회사에 좋은 점수를 주기 위해 위에 있는 모든 대답을 들어야 할 필요는 없다. 어떤 사람들은 선천적으로 자랑을 잘하지 못한다는 사실을 염두에 둬야 한다. 그러니 이 질문에서 아주 긍정적인 대답을 듣지 못하더라도 괜찮다.&lt;/p&gt;
&lt;p&gt;하지만 다음에 오는 대답을 들으면서 확신에 찬 대답을 조금밖에 듣지 못한다면, 고민이 될 것이다.&lt;/p&gt;
&lt;ul&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;li&gt;확신 없는 대답: … (침묵)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;꾸며낸 말이 결코 아니다. 나는 실제 면접에서 저런 대답을 들었던 경험이 있다.&lt;/p&gt;
&lt;p&gt;확신 없는 대답을 하나 들었다고 해서 바로 그 회사가 나쁘다고 판단하지는 않는다. 하지만 세 번째 질문에 대한 답이 모두 저런 식이라면, 나는 보통 다른 곳을 알아본다.&lt;/p&gt;
&lt;h3 id=&quot;4-유닛-테스트를-작성하십니까&quot;&gt;&lt;a href=&quot;#4-%EC%9C%A0%EB%8B%9B-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%98%EC%8B%AD%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 유닛 테스트를 작성하십니까?&lt;/h3&gt;
&lt;p&gt;어떤 팀을 유닛 테스트 활용 사례를 바탕으로 평가할 때는 신중해야 한다. 유닛 테스팅에 대한 질문을 했을 때 기분이 좋아 보인다면 그것은 일반적으로 좋은 신호다. 그에 반해 &lt;strong&gt;왜&lt;/strong&gt; 유닛 테스트를 작성해야 하는지, 유닛 테스트의 결점은 무엇인지에 대해 설명하지 못한다면 눈먼 양 도그마&lt;sup id=&quot;fnref-2&quot;&gt;&lt;a href=&quot;#fn-2&quot; class=&quot;footnote-ref&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;에 빠져 있다는 징후로 볼 수 있다. “시간이 없다”는 등의 변명을 들며 테스트를 작성하지 않는다고 말한다면, 나는 좋지 않은 신호로 받아들인다.&lt;/p&gt;
&lt;h3 id=&quot;5-지속적-통합ci-프로세스를-갖추고-있습니까&quot;&gt;&lt;a href=&quot;#5-%EC%A7%80%EC%86%8D%EC%A0%81-%ED%86%B5%ED%95%A9ci-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EA%B0%96%EC%B6%94%EA%B3%A0-%EC%9E%88%EC%8A%B5%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 지속적 통합(CI) 프로세스를 갖추고 있습니까?&lt;/h3&gt;
&lt;p&gt;내가 아는 최고의 개발팀은 Jenkins, Travis, 그리고 BuildBot 같은 도구를 사용한다. 그 팀이 CI 도구를 사용하고 있지 않다면 관련 개념을 알고 있는지 알아본다. 만약 모른다면 이건 내 경험상 좋지 않은 신호다. CI 시스템을 갖추고 있다는 것은 그 팀이 자동화에 대한 믿음을 가지고 있다는 의미이며, 이는 매우 좋은 신호가 될 수 있다.&lt;/p&gt;
&lt;p&gt;어떤 팀에게 이 질문은 &lt;a href=&quot;http://www.amazon.com/Continuous-Delivery-Deployment-Automation-Signature/dp/0321601912&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;지속적 배포(CD)&lt;/a&gt;에 대한 토론으로 이어지게 한다. 지속적 배포는 지속적 통합과 관련은 있지만 다른 개념이다. 웹 개발자 직군이라면 나는 적어도 CD에 대해 들어봤길 기대한다. 그리고 건강한 개발팀은 CD를 시스템에 부분적으로라도 적용하려고 시도한다.&lt;/p&gt;
&lt;p&gt;이어질 수 있는 질문:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;당신의 팀은 CI가 실패했다는 알림을 받으면 오류를 수정하기까지 보통 얼마나 걸립니까?&lt;/li&gt;
&lt;li&gt;구축된 CI 시스템에서 좋은 점 / 안 좋은 점은 무엇입니까?&lt;/li&gt;
&lt;li&gt;CI가 실행되는 데 시간이 얼마나 걸립니까? 더 빠르게 만들 수 있습니까?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;6-어떤-지표를-측정합니까&quot;&gt;&lt;a href=&quot;#6-%EC%96%B4%EB%96%A4-%EC%A7%80%ED%91%9C%EB%A5%BC-%EC%B8%A1%EC%A0%95%ED%95%A9%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. 어떤 지표를 측정합니까?&lt;/h3&gt;
&lt;p&gt;이 질문은 정답이 없으며, 그 팀이 소프트웨어의 성능을 측정하기 위한 노력을 하고 있는지 파악하기 위한 것이다. 웹 개발팀은 보통 서버 응답 시간이나 요청 처리량, 사용자 수, 클라이언트 사이드의 반응성 등의 성능 지수에 대한 답변에 치중하는 경향이 있다. 하지만 이와 관련된 토론은 서로 다른 언어를 사용하는 사용자의 수, 브라우저 오류, 캐시 히트/미스 비율, 그리고 무수히 많은 다른 주제들로 이어질 수 있다. 만약 팀이 지표 측정에 시간을 들이고 있지 않다면, 이는 그들이 데이터에 기반을 둔 결정을 내리지 않음을 가리킨다. 나는 실제로 측정된 진짜 데이터를 기반으로 성능을 판단하는 팀에 좋은 점수를 매긴다. 하지만 이는 성능 측정뿐 아니라 다른 많은 것들에도 적용될 수 있다.&lt;/p&gt;
&lt;p&gt;만약 면접관이 이 질문과 관련된 많은 대답을 할 수 있다면 그 팀은 높은 수준을 가지고 있다는 좋은 신호로 볼 수 있다. 만약 그들이 왜 굳이 그런 지표를 측정해야 하는지 모르겠다고 말한다면, 부정적인 신호로 보면 된다.&lt;/p&gt;
&lt;p&gt;다시 도그마에 대한 법칙이 여기에서 적용된다. 그 팀이 굳이 가치 있고 활용 가능한 정보를 제공하지 않는 데이터를 측정하고 있으면서도 당신에게 만족할 만한 대답을 주지 못한다면, 그것은 경고 신호일 수 있다.&lt;/p&gt;
&lt;p&gt;이어질 수 있는 질문:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;제품의 가장 중요한 지표는 무엇입니까?&lt;/li&gt;
&lt;li&gt;성능 측정 시스템으로 무엇을 사용합니까? (예,  &lt;a href=&quot;https://mixpanel.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MixPanel&lt;/a&gt;, &lt;a href=&quot;https://github.com/etsy/statsd&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;statsd&lt;/a&gt;, 기타)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;7-어떻게-버그를-찾고-수정합니까&quot;&gt;&lt;a href=&quot;#7-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%B2%84%EA%B7%B8%EB%A5%BC-%EC%B0%BE%EA%B3%A0-%EC%88%98%EC%A0%95%ED%95%A9%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. 어떻게 버그를 찾고 수정합니까?&lt;/h3&gt;
&lt;p&gt;좋은 팀은 보통 전용 테스터를 가지고 있으며 개발자는 서비스의 품질에 공을 들인다. 진짜로 수준 높은 팀은 놀랄 만한 테스트 자동화를 갖추고 있다. 어떤 팀은 전용 테스터나 테스트 자동화를 갖추기에는 너무 작지만 그게 나쁜 팀이라는 의미는 아니다. 나는 그 팀이 가진 프로세스가 어떤지 확인하기 위해 이 질문을 한다. 개발자들이 너무 바빠서 다른 일을 할 수 없을 정도인가? 버그를 찾고 우선순위를 매기기 위한 제대로 된 프로세스를 가지고 있는가? 사용자에게 버그를 찾게 놔두고 있는 건 아닌가? 라는 질문에 답을 얻기 위해서다.&lt;/p&gt;
&lt;p&gt;이어질 수 있는 질문:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;버그 수정 우선순위를 어떻게 매깁니까?&lt;/li&gt;
&lt;li&gt;어떤 버그 트래킹 도구를 사용합니까? (그 도구는 어떤 점에서 마음에 들지 않습니까?)&lt;/li&gt;
&lt;li&gt;버그 트래킹에 Excel을 사용합니까? (&lt;a href=&quot;http://www.nooooooooooooooo.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Nooooo~~!&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;현재 버그 트래킹 도구에는 몇 개의 버그가 있습니까?&lt;/li&gt;
&lt;li&gt;버그 수정에 얼마나 걸립니까? (최소/최대/일반적으로)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;8-협업-도구로-무엇을-사용합니까&quot;&gt;&lt;a href=&quot;#8-%ED%98%91%EC%97%85-%EB%8F%84%EA%B5%AC%EB%A1%9C-%EB%AC%B4%EC%97%87%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A9%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. 협업 도구로 무엇을 사용합니까?&lt;/h3&gt;
&lt;p&gt;내 경험상 높은 효율은 가진 팀은 다양한 협업 도구를 사용한다. 그들은 주로 협업을 위해 채팅 서비스(Slack, IRC, HipChat, Jabber)와 코드 리뷰 서비스(Gerrit, GitHub, GitLab, Review Board)를 사용한다. 물론, 유서 깊은 서비스라고 할 수 있는 Email도 사용한다. 나는 이 질문을 통해 개발자들이 서로 하는 일을 알고 있는지 알아보려 한다. 물론 내가 여기서 말하는 ‘안다’의 기준은 일반적인 의미의 ‘안다’이며 서로를 아주 자세하게 속속들이 알고 있다는 의미는 아니다. 또 협업 도구를 잘 활용하고 있는지도 확인한다. 가장 간단한 예는 자동 빌드 시스템에서 실패가 발생했을 때의 이메일 자동 발송 여부 같은 것이다. 그리고 웹 개발팀이라면 시스템에서 심각한 오류가 발생했을 때, 일정 수준 이상의 네트워크 처리량이 발생했을 때 채팅방에 자동으로 알림이 오는 것 같은 사례가 있을 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;9-어떤-프레임워크를-사용합니까&quot;&gt;&lt;a href=&quot;#9-%EC%96%B4%EB%96%A4-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A9%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9. 어떤 프레임워크를 사용합니까?&lt;/h3&gt;
&lt;p&gt;프레임워크에 대한 나의 취향은 2가지로 나뉜다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;나는 현대적인 것을 좋아한다.&lt;/li&gt;
&lt;li&gt;나는 새로운 것을 좋아한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;어떤 팀이 &lt;a href=&quot;https://en.wikipedia.org/wiki/Widget_toolkit&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Motif&lt;/a&gt;로 &lt;a href=&quot;https://en.wikipedia.org/wiki/IBM_AIX&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;AIX&lt;/a&gt; 데스크톱 앱을 만들고 있다면 나는 거기에 흥미를 느끼지 못할 것이다. 하지만 당신은 아닐 수 있다. 이건 무척 개인적인 차이라서 이 주제에는 당신이 가진 취향에 대한 확실한 이해를 바탕으로 접근해야 한다.&lt;/p&gt;
&lt;p&gt;당신이 가진 프레임워크에 대한 취향에는 상관없이, 그 팀이 &lt;strong&gt;왜&lt;/strong&gt; 그 프레임워크를 선택했는지 이해하는 것이 중요하다. 시대를 너무 앞서간 기술을 사용하고 있는가? 그들은 프레임워크를 마치 속옷을 갈아입듯이 바꾸지는 않는가? 그들의 코드가 매달 바뀌는 프레임워크로 인해 산산조각이 나 있지는 않은가? 너무 오래된 프레임워크를 버리지 못하고 있는 건 아닌가?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;왜&lt;/strong&gt;라는 관점에서, 나는 얼마나 많은 개발자가 그들이 사용할 기술을 결정하는 데 관여하고 있는지를 이해하려 한다. 관리자들이 기술 결정 권한을 독점하고 있는가? 관리자들이 개발자들에게 결정을 양보하고 있는가? 이 주제에 대한 답을 확실히 이끌어내기 위해 나는 보통 이런 질문을 한다.  “프레임워크 X를 어떻게 도입하게 되었나요?“. 만약 개발자들이 답을 하지 못한다면 나쁜 신호이거나, 아니면 그들이 그 회사에 온 지 얼마 되지 않아서 잘 모르는 것일 수도 있다.&lt;/p&gt;
&lt;p&gt;나는 개발팀이 그들이 사용하는 오픈소스에 직접 참여(contribute)하는 것을 좋아한다. 이는 그들이 그저 단순히 코드를 사용하기만 하는 것이 아니라 참여를 할 수 있을 만큼 충분히 잘 알고 있다는 말이기 때문이다. 이런 활동을 하는 사람들이 내가 함께 일하고 싶은 사람들이다. 만약 그 회사가 개발자들의 오픈소스 활동을 적극적으로 지원한다면 더욱 좋다. 왜냐하면, 그 회사는 오픈소스 진영의 일부가 된다는 것의 의미를 잘 이해하고 있다는 말이기 때문이다.&lt;/p&gt;
&lt;p&gt;만약 그 팀이 개발에 도움을 줄 수 있는 도구가 이미 있는데도 새로운 도구를 처음부터 새로 만들고 있다면, 나는 걱정을 할 것이다. 물론 여기에는 예외가 있긴 하다. 예를 들어 Facebook은 &lt;a href=&quot;https://facebook.github.io/react/blog/2013/06/05/why-react.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;도구를 직접 만들어서 사용해오고 있었다&lt;/a&gt;. 나는 거기에는 토를 달지 않았으며, 앞으로도 그럴 것이다.&lt;/p&gt;
&lt;h3 id=&quot;10-함께-작업해볼-수-있습니까&quot;&gt;&lt;a href=&quot;#10-%ED%95%A8%EA%BB%98-%EC%9E%91%EC%97%85%ED%95%B4%EB%B3%BC-%EC%88%98-%EC%9E%88%EC%8A%B5%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10. 함께 작업해볼 수 있습니까?&lt;/h3&gt;
&lt;p&gt;만약 그 팀과 일하는 형태가 어떤 것인지 확실히 알고 싶다면, 실제로 함께 일해보길 바란다. 개인적으로 나는 이런 시도를 해 본 적은 없지만, 내 친구는 해 본 적이 있다. 나는 이게 아주 멋진 아이디어라고 본다. 만약 그 팀에 대한 모든 것을 알 수 있길 원한다면, 그 회사에 가서 반나절만 함께 일해보길 바란다. 함께 일해보기 위해 기밀유지 서약서에 사인해야 할 수도 있다. 만약 그 팀이 당신의 제안을 기꺼이 수용한다면 매우 좋은 신호로 볼 수 있다고 생각한다.&lt;/p&gt;
&lt;p&gt;아마 이런 시도를 위해서는 관리자나 경영진의 누군가와 약속을 잡아야 할 것이다. 그래서 이 질문은 개발자들의 반응을 얻어내는 데 더 초점이 맞춰져 있다. 그들은 아마 이 아이디어를 들으면 관리자와 함께해야 한다는 생각에 진저리를 칠 것이다.&lt;/p&gt;
&lt;h3 id=&quot;11-다음-작업-마감은-언제입니까&quot;&gt;&lt;a href=&quot;#11-%EB%8B%A4%EC%9D%8C-%EC%9E%91%EC%97%85-%EB%A7%88%EA%B0%90%EC%9D%80-%EC%96%B8%EC%A0%9C%EC%9E%85%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;11. 다음 작업 마감은 언제입니까?&lt;/h3&gt;
&lt;p&gt;이 질문은 그 회사가 실제로 적용하고 있는 개발 프로세스에 대해 당신에게 더 많은 것을 알려주기 위해 고안되었다. 이 질문 자체로서는 그렇게 많은 정보를 얻을 수 없지만, 여기에 아래의 질문들을 추가한다면 많은 흥미로운 사실을 알 수 있게 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;누가 마감 기한을 결정합니까?&lt;/li&gt;
&lt;li&gt;저번 마감 기한은 지켰습니까? 만약 그러지 못했다면, 이유는 무엇입니까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;높은 수준의 팀은 마감 시간을 지키는 것에 모두 동의한다. 제대로 지켜지지 않는 마감 기한은 그 팀이 제대로 굴러가고 있지 않다는 신호다. 또는 최소한 그 개발자는 일정을 결정하는 회의에 참석하지도 않았다는 의미다&lt;/p&gt;
&lt;h3 id=&quot;12-새로운-개발-환경을-세팅하는데-얼마나-걸립니까&quot;&gt;&lt;a href=&quot;#12-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD%EC%9D%84-%EC%84%B8%ED%8C%85%ED%95%98%EB%8A%94%EB%8D%B0-%EC%96%BC%EB%A7%88%EB%82%98-%EA%B1%B8%EB%A6%BD%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;12. 새로운 개발 환경을 세팅하는데 얼마나 걸립니까?&lt;/h3&gt;
&lt;p&gt;이 질문은 그 회사가 개발자 경험을 위해 얼마나 많은 노력을 하고 있는지를 측정할 수 있게 도와준다. 개발자가 컴퓨터를 세팅해서 코딩을 시작하기까지 몇 시간, 며칠, 몇 주가 걸리는가? 그 과정은 자동화가 되어 있는가 아니면 수동으로 직접 해야 하는가? 이는 그 팀이 소프트웨어 개발에 직접 관련되어 있지는 않지만 도움을 주는 “보조 활동”에 얼마나 능숙한지를 당신에게 말해줄 것이다. 그리고 그런 활동에 얼마나 큰 가치를 두고 있는지도 알려줄 것이다.&lt;/p&gt;
&lt;p&gt;어떤 회사들은 개발 환경 설정 프로세스가 너무나 빨라서 개발자들이 입사 첫날에 코딩을 시작할 수 있다는 사실에 자부심을 가지고 있기도 하다. 이는 그 회사가 개발자들에게 깔끔한 개발 경험을 제공한다는 사실을 무척 중요하게 여기고 있다는 말이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;관리자를-위한-질문&quot;&gt;&lt;a href=&quot;#%EA%B4%80%EB%A6%AC%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%A7%88%EB%AC%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;관리자를 위한 질문&lt;/h2&gt;
&lt;h3 id=&quot;1-언제-마지막으로-코드를-작성해-보셨습니까&quot;&gt;&lt;a href=&quot;#1-%EC%96%B8%EC%A0%9C-%EB%A7%88%EC%A7%80%EB%A7%89%EC%9C%BC%EB%A1%9C-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%B4-%EB%B3%B4%EC%85%A8%EC%8A%B5%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 언제 마지막으로 코드를 작성해 보셨습니까?&lt;/h3&gt;
&lt;p&gt;나는 관리자가 탄탄한 기술 기반이 있길 바란다. MBA 학위를 가진 내 친구들을 나무라는 것은 아니지만, 내가 정말로 좋아하는 관리자들은 내가 하는 일을 해 본 경험이 있는 사람들이라는 사실을 알 수 있었다.&lt;/p&gt;
&lt;h3 id=&quot;2-어떻게-관리자가-되었습니까&quot;&gt;&lt;a href=&quot;#2-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B4%80%EB%A6%AC%EC%9E%90%EA%B0%80-%EB%90%98%EC%97%88%EC%8A%B5%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 어떻게 관리자가 되었습니까?&lt;/h3&gt;
&lt;p&gt;나는 억지로 관리자가 된 사람보다는 그 일을 좋아하고 잘해서 관리자가 되기로 스스로 선택한 사람을 더 좋아한다. 또 그들의 팀을 지원하는 일에 집중하는 관리자를 더 좋아한다. 내가 가장 좋아하는 관리자는 팀원들의 삶이 더 나아지기 위해 노력하는 사람이지 관리 그 자체에만 신경을 쓰는 사람이 아니다. 내가 좋아하는 관리자는 자신이 팀의 조력자이며 보호자라고 생각한다. 그들은 봉사하는 태도를 보이고 있으며 자신의 가장 중요한 일은 팀원들이 하는 일이 더 나아지게 만드는 것으로 생각한다.&lt;/p&gt;
&lt;h3 id=&quot;3-팀원들은-자신이-오늘-무엇을-해야-하는지-어떻게-확인합니까&quot;&gt;&lt;a href=&quot;#3-%ED%8C%80%EC%9B%90%EB%93%A4%EC%9D%80-%EC%9E%90%EC%8B%A0%EC%9D%B4-%EC%98%A4%EB%8A%98-%EB%AC%B4%EC%97%87%EC%9D%84-%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EC%A7%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%99%95%EC%9D%B8%ED%95%A9%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 팀원들은 자신이 오늘 무엇을 해야 하는지 어떻게 확인합니까?&lt;/h3&gt;
&lt;p&gt;이 질문을 개발자에게 했기 때문에 관리자에게도 같은 질문을 해서 답을 비교해 본다. 서로의 답이 일치하지 않는다면 뭔가 잘못된 것이 있다는 의미다. 나는 이런 종류의 불일치를 바로잡는 것은 관리자의 역할이라고 믿는다.&lt;/p&gt;
&lt;h3 id=&quot;4-지금-당신의-팀이-가지고-있는-가장-큰-과제는-무엇입니까&quot;&gt;&lt;a href=&quot;#4-%EC%A7%80%EA%B8%88-%EB%8B%B9%EC%8B%A0%EC%9D%98-%ED%8C%80%EC%9D%B4-%EA%B0%80%EC%A7%80%EA%B3%A0-%EC%9E%88%EB%8A%94-%EA%B0%80%EC%9E%A5-%ED%81%B0-%EA%B3%BC%EC%A0%9C%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 지금 당신의 팀이 가지고 있는 가장 큰 과제는 무엇입니까?&lt;/h3&gt;
&lt;p&gt;관리자들은 보통 인원 부족이 가장 큰 문제라고들 한다. 이는 흔하고 뻔한 대답(결국에는 부족한 사람을 다 고용하게 된다)이기 때문에, 그럼 나는 두 번째로 가장 큰 과제는 무엇이냐고 물어본다. 이 질문을 통해 일정이 지연되고 있거나, 품질 관리에 문제가 있거나, 팀원들 사이의 인간관계에 문제가 있다거나 하는 경고 신호를 찾는다. 어떤 팀이든 문제를 가지고 있다. 그러기에 당신이 받게 될 답변은 다음과 같은 여러 가지 정황에 영향을 받는다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;관리자의 문제에 대한 인지 여부&lt;/li&gt;
&lt;li&gt;관리자가 당신에게 솔직히 대하려는 의지 정도의 차이&lt;/li&gt;
&lt;li&gt;그 팀의 문제의 심각성의 정도&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;5-개인-성과-측정은-어떻게-합니까&quot;&gt;&lt;a href=&quot;#5-%EA%B0%9C%EC%9D%B8-%EC%84%B1%EA%B3%BC-%EC%B8%A1%EC%A0%95%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%A9%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 개인 성과 측정은 어떻게 합니까?&lt;/h3&gt;
&lt;p&gt;뛰어난 매니저는 이를 위한 좋은 기술을 가지고 있다. 가장 훌륭한 매니저는 팀원들의 개인 성과를 측정할 때, 팀 전체로부터 조심스럽게 피드백을 수집한다. 나쁜 매니저는 팀원들의 얘기는 듣지 않고 개인적인 관점에서 판단을 내린다.&lt;/p&gt;
&lt;h3 id=&quot;6-공식적인-성과-리뷰를-가집니까&quot;&gt;&lt;a href=&quot;#6-%EA%B3%B5%EC%8B%9D%EC%A0%81%EC%9D%B8-%EC%84%B1%EA%B3%BC-%EB%A6%AC%EB%B7%B0%EB%A5%BC-%EA%B0%80%EC%A7%91%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. 공식적인 성과 리뷰를 가집니까?&lt;/h3&gt;
&lt;p&gt;나는 팀원들이 성장할 수 있도록 피드백을 주는 일을 중요하게 생각하는 매니저와 함께 일하는 것을 좋아한다. 성과 리뷰는 고통스러울 수도 있고 긍정적인 경험이 될 수도 있다. 내 개인적인 경험에 의하면, 사람 대부분은 그것을 끔찍한 일로 여긴다. 정말로 훌륭한 관리자는 이 점을 잘 알고 있다. 그리고 좋은 방법을 사용해서 개인 성과 리뷰가 팀원에게 좋은 인상을 남길 수 있도록 하며, 자신과 함께 일하고 싶어지게 만들 것이다.&lt;/p&gt;
&lt;p&gt;이어질 수 있는 질문:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;개인 성과 향상을 위해 도움을 줬던 일에 관해서 얘기해 주실 수 있을까요?&lt;/li&gt;
&lt;li&gt;리뷰를 하는 동안 사람들에게 어떤 식으로 조언합니까?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;7-해마다-연봉을-올려주고-있습니까&quot;&gt;&lt;a href=&quot;#7-%ED%95%B4%EB%A7%88%EB%8B%A4-%EC%97%B0%EB%B4%89%EC%9D%84-%EC%98%AC%EB%A0%A4%EC%A3%BC%EA%B3%A0-%EC%9E%88%EC%8A%B5%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. 해마다 연봉을 올려주고 있습니까?&lt;/h3&gt;
&lt;p&gt;나는 내가 회사에 이바지를 한 만큼 보상을 받을 수 있는지 알고 싶다. 이 질문에 긍정적인 답을 주는 회사라면, 나는 형평성에 대해 질문한다. 나에게 스톡옵션을 줄 것입니까? 매년 스톡옵션을 더 지급해 줄 것입니까?&lt;/p&gt;
&lt;p&gt;어떤 개발자들은 이런 금전적인 질문을 하는 것을 불편해한다. 그러지 마라. 일반적으로 개발자들은 이 주제에 대한 생각을 너무 적게 한다. 하지만 관리자들은 이런 종류의 대화를 &lt;strong&gt;일하는 내내&lt;/strong&gt; 한다. 이런 질문들은 관리자에게는 불편한 것이 아니어야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;임금 인상을 위한 예산은 어떻게 잡습니까?&lt;/li&gt;
&lt;li&gt;지난해 당신의 팀의 연봉 인상 금액의 중간값은 얼마입니까? (또는 인상 비율)&lt;/li&gt;
&lt;li&gt;1년 후 저는 얼만큼의 연봉 인상을 기대할 수 있습니까? 최고와 최악의 경우는 어떻게 됩니까?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;나는 이 질문을 연봉 인상에 대한 확실한 보장을 받기 위해서 하는 것이 아니다. 대신 그 회사가 어떻게 연봉을 관리하는지를 이해하기 위해서 한다. 이 회사에서는 연봉 인상을 위해 내 방식대로 밀고 나가야 할 것인가? 아니면 이미 표준화된 프로세스가 정립되어 있는가?&lt;/p&gt;
&lt;h3 id=&quot;8-회사의-복지를-설명해주는-자료를-가져갈-수-있을까요&quot;&gt;&lt;a href=&quot;#8-%ED%9A%8C%EC%82%AC%EC%9D%98-%EB%B3%B5%EC%A7%80%EB%A5%BC-%EC%84%A4%EB%AA%85%ED%95%B4%EC%A3%BC%EB%8A%94-%EC%9E%90%EB%A3%8C%EB%A5%BC-%EA%B0%80%EC%A0%B8%EA%B0%88-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C%EC%9A%94&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. 회사의 복지를 설명해주는 자료를 가져갈 수 있을까요?&lt;/h3&gt;
&lt;p&gt;나는 사람 대부분이 이 질문은 알고 있다고 생각하지만 완벽함을 위해 언급할 가치가 있을 것 같다. 회사 대부분은 그들의 복지를 소개해주는 한 뭉치의 서류를 건네주거나 웹 사이트를 소개해 줄 것이다. 복지 혜택에 대해 알고 있는 것은 중요하다. 왜냐하면, 당신이 얻는 보상에서 무척 큰 부분을 차지하기 때문이다. 이 질문은 미국에서만 해당하는 사항일지도 모른다. 나는 다른 나라에서는 기업에서 제공되는 의료보험이 어떻게 작동하는지 잘 모른다.&lt;sup id=&quot;fnref-3&quot;&gt;&lt;a href=&quot;#fn-3&quot; class=&quot;footnote-ref&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;/p&gt;
&lt;h3 id=&quot;9-당신은-직원들의-순위를-매깁니까&quot;&gt;&lt;a href=&quot;#9-%EB%8B%B9%EC%8B%A0%EC%9D%80-%EC%A7%81%EC%9B%90%EB%93%A4%EC%9D%98-%EC%88%9C%EC%9C%84%EB%A5%BC-%EB%A7%A4%EA%B9%81%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9. 당신은 직원들의 순위를 매깁니까?&lt;/h3&gt;
&lt;p&gt;어떤 회사들은 급여 인상과 보너스를 직원들의 순위를 매겨서 결정한다. 직원들의 순위로 정렬된 긴 목록을 일정 비율로 “좋음”, “평균”, “나쁨”으로 잘라버린 후 결정하는 식이다.&lt;/p&gt;
&lt;p&gt;나는 저런 방식을 좋아하는 개발자를 만나 본 적이 없다. 큰 회사에서는 이런 방식을 일반적으로 사용한다. 저 방법은 당신에게 함께 일하는 동료가 언젠가는 보수를 위해 경쟁해야 할 상대라는 인식을 심어줘서 직원 사이의 소통에 문제가 생기게 만들 수 있다.&lt;/p&gt;
&lt;p&gt;면접을 보는 회사가 저런 정책을 시행하고 있다고 해서 그 회사가 일하기에 끔찍한 곳이라고 판단해야 한다는 의미는 아니다. 저런 회사에서는 다른 생각을 하는 멋진 사람들이 조금씩 있게 마련이다. 면접관들에게 저런 정책을 어떻게 생각하느냐고 물어보라. 어떤 관리자는 꽤 솔직하게 회사의 시스템에 대한 비호감을 드러내기도 할 것이다. 그리고 몇몇은 자신의 팀을 위해 어떻게 시스템을 잘 활용하는지 알려주기도 한다. 만약 그런 관리자를 만난다면 직원들의 순위를 매기는 시스템은 눈감아 줄 수도 있을 것이다.&lt;/p&gt;
&lt;p&gt;그리고 모든 사람이 저런 시스템을 싫어하지 않는다는 사실을 명심해야 한다. 내가 어떤 종류의 사람들을 만나지 못했다고 해서 아예 그런 사람이 없는 것은 아니기 때문이다.&lt;/p&gt;
&lt;p&gt;이어질 수 있는 질문:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;당신은 정말로 “X%“의 직원들이 “나쁨”이라고 생각합니까? 그들은 당신의 채용 프로세스에 대해서 뭐라고 얘기하나요? (이는 다소 대담한 질문이다. 신중히 하길 바란다)&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;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;10-주기적인-팀-회고를-진행합니까&quot;&gt;&lt;a href=&quot;#10-%EC%A3%BC%EA%B8%B0%EC%A0%81%EC%9D%B8-%ED%8C%80-%ED%9A%8C%EA%B3%A0%EB%A5%BC-%EC%A7%84%ED%96%89%ED%95%A9%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10. 주기적인 팀 회고를 진행합니까?&lt;/h3&gt;
&lt;p&gt;만약 한다면, 어떤 방식입니까? 팀원들에게 얼마나 자주 피드백을 받습니까? 언제 마지막으로 직원들의 피드백을 바탕으로 당신의 관리 방식을 조정했습니까?&lt;/p&gt;
&lt;p&gt;이런 질문들은 관리자가 직원들에게 얼마나 민감하게 반응하는지, 그리고 직원들은 얼마나 피드백을 잘 전달하면서 관리자와 소통하는지를 알 수 있도록 해준다.&lt;/p&gt;
&lt;p&gt;회고가 이루어지지 않는다면 팀에게 있는 문제를 파악하기 어려우며 문제 해결을 위해 홀로 노력해야 한다. 그리고 만약 관리자가 팀의 발전을 위해 피드백을 받는 것을 좋아하지 않는다면, 그 팀은 아마도 문제가 있어도 모른 척하거나 피드백을 주는 것에 부담을 느끼는 분위기가 조성되어 있을 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;경영진을-위한-질문&quot;&gt;&lt;a href=&quot;#%EA%B2%BD%EC%98%81%EC%A7%84%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%A7%88%EB%AC%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;경영진을 위한 질문&lt;/h2&gt;
&lt;p&gt;면접을 볼 때 항상 회사의 고위층과 얘기를 나눌 수 있는 것은 아니지만, 특히 큰 회사라면 더욱, 그래도 만약 그런 기회가 주어진다면 나는 그것을 그 회사의 재정적 생존력을 판단할 기회로 삼는다. 나는 그런 판단을 할 자격이 있는 사람은 아니지만, 면접을 통해 파악할 수도 있는 명백한 문제들이 있다. 그리고 나는 회사의 수직 문화가 어떠한지 알고 싶어한다. 왜냐하면, 그 정보는 회사가 개발자를 어떻게 평가하는지 알 수 있기 때문이다. 그것이 긍정적인 방향이든, 부정적인 방향이든 말이다.&lt;/p&gt;
&lt;h3 id=&quot;1-어떻게-투자를-받았습니까&quot;&gt;&lt;a href=&quot;#1-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%88%AC%EC%9E%90%EB%A5%BC-%EB%B0%9B%EC%95%98%EC%8A%B5%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 어떻게 투자를 받았습니까?&lt;/h3&gt;
&lt;p&gt;나는 회사를 지탱하는 자금의 실체를 파악하려고 한다. 자금을 조달한 곳이 벤처캐피털인지, 사모펀드인지, 주식인지, 회사의 이윤에 의한 것인지 알고 싶기 때문이다. 인터뷰 전에 쉽게 찾아볼 수 있는 내용이기도 하지만, 회사 고위층은 내가 구글이나 CrunchBase를 통해서는 알 수 없는 내용을 알려줄 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;2-수익이-나고-있습니까&quot;&gt;&lt;a href=&quot;#2-%EC%88%98%EC%9D%B5%EC%9D%B4-%EB%82%98%EA%B3%A0-%EC%9E%88%EC%8A%B5%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 수익이 나고 있습니까?&lt;/h3&gt;
&lt;p&gt;만약 그렇다면, 문제없다! 만약 아니라면, “수익을 내기 위한 어떤 계획을 하고 있습니까?”라는 질문을 할 수 있을 것이다. 어떤 스타트업들은 수익을 내기 위한 계획을 하는 반면, 어떤 곳은 합병이나 IPO를 할 방법을 찾고 있기도 하다.&lt;/p&gt;
&lt;p&gt;이어질 수 있는 주제:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;지난 몇 분기/년 동안의 수익 증가 추이&lt;/li&gt;
&lt;li&gt;수익에 위험이 될 수 있는 경쟁자, 가격, 적자&lt;/li&gt;
&lt;li&gt;추가 자금을 조달하기 전까지 회사가 운영될 수 있는 기간&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-외주에-대해서는-어떻게-생각합니까&quot;&gt;&lt;a href=&quot;#3-%EC%99%B8%EC%A3%BC%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%83%9D%EA%B0%81%ED%95%A9%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 외주에 대해서는 어떻게 생각합니까?&lt;/h3&gt;
&lt;p&gt;나는 내가 지원하는 직무가 장차 외주화될 수 있는지, 또는 외주 개발자를 관리하는 직무로 변경될 가능성이 있는지 알고 싶다. 여기서 말하는 것은 해외 외주만이 아니며 계약자도 포함한다.&lt;/p&gt;
&lt;h3 id=&quot;4-기업-문화에-대해서-알려주십시오&quot;&gt;&lt;a href=&quot;#4-%EA%B8%B0%EC%97%85-%EB%AC%B8%ED%99%94%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%95%8C%EB%A0%A4%EC%A3%BC%EC%8B%AD%EC%8B%9C%EC%98%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 기업 문화에 대해서 알려주십시오.&lt;/h3&gt;
&lt;p&gt;이 질문은 경영진의 관점과 개발자의 관점의 차이를 조정하기 위해 사용하는 또 다른 방법이다. 관점의 차이는 문제의 신호가 될 수 있기 때문이다. 만약 경영진과 개발자가 같은 대답을 한다면 상하 소통이 잘 되고 있다고 볼 수 있다. 나는 고위층과 말단 직원들 사이에 접점이 있는지 알고 싶다. 그리고 경영진이 미래에 대한 비전을 잘 공유하고 있는지도 알고 싶다. 내가 가장 일하고 싶은 회사는 경영진과 직원들이 뚜렷한 비전을 공유하고 있는 곳이다.&lt;/p&gt;
&lt;h3 id=&quot;5-이-회사의-성공에-대해-어떤-확신이-있습니까&quot;&gt;&lt;a href=&quot;#5-%EC%9D%B4-%ED%9A%8C%EC%82%AC%EC%9D%98-%EC%84%B1%EA%B3%B5%EC%97%90-%EB%8C%80%ED%95%B4-%EC%96%B4%EB%96%A4-%ED%99%95%EC%8B%A0%EC%9D%B4-%EC%9E%88%EC%8A%B5%EB%8B%88%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 이 회사의 성공에 대해 어떤 확신이 있습니까?&lt;/h3&gt;
&lt;p&gt;이 질문을 통해서는 영업을 위한 수사가 아닌 진짜 근거를 얻으려 한다. 만약 경영진이 수익, 시장 규모, 자본과 관련된 실제 수치를 들려준다면 이는 좋은 신호다. 또 이 수치를 다른 소스를 통해 검증할 수 있다면 훨씬 더 좋은 신호다. 하지만 그 수치들이 매우 좋지 못할 수도 있다.&lt;/p&gt;
&lt;h3 id=&quot;6-보고-구조에-대해서-알려주십시오&quot;&gt;&lt;a href=&quot;#6-%EB%B3%B4%EA%B3%A0-%EA%B5%AC%EC%A1%B0%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%95%8C%EB%A0%A4%EC%A3%BC%EC%8B%AD%EC%8B%9C%EC%98%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. 보고 구조에 대해서 알려주십시오.&lt;/h3&gt;
&lt;p&gt;나에게 가장 좋은 대답은 단순한 형태다. 간단한 조직도를 그리면서 보고 구조를 설명할 수 있다면 나는 만족한다. 개인적으로 선호하는 업무 형태는 구조적인 소통에 드는 비용이 적은 작고 민첩한 회사에서 일하는 것이다. 당신이 선호하는 형태는 다를 수 있다. 그것은 그대로 괜찮으며, 이 질문은 회사에 대해 정보에 기반을 둔 판단을 내리는 데 필요한 정보를 얻는 데 도움을 주기 위해서 고안되었다.&lt;/p&gt;
&lt;h2 id=&quot;결론&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;
&lt;p&gt;면접은 양방향이다. 합격이라는 좋은 결과가 있을 수 있으니 면접을 볼 때 회사에 대한 제대로 된 판단을 내리기 위한 모든 정보를 얻도록 하자. 행운을 빈다!&lt;/p&gt;
&lt;div class=&quot;footnotes&quot;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;Linus Torvalz, 리눅스와 Git을 만든 개발자&lt;/p&gt;
&lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;dogma, 독단적인 신념 또는 가치관&lt;/p&gt;
&lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;미국은 높은 의료비로 유명하며 의료보험비도 연간 수만 달러에 이른다&lt;/p&gt;
&lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot;&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[구글 애널리틱스 API를 사용한 Flask 앱을 uWSGI와 nginx로 배포한 과정]]></title><description><![CDATA[
 이미지 출처:  Link 포스트 통계 기능을 위해 사용한 Google Analytics 서비스 API 이 블로그는 CMS 서비스나 별도의 백엔드 서비스가 연결되어 있지 않은 웹사이트라서 따로 관리하고 있는 통계 데이터가 없다. 대신 구글 애널리틱스는 처음부터 연결…]]></description><link>https://blog.rhostem.com//posts/2018-11-20-deploy-flask-with-uwsgi</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-11-20-deploy-flask-with-uwsgi</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Tue, 20 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/17iEP9ldkwCW2Euia2UU2q/9760566e57a1124264ee4918324761b4/main.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 29.625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAMCAMAAAAnHQ9/AAABPlBMVEX////S0tLGxsbHx8fExMTe3t69vb2cnJyvr6/R0dH4+PjFxcXCwsK6urrPz8/zzcrwv7v19fX5+fnNzc3IyMiipITFt17y8OHKysr6+vry8vLO5MDs6I7q6urBwcHU1NTb29vx8fHv7+/i4uLt7e3Kk4PVl2Py8erZ2dnj4+PD27zE27zM4MXI3cHJ3sLZ49bg4OD29vb5+/G/v7uwr7Cnp6a8u7zBwcKysrLJycnQ0NDLy8uxsbGfyJTQz1Tt7OTX19fh4eHr6+vN4Ma61bG206271bPJ3sPI3sHX4NT09PTl7MGmqZeura+bmpuhoaKYl5nk5OSTk5Puv7rrrqXc3NzW1tbm5ubV1dXp6enAwMDo6Oju7u6crofGxVzu7N739/f8/Pzd3d3l5eXMzMzY2Njb7NHy76u+vr7s7OzYL/72AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAOxJREFUGBl9wddCgzAABdALCVppHSAutJYRFeoKbqh7722LW+v+/x8wVB815+CXohIZqqGpOdcCHflCrrUN/6MQ2jsMsxNyFoSu7h7a22dDhkDoHzCKgyUHf3ENzzfBQCAMDY8E+ZCVR8fGyxOTvsejqemZ2bl5zfO0BRonFXXRJhCWlldW1bX1jc2t7Z3dPc60/YPDo+OTU4cx5+ycuxVCQgLh4lIvFkpXyPCgGiRahEwtVa7h39zad25EINw/PNKYhU8QnutVJ31Bxqu8viU1/CAQ3j9004qVEBIWGgx81vkXJCgadKQqkaH8G2K8H2eM4o+CAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;main&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/17iEP9ldkwCW2Euia2UU2q/9760566e57a1124264ee4918324761b4/main.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/17iEP9ldkwCW2Euia2UU2q/9760566e57a1124264ee4918324761b4/main.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/17iEP9ldkwCW2Euia2UU2q/9760566e57a1124264ee4918324761b4/main.png?w=800 800w,
https://images.ctfassets.net/rpmifyuylbfw/17iEP9ldkwCW2Euia2UU2q/9760566e57a1124264ee4918324761b4/main.png?w=1600 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;이미지 출처: &lt;a href=&quot;https://hackernoon.com/a-guide-to-scaling-machine-learning-models-in-production-aa8831163846&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Link&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;포스트-통계-기능을-위해-사용한-google-analytics-서비스-api&quot;&gt;&lt;a href=&quot;#%ED%8F%AC%EC%8A%A4%ED%8A%B8-%ED%86%B5%EA%B3%84-%EA%B8%B0%EB%8A%A5%EC%9D%84-%EC%9C%84%ED%95%B4-%EC%82%AC%EC%9A%A9%ED%95%9C-google-analytics-%EC%84%9C%EB%B9%84%EC%8A%A4-api&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;포스트 통계 기능을 위해 사용한 Google Analytics 서비스 API&lt;/h2&gt;
&lt;p&gt;이 블로그는 CMS 서비스나 별도의 백엔드 서비스가 연결되어 있지 않은 웹사이트라서 따로 관리하고 있는 통계 데이터가 없다. 대신 구글 애널리틱스는 처음부터 연결해 뒀기 때문에 페이지뷰를 확인하는 것은 가능하다. 저번에 블로그 검색 기능을 추가했으니, 이번에는 인기 포스트를 보여주는 기능을 추가하고 싶어서 &lt;a href=&quot;https://developers.google.com/analytics/devguides/reporting/core/v4/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;구글 애널리틱스 Reporting API&lt;/a&gt;의 힘을 빌리기로 했다. 그런데… 서비스 애플리케이션으로 지원하는 언어로 Java, Python, PHP를 지원하고 자바스크립트는 없는 게 아닌가? 자바스크립트가 백엔드 언어로 활용되기 시작한 역사가 오래되지 않긴 했지만 그래도 node.js를 만든 구글에서 지원하지 않는다는 건 조금 아쉬웠다. 그래서 공부도 할겸 파이썬으로 개발을 진행하기로 했다.&lt;/p&gt;
&lt;h3 id=&quot;서비스-애플리케이션을-위한-인증-시나리오&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%9D%B8%EC%A6%9D-%EC%8B%9C%EB%82%98%EB%A6%AC%EC%98%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스 애플리케이션을 위한 인증 시나리오&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/identity/protocols/OAuth2&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;구글 API를 사용하기 위한 OAuth 2.0 인증 시나리오&lt;/a&gt;에는 서비스 애플리케이션, 설치형 앱, 웹 애플리케이션이 있다.&lt;/p&gt;
&lt;p&gt;서비스 애플리케이션은 백엔드(내가 구현한 앱)와 백엔드(구글 애널리틱스 API) 사이에서 인증을 진행한다. 따라서 웹사이트를 사용하는 엔드 유저는 인증을 하지 않아도 된다. 이 블로그에서는 비인증 유저에게 통계 데이터를 제공해야 하므로 나는 서비스 애플리케이션에 해당하는 시나리오로 인증을 구현했다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3E16zrN7Y4CgEeOGm0Ig0y/99c733e32ef92b4ffe83aafd65cb859f/serviceaccount.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 325px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 92.92307692307692%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAlCAAAAAAVC/RvAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4bG1nergAAAblJREFUOMvFlNlOwkAUhn1kn8FX0MQLoxcaXCPGRIOaCG5Q9i4UWpkKBSuUddrSspaOFJSytNDEC/+LmYvz5Zz8c/7MBnKpDSTDqdpzpbpVgI0RCEuBJIiLd3gVwTmwds9kQhHwSjAxGpqgTDHFpIgHKgtgk0rTN0SdyGVJYIKq2ZofD+jPgRK0KtIIHAsaTibg1IwpOofJDqA0CxoRxANUzuB0pEZT7AowjHg/X2JT4WQ1mBFWg6M+KexZvvU8orVgicw2eKiuB12Y+Ts4TAFKXwJBpVnD3jluYIFtCiWUxeepvD350zhb9n/wU23HUVRa7PgFQDGboxIcZ3UkcBvw8+XxDcsT2AtjgRphA9rpP0Ct3HUAw+OEGkNdH/R73U7Hu398rytidAncxSVTstJStXan2x/o/aGhK/ia0ezpzrn3dG/gwkx3cp5tHWpuXBtaQTXcxQy6jBmZxpdi1hAVuVFtaeWeKjR/Y0Yux0zIB7zU9cMJ6c/6rgRhErPNmK3rPFco0jLhKzmtEAU9Bx+jqxAI+S5BkBWthF8ceWZGIznMtO3NFIP5GTNo4cNz/qR+9mAndR5cr2+EhIJ1pEb1CgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;serviceaccount&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3E16zrN7Y4CgEeOGm0Ig0y/99c733e32ef92b4ffe83aafd65cb859f/serviceaccount.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3E16zrN7Y4CgEeOGm0Ig0y/99c733e32ef92b4ffe83aafd65cb859f/serviceaccount.png?w=81 81w,
https://images.ctfassets.net/rpmifyuylbfw/3E16zrN7Y4CgEeOGm0Ig0y/99c733e32ef92b4ffe83aafd65cb859f/serviceaccount.png?w=163 163w,
https://images.ctfassets.net/rpmifyuylbfw/3E16zrN7Y4CgEeOGm0Ig0y/99c733e32ef92b4ffe83aafd65cb859f/serviceaccount.png?w=325 325w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;서비스 애플리케이션 인증 시나리오&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;웹 애플리케이션은 엔드 유저에게 로그인을 요구한다. 이 시나리오는 소셜 로그인이나 SNS 공유를 하려고 할 때 해당 서비스에 로그인을 요구하는 사례에 해당한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3oHIMIJ2OsMuIiOi0228sq/346802645b6bfd198a107af783327968/webflow.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 364px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 103.57142857142856%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAApCAAAAABiyTQUAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4chT1LDQAAAiNJREFUOMul1c9vEkEUB3D+bi+ePGpqvKg3tTFRaVraiG1KCT8NuAgFammQH11mU1hnl9lZYH8x8PXQpQW6azfx3Wbnk7w3781kY4gYMQBzOuaccc4n6zu6xrnBNc45133o2sWjfDqRB1+H/FMqGa/Fv5TjBe5D749BVSYTvgEp1ZS+SimjmuZDADBFYGVso0YAy1bfCnD21bWzCU0ZFQAQGgAhPH+3Cbe5CamMOCGEtA/jB/Hdxvv9Y0oIyT+DW34AM/4XZy21BLd0DynfgAiDIldchsEGvNodtBOSbZEr68Raj+F4OJ4qbDRw9DHXhkAMaDum3q7rR/p61G9+nCjpRKHZqmZz+QsgBsiD0BoVCHKXmnaGdqTD9MppcQ8HdmumUmJaAe3x+/ikYTDGK8VC/nv27Ntowg3GckFwldq+zEmr1JV/TUb0e8MVHO28IqFwPZZjexkJYiYQDVph8LzalCrlulzJaACwqHdZ8H0ckbZCSop63VcJIfXXBKGn7uydn747Nm4XKkE2Uo3/CRcAZDZ1hGe7nitC4dMqZ4ykDs9+HX9MZvY7psFY95HUq2nAeLPTjDZCk4pok5lPIo5QmCGQzeFYgAPoAKDI3ZtgWPpaSB7sdj839iQAswJcKWjWC2Ba/92r6akUAI/8fP7gKWgd97Zh7mVNyvYu/DtTcSdbl2L24QUJOAt9+3K4CSG0ZVAfPX3rKQCz4L+Gh234WPwFvxcb3k6MkCEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;webflow&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3oHIMIJ2OsMuIiOi0228sq/346802645b6bfd198a107af783327968/webflow.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3oHIMIJ2OsMuIiOi0228sq/346802645b6bfd198a107af783327968/webflow.png?w=91 91w,
https://images.ctfassets.net/rpmifyuylbfw/3oHIMIJ2OsMuIiOi0228sq/346802645b6bfd198a107af783327968/webflow.png?w=182 182w,
https://images.ctfassets.net/rpmifyuylbfw/3oHIMIJ2OsMuIiOi0228sq/346802645b6bfd198a107af783327968/webflow.png?w=364 364w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;웹 애플리케이션 인증 시나리오&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;그리고 나머지 하나는 설치형 애플리케이션을 위한 시나리오인데, 안드로이드, iOS 앱 및 기타 설치형 애플리케이션에 해당하는 시나리오다.&lt;/p&gt;
&lt;h2 id=&quot;flask-프레임워크&quot;&gt;&lt;a href=&quot;#flask-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Flask 프레임워크&lt;/h2&gt;
&lt;p&gt;인기 있는 파이썬 프레임워크로는 Django, Flask가 있는데 Django는 지원하는 기능이 무척 많고 덩치가 크다. 나는 최소한의 기능만 있으면 되기에 가벼움 그 자체인 Flask를 선택했다. Flask 홈페이지에도 &lt;a href=&quot;http://flask.pocoo.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“Flask is a microframework for Python”&lt;/a&gt;이라고 소개되어 있을 정도다.&lt;/p&gt;
&lt;p&gt;처음 시도한 프레임워크였기에 환경 설정, 에디터 설정, 패키지 관리 등 개발 외적인 작업에 들어간 시간이 많았지만 필요한 기능 자체는 시작일, 종료일과 컨텐츠 경로를 기준으로 한 페이지뷰 카운트 데이터를 가져오는 것 하나여서 아주 오랜 시간이 걸리지는 않았다. 그리고 개발을 진행하다 보니, 파이썬은 문법이 무척 간결해서 익숙해지기만 한다면 무척 사용하기 즐거울 것 같았다. 언젠가 파이썬으로 더 많은 것을 해 보고 싶어졌다.&lt;/p&gt;
&lt;h2 id=&quot;uwsgi로-파이썬-앱-배포하기&quot;&gt;&lt;a href=&quot;#uwsgi%EB%A1%9C-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%95%B1-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;uWSGI로 파이썬 앱 배포하기&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9B%B9_%EC%84%9C%EB%B2%84_%EA%B2%8C%EC%9D%B4%ED%8A%B8%EC%9B%A8%EC%9D%B4_%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;WSGI&lt;/a&gt;는 Web Server Gateway Inerface의 약자로서, 웹 서버(etc. nginx)와 파이썬 웹 애플리케이션 사이의 통신을 위한 프로토콜이다. 과거에는 파이썬 앱이 웹 서버와 통신하기 위한 선택지에 제한이 있었기에 보다 다양한 방법으로 통신하기 위해 만들어졌다고 한다.&lt;/p&gt;
&lt;p&gt;Flask로 만든 앱은 WSGI 스펙을 기반으로 한 앱이 된다. 그런데 nginx 웹 서버는 WSGI 스펙을 구현해두지 않았기 때문에 Flask 앱과 직접 통신할 수가 없다. 그래서 &lt;a href=&quot;https://uwsgi-docs.readthedocs.io/en/latest/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;uWSGI&lt;/a&gt; 서버를 중간에 둔다. uWSGI는 WSGI 스펙을 구현한 서버인 동시에 프로토콜이며, nginx와도 통신할 수 있다. uWSGI가 nginx와 Flask 앱 중간에서 미들웨어 역할을 하게 되는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;uwsgi-엔트리-포인트-작성&quot;&gt;&lt;a href=&quot;#uwsgi-%EC%97%94%ED%8A%B8%EB%A6%AC-%ED%8F%AC%EC%9D%B8%ED%8A%B8-%EC%9E%91%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;uWSGI 엔트리 포인트 작성&lt;/h3&gt;
&lt;p&gt;기존의 앱 메인 파일 외에 uWSGI 서버 실행을 위한 엔트리 포인트 역할을 할 모듈을 별도로 추가한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# wsgi.py&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; app &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; app

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;uwsgi-서버-실행&quot;&gt;&lt;a href=&quot;#uwsgi-%EC%84%9C%EB%B2%84-%EC%8B%A4%ED%96%89&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;uWSGI 서버 실행&lt;/h3&gt;
&lt;p&gt;uwsgi를 설치한 후 엔트리 포인트로 서버를 구동한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sg&quot;&gt;&lt;pre class=&quot;language-sg&quot;&gt;&lt;code class=&quot;language-sg&quot;&gt;uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;커맨드라인으로 실행도 가능하지만 역시 설정파일을 따로 두는 것이 좋다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ini&quot;&gt;&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;# myproject.ini
&lt;span class=&quot;token selector&quot;&gt;[uwsgi]&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; wsgi:app&lt;/span&gt;

&lt;span class=&quot;token constant&quot;&gt;master&lt;/span&gt; &lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; true&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;processes&lt;/span&gt; &lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; 5&lt;/span&gt;

&lt;span class=&quot;token constant&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; blogapi.sock # uWSGI가 만드는 socket 파일의 이름. nginx와 연결할 때 사용된다.&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;chmod-socket&lt;/span&gt; &lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; 660&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;vacuum&lt;/span&gt; &lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;설정파일을 만든 후에는 아래와 같은 방식으로 실행한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;uwsgi —ini blogapi.ini&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;실행하면 앱을 실행한 위치에 &lt;code class=&quot;language-text&quot;&gt;blogapi.sock&lt;/code&gt;라는 이름의 소켓 파일이 만들어질 것이다.&lt;/p&gt;
&lt;h3 id=&quot;nginx-서버에-uwsgi-소켓-연결&quot;&gt;&lt;a href=&quot;#nginx-%EC%84%9C%EB%B2%84%EC%97%90-uwsgi-%EC%86%8C%EC%BC%93-%EC%97%B0%EA%B2%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;nginx 서버에 uWSGI 소켓 연결&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;nginx에 가상 호스트를 추가하고 uWSGI 소켓을 연결&lt;/a&gt;한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;nginx&quot;&gt;&lt;pre class=&quot;language-nginx&quot;&gt;&lt;code class=&quot;language-nginx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# /etc/nginx/sites-enabled/blogapi&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;server_name&lt;/span&gt; blogapi&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rhostem&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# uWSGI가 만든 소켓 파일을 연결한다.&lt;/span&gt;
        uwsgi_pass unix&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;home&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ubuntu&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;www&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;blogapi&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;blogapi&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sock&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; uwsgi_params&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;nginx 서버를 재시작하면 이제 &lt;code class=&quot;language-text&quot;&gt;http://blogapi.rhostem.com&lt;/code&gt;을 통해 앱에 접속할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;flask-앱-자동-시작을-위한-시스템-서비스-등록&quot;&gt;&lt;a href=&quot;#flask-%EC%95%B1-%EC%9E%90%EB%8F%99-%EC%8B%9C%EC%9E%91%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%93%B1%EB%A1%9D&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Flask 앱 자동 시작을 위한 시스템 서비스 등록&lt;/h3&gt;
&lt;p&gt;서버가 재시작될 때 Flask 앱도 자동으로 시작하도록 시스템 서비스 파일을 만들어서 등록하면 앱 관리가 더욱 편해진다. ubuntu의 시스템 서비스 파일은 &lt;code class=&quot;language-text&quot;&gt;/etc/systemd/system&lt;/code&gt; 폴더에 만든다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;systemd&quot;&gt;&lt;pre class=&quot;language-systemd&quot;&gt;&lt;code class=&quot;language-systemd&quot;&gt;# /etc/systemd/system/blogapi.service

[Unit]
Description=uWSGI instance to serve flask app blog-api
After=network.target

[Service]
User=ubuntu # 로그인 사용자
Group=www-data
WorkingDirectory=/home/ubuntu/www/blogapi # 실행 디렉토리
Environment=&amp;quot;PATH=/home/ubuntu/www/blogapi/venv/bin&amp;quot; # 환경 변수
ExecStart=/home/ubuntu/www/blogapi/venv/bin/uwsgi --ini blogapi.ini # 실행 명령어

[Install]
WantedBy=multi-user.target # 자동 실행&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서비스 실행 명령어는 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl start blogapi
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token function&quot;&gt;enable&lt;/span&gt; blogapi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;그-외의-작업들&quot;&gt;&lt;a href=&quot;#%EA%B7%B8-%EC%99%B8%EC%9D%98-%EC%9E%91%EC%97%85%EB%93%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그 외의 작업들&lt;/h2&gt;
&lt;p&gt;이 외에도 파이썬 프로젝트를 위한 virtualenv 설정, pip로 패키지 관리하기, CORS 설정, blog.rhostem.com이 https 프로토콜을 사용하기 때문에 Certbot으로 무료 인증서 설치하기 등의 작업이 필요했다. 이번에 구현한 파이썬 서버는 아래 링크된 저장소에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rhostem/blog-api&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;rhostem/blog-api: api service for blog&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고-자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/38601440/what-is-the-point-of-uwsgi&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;python - What is the point of uWSGI? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/identity/protocols/OAuth2#serviceaccount&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Using OAuth 2.0 to Access Google APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-uswgi-and-nginx-on-ubuntu-18-04&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;How To Serve Flask Applications with uWSGI and Nginx on Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://askubuntu.com/questions/894665/add-startup-service-on-16-04&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;add startup service on 16.04 - Ask Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[자바스크립트 정규표현식의 capturing group]]></title><description><![CDATA[정규표현식 정규표현식 은 문자열을 표현할 수 있는 패턴이다. 이메일, 전화번호 등의 유효성을 검증하거나, 문자열에서 원하는 부분을 추출하기 위한 용도로 사용된다. 예를 a로 시작하는 영어 단어의 패턴은 아래와 같다. 정규표현식은 자바스크립트 객체이며  ,   등 정규…]]></description><link>https://blog.rhostem.com//posts/2018-11-11-regex-capture-group</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-11-11-regex-capture-group</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 11 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정규표현식&quot;&gt;&lt;a href=&quot;#%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;정규표현식&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/RegExp&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;정규표현식&lt;/a&gt;은 문자열을 표현할 수 있는 패턴이다. 이메일, 전화번호 등의 유효성을 검증하거나, 문자열에서 원하는 부분을 추출하기 위한 용도로 사용된다. 예를 a로 시작하는 영어 단어의 패턴은 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token regex&quot;&gt;/^a[a-z]*$/i&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;정규표현식은 자바스크립트 객체이며 &lt;code class=&quot;language-text&quot;&gt;test&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;exec&lt;/code&gt; 등 정규표현식을 활용하기 위한 여러 가지 메소드를 가지고 있다. 객체(Object)를 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드 없이 중괄호(&lt;code class=&quot;language-text&quot;&gt;{}&lt;/code&gt;, brace)만으로 선언 가능한 것처럼, 정규표현식도 위에서 제시한 것처럼 슬래쉬(/) 사이에 패턴을 입력하는 것으로 만들 수도 있고 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드를 사용해서 만들 수도 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; startWithA &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Regexp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;^a[a-z]*$&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;i&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;정규표현식은 특수한 의미가 있는 기호들을 잘 활용해야 한다. 사실 그게 전부라고도 할 수 있다. 하지만 그런 기호들은 실제 의미와 어떠한 관계도 없어 보이는 것들이 대부분이라 정규표현식을 처음 접하면 그저 이해할 수 없는 암호처럼 보이곤 한다. 계속 사용하면서 익숙해지거나 외우는 수밖에 없는 부분이다.&lt;/p&gt;
&lt;h2 id=&quot;캡쳐링-그룹capturing-group&quot;&gt;&lt;a href=&quot;#%EC%BA%A1%EC%B3%90%EB%A7%81-%EA%B7%B8%EB%A3%B9capturing-group&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;캡쳐링 그룹(capturing group)&lt;/h2&gt;
&lt;p&gt;정규표현식에서 캡쳐링 그룹은 괄호로 둘러싼 영역이다. 캡쳐링 그룹이 있으나 없으나 패턴의 유효성을 검증하는 &lt;code class=&quot;language-text&quot;&gt;test&lt;/code&gt; 메소드의 결과는 같다. 하지만 문자열(String) 객체가 가지고 있는 match 메소드의 결과는 달라진다. 예를 들어 ‘github.com’ 사이트가 복수의 서브도메인을 가진다고 할 때, 서브도메인을 포함한 주소를 표현하는 패턴은 다음과 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://rhostem.github.com&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; domain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/https?:\/\/\w+\.github.com/&lt;/span&gt;
url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0&lt;/span&gt;
url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&quot;https://rhostem.github.com&quot;, index: 0, ...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서브도메인 영역을 괄호로 둘러싸서 캡쳐링 그룹을 만들면, &lt;code class=&quot;language-text&quot;&gt;match&lt;/code&gt; 메소드의 결과는 달라진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://rhostem.github.com&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; domain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/https?:\/\/(\w+)\.github.com/&lt;/span&gt;
url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 0&lt;/span&gt;
url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&quot;https://rhostem.github.com&quot;, &quot;rhostem&quot;, index: 0, ...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/search&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;search&lt;/a&gt; 메소드는 문자열 내부에서 정규표현식이 매칭이 시작되는 지점을 반환한다. 위의 예제에서는 문자열 처음부터 매칭되기 때문에 결과가 &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;이다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/match&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;match&lt;/a&gt; 메소드의 결과 배열에서  첫 번째 항목은 전체 매칭 문자열이며, 두 번째부터는 캡쳐링 그룹 안에 있는 문자열이 차례대로 들어간다. 캡쳐링 그룹을 사용하면 패턴 안의 원하는 부분을 보다 편리하게 분리해낼 수 있다.&lt;/p&gt;
&lt;p&gt;그리고 String 객체의 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;replace&lt;/a&gt; 메소드도 정규표현식을 활용하는데, 캡쳐링 그룹을 사용하면 패턴 확인과 함께 원하는 부분만 교체하는 기능을 구현할 수 있다. 위의 정규표현식을 조금 수정해서 서브도메인 영역을 &lt;code class=&quot;language-text&quot;&gt;www&lt;/code&gt;문자열로 바꿔보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;rhostem.github.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/^\w+(\.github\.com)$/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;www$1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// www.github.com&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서브도메인 영역을 제외한 부분을 캡쳐링 그룹으로 둘러쌌고, replace 메소드의 두번째 인자에는 대체할 문자열을 패턴을 전달했다. 여기서 &lt;code class=&quot;language-text&quot;&gt;$1&lt;/code&gt;은 첫 번째 캡쳐링 그룹으로 둘러싼 영역에해당한다. 만약 두 번째 캡쳐링 그룹이 있다면 해당 문자열은 &lt;code class=&quot;language-text&quot;&gt;$2&lt;/code&gt;가 될 것이다. 자세한 사항은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;링크&lt;/a&gt;를 참조하기 바란다.&lt;/p&gt;
&lt;h3 id=&quot;코드-에디터에서의-정규표현식과-캡쳐링-그룹의-활용&quot;&gt;&lt;a href=&quot;#%EC%BD%94%EB%93%9C-%EC%97%90%EB%94%94%ED%84%B0%EC%97%90%EC%84%9C%EC%9D%98-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D%EA%B3%BC-%EC%BA%A1%EC%B3%90%EB%A7%81-%EA%B7%B8%EB%A3%B9%EC%9D%98-%ED%99%9C%EC%9A%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;코드 에디터에서의 정규표현식과 캡쳐링 그룹의 활용&lt;/h3&gt;
&lt;p&gt;코드 에디터에서 검색을 할 때 정규표현식을 지원하는 경우가 많다. 그리고 캡쳐링 그룹과 &lt;code class=&quot;language-text&quot;&gt;$1&lt;/code&gt;같은 파라미터 문자열도 지원한다. 이를 사용하면 여러 파일을 한꺼번에 쉽게 수정하는 것이 가능하다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;a href=&quot;https://www.styled-components.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;styled-components&lt;/a&gt; 라이브러리를 사용하고 있는데, v3에서 v4로 업그레이드하려고 할 때 모듈 사용 방법이 달라져서 수정할 사항이 많이 생긴다. 대표적으로 &lt;code class=&quot;language-text&quot;&gt;extend&lt;/code&gt; 메소드의 사용방식이 달라진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// v3&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ExtendedComponent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Component&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;extend&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
 color: green;
`&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// v4&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ExtendedComponent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;styled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Component&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
 color: green;
`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;extend&lt;/code&gt; 메소드를 많이 사용했다면 하나하나 고치는데 제법 많은 시간이 걸릴 것이다. 이럴 때 정규표현식과 Visual Studio Code의 ‘파일에서 바꾸기’ 기능을 사용하면 한 번에 모두 바꿀 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3rESfUCrzquiA8MiEGwQ6g/2f095332fea3a43dfa0a33367f7500d1/vscode-replace-with-regex.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 63.96551724137931%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;vscode-replace-with-regex&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3rESfUCrzquiA8MiEGwQ6g/2f095332fea3a43dfa0a33367f7500d1/vscode-replace-with-regex.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3rESfUCrzquiA8MiEGwQ6g/2f095332fea3a43dfa0a33367f7500d1/vscode-replace-with-regex.png?w=290 290w,
https://images.ctfassets.net/rpmifyuylbfw/3rESfUCrzquiA8MiEGwQ6g/2f095332fea3a43dfa0a33367f7500d1/vscode-replace-with-regex.png?w=580 580w,
https://images.ctfassets.net/rpmifyuylbfw/3rESfUCrzquiA8MiEGwQ6g/2f095332fea3a43dfa0a33367f7500d1/vscode-replace-with-regex.png?w=1160 1160w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;이처럼 정규표현식은 에디터에서도 검색과 수정에 유용하게 활용할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;논-캡쳐링-그룹non-capturing-group&quot;&gt;&lt;a href=&quot;#%EB%85%BC-%EC%BA%A1%EC%B3%90%EB%A7%81-%EA%B7%B8%EB%A3%B9non-capturing-group&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;논-캡쳐링 그룹(non-capturing group)&lt;/h2&gt;
&lt;p&gt;그룹 시작 부분에 &lt;code class=&quot;language-text&quot;&gt;?:&lt;/code&gt; 기호를 추가하면 non-capturing 그룹이 된다. 캡쳐링 그룹이 결과 배열에 추가되는 것과는 달리 non-capturing 그룹은 결과 배열에 추가되지 않는다. 아래는 두 개의 캡쳐링 그룹을 사용해서 단어의 위치를 변경하는 예제다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;javascript&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/(java)(script)/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`$2-$1`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// script-java&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;java&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt;를 캡쳐링 그룹으로 지정했기에 패턴을 사용해서 위치를 바꿀 수 있었다. 하지만 캡쳐링 그룹의 시작 부분에 &lt;code class=&quot;language-text&quot;&gt;?:&lt;/code&gt;을 추가하면 non-capturing 그룹이 되어 결과 배열에 추가되지 않으며, 결과적으로 &lt;code class=&quot;language-text&quot;&gt;$2&lt;/code&gt;에 해당하는 패턴을 찾을 수 없어 다른 결과가 나온다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&apos;javascript&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/(java)(?:script)/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`$2-$1`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;$2-java&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그럼 이런 생각이 들 것이다. 저건 결국 괄호로 둘러싸지 않은 것과 같은데, 왜 굳이 non-capturing 그룹이라는 패턴을 지원할까? 라고 말이다. non-capturing 문법이 있는 이유는 그룹이 다른 역할로도 사용되기 때문이다. 예를 들어 앞서 서브도메인을 추출한 예제에서, http 프로토콜이 포함된 것과 포함되지 않은 문자열을 모두 매칭시키면서 서브도메인만 캡쳐링 그룹에 포함하고 싶을 때가 있을 것이다. 그러면 아래와 같이 작성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token regex&quot;&gt;/(?:https?:\/\/)?(\w+)\.github.com/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;rhostem.github.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// [&quot;rhostem.github.com&quot;, &quot;rhostem&quot;, index: 0, ...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token regex&quot;&gt;/(?:https?:\/\/)?(\w+)\.github.com/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://rhostem.github.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// [&quot;https://rhostem.github.com&quot;, &quot;rhostem&quot;, index: 0, ...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;http 프로토콜 패턴 부분을 그룹으로 만들고, 바로 다음에 조건부 매칭을 의미하는 &lt;code class=&quot;language-text&quot;&gt;?&lt;/code&gt; 기호를 붙여서 프로토콜에 해당하는 문자열 전체가 조건부 패턴이 되었다. 하지만 매칭 결과 배열에는 포함하고 싶지 않기 때문에 &lt;code class=&quot;language-text&quot;&gt;?:&lt;/code&gt; 기호를 사용해서 non-capturing 그룹으로 지정했다. non-capturing 그룹을 사용하지 않았다면 아래처럼 프로토콜 영역도 결과에 포함된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://rhostem.github.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rhostem&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;non-capturing 그룹 덕분에 항상 결과 배열의 2번째 아이템이 서브도메인이 될 것이므로, 다음과 같은 함수를 작성해서 서브도메인을 추출할 수 있게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; getSubDomain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; matches &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/(?:https?:\/\/)?(\w+)\.github.com/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; matches &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; matches&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 첫 번째 캡쳐링 그룹&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lookahead&quot;&gt;&lt;a href=&quot;#lookahead&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lookahead&lt;/h2&gt;
&lt;p&gt;Lookahead도 그룹 패턴으로, Lookahead 그룹 안에 있는 패턴이 &lt;strong&gt;뒤에&lt;/strong&gt; 올 때만 매칭이 된다. 캡쳐링 그룹과 다른 점은 &lt;strong&gt;전체 매칭 결과에 Lookahead 그룹 안에 있는 패턴이 포함되지 않는다&lt;/strong&gt;는 것이다. lookahead(앞쪽을 보다)라는 이름이 가지는 의미 그대로 관찰자 역할을 할 뿐 전체 패턴에서는 제외되어 있다고 여기면 될 것 같다&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// capturing group&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;javascript&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&quot;javascript&quot;, &quot;script&quot;, index: 0, ...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// positive lookahead&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;java&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;javascript&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&quot;java&quot;, index: 0, ...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lookahead에는 positive와 negative가 있는데, 앞서 제시한 &lt;code class=&quot;language-text&quot;&gt;?=&lt;/code&gt; 기호는 positive lookahead로서 그룹 안의 패턴이 뒤에 와야 함을 의미하며, negative lookahead를 나타내는 &lt;code class=&quot;language-text&quot;&gt;?!&lt;/code&gt; 기호는 그룹 안의 패턴이 뒤에 오지 말아야 함을 의미한다. Negative lookahead 패턴을 사용한 아래의 정규표현식을 사용하면 ‘java’ 뒤에 ‘script’라는 문자열이 오지 않는 모든 문자열을 매칭시킬 수 있다&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token regex&quot;&gt;/java(?!script)/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;java coffee&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&quot;java&quot;, index: 0, ... ]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lookbehind&quot;&gt;&lt;a href=&quot;#lookbehind&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lookbehind&lt;/h2&gt;
&lt;p&gt;Lookahead와 정반대의 역할을 하며 그룹 안의 패턴이 &lt;strong&gt;앞에&lt;/strong&gt; 나올 때 매칭이 된다. 하지만 ECMAScript 2018 스펙이라서 글을 쓰는 시점에서는 Chrome 브라우저에서만 사용 가능하며 Safari, FireFox는 브라우저는 지원하지 않는 문법이다.&lt;/p&gt;
&lt;p&gt;Lookbehind도 positive, negative가 있으며, positive lookbehind는 &lt;code class=&quot;language-text&quot;&gt;?&amp;lt;=&lt;/code&gt;, negative lookbehind는 &lt;code class=&quot;language-text&quot;&gt;?&amp;lt;!&lt;/code&gt; 기호로 표현한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// java 다음에 script가 와야 한다.&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt;java&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;javascript&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&quot;script&quot;, index: 6, ...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// java가 아닌 문자열 다음에 script가 와야 한다.&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;java&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;purescript&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [&quot;script&quot;, index: 4, ...]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Nuxt, Vue, Express로 프론트엔드와 백엔드를 하나의 서버 앱에서 사용하기]]></title><description><![CDATA[SPA(Single Page Application)의 서버 렌더링 웹사이트의 내용을 페이지가 바뀔 때마다 완전히 새로 구성하지 않고 필요한 부분만 동적으로 변경하는 방식의 앱을 SPA라고 한다. 서버는 클라이언트에게 웹사이트에 필요한 필수적인 자원만 넘겨준 후 그 다…]]></description><link>https://blog.rhostem.com//posts/2018-10-28-nuxtjs-universal</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-10-28-nuxtjs-universal</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 28 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;spasingle-page-application의-서버-렌더링&quot;&gt;&lt;a href=&quot;#spasingle-page-application%EC%9D%98-%EC%84%9C%EB%B2%84-%EB%A0%8C%EB%8D%94%EB%A7%81&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SPA(Single Page Application)의 서버 렌더링&lt;/h2&gt;
&lt;p&gt;웹사이트의 내용을 페이지가 바뀔 때마다 완전히 새로 구성하지 않고 필요한 부분만 동적으로 변경하는 방식의 앱을 SPA라고 한다. 서버는 클라이언트에게 웹사이트에 필요한 필수적인 자원만 넘겨준 후 그 다음은 모두 브라우저가 처리하도록 한다. WWW가 생긴 후 오랜 기간동안 일반적인 웹사이트는 페이지가 변경될 때마다 서버에서 모든 컨텐츠를 다시 내려받는 방식이었다. 하지만 SPA는 그 전통적인 방식을 벗어난 것이며, 예전과는 비교할 수 없을 정도의 빠르고 쾌적한 사용자 경험을 제공할 수 있게 되었다.&lt;/p&gt;
&lt;p&gt;그런데 SPA에게는 사용자에게는 보이지 않는 문제가 있다. 검색엔진같은 소프트웨어에게는 SPA로 만들어진 웹사이트는 아마 아무런 내용이 없는 빈 페이지로 보일 것이며, 심지어 모든 페이지의 &lt;code class=&quot;language-text&quot;&gt;head&lt;/code&gt; 태그 안에는 같은 내용이 들어가 있을 것이기 때문에 페이지의 구분도 불가능하다. 즉 검색 엔진 최적화(SEO, Search Engine Optimization)가 어려우며 검색 엔진에 웹사이트가 가지고 있는 다양한 컨텐츠가 노출되지 않는다. 더 많은 사용자 유입을 위해 검색 엔진의 힘을 빌려야 하는 언론, 쇼핑몰, 블로그 같은 웹사이트라면 SPA를 도입하기 망설여질 것이다.&lt;/p&gt;
&lt;p&gt;그래서 개발자들은 이를 극복하기 위해 서버 기반 웹앱과 SPA를 결합하는 방법을 고안했다. 서버가 브라우저에 아무것도 없는 HTML 페이지를 전달하는 대신, 최초에 표시될 내용만큼은 모두 서버에서 확실히 구성한 후 브라우저에 보내자는 것이다. 그리고 그 이후부터는 SPA처럼 동작하게 한다. 그러면 검색 엔진에게도 제대로 된 웹페이지가 보여질 것이고, 사용자에게도 여전히 빠른 웹사이트를 제공할 수 있을 것이다. 이 방식을 서버 사이드 렌더링, 또는 Universal Web App이라고 부른다. 서버 사이드 렌더링 아이디어가 없었다면 SPA의 활용 범위는 지금보다 훨씬 더 좁았을 것이다.&lt;/p&gt;
&lt;h2 id=&quot;vue의-서버-사이드-렌더링을-쉽게-만드는-nuxt-프레임워크&quot;&gt;&lt;a href=&quot;#vue%EC%9D%98-%EC%84%9C%EB%B2%84-%EC%82%AC%EC%9D%B4%EB%93%9C-%EB%A0%8C%EB%8D%94%EB%A7%81%EC%9D%84-%EC%89%BD%EA%B2%8C-%EB%A7%8C%EB%93%9C%EB%8A%94-nuxt-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Vue의 서버 사이드 렌더링을 쉽게 만드는 Nuxt 프레임워크&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://nuxtjs.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Nuxt&lt;/a&gt;는 Vue.js를 프론트엔드 레이어로 하는 서버 사이드 렌더링 프레임워크다. React 서버 사이드 렌더링 프레임워크인 Next.js와 이름이 유사하다는 점에서 추측할 수 있겠지만, 둘 다 &lt;a href=&quot;https://zeit.co&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Zeit&lt;/a&gt;팀에서 시작한 오픈소스 프레임워크다.&lt;/p&gt;
&lt;p&gt;서버 사이드 렌더링 아이디어 자체는 간단하지만, 그걸 구현하는 것은 그리 녹록치 않은 일이다. 앱의 시작 지점이 서버니까 일단 서버 앱이 있어야 하며, 클라이언트 스크립트도 이 페이지가 서버에서 시작하는지, 웹브라우저에서 시작하는지를 구분해서 처리해야 하고, 라우트별로 다른 API를 호출해서 HTML의 &lt;code class=&quot;language-text&quot;&gt;head&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt;를 구성해야 하는 등 할 일이 많다. Vue가 등장하기 전이며 React는 0.1x였던 시기에는 프로젝트 초기 구성 자체가 큰 허들이었던 걸로 기억한다.&lt;/p&gt;
&lt;p&gt;하지만 이제는 Next.js, Nuxt.js라는 멋진 프레임워크가 있기에 처음부터 끝까지 모든 것을 제어하고 싶은 사람이 아니라면, 아주 간단히 서버 사이드 렌더링을 구현할 수 있다. 특히 프레임워크 차원에서 개발자에게 필요한 각종 유용한 기능을 제공하기 때문에 더욱 좋다.&lt;/p&gt;
&lt;p&gt;그런데 문득 이런 생각이 들었다. 프론트엔드 앱의 시작 지점이 서버라면, 굳이 API 서버 앱을 별도로 둘 필요 없이 예전처럼 하나의 앱으로 개발하면 되지 않을까? Express.js라는 훌륭한 자바스크립트 서버 프레임워크도 있고 말이다. 하지만 자바스크립트가 아닌 다른 언어를 사용하는 백엔드 개발자가 더 많은데다, SPA 프레임워크를 사용하면서 서버 개발자가 마크업을 건드릴 일이 사라졌으니 반드시 같이 있어야 할 필요가 없기도 하다. 최근에는 서버리스 컴퓨팅이나 마이크로 서비스 아키텍쳐까지 트렌드를 타고 있어서 더욱 그렇다. 하지만 공부나 사이드 프로젝트를 위해 혼자 앱을 개발할 때는 안성맞춤일 것 같고, 풀 스택 개발자들이 함께 앱을 만들어 나갈때도 좋을 것 같다.&lt;/p&gt;
&lt;h2 id=&quot;nuxt로-만든-앱에-api-라우트를-연결하는-과정&quot;&gt;&lt;a href=&quot;#nuxt%EB%A1%9C-%EB%A7%8C%EB%93%A0-%EC%95%B1%EC%97%90-api-%EB%9D%BC%EC%9A%B0%ED%8A%B8%EB%A5%BC-%EC%97%B0%EA%B2%B0%ED%95%98%EB%8A%94-%EA%B3%BC%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nuxt로 만든 앱에 API 라우트를 연결하는 과정&lt;/h2&gt;
&lt;h3 id=&quot;nuxtjs-앱-설치&quot;&gt;&lt;a href=&quot;#nuxtjs-%EC%95%B1-%EC%84%A4%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nuxt.js 앱 설치&lt;/h3&gt;
&lt;p&gt;Nuxt는 &lt;a href=&quot;https://nuxtjs.org/guide/installation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;간편한 설치 과정&lt;/a&gt;을 제공한다. 설치 과정에서 여러가지 옵션을 선택할 수 있는데, Custom Server 옵션으로 express를 선택하도록 하자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2rPkbQiyHWSg0y6QggoqiY/b50cbb13dabf39c952575fe7ea1e9452/select-express.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 38.40579710144928%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;select-express&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2rPkbQiyHWSg0y6QggoqiY/b50cbb13dabf39c952575fe7ea1e9452/select-express.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2rPkbQiyHWSg0y6QggoqiY/b50cbb13dabf39c952575fe7ea1e9452/select-express.png?w=276 276w,
https://images.ctfassets.net/rpmifyuylbfw/2rPkbQiyHWSg0y6QggoqiY/b50cbb13dabf39c952575fe7ea1e9452/select-express.png?w=552 552w,
https://images.ctfassets.net/rpmifyuylbfw/2rPkbQiyHWSg0y6QggoqiY/b50cbb13dabf39c952575fe7ea1e9452/select-express.png?w=1104 1104w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;h3 id=&quot;nuxtconfigjs에서-미들웨어-설정&quot;&gt;&lt;a href=&quot;#nuxtconfigjs%EC%97%90%EC%84%9C-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;nuxt.config.js에서 미들웨어 설정&lt;/h3&gt;
&lt;p&gt;설치가 완료되고 &lt;code class=&quot;language-text&quot;&gt;npm run dev&lt;/code&gt; 명령어를 실행하면 앱이 잘 실행되는 것을 확인할 수 있다. 옵션 선택 과정에서 prettier나 eslint를 선택했으면 문법 오류 때문에 빌드가 안될 수 있는데, 그럴 땐 일단 webpack에서 관련 설정을 임시로 제거하도록 하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// nuxt.config.js&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

  build&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/*
    ** You can extend webpack config here
    */&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Run ESLint on save&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDev &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isClient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 이 부분을 주석 처리하고 코드는 나중에 정리하자&lt;/span&gt;
        config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          enforce&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;pre&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          test&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/\.(js|vue)$/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          loader&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;eslint-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          exclude&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/(node_modules)/&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 nuxt.config.js 파일에서 serverMiddleware 속성에 새로운 값을 할당한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// nuxt.config.js&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

  serverMiddleware&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;project root&gt;/api/index.js 모듈을 미들웨어로 추가&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;~/api/index.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 미들웨어로 사용할 express 앱을 모듈로 추가한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// &amp;lt;project root&gt;/api/index.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; express &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;express&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// express 인스턴스 생성&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 실제로는 /api 라우트를 처리하는 메소드가 된다.&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;API root&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 모듈로 사용할 수 있도록 export&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 앱의 /api/* 라우트로 접근하는 모든 요청은 모두 app 인스턴스에게 전달된다.&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/api&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  handler&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; app
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 설정과 파일을 구성하면 Nuxt는 자동으로 api/index.js 파일을 불러와서 미들웨어로 사용한다. 브라우저에서 /api 라우트에 접근하면 &lt;code class=&quot;language-text&quot;&gt;res.send&lt;/code&gt; 메소드로 전달한 문자열이 출력되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/Bu3GbmSpSSqoy2EIOQYuo/83bff8e9007fe243d7b67fc61ec34564/mw_api_root.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 85.56149732620321%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;mw api root&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/Bu3GbmSpSSqoy2EIOQYuo/83bff8e9007fe243d7b67fc61ec34564/mw_api_root.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/Bu3GbmSpSSqoy2EIOQYuo/83bff8e9007fe243d7b67fc61ec34564/mw_api_root.png?w=374 374w,
https://images.ctfassets.net/rpmifyuylbfw/Bu3GbmSpSSqoy2EIOQYuo/83bff8e9007fe243d7b67fc61ec34564/mw_api_root.png?w=748 748w,
https://images.ctfassets.net/rpmifyuylbfw/Bu3GbmSpSSqoy2EIOQYuo/83bff8e9007fe243d7b67fc61ec34564/mw_api_root.png?w=1496 1496w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;이제 express가 미들웨어로 연결되었으니 여기서부터는 일반적인 express 앱 개발을 진행하면 된다. 앱에 라우트를 추가하고, 미들웨어를 추가하고, 데이터베이스를 연결하면 API 서버가 되는 것이다. 조금 더 상세한 예제는 &lt;a href=&quot;https://github.com/nuxt-community/express-template/tree/master/template&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;템플릿 프로젝트&lt;/a&gt;를 참조하도록 하자.&lt;/p&gt;
&lt;h3 id=&quot;프론트엔드에서-api-호출&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-api-%ED%98%B8%EC%B6%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프론트엔드에서 API 호출&lt;/h3&gt;
&lt;p&gt;이제 Vue 페이지에서 API 라우트를 호출해서 컴포너트에 데이터를 연결하도록 하자. Nuxt는 Vue 컴포넌트에서 페이지 로딩 전에 비동기 데이터를 호출하기 위한 메소드, &lt;code class=&quot;language-text&quot;&gt;head&lt;/code&gt; 태그를 커스터마이징하기 위한 메소드를 제공하기 때문에 무척 편리하다. 아래 예제 코드는 &lt;code class=&quot;language-text&quot;&gt;/api/user/:id&lt;/code&gt; 라우트가 &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; 속성을 가진 JSON 데이터를 제공한다고 가정하고 작성한 컴포넌트다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// pages/user.vue&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt; asyncData 메소드에서 전달한 데이터를 마크업에 연결 &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    이름 &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;template&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Nuxt.js에서 제공하는 페이지 로딩 전에 비동기 데이터를 호출하기 위한 메소드&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Vue.js의 data 메소드와 유사한 역할을 한다&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;asyncData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; query &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 설치 과정에서 axios 플러그인을 선택했다면 axios를 아래와 같은 형태로 사용 가능하다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`/api/user/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; user &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Nuxt.js에서 제공하는 head 태그 수정용 메소드&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`User | &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// asyncData에서 리턴한 user 데이터를 사용할 수 있다.&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;script&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 Nuxt 프레임워크의 도움으로 간단하게 프론트, 백엔드가 합쳐진 서버 사이드 렌더링 SPA 앱이 만들어졌다.&lt;/p&gt;
&lt;h2 id=&quot;참고-자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nuxtjs.org/guide/installation&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Installation - Nuxt.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nuxt-community/express-template&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Starter template for Nuxt.js with Express.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://expressjs.com/en/guide/routing.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Express routing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[[번역] 자바스크립트 반응성(Reactivity)에 대한 가장 좋은 설명]]></title><description><![CDATA[Gregg Pollack의  The Best Explanation of JavaScript Reactivity 🎆 를 번역한 글이다. SPA 프레임워크에서 컴포넌트의 상태가 변경되면 렌더링이 다시 실행되는 것처럼, 객체의 속성이 변경될 때 어떤 코드가 자동으로 실행되…]]></description><link>https://blog.rhostem.com//posts/2018-09-12-javascript-reactivity</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-09-12-javascript-reactivity</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Wed, 12 Sep 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Gregg Pollack의 &lt;a href=&quot;https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;The Best Explanation of JavaScript Reactivity 🎆&lt;/a&gt;를 번역한 글이다.&lt;/p&gt;
&lt;p&gt;SPA 프레임워크에서 컴포넌트의 상태가 변경되면 렌더링이 다시 실행되는 것처럼, 객체의 속성이 변경될 때 어떤 코드가 자동으로 실행되게 한다면 그 애플리케이션은 반응적이라고 할 수 있다. 이를 구현하기 위해서 &lt;code class=&quot;language-text&quot;&gt;Object.defineProperty&lt;/code&gt; 함수를 사용해 getter, setter 함수를 다시 할당하는 방법을 설명하는 글이다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1Ld2dlcQiUGui6006maeg2/953099916e44cad0405f7f85a97e1a36/main.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 30%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAlgCWAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAMACgDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAABAUAAggJ/8QAKhAAAgEDAgUEAQUAAAAAAAAAAQIDBAURADEGEhMhQRQiUXEHCDNhwdH/xAAWAQEBAQAAAAAAAAAAAAAAAAAEAgP/xAAbEQACAwEBAQAAAAAAAAAAAAABAgADEUESIf/aAAwDAQACEQMRAD8A59cLPy2emLKzqqL+2RzAY777fwcb50dcJUiWMCndoz7ilQ5Yr4GSvLk7/QIHzpHZ5GhtFIyHBaMA/WB/utOSfgzh4/p6u3Gxmrzeaa3x1MamVeiH69KhyvLkjEz+fjSWKjN7Aisv6I5M+x3OlSn6b0ysSBnphu3zu3wNLZbpblWSSd1VshCGABLY/rtn7Gr+pk5g2RkbYUaX26iS6y3Z6gh/TSRrGpRGADbjBBxt4x50WykIN2ZrWHP2BXq4W6W2TJTzZlIxgkHOpow26muvCNxrXgSCalqljXoKFVh02b3Dz3wPrU1KDBG1qFGCf//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;main&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1Ld2dlcQiUGui6006maeg2/953099916e44cad0405f7f85a97e1a36/main.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1Ld2dlcQiUGui6006maeg2/953099916e44cad0405f7f85a97e1a36/main.jpeg?w=350 350w,
https://images.ctfassets.net/rpmifyuylbfw/1Ld2dlcQiUGui6006maeg2/953099916e44cad0405f7f85a97e1a36/main.jpeg?w=700 700w,
https://images.ctfassets.net/rpmifyuylbfw/1Ld2dlcQiUGui6006maeg2/953099916e44cad0405f7f85a97e1a36/main.jpeg?w=1400 1400w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;많은 프론트엔드 자바스크립트 프레임워크(Angular, React, Vue, etc)는 자신만의 반응성(Reactivity) 엔진을 가지고 있다. 반응성이 무엇인지, 어떻게 동작하는지를 이해함으로써 당신의 개발 기술을 향상할 수 있고 자바스크립트 프레임워크를 보다 효율적으로 사용할 수 있다. 아래의 영상과 비디오를 통해 우리는 Vue 소스 코드에서 확인할 수 있는 것과 같은 종류의 Reactivity를 구현할 것이다.&lt;/p&gt;
&lt;div class=&quot;iframe-video-wrapper&quot;&gt;
  &lt;iframe class=&quot;iframe-video video&quot; src=&quot;https://www.youtube.com/embed/7Cjb7Xj8fEI&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; allow=&quot;autoplay; encrypted-media&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;글을 읽는 대신 비디오를 시청하고 싶다면 &lt;a href=&quot;https://www.vuemastery.com/courses/advanced-componentscode/evan-you-on-proxies/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;이 시리즈의 다음 영상&lt;/a&gt;을 보면서 Vue를 만든 Evan You와 함께 반응성과 프록시에 대해 토론하는 내용을 확인하길 바란다.&lt;/p&gt;
&lt;h2 id=&quot;반응성-시스템&quot;&gt;&lt;a href=&quot;#%EB%B0%98%EC%9D%91%EC%84%B1-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;반응성 시스템&lt;/h2&gt;
&lt;p&gt;Vue의 반응성 시스템을 처음 보면 마치 마법처럼 느껴질 수 있다. 간단한 Vue 앱을 확인해보자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2PtypFvs8E0oWAAqeKUgQE/30e3e124359ccf5a113c3a8caaa65c7c/code_1.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 21.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAJCAMAAAB30J7MAAAA2FBMVEUeHh4nKCkmLTUhJSgoLC8wMzYwKyk2LywuKiglJSUgISElKS4iJSgmKSssLi8nJSQ1LiwjIiEmJiYfHx8gICAjIyMgIiMrNUAmKzA0NDQ5OTk7OzstLS0zMzM6Ojo1NTUpLDAoMTkqLTAnJyctOkYnLDE2NjZGRkZAQEAsLCwrKys9PT03Nzc/Pz8+Pj4kJCQ4ODgvLy8tOUYlLDIiJCYuLi4wMDAlJiglKjAlJicjJCQmLDMjJigqKioxMTEyMjIpKSkkJigjKC0kJykoKCguNj0mLjUpKyw9X1sHAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAIxJREFUGBmtwbsOwVAAx+HfvyrqVClVncRg9hZe2mPYTMwuFYnb0JweiUvSpIkYfB+/Evq4840wekP58JDiPJtTJxg09VKW8q1tSWvqBFM9XW3v1C9usZDzJJ3NkgofOr7snqSz28yK45itbRA6uomo8iAMglWcXoJJ2I7GG7JoMHKZjDFUibmkBf/zAKYZIgYV2K1xAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 1&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2PtypFvs8E0oWAAqeKUgQE/30e3e124359ccf5a113c3a8caaa65c7c/code_1.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2PtypFvs8E0oWAAqeKUgQE/30e3e124359ccf5a113c3a8caaa65c7c/code_1.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/2PtypFvs8E0oWAAqeKUgQE/30e3e124359ccf5a113c3a8caaa65c7c/code_1.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/2PtypFvs8E0oWAAqeKUgQE/30e3e124359ccf5a113c3a8caaa65c7c/code_1.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1keZmdDB9G8emkmEay4E6U/8922bbc86473f034730fa30c4db7f510/code_2.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 51.75000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAVCAMAAAADxFwsAAABnlBMVEUeHh4iIiIkJCQhISEoJiUnJCMlIyIjISEmJCMgHx8iISAhICAjIiEqJyYiIiMfICEgISMgIyUgICApKSk6OjouLi5CQkIqKio3NzcvLy81LywxKylCODQpJiQqJiU8NDAwKykvKig+NTE6Mi8uKScxLCk6My8tKSdBODMuKihANjIkIyMrKyspLjMmLjUnLjUnLzclJSUsLCw/Pz8xMTFHR0c8PDwgIiQmLTMmJiYoMDkkKi8yMjIgISIiJSkkJignJyckIyImKCslJigvNjojIyM8My9JPTglJCM8SE82P0UoKisqLjEtMjYsLioyNS8wMi0qLzImKSspLjAmKCklJyUlJiQvMS45Q0k5Q0ouNDgwNzsvNTghISApKygjJCUsMTUzO0AuNDctNDctMjUgICEnKy0xNjorLjAtMDIyNTYoKCgfHx85OTk1NTU2NjYmLTQoMTopMzwlKzErNkEnLDI7Ozs9PT0qND4lKS1ERERGRkYtLS0pKyctLys7QDcqJSIpIyEmIiAtJSElIR8jIB8mIR8iIB4pIyAgHx7nid0tAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAkM7zeHQAAAQ1JREFUGBmNwTFLAmEAx+Hf/+4974VwOYmoxSWiKGyJImiqiL5F1JpDH6Clj9HU2FLQ3tAQLoE6JUSTEETLVVDBWWp3GkTkeT6PEAh1XEmRRergqKMvGXWdjzHFQmKGQD0tX78iK7V8/QiJGYqKVdtkEKyo2/yckaKH6UZIGgdyOTv7aq2tT92TzoFreZ7xPI/aU0gqQ8zTRu2R4URi06qyJl2QzpDIS+uSGMKQ8NVDhmCJbAJ2lLi9aTOEAXJKULL+/Glp4e6SQVygVm02Fs+3Kvnl+nM4d/XOIKLnQBLSyZ7eznYjKx0XtS3piL8K9JXpKwMB/wTuKiMJJhjJ+CQjMBT2FTskg+HFU4ws38zJOhLHt3EcAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 2&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1keZmdDB9G8emkmEay4E6U/8922bbc86473f034730fa30c4db7f510/code_2.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1keZmdDB9G8emkmEay4E6U/8922bbc86473f034730fa30c4db7f510/code_2.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1keZmdDB9G8emkmEay4E6U/8922bbc86473f034730fa30c4db7f510/code_2.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1keZmdDB9G8emkmEay4E6U/8922bbc86473f034730fa30c4db7f510/code_2.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;Vue가 만약 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt; 값이 변했다는 사실을 알게 된다면 아래 3가지 일을 할 것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;웹페이지의 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt; 값을 업데이트한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;price * quantity&lt;/code&gt; 를 다시 계산하고 페이지를 업데이트한다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;totalPriceWithTax&lt;/code&gt; 함수를 다시 호출하고 페이지를 업데이트한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;하지만 잠깐, 당신은 이런 의문을 가지게 될 것이다. Vue는 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;가 변경되면 &lt;strong&gt;무엇을&lt;/strong&gt; 업데이트해야 하는지 어떻게 알며, &lt;strong&gt;어떻게&lt;/strong&gt; 모든 것을 추적하고 있는가?&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/fd7rL8RM1GQ2egkWeKQak/ee3820b70e3ebf8bca295028314339f6/code_3.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAlgCWAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAWACgDASIAAhEBAxEB/8QAGwABAAEFAQAAAAAAAAAAAAAAAAUBBAYHCAn/xAArEAACAQMEAQMCBwEAAAAAAAABAgMEBREABiExEhMiQQhRBxYyQlJxgZL/xAAYAQEAAwEAAAAAAAAAAAAAAAAEAAECBf/EACERAAEEAgICAwAAAAAAAAAAAAEAAgMRITESIgRBUWFx/9oADAMBAAIRAxEAPwDzxsKVL2ShCQ2TwjQkS1FqieQ5BJDuR7jhvnnrHWpKCORo2U2+yVKOGA9CzoZBk54+2OQCQe+AccWO2aj0LVTlZGhcRowcY7A47Gpm40dZXQ+uxnnjiVWknkViPdgBmb4HQH+aTwGMIJkfZoqjRSvTyeNhtcQLFi7WmJiMgDgYHHPA4551r6XZVSkjKsquo/cF71mSVFRCqRxzxxLx+nB66yTnUTPfayFpIacQzyhvEkyKpUY65xknI6z1/Y0NzZG7WQ+UnrlYvV7elpKd5WlUhRnGDzpqZv1bdTbGSspljichVPkDzz0QMfB+dNRtnaZGXEdlCQ7krYII4VdPCNfFR4DrXTG6frFtFw/AObYtqs1VTXGot9NRTTywRiEFDAZWUrJn3NTqRlflvvpproQAOY8uF0MfSF5BLJI+Jqzmvf78rmn8z1380/41Zm61fnMy1EkXrEFxGxUMR1kDTTQySdp4aBoLev01zUu4tsb+2xW0cVQptVRcYZ5IomKy+i8IJLIX9plV18GXDJz5A40001S0v//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 3&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/fd7rL8RM1GQ2egkWeKQak/ee3820b70e3ebf8bca295028314339f6/code_3.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/fd7rL8RM1GQ2egkWeKQak/ee3820b70e3ebf8bca295028314339f6/code_3.jpeg?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/fd7rL8RM1GQ2egkWeKQak/ee3820b70e3ebf8bca295028314339f6/code_3.jpeg?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/fd7rL8RM1GQ2egkWeKQak/ee3820b70e3ebf8bca295028314339f6/code_3.jpeg?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vue가 하는 일은 자바스크립트가 일반적으로 동작하는 방식이 아니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다시 말해, 우리가 의문을 가지고 있는 문제는 일반적인 프로그램 동작 방식이 아니라는 말이다. 예를 들어 만약 아래의 코드를 실행한다면:&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4IhAqhRmTe6AE2UoAwoaAI/4cec430a8ee7bf9ce9684f6dafb1e183/code_4.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 21.375000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAJCAMAAAB30J7MAAAA21BMVEUeHh4iJSgkKi8hIyU0NDQtLS0wMDArKysnKSYiIyEiJikhJCcgIiQqKioiIiIhIiEhISEjJCIiJiooMDgiJSlKSkpDQ0M4ODg8PDwyMjIrLSklKi8oMDkjJio+Pj4uLi43Nzc9PT07OztFRUVBQUEkKSIhJCAsNygmLiQvPConLyQhIyAlJSUmJiYqKygpKygfIB8fIR8vLy8sLi4kJCQtLishICAfHh4gICAhISAjKigtPTkqOTYtPjopMzEzMzNOTk42MCw7My9BNzMlIyI2Lyw5OTk6OjpERETkffsTAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAIVJREFUGBmdwbELAVEAx/Hfl8e9M0hZUBZ1NxkMMvj/y2iQIgNnM3Avi94NeJQidcnno18hXi4qgxpQRCLIVcLIQkxwaucqYRSDtzp0uhj6sOvtE7hWgaXeoGFWpLcKmZPUclLzrGDEXB9QVE+AhXWaIFZO49pM39CUp+2Ah03K+ui9/ncHSvQePBGayBUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 4&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4IhAqhRmTe6AE2UoAwoaAI/4cec430a8ee7bf9ce9684f6dafb1e183/code_4.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4IhAqhRmTe6AE2UoAwoaAI/4cec430a8ee7bf9ce9684f6dafb1e183/code_4.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/4IhAqhRmTe6AE2UoAwoaAI/4cec430a8ee7bf9ce9684f6dafb1e183/code_4.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/4IhAqhRmTe6AE2UoAwoaAI/4cec430a8ee7bf9ce9684f6dafb1e183/code_4.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;무엇이 출력될 것으로 생각하는가? 보통의 자바스크립트 코드라서 &lt;code class=&quot;language-text&quot;&gt;10&lt;/code&gt;을 출력할 것이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3OQ2OwB6goiyiqi6eQesYM/f06b9839066fc4bd6fae39dac40d3524/code_5.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 9%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAECAAAAADZ+yL8AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAACdJREFUCNdjkCMSMKioqqvpaqioqOkQUmigaW5tqmOobk5AIbFWAwBghBPNsewKcQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 5&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3OQ2OwB6goiyiqi6eQesYM/f06b9839066fc4bd6fae39dac40d3524/code_5.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3OQ2OwB6goiyiqi6eQesYM/f06b9839066fc4bd6fae39dac40d3524/code_5.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/3OQ2OwB6goiyiqi6eQesYM/f06b9839066fc4bd6fae39dac40d3524/code_5.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/3OQ2OwB6goiyiqi6eQesYM/f06b9839066fc4bd6fae39dac40d3524/code_5.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;Vue에서는 우리는 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;나 &lt;code class=&quot;language-text&quot;&gt;quantity&lt;/code&gt;가 업데이트되면 &lt;code class=&quot;language-text&quot;&gt;total&lt;/code&gt;도 업데이트되길 바란다. 바로 다음과 같이 출력되길 원한다:&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/ZZPtJ9Wbgke4oWmiEkWMk/c39d34f441c8447f3f9efb1138a5734b/code_6.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 9.749999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAECAAAAADZ+yL8AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAACpJREFUCNdjkCMSMMhpKWlr6WopaskbaeBXaKipZ29jreig5KaHXyGRAAB60RQeTmfZxwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 6&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/ZZPtJ9Wbgke4oWmiEkWMk/c39d34f441c8447f3f9efb1138a5734b/code_6.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/ZZPtJ9Wbgke4oWmiEkWMk/c39d34f441c8447f3f9efb1138a5734b/code_6.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/ZZPtJ9Wbgke4oWmiEkWMk/c39d34f441c8447f3f9efb1138a5734b/code_6.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/ZZPtJ9Wbgke4oWmiEkWMk/c39d34f441c8447f3f9efb1138a5734b/code_6.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;하지만 불행히도 자바스크립트는 반응형이 아니라 절차적(procedural) 언어라서 우리가 기대하는 기능을 바로 제공하지 않는다. &lt;code class=&quot;language-text&quot;&gt;total&lt;/code&gt; 변수가 반응적이 되도록 만들기 위해서는 자바스크립트를 다른 접근 방식이 필요하다.&lt;/p&gt;
&lt;h2 id=&quot;️-문제&quot;&gt;&lt;a href=&quot;#%EF%B8%8F-%EB%AC%B8%EC%A0%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;⚠️ 문제&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;total&lt;/code&gt; 값을 계산하는 코드를 저장해 둘 필요가 있고, &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;나 &lt;code class=&quot;language-text&quot;&gt;quantity&lt;/code&gt; 값이 바뀔 때마다 그 코드를 다시 실행해야 한다.&lt;/p&gt;
&lt;h2 id=&quot;-해결책&quot;&gt;&lt;a href=&quot;#-%ED%95%B4%EA%B2%B0%EC%B1%85&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 해결책&lt;/h2&gt;
&lt;p&gt;우선 우리의 애플리케이션에게 다음의 메시지를 전달할 방법이 필요하다, “내가 실행하려는 코드가 하나 있는데, 이걸 저장해 둬. 나중에 네가 다시 실행할 필요가 생길거야”. 그런 일이 가능하다면 우리는 코드를 한번 실행한 후에, &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;나 &lt;code class=&quot;language-text&quot;&gt;quantity&lt;/code&gt; 값이 바뀔 때 앞서 저장된 코드를 다시 실행하면 된다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/7lZ7lrUcgMs6w8uMmea4A/c8e3b447df3175f7687cd84db32a413a/code_7.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 34.125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAOCAMAAABq1a50AAABtlBMVEUSBgRXHhQJAwIAAAB8Nyjnj2hcIRYmJiYkJCQjIyMbGxstLS0ICAgqDgqLNiEaCAYODg4KCgoHBwcLCwsNDQ0MDAwDAwMFChIIEiAGDhgYIi8iJzoTJCwVKjEVIisUJCwVKTAVKDAYJS8TIywWKjEWLDMRHicRHygAAQE6Z4tmrNdYlLpal75SiKxipc9Nh7QRGiMXHSoPGyIRICYRIScRHCQRHyYQHyURGyMQHSQTJSoOGCAOGSEWKB0CAwMlNUvD0+anvtqNq9CcttZIW3USHxcLFA4rNkX///+vyuJ5r9eVutpZZHQzMzMlJSUyMjIrKysuLi4xMTEPGxQZLCAEBwUBAQEPGxMNFxERHRUvPUqHkZeEjpNHVGQKEQwiLzs/VGk8UWYtO00EBgQLGSc3bqkPIDMOGBESIBcLEg4CAwIGCQYaJBcYIhYlNCEOFA0cPWCLvfIiSXMwMDAgICAoKCg2NjYqKiovLy8+Pj4PDw8JEAwUJBoPGhMVJhwXKB0LEw4JDQkUHBIcKBkgLR0YIxYPFQ4BAgQTKUACBQgFCAUTGhElNSEfLRwEBQMkMyAZIxYbJhjbOv5GAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAOJJREFUGBmNwb0ug1EAgOH31dOkNTZSMdhdB5vJ72TpIDEJi0QkLIZGYhKTRFIXQBiMbK7AJBLMvQAN+uVzTtVPtKTPIyoDMJigtvhPKPupxW8jEtkkCs8wqqLjJu23YfSepGJHkygAVT+0i/qah4BWb4iqRlkgCcAtPSanroCSURZIAjBtcsEP1zOzngF5TlcAyvQ6Z2HRCJDEAn9aUgFPoNZwWY/pZ66iR8DKS8lkXX28XPVgzXbxbiILe5uaD5mFXWBHjLbBum7xZf6Urlqjrj6NlTbosEB/+/pwyDcLDOYd5KsucDVQa3MAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 7&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/7lZ7lrUcgMs6w8uMmea4A/c8e3b447df3175f7687cd84db32a413a/code_7.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/7lZ7lrUcgMs6w8uMmea4A/c8e3b447df3175f7687cd84db32a413a/code_7.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/7lZ7lrUcgMs6w8uMmea4A/c8e3b447df3175f7687cd84db32a413a/code_7.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/7lZ7lrUcgMs6w8uMmea4A/c8e3b447df3175f7687cd84db32a413a/code_7.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;이는 함수를 기록하는 방법을 통해 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1P2R1jAk6I2w446omKmWoE/5a96d383f5b8184be3da902050e8e4d3/code_8.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 40.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAQCAMAAABTCc2fAAABOFBMVEUeHh4iJionLzYjJCU3NzcxMTEwMDAqKioqLCggISAiJikkKS4jJCYsLCwnJyYkJCQmJyUlKjAiIyQ7Ozs0NDQuLi4vLy4nKCYjJiopMjsgIiRCQkI6Ojo1NTUvMi0hIiEpMjxEREQ9PT0oMDghISEfHx8mJiYlJSUnJyciIiIgISIhJCciJSkgIyUpKSkgICAjIyM2NjYgISElKzEnLjUjJywmLjUtLS05OTk4ODgoKCgrKytBQUFFRUU8PDwyMjIhIyAgIiAgIyAfIR8hJCAfIB4fHx4iJiEgIR8eHx4fIB8gIh8lJiQjJyEpNCYrNigsNygpMiYoMSUmLSQnLyUiJyElLCMlKiMpMSUiJSEnLyQkKSIpMSZAQEAlJiUmLCMqMyYqNScoMCUsOCgrNiclKyMrNScjKCJv2tSqAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAANlJREFUGBmdwU0uA2EAx+H/753Pd6Q0JT7KhrSSbixs3MDCAVxRXKB7N+gBJMJGSAiNms636aLLmU48j7pCrMVqgyJIUAB8qYWruE/APCQJ1AYdsvJ+wLPaOKq2i2ARJfP+IM20yWisTVydw5NHdAqFCzM1MFL02CvDibUWm8RjNUC6iN72Z5eqDJD5PKhBr6cujE7O1IUrfzEypjhmrSorb3rtpKFK8sLLg2qqmqtkL3kxV4ja0rK05M4t2Q6pD3odlmjF1Uc6vLkb8LsF30fUPk25+3Ov//oD4ds8gw1OLYIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 8&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1P2R1jAk6I2w446omKmWoE/5a96d383f5b8184be3da902050e8e4d3/code_8.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1P2R1jAk6I2w446omKmWoE/5a96d383f5b8184be3da902050e8e4d3/code_8.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1P2R1jAk6I2w446omKmWoE/5a96d383f5b8184be3da902050e8e4d3/code_8.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1P2R1jAk6I2w446omKmWoE/5a96d383f5b8184be3da902050e8e4d3/code_8.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt; 변수에 익명 함수를 할당해서 재실행할 코드를 저장하고, &lt;code class=&quot;language-text&quot;&gt;record&lt;/code&gt; 함수를 호출했다. ES6 화살표 함수를 사용하면 아래처럼 작성할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4egigEwaliAIUSOc4Ey2kE/8d450d61c5e1fa79cb4cd3a9ddb16f58/code_9.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 8.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAADCAAAAADE/hJEAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAADlJREFUCNdjkCMSMMjp2lg7qKjYqBjoaZk56WuZmZtqa9va2hlZ66IplJNTBGJ5MJKDkiBCQR5FIQDxvBA+I2oPjwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 9&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4egigEwaliAIUSOc4Ey2kE/8d450d61c5e1fa79cb4cd3a9ddb16f58/code_9.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4egigEwaliAIUSOc4Ey2kE/8d450d61c5e1fa79cb4cd3a9ddb16f58/code_9.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/4egigEwaliAIUSOc4Ey2kE/8d450d61c5e1fa79cb4cd3a9ddb16f58/code_9.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/4egigEwaliAIUSOc4Ey2kE/8d450d61c5e1fa79cb4cd3a9ddb16f58/code_9.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;함수 &lt;code class=&quot;language-text&quot;&gt;record&lt;/code&gt;는 간단히 아래처럼 정의한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3gF8Xgo8ByEIKSuKoCuoKu/471fe530cc12f775fc2fbf39322313c5/code_10.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 20.125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAICAMAAAC8jE1pAAAAzFBMVEUeHh4kKC0qND4oKCg8PDw2NjZFRUUxMTEpKSksLCwrKyslKyMkKSIqMyYmLCMkKCIqNCcpMiYmLSQoMCUjKCIoMSUpMyYnLiQlKiMpMSYkJCQiIiIhISEgIh8iJCcgIiMhJCYgIiQnJycjIyMfIR8iJSEgIyAhIyAfIB8gIR8gIiAiJSAhJCAfHx4fIB4nLzYlKzIkKi8mLDMmJiY3NzcwMDAjJiIhJSAjJyEkKiInLyUtLS05OTk4ODhHR0dCQkI/Pz8+Pj5EREQgICCMgI1wAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAIZJREFUGBmNwckOwWAAhdHvdlJTIxJEDfEEHgCv362dhbVgZyFFdfqxIkTSc6hK6AdSkfvGypzENHVpHXkLYTTmw4QvYiqJ1OALhJ4obMVtSZlB3tXNa8l952AHekFr3HmyL71BduiGwemc9zralLP+1njxEMFS0q2haCWTRvxXpxKLBZU8AAa2JtzdF63gAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 10&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3gF8Xgo8ByEIKSuKoCuoKu/471fe530cc12f775fc2fbf39322313c5/code_10.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3gF8Xgo8ByEIKSuKoCuoKu/471fe530cc12f775fc2fbf39322313c5/code_10.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/3gF8Xgo8ByEIKSuKoCuoKu/471fe530cc12f775fc2fbf39322313c5/code_10.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/3gF8Xgo8ByEIKSuKoCuoKu/471fe530cc12f775fc2fbf39322313c5/code_10.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;storage 배열에 target 함수를 저장한다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt; (코드에서는 &lt;code class=&quot;language-text&quot;&gt;{ total = price * quantity }&lt;/code&gt;)을 저장했으니 나중에 다시 실행할 수 있다. 저장한 모든 함수를 실행할 &lt;code class=&quot;language-text&quot;&gt;replay&lt;/code&gt; 함수가 필요할 것이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5pubyBMpTqMucWws0iYuyO/9a630aafc0a57e05c1027d17ce54c584/code_11.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 14.750000000000002%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAGCAMAAACGhiwZAAAAYFBMVEUeHh4qND0qMz0mLTQnLzgoMDgrKytAQEA7OzsoKCgwMDAsLCwfICEqKisrLC0rLS4pKiojIyM4ODg0NDQuLi4xMTEnJyclJSUpKSlFRUUqKioyMjI5OTk2NjYtLS0fHx+zYCinAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAEVJREFUCNdjYCASMAIRFPxnYmT8ik8hD0TdD06wWob/n/kZXmAqZGJg+MLB+puTk0mQk/Er4xeG/x9E2D5hNVNelIGqAAB+Lw5JufK22gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 11&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5pubyBMpTqMucWws0iYuyO/9a630aafc0a57e05c1027d17ce54c584/code_11.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5pubyBMpTqMucWws0iYuyO/9a630aafc0a57e05c1027d17ce54c584/code_11.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/5pubyBMpTqMucWws0iYuyO/9a630aafc0a57e05c1027d17ce54c584/code_11.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/5pubyBMpTqMucWws0iYuyO/9a630aafc0a57e05c1027d17ce54c584/code_11.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;replay&lt;/code&gt; 함수를 실행하면 &lt;code class=&quot;language-text&quot;&gt;storage&lt;/code&gt; 배열에 저장한 모든 익명 함수를 실행한다.&lt;/p&gt;
&lt;p&gt;이제 간단히 다음처럼 하면 된다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/693gbCSts4gaMyAWyEUIEI/f9b053c10dac97c0b23a3649ecebc308/code_12.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 18.250000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAHCAMAAABN2v+8AAAArlBMVEUeHh4gICAiIyEiIiFLS0sxMTFDQ0MnJycsLCwyNS82OTIpNTIqODUpNjMmLy0yMjJAQEAwMDA8PDw5OTk/Pz8hIyAlKyMiJSEmLSQjJyIqNCYqLi0nKSguMjElKCcvMjElJSUuLi4oKCgmJiYjIyMpKSkfHx8fHx4gIR82NjY3Nzc9PT0zMzMsPTksPDguQDwqNTM4ODhEREQ+Pj5KSkohJCAlLCMlKiMwPiohISE0MQCWAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAGBJREFUGBl9wbEKgmAYQNF77SOEEpwbdBGlrbX3X1obhZYWe4EQt8oIF+k/hw+BbEOaEH6NJAk78edZOBtYCyhd7Lf2dA6sCcfHQacbaQFR+cqKaHO9nrxbeyGhOQPy1xsobgy1fOCtGAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 12&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/693gbCSts4gaMyAWyEUIEI/f9b053c10dac97c0b23a3649ecebc308/code_12.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/693gbCSts4gaMyAWyEUIEI/f9b053c10dac97c0b23a3649ecebc308/code_12.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/693gbCSts4gaMyAWyEUIEI/f9b053c10dac97c0b23a3649ecebc308/code_12.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/693gbCSts4gaMyAWyEUIEI/f9b053c10dac97c0b23a3649ecebc308/code_12.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;간단하지 않은가? 아래의 코드를 통해 전체 흐름을 다시 한 번 살펴보기 바란다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5vj6V4Le5GOuOaIkeSsmGs/23b52c5ec8507c5d5da8787097e6d7c5/code_13.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 11.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAFBAMAAADF4rO2AAAAIVBMVEX8/Pz5+Pvs6/b////39vuooeDx8Pr9/f3t6/iYkNr39vra4zdWAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAACJJREFUCNdjYBBiwATGrsnG6IChvIC9HB0wGHdiU8mFqRIA8bYaF8PE4x0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 13&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5vj6V4Le5GOuOaIkeSsmGs/23b52c5ec8507c5d5da8787097e6d7c5/code_13.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5vj6V4Le5GOuOaIkeSsmGs/23b52c5ec8507c5d5da8787097e6d7c5/code_13.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/5vj6V4Le5GOuOaIkeSsmGs/23b52c5ec8507c5d5da8787097e6d7c5/code_13.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/5vj6V4Le5GOuOaIkeSsmGs/23b52c5ec8507c5d5da8787097e6d7c5/code_13.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;h2 id=&quot;️-문제-1&quot;&gt;&lt;a href=&quot;#%EF%B8%8F-%EB%AC%B8%EC%A0%9C-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;⚠️ 문제&lt;/h2&gt;
&lt;p&gt;필요한 만큼 target을 기록해 둘 수 있지만, 앱의 확장성을 위해 보다 강력한 솔루션을 만드는 편이 좋을 것이다. target 목록을 유지하면서 우리가 필요할 때 재실행할 수 있도록 알려주는 클래스 같은 것 말이다.&lt;/p&gt;
&lt;h2 id=&quot;-해결책-의존-클래스&quot;&gt;&lt;a href=&quot;#-%ED%95%B4%EA%B2%B0%EC%B1%85-%EC%9D%98%EC%A1%B4-%ED%81%B4%EB%9E%98%EC%8A%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 해결책: 의존 클래스&lt;/h2&gt;
&lt;p&gt;이 문제를 해결할 수 있는 방법 하나는 위의 동작을 표준 프로그래밍 옵저버(observer) 패턴을 구현하는 &lt;strong&gt;의존 클래스&lt;/strong&gt;로 캡슐화(encapsulation)하는 것이다.&lt;/p&gt;
&lt;p&gt;그래서, target 같은 종속물(dependencies)을 관리하기 위해 자바스크립트 클래스를 만든다면 아래와 같은 형태가 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dep&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// dependency의 약자&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscribers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// subscribers는 클래스에 종속되어 있는 target 함수들을 저장하는 배열.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// notify() 메소드가 호출되면 배열에 저장된 함수들이 실행되어야 한다.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;depend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 앞서 작성한 record 함수를 대체한다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscribers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// target이 subscribers에 저장되어 있지 않아야 한다.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscribers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 앞서 작성한 replay 함수를 대체한다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscribers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sub &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// target 또는 observer를 실행한다.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 &lt;code class=&quot;language-text&quot;&gt;storage&lt;/code&gt; 대신 &lt;code class=&quot;language-text&quot;&gt;subscribers&lt;/code&gt;에 익명 함수를 저장하며, &lt;code class=&quot;language-text&quot;&gt;record&lt;/code&gt; 함수 대신 &lt;code class=&quot;language-text&quot;&gt;depend&lt;/code&gt; 함수를 사용한다. 그리고 &lt;code class=&quot;language-text&quot;&gt;replay&lt;/code&gt; 함수 대신 &lt;code class=&quot;language-text&quot;&gt;notify&lt;/code&gt; 함수를 사용한다. 이 클래스를 작동시켜 보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dep &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 인스턴스 생성&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; price &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; quantity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; total &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;target&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; price &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; quantity &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

dep&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;depend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// subscribers에 target 함수를 추가한다.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// target 함수를 실행한다.&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;total&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; 10 .. 원하는 결과가 맞다.&lt;/span&gt;
price &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;total&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; 10 .. total이 자동으로 업데이트 되지 않았다.&lt;/span&gt;
dep&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// subscribers에 등록된 함수들을 실행한다.&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;total&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// =&gt; 40 .. 제대로 된 결과를 얻었다.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;잘 작동한다. 이제 우리의 코드를 재사용할 수 있을 것 같다. 여전히 이상하게 느껴지는 건 &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;을 등록하고 실행하는 방식이다.&lt;/p&gt;
&lt;h2 id=&quot;️-문제-2&quot;&gt;&lt;a href=&quot;#%EF%B8%8F-%EB%AC%B8%EC%A0%9C-2&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;⚠️ 문제&lt;/h2&gt;
&lt;p&gt;개발이 진행되면 변수마다 Dep 클래스를 생성하게 될 것이다. 그리고 업데이트를 감지하는 역할을 하는 익명 함수(target)를 생성하는 과정을 캡슐화하면 좋을 것 같다. &lt;code class=&quot;language-text&quot;&gt;watcher&lt;/code&gt; 함수가 이런 역할을 할 수 있다.&lt;/p&gt;
&lt;p&gt;그러면 아래의 코드를 실행하는 대신:&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3d2g6Qow52S2yegW0s8i6o/d331560224d530ce5fe88e6ba180da64/code_14.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 12.375%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAFCAAAAAASp/FZAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAFpJREFUCNeFyjEOQDAYgNGe5OuvUtW0DGjoIBERwv0PZBUDb34KAQGLNoKUIKBBDIYnhc/nYl2q+ujzFkKeso/rOk8pvuM1HPtytHxTuCaNweniNxah87Xlzw1kwRtsMnisnAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 14&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3d2g6Qow52S2yegW0s8i6o/d331560224d530ce5fe88e6ba180da64/code_14.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3d2g6Qow52S2yegW0s8i6o/d331560224d530ce5fe88e6ba180da64/code_14.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/3d2g6Qow52S2yegW0s8i6o/d331560224d530ce5fe88e6ba180da64/code_14.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/3d2g6Qow52S2yegW0s8i6o/d331560224d530ce5fe88e6ba180da64/code_14.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;아래처럼 실행할 수 있다:&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4JUiQgB0SAwKe8EKa4KWEC/5b63da4fdae41cf172f9b1ef3e49555f/code_15.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 15.625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAGCAAAAACUM4P3AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAExJREFUCNdjkCMSMMjJOdrbOZia6uoa6hFQqKyrq6thoKahqievpKaiqqOhgEOhnJaVtY6GrqmxpZKmg7WluY4RDoUmukS6UZUozwAASlsfRC3SfoMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 15&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4JUiQgB0SAwKe8EKa4KWEC/5b63da4fdae41cf172f9b1ef3e49555f/code_15.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4JUiQgB0SAwKe8EKa4KWEC/5b63da4fdae41cf172f9b1ef3e49555f/code_15.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/4JUiQgB0SAwKe8EKa4KWEC/5b63da4fdae41cf172f9b1ef3e49555f/code_15.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/4JUiQgB0SAwKe8EKa4KWEC/5b63da4fdae41cf172f9b1ef3e49555f/code_15.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;h2 id=&quot;-해결책-watcher-함수&quot;&gt;&lt;a href=&quot;#-%ED%95%B4%EA%B2%B0%EC%B1%85-watcher-%ED%95%A8%EC%88%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 해결책: Watcher 함수&lt;/h2&gt;
&lt;p&gt;Watcher 함수 내부에서 몇가지 간단한 작업을 할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myFunc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myFunc  &lt;span class=&quot;token comment&quot;&gt;// 현재 처리하고 있는 target으로 myFunc을 설정한다.&lt;/span&gt;
  dep&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;depend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// target을 종속물(=종속된 코드)로 추가한다.&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;         &lt;span class=&quot;token comment&quot;&gt;// target을 실행한다.&lt;/span&gt;
  target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;// target을 초기화한다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;확인할 수 있는 것처럼 &lt;code class=&quot;language-text&quot;&gt;watcher&lt;/code&gt; 함수는 &lt;code class=&quot;language-text&quot;&gt;myFunc&lt;/code&gt;를 인자로 전달받고, 전역 변수인 &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;으로 할당하고, &lt;code class=&quot;language-text&quot;&gt;dep.depend()&lt;/code&gt;를 호출해서 subscriber에 추가하고, &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;을 호출한다. 그리고 &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;를 초기화한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/B1B4Z9sCaGESW2KqAIwIs/b02b58726f16be3e3f37bad0c54617da/code_16.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 18.250000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAHCAMAAABN2v+8AAAAk1BMVEUeHh4gICAlJiQvLy8/Pz82NjZDQ0MlJSUsLCwuMCw2OjMhJSQpNjMrOTUrOjcqODUnMC4yMjI1NTU9PT08PDw3NzcfHx8uMjEkJiUmKSglKCcvMTExMTEzMzMkJCQjIyMmJiYnJydCQkI+Pj4tLS04ODgpKSlAQEAsPTksOzguQT0qNTNLS0s6OjpBQUFFRUUiIiItfKw5AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAFJJREFUGNNjYGBgYARiJiYG/ICRgZkRAr4RUsgNUfeVh5HxP9NrnApZYAohNCGF74UYue/8xWs1M8NvIRYWLta3fGIftCX+fGcgCOTYgYQBbnkAtkENws8qEKoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 16&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/B1B4Z9sCaGESW2KqAIwIs/b02b58726f16be3e3f37bad0c54617da/code_16.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/B1B4Z9sCaGESW2KqAIwIs/b02b58726f16be3e3f37bad0c54617da/code_16.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/B1B4Z9sCaGESW2KqAIwIs/b02b58726f16be3e3f37bad0c54617da/code_16.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/B1B4Z9sCaGESW2KqAIwIs/b02b58726f16be3e3f37bad0c54617da/code_16.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2yjXqHR7Q0W2IuowKgawSY/1d3a11f8da69db61388e6bd5bd490590/code_17.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 13.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAFBAMAAADF4rO2AAAAIVBMVEX8/Pz8/Pv4+Pr19fn////NyeyrpOLBu+iwquP49/rw7/fWdQrtAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAACJJREFUCNdjYBAyYMAALq4pLugAKCyARaV7CxaVnAswFAIAbQoMAD+cPl8AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 17&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2yjXqHR7Q0W2IuowKgawSY/1d3a11f8da69db61388e6bd5bd490590/code_17.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2yjXqHR7Q0W2IuowKgawSY/1d3a11f8da69db61388e6bd5bd490590/code_17.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/2yjXqHR7Q0W2IuowKgawSY/1d3a11f8da69db61388e6bd5bd490590/code_17.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/2yjXqHR7Q0W2IuowKgawSY/1d3a11f8da69db61388e6bd5bd490590/code_17.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;당신은 아마 왜 &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;을 함수에 직접 전달하지 않고 전역 변수로 설정했는지 궁금할 것이다. 이렇게 구현한 데에는 이유가 있으며, 이 글의 다 읽으면 그 이유가 명확해지고 좋은 방법이었다는 것을 알게 될 것이다.&lt;/p&gt;
&lt;h2 id=&quot;️-문제-3&quot;&gt;&lt;a href=&quot;#%EF%B8%8F-%EB%AC%B8%EC%A0%9C-3&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;⚠️ 문제&lt;/h2&gt;
&lt;p&gt;우리는 하나의 &lt;code class=&quot;language-text&quot;&gt;Dep&lt;/code&gt; 클래스를 가지고 있다. 하지만 우리가 정말로 원하는 것은 변수마다 그만의 Dep 클래스를 가지는 것이다. 더 진행하기 전에 값들을 객체의 속성으로 옮기도록 하자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tEqUKxxf20MyYYmSM4EYG/4819890669f1be5e0018b28ece5f1980/code_18.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 7.375000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAADCAMAAADWS72qAAAASFBMVEUeHh4hJCcpMz0lKjBDQ0NAQEA2NjYsLCwxMTEsMTQvNjo2P0UzOT0fHx84OzUmKSs5REs6RUwxOT41PkMyODsiIiEtLyseHx+pYF+5AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAABhJREFUCB1jIBYwMjBiBZ9+MBABxBkwAQB2ngIZgocwgAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 18&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tEqUKxxf20MyYYmSM4EYG/4819890669f1be5e0018b28ece5f1980/code_18.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tEqUKxxf20MyYYmSM4EYG/4819890669f1be5e0018b28ece5f1980/code_18.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1tEqUKxxf20MyYYmSM4EYG/4819890669f1be5e0018b28ece5f1980/code_18.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1tEqUKxxf20MyYYmSM4EYG/4819890669f1be5e0018b28ece5f1980/code_18.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;각각의 속성(&lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;quantity&lt;/code&gt;)이 각각 Dep 클래스를 가지고 있다고 잠시동안 가정해보자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/HfkmmLiBmSMCoKmSACcc4/41376cd5afaf022b770ffdf92b8d775d/code_19.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 45.24999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAASCAMAAAAewWyUAAABX1BMVEUAAAACBAgzX4o/cp0fOlg/cZ0+cJsPHjAHDxpeotNqs95YlLhfoMZencNgospencRgoslho8teo9M+cJxips5YkrZcm8Fmq9VbmL5ipc1fn8Zpst0iQGBSiKpRh6lNf59al7xdncNPg6RWkLRXkLQHDxlPg6NQhKVho8pensVVjrFWkLNor9kCAwYvWIJdk8Vom8pek8YvWYQaM05OhLhomslAc6QNGSoMGSvr8PX////y9fgNHDGitMxWaIAMGizu8vaFq9F3qNF3p9CBqM/09/kNHTKlts+sxN5uo9Buo89tos9rn83a4+5Za4Nlns1fpchanr9eo8ZYm7phpsthnMyXuNljqdBXmrhTlbBbnr9bn8BWmbdcocNjqtjK1+h9ps5vpNBtocx4o823y+F9qtJ7qM56p8x7p9Df5/EMGi2lts5ZaoMGDBaBlbCWqL6EmLMHDhlUaYWVp74pOE61pRJDAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAANhJREFUGBmNwTEvA2EAgOH3/frJNZUcuhiEySYsIrFYLX5A/6mBTSxmiYEVizQdKsep9pxLmnxNl+95yCUqK5qmiS5I9awjhYI6jyo19KvvrS8YqPOo1eYEhq+RzlA/hrbegf2XaYTZrtZ9P/ec0JJjlYTOHk/9JbGhD8K5ktB7uJiTindEKPyhEGwgVAwEBlNKAV30HLMNkU55e2nr+qpiqXRpTCvS2RnZAKPwxlKpDRCUf5HOM2ueWBHIFMgUyBTIFMgU4Ih1h6w4gAi9E0noDRRnJKLk+wMeQS5TBdesdwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 19&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/HfkmmLiBmSMCoKmSACcc4/41376cd5afaf022b770ffdf92b8d775d/code_19.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/HfkmmLiBmSMCoKmSACcc4/41376cd5afaf022b770ffdf92b8d775d/code_19.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/HfkmmLiBmSMCoKmSACcc4/41376cd5afaf022b770ffdf92b8d775d/code_19.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/HfkmmLiBmSMCoKmSACcc4/41376cd5afaf022b770ffdf92b8d775d/code_19.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;이제는 아래의 코드를 실행한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2iPrdVNxJuyw2QiscaSQog/87c49d0943c3d0884a895d96f3b4d98c/code_20.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 14.124999999999998%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAGCAAAAACUM4P3AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAGFJREFUCNeNyl0SQCAUBtBW8plSQkqp63+MDRj73413hnGeDwOQgYNLAALvGLCd87HuY7HW+juisj5IlbrSGm+MIKed8dToR1Qx9pxPlIaSWpXPMQ4LdbW/xxDwAwNk/ideC98goHhfnN0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 20&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2iPrdVNxJuyw2QiscaSQog/87c49d0943c3d0884a895d96f3b4d98c/code_20.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2iPrdVNxJuyw2QiscaSQog/87c49d0943c3d0884a895d96f3b4d98c/code_20.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/2iPrdVNxJuyw2QiscaSQog/87c49d0943c3d0884a895d96f3b4d98c/code_20.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/2iPrdVNxJuyw2QiscaSQog/87c49d0943c3d0884a895d96f3b4d98c/code_20.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;watcher에 전달된 익명 함수(target) 내부에서 &lt;code class=&quot;language-text&quot;&gt;data.price&lt;/code&gt; 값이 사용되었다. 재실행할 코드에서 &lt;code class=&quot;language-text&quot;&gt;data.price&lt;/code&gt; 값이 사용되고 있으니 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt; 속성에 연결된 Dep 클래스의 subscriber 배열(저장소)에 이 익명 함수를 추가(&lt;code class=&quot;language-text&quot;&gt;dep.depend()&lt;/code&gt;)하고 싶다. 익명 함수 내부에서 &lt;code class=&quot;language-text&quot;&gt;data.quantity&lt;/code&gt; 값도 사용되었으니 &lt;code class=&quot;language-text&quot;&gt;data.quantity&lt;/code&gt;에 연결된 Dep 클래스에도 같은 작업을 하고 싶다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/QKhOY7qGA0WwGKGIkGMWS/682c986194cd2326f369378b71efbc33/code_21.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 66.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAbCAMAAAA5zj1cAAAC+lBMVEUAAAAVIxlno3tjnXcQHBQGEAgHFQpoiW5ynnuNsaBXclwJHA0LIxQOKR0MJhkNJxoLJBYWKxoUKRkKIA8QIxYIHw0TKRg9YzuXyYiSx4ovRCsJGwwWNjISMCkQLCMTMSsOKR4dMSIYLRwNIRIgKygLIREZLR0pSCYoRiUJHAwOLBoPLx0SNSMQMB4QJhYTNiQTNyUNKBcQMR8PLhwRMiALIhEZLh8UNyURMyELJBMSNCIVOScNKxkRMSALJxULIQ8LIhAKJBIMJxUNKhgJIA4OKxkNKRcULx4LJRMMKBYRKBcLJhQlOCkIFQoKGw0JGgwKGgwIDQoGCgcAAQECAwYcMiQMFQ8BAgQBAgMwWIFXmc1WmcwZMUwiPSwGCwgHDxpPjsNHgbUCBAdAc55qs95Rh6hUi65Wj7Jcm8FXkrZUjK5ho8slRGQkQC4LFylkq9ljps5OgaFSiatfoMdfn8ZYk7dWkLRZlbpbn9AECA49bplqst5dnMNgoMhZlLhgospencRfoMhkqNFpst4hP18tUToBAgEKFSZiqNdnrthdnMJjps9fn8dhostYm84DBgsIEB1qi7ahutehuddIaJE8bU8ECAYAAQIhPWKku9iovtqUsNITJ0OwwNb////m7PTW4O3v8/d0hZs2YUYIDgsrO1L3+fvE0uXD0uXH1eb+/v7u8vcLGCl1pM9hp9Zdoc1anshbn8tfpNOZudgvVD0PGxTM2OpfpMtgps5dosliqNJWmMzz9vl3p9Fnr9xgps1eo8ldocdlrNeautkDBQQlQTAZLCDM2epcoNFSk7hTlLlZncZbnshYm8NSlMn09vrB0eXl6/MVJhsJEAwdMyX6+/3U3+zY4e0aLSEYKx8TIhkSIBgOGRIHDAkPGhMNFxETIBgaLiItP1V3j580STg4TTxofI8kQC8BAQE5VGRmd2l7jIZhe4lfdIYqO1I7Tz9YbGRviKWFnKmFnKiJn6xMYHoZJzubsMCNorCNo7CSqb8HDxsBAQMBAwXDamxnAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAZZJREFUGBmNwU9vy3EcwPH3+9dPt3btqjNsxwUHLBKPwsnFSETCTVyIeBSegKO4kTh4CC4Sibg4yoQQRCyZkWXdotM/vr92WX9bLbxe/C/5B5MOEAyV5e9MOkAwNOFIn4JM3QKCoaYjPynoHXaZJBhqfD9qgv6eqJv0StqaNrdMEuxYVPsZ9kra69qpbn9baNW6UaZFLthRf7PBPquskJwhFwyd9CwHkANdXGJMcEn2eAohV7oUxRO5KkX6iOvqNkWTBlOC2g3tZ22oQPP+HTepm2zWdQ2OGMwJajd6JW1TEebv6QYN12rMrTcNmDU4pozoV2iqM0ITOATT6I3g7kNRBuwSt6HhtcdtKkpOW9MS5Bom/axTXiU3I0nzV1X6mdoCglzTdkXtlFdJXijJjAPoZyDIfTnHgOQaStKQAV+SBLneawomTr0leUVRxriMjDHBmPdSWmK/YL+PLnD8k8/YK+C8jKi3YOXDiQvbjEw+J6B6moLlWWCeHy4y8g6EdWWX1shtKbvUckDtJgXrDExdbjDyoMQfc39W1DQmbOkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 21&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/QKhOY7qGA0WwGKGIkGMWS/682c986194cd2326f369378b71efbc33/code_21.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/QKhOY7qGA0WwGKGIkGMWS/682c986194cd2326f369378b71efbc33/code_21.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/QKhOY7qGA0WwGKGIkGMWS/682c986194cd2326f369378b71efbc33/code_21.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/QKhOY7qGA0WwGKGIkGMWS/682c986194cd2326f369378b71efbc33/code_21.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;만약 &lt;code class=&quot;language-text&quot;&gt;data.price&lt;/code&gt;를 참조하는 또 다른 익명 함수를 가지고 있다면, 그 함수는 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt; 속성의 Dep 클래스에만 추가하고 싶다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5YdhXNeGyWYkSwICoiugqo/0b4fbe1969052491f0d54289bda901b0/code_22.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 28.249999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAALCAMAAAA6GD/HAAACB1BMVEUAAAAGCxIVKDwVKTwFChEFCxIFCxEmRF9qs95cmb9bl7xmq9Vho8tal7xipc0jQFkkQl1bmL1mq9RalrskQlwBAQEDBQM4VT9rnX0uUFxdmspmo9FgmMRckbtkn8xim8delL5dmskXKz8YLUJhmcVdkrxelcBinMhcmskFDgcLJBUNJxoNKBwQKBkSKBcNIRIPJRQIHw1slWh/rIM2RzW9yNf////e5vHS3ey7xtW/ytnY4+/M2uva5PC5xNMOKh0PLSASLh4SKxoOJBUPIxQRKxoLJRMKIxEKIhEKIQ8XLBYrRiUFDAa+ydh7sNdlrNVhp81fpMhnrth8sNi8yNbAy9p6sNddocReo8Vgpst+stkOJBMNKhgOLBoNKRcMKBcLJhQPJxYPLhwRLhwMJxYMJxUQJhUYKRUSJRLI2eq50ObJ2erQ3uzC1efR3+0TKRgLIhAKIxAaPSUVNB0YOiKZqbSttq6Yp7IBAwEDCAQGDQgSIhgYLR4UJhoPHRMYKh5IYGUMJBFIYGQNDQ02NjY6OjouLi4rKysqKiozMzMvLy8oKCgnJycxMTElJSU8PDw1NTUDAwOFl6CFkoeElp+HmaOHlIqElZ8ODg4eHh4fHx8iIiIgICAkJCRacHYwRDRZb3VacXcCAgIFBQUHBwcGBgYEBAQICAgKCgodJzQtOEYeJzUdJjNKAvXDAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAANRJREFUGBldwbFOwlAAQNF76ysI1LAUHJo46+bsxGji4Ef4X36LupD4DSwGFxMnoxFp8Un7iCGcIwdUkpi5kSSGwIGe2FnTd8Wgydf9ek3hRPb4LzY9/Xbgj30/T+pQ2kHZMgFiPJpz1eQCjo+fApbuxMzW26mtZQXVS6WuBouawKVJ3bP1CpWAS1pTARcQKH0EZurmAajOL+jMZOuZnXAr13T0RlFJZF8YuUOTm5AIM0mch4lfxcfY+J5P72jdSyIUvyQSho4stG7OMjojSSIMI0nGH2DxMW3cOECpAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 22&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5YdhXNeGyWYkSwICoiugqo/0b4fbe1969052491f0d54289bda901b0/code_22.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5YdhXNeGyWYkSwICoiugqo/0b4fbe1969052491f0d54289bda901b0/code_22.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/5YdhXNeGyWYkSwICoiugqo/0b4fbe1969052491f0d54289bda901b0/code_22.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/5YdhXNeGyWYkSwICoiugqo/0b4fbe1969052491f0d54289bda901b0/code_22.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;추가되는 watcher는 속성들 중 하나에만 적용될 것이다&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;어느 시점에 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;의 subscriber에 연결된 &lt;code class=&quot;language-text&quot;&gt;dep.notify()&lt;/code&gt;가 호출되어야 할까? 나는 그 순간이 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt; 속성에 값이 할당되었을 때가 되길 바란다. 모든 것이 구현되면 콘솔에서 아래와 같은 출력을 보고 싶다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/7E5W4rbOKWWeG44M0mOcsG/afe049510ee2dd5c183d5cbdfa674c0d/code_23.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 15.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAGCAAAAACUM4P3AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAF9JREFUCB2NwdsRwyAMBEBVogzi9MBYgM1H+u8sJZBdKogAn5Hd1zQ+I03VIp3VXBqqFUitIYAVq+quFS4GJm3fnfJyXBsjd3qGPrfvNm3F7Kulrz7mhzDydT4jLP7LD1RGH/GI3Y7VAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 23&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/7E5W4rbOKWWeG44M0mOcsG/afe049510ee2dd5c183d5cbdfa674c0d/code_23.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/7E5W4rbOKWWeG44M0mOcsG/afe049510ee2dd5c183d5cbdfa674c0d/code_23.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/7E5W4rbOKWWeG44M0mOcsG/afe049510ee2dd5c183d5cbdfa674c0d/code_23.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/7E5W4rbOKWWeG44M0mOcsG/afe049510ee2dd5c183d5cbdfa674c0d/code_23.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;우리는 data 속성(&lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;나 &lt;code class=&quot;language-text&quot;&gt;quantity&lt;/code&gt;)에 원하는 동작을 연결할(hook into) 어떤 방법이 필요하다. 그러면 target 함수 내부에서 어떤 속성이 참조되면 그 속성의 subscriber 배열에 target을 저장할 수 있고, 속성이 변경되면 subscribers 배열에 저장된 함수를 실행하도록 만들 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;-해결책-objectdefineproperty&quot;&gt;&lt;a href=&quot;#-%ED%95%B4%EA%B2%B0%EC%B1%85-objectdefineproperty&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 해결책: Object.defineProperty()&lt;/h2&gt;
&lt;p&gt;ES5에서 제공하는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Object.defineProperty&lt;/a&gt; 함수에 대해 공부할 필요가 있다. 이 함수는 우리에게 객체 속성의 getter와 setter 함수를 정의할 수 있도록 한다. 이 함수를 Dep 클래스에 적용하기 전에 기본적인 사용법을 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; quantity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defineProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;price&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// data의 price 속성에 대해서만 정의한다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// get 함수 생성. price의 값을 참조하는 역할&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;I was accessed&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newValue&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// set 함수 생성. price에 새로운 값을 할당하는 역할&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;I was changed&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4uQsoyh2z62o0Ogimmgaos/4c434eacdee1ffc40bea19be753a8959/code_24.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 43.24999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAARCAMAAACYVR46AAABiVBMVEUeHh4fICIeHx8hISEfHyAiIiEfHx8gISEgIB8hJCcnLzcoKClAQEAyMjIoKCgpKSktMzcsMjUyOj4iIiItLyssMTQzO0AyOj8uMzYmJiUnJyckKyouQD0pNjMmLiw7Ozs2NjY8PDw1NTU6OjpDQ0MlJSUuKig2Ly0zLSopJiUkJCQhJCAqMyYhJSAnMCUpMiYiJiEpMyYlKyMmLCMoMSUlKiMnLyUrNigoMCUpMSUrKysjIyMhIyAmLSQnLyQjKCIjJyElKCcrNDIoMS8iJiQxMTEwMTApKCUpJyUwLCkoKCQtKicwKykvLCgwLSknKCYfISEjKCciKCYhIyMuLi4jISEkIiIqJyUjIiEpJiQnJCMmJCMgICAfHx4eHx49PT00NDQqKiolLCMkKSIrNSchJSQpNzQqNzQlKiktKScuKSc7My8oJSQ5Mi5ANjIuKyktLS0gIyAkKiIiJSAnLiQiJiA5OTkvLy8wMDAnKCcoKicoKycjJyImLiQlJiQkJSMfIB8iJSEgIR+dxJuxAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAP1JREFUGBmdwT1LAnEAx/Hv7x7+WahHkArawxBEEAQONhgtbS0Nzl5b9BZ6F429iGhoD1oKQcTX0NAZFKIdhT1Q1tmSnsPR5wOysMHGARfMDCnXECdmNeYt1V1Qh/8TJY3w1c1LQ0uRYBGNaZCYgGyugPrzCr3Qe0n3cFvECdg2EvrjgjgH6K1JQ+u+qJGhdcsUDvDxeENChrpt6hifX/4Bk2ygWNt8CjL7dnpu2S6sr+T2eFhqM0H8OJTQ4NxX5NM56zBd1ZCE55GEqAa7dxu0Ko1qUxVJjWumEd7Re7+gyOAya62WntsqnxDjkFdz65TXYylTuyqHDjuuiPsGKi03dfKzNEgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 24&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4uQsoyh2z62o0Ogimmgaos/4c434eacdee1ffc40bea19be753a8959/code_24.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4uQsoyh2z62o0Ogimmgaos/4c434eacdee1ffc40bea19be753a8959/code_24.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/4uQsoyh2z62o0Ogimmgaos/4c434eacdee1ffc40bea19be753a8959/code_24.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/4uQsoyh2z62o0Ogimmgaos/4c434eacdee1ffc40bea19be753a8959/code_24.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1cTEyUtfSAoAyc2QUsg6cu/042113d4c1c1174d91b917dd78a2b1c3/code_25.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 13.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAFCAMAAAAAEl63AAAAdVBMVEX9/f39/P3u7+/8/Pz4+Pj////S09X6+vq5u763ubvb3N3Z2tu9vsDMzc++wMLLzM29v8G0trjV1tfW2NnLzM7Oz9Hn5+jo6enP0NLIycvU1dbQ0dLGx8nq6+vr7Ozo6Ony8vLz9PTv8PDm5+fx8vLq6uvs7e0ePBKWAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAD9JREFUCNdjZGBgZGRkgAPGPwzYASMrWCUCfMahkOUfA3GAhYmBge8LL8y8759wKvzNwMD1gQui7LkU11tcCgEmHAoReMnRggAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 25&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1cTEyUtfSAoAyc2QUsg6cu/042113d4c1c1174d91b917dd78a2b1c3/code_25.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1cTEyUtfSAoAyc2QUsg6cu/042113d4c1c1174d91b917dd78a2b1c3/code_25.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1cTEyUtfSAoAyc2QUsg6cu/042113d4c1c1174d91b917dd78a2b1c3/code_25.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1cTEyUtfSAoAyc2QUsg6cu/042113d4c1c1174d91b917dd78a2b1c3/code_25.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;보이는 바와 같이 단지 2줄이 로그에 표시된다. 하지만 실제로는 어떤 값도 가져오거나(get) 할당하지(set) 않는다. 우리가 get, set 함수를 새로 덮어씌웠기 때문이다. 그러니 다시 필요한 코드를 추가하도록 하자. &lt;code class=&quot;language-text&quot;&gt;get()&lt;/code&gt; 함수는 값을 반환해야 하고, &lt;code class=&quot;language-text&quot;&gt;set()&lt;/code&gt; 함수는 값을 갱신해야 한다. 그를 위해 &lt;code class=&quot;language-text&quot;&gt;intervalValue&lt;/code&gt;라는 변수에 현재 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;값을 저장한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3NRYFUJrcsesiQkwukw6MW/c14a2e1d4b0827561a5455b072829534/code_26.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 54.125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAWCAMAAACFUC6CAAABwlBMVEUeHh4fISIeHyAhISEgICAfICAiIyIgISInLzYiJilAQEA6OjooKCgpKSksMjYsMjUyOT4iIyMtLislKCo2QEY0PUItMzYtMjYmJyUnJycpMToiJio5OTk8PDw3Nzc9PT0+Pj4vLy87Ozs0NDQ2NjYlJyUkKSIoMCUlKyMjKCImLiQpMiYiJiEqMyYfHx4gIyIpNjMnMC4lLiwuLi4yMjI4ODgxMTEwMDA1NTUzMzMkJCQoJSQwKykvKigmJCMiIiIhIyAhJCAlLCMiJSAkKiImLCQoMSUjJyEiJiUhJSQjIyMlJSUmJiYjIiEjISElIyIhICAfIB8eHx4gIR8iJSEgIiAgIh8fIR8gIyApKiojJSMmKiQrMCYoLyUkJyInLyUnLiQpMCcfHx8iISEqODUmLixBQUEpJiQ7My80LisxLCk1LywqKiooMDgnLjUlKiMmLSQsMTAsMjEtLy8oJiQqKCUsKyYqKiUoKiQqKSYoJyQnKSQmKCQxNDAtMSwlJCQoMC4mLy0pLi0kIyIvKygrJyY0LiwyLiwrKCYtKScrKyssLCxEREQqNCYpMyYpKicuMCwmKyQmLCMoLyYiIiEhISBRP6MHAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAaVJREFUGBmlwftX0lAAB/BvgWRuq+xN3S1w1GYO0uDymNXuTNLGSi+MZU9rWWlRYu/sZaX20N71/3ayXzxiHk59PgDWrQ+FEUILEAFaNiAcCWNVrRvbBFHatHlL+9Zt23fs3BXZHcX/2bOXyMq+mBAXYx0qSew/oOmdB7UuI5lM4Z8c6u45nFYycoZmc/mC2Xvk6DHLZEyz9b7jmqH3G8UTfQP4bXDwpOmUHEeyxKhrnjo9NGxZvFxJeYxXmc+Yb5wZwR+maFlln1UZ5yN+1bPxN7G0e9Y/d/7CxUv6qKddvmIFgBPC1TBWGhvrvkau3xifGL95a6KXqpRShcg1CSu13r5TE6ggtxEHa0mjSQGalImriazrcq9zsmh7ulEfrWNV4tTU3Xvx3P0HDx89fjL9dPrZ8xc16aWJRj0zr16rHW9m5+bfvns/NDecz+aybgkNAoUkKMnnRCILAdbwAc0IKJokywviolxQBam2WKAxkigZ7KM2kOzq/1THMiFFXUgXxM9fKl5R975+s79P2lhSAcdyUdd1Svjxs1xOMW4z7tscjX4BhydX6FzRLagAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 26&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3NRYFUJrcsesiQkwukw6MW/c14a2e1d4b0827561a5455b072829534/code_26.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3NRYFUJrcsesiQkwukw6MW/c14a2e1d4b0827561a5455b072829534/code_26.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/3NRYFUJrcsesiQkwukw6MW/c14a2e1d4b0827561a5455b072829534/code_26.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/3NRYFUJrcsesiQkwukw6MW/c14a2e1d4b0827561a5455b072829534/code_26.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;&lt;code class=&quot;language-text&quot;&gt;internalValue&lt;/code&gt;에서 실제 값을 관리한다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;이제 우리의 get, set 함수는 제대로 동작한다. 콘솔에는 무엇이 출력될까?&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tVHXhJ2NywG20000Og0oQ/e39bb0f6c4fbd2eddb7946ef1b1423e0/code_27.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 10.375%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAECAMAAADLTo0SAAAAhFBMVEX29vb9/f3x8fLt7e7l5ubu7u7u7u/09PTx8fHp6erz8/P7+/vq6+v39/f+/v7u7+/R09Tn6Ojj5OXa2tzc3N7j5OTn5+jY2drm5+jr7Ozh4eLh4uPl5ufx8vLr6+zw8PHq6uv4+Pjf4OHm5uf19fXg4eLs7O3j4+Tm5+fi4uPp6urk5eUNY3gCAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAElJREFUCB11wbEOQDAARdF3tQbS2rD5/0+TWiQtA4LEYGrPQfyumk0l1vN5KiC5SiVWauE2kHw4+gYWZdnIAGsHbh9nE6YzKusFTl8QLa/Q/OQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 27&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tVHXhJ2NywG20000Og0oQ/e39bb0f6c4fbd2eddb7946ef1b1423e0/code_27.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tVHXhJ2NywG20000Og0oQ/e39bb0f6c4fbd2eddb7946ef1b1423e0/code_27.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1tVHXhJ2NywG20000Og0oQ/e39bb0f6c4fbd2eddb7946ef1b1423e0/code_27.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1tVHXhJ2NywG20000Og0oQ/e39bb0f6c4fbd2eddb7946ef1b1423e0/code_27.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;이렇게 값을 가져오고(get) 할당할 때(set) 알림을 받을 수 있게 되었다. 그리고 재귀(recursion)를 조금 사용하면 모든 속성에서 이 기능이 작동하도록 할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6UIHU7bIjeyUaWM2cqAUgW/94c021236a39295d6f53b7b822ebea57/code_28.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 45.62500000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAASCAMAAAAewWyUAAABgFBMVEUeHh4fICEfHyAhISEgICAeHx8fICAeHh8hIiEfHx8gISEgISIiJSkjKCwzMzM3NzclJSUmJiYtMzcqLjEqLjAmJyUkJSYyOT4wNzspLjEpLC4lJiQhJSQfISEiIiIkJCQjIyMfHx4gIR8gIh8eHx4fIB4fIB8fIR8lLCspNTQnMjEtLi44ODg2NjY+Pj45OTkoKCgyMjI7Ozs8PDwtLS0wMDAhIyAiJSEkKSIlKiMnLiQlKyMmLCQkKiMiJiEjJyElLCMoMCUkKCInLyUmLCMkKSMmLjIkKSwxMzM0NDQnJyc6OjopKSklLSwrNjQsNTMvLy81NTUxMTErKysvMTAmKCgoKyoiIyIkIiEmIyIjISEnMTAoMzIqLSw3Mi8xLSw6MzEtKysoJycsLCwjKC0kKC0jJywuLi4vMTE0NjY2OTkyMzMkJCMlIyInJSQkIyIkIiIiIiErMzEsNDMtNjQvMjFAQEAqKioyLSssKSg5MzE0MzIuKSc3ODYtLSwoKidqkcRKAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAUVJREFUGBmNwWkjAkEYAOCXCeWoGfc5M9UWYm3DrpD73oRtct+E3Dfr9tf5no3nASgoRC5ARcUlbvCUloEbgYPyCq8Pk8qq6prauvqGxiYCDpqLW5CHMspdnHNwIX8g6FeUkD8YCihKWGn1K+GwogBAW3uko1Pt0qKi29ejG6Q31tcfG4gP9g8NjwyM9o6NxyYmp+LwY3pm1tTUHl9CdM+pSQ8CJ/MLi5ZISc0QpolTaQyO1KXllVXK19Y3GEaIcEYJQRRBrs3I1nZXemd3b194D6yM0KUpEhhyoMOj46S0DGlSyEsnnBEOf8uenJ6dGxeXV9eYUHrDMCfkFn5zd//w+GQ/v7y+SYHfiWUaaQa/oBk9a0hBba+EfFgC/oXaFP4lrTOaIAxTGsWcEAxOsqpla/LjM8pl1hDJDDixLd3W6Zcb8vsG6Ow8YMcYGKIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 28&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6UIHU7bIjeyUaWM2cqAUgW/94c021236a39295d6f53b7b822ebea57/code_28.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6UIHU7bIjeyUaWM2cqAUgW/94c021236a39295d6f53b7b822ebea57/code_28.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/6UIHU7bIjeyUaWM2cqAUgW/94c021236a39295d6f53b7b822ebea57/code_28.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/6UIHU7bIjeyUaWM2cqAUgW/94c021236a39295d6f53b7b822ebea57/code_28.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Object.keys 함수를 사용해서 data 객체에 존재하는 모든 속성에 같은 기능을 적용했다&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;이제 모든 속성이 getter와 setter 함수를 가지게 된 것을 콘솔에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1JcJG2JcBaeC0SQCWMkCUC/c520b1479d8328e60b06ba94f1a47d92/code_29.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 13.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAFCAMAAAAAEl63AAAAh1BMVEX9/f3i4+Pk5OXm5ufn6On09PTk5ebr6+zl5ub3+Pjp6uvr7Ozw8PHt7u7q6+vv7/Du7+/y8vP////R09TY2drX2Nno6erOz9HNz9Dc3d7g4eL29vbs7O3x8vL29vfw8fHz9PTy8/P6+vrj4+Th4uLm5+fn5+jg4eHn6Oji4+T39/fa29z4+Pi01/D1AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAEhJREFUCB11wbsOQEAUQMFzZEUi4hX//2kq0SvQrLCV7s4I/i5iCVqLs3tycxFzRsUi1+4TEWF8Bj2q3uJdCSRY1K0fvFOtRD53Mg0+RraIcgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 29&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1JcJG2JcBaeC0SQCWMkCUC/c520b1479d8328e60b06ba94f1a47d92/code_29.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1JcJG2JcBaeC0SQCWMkCUC/c520b1479d8328e60b06ba94f1a47d92/code_29.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1JcJG2JcBaeC0SQCWMkCUC/c520b1479d8328e60b06ba94f1a47d92/code_29.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1JcJG2JcBaeC0SQCWMkCUC/c520b1479d8328e60b06ba94f1a47d92/code_29.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;h2 id=&quot;-지금까지의-아이디어를-합치기&quot;&gt;&lt;a href=&quot;#-%EC%A7%80%EA%B8%88%EA%B9%8C%EC%A7%80%EC%9D%98-%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4%EB%A5%BC-%ED%95%A9%EC%B9%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🛠 지금까지의 아이디어를 합치기&lt;/h2&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6pXKfL98TSCIWCa0UIe4im/3b4128522e354d2fb30f03a6efb49ffb/code_30.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 7.250000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAADCAAAAADE/hJEAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAADJJREFUCNdjkAMCeTnCgEFOzt7BW0nHxd3S3tbUVN/GxUXfzc3W2RiLQhiQR2YooisEABFHEKbX+gOJAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 30&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6pXKfL98TSCIWCa0UIe4im/3b4128522e354d2fb30f03a6efb49ffb/code_30.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6pXKfL98TSCIWCa0UIe4im/3b4128522e354d2fb30f03a6efb49ffb/code_30.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/6pXKfL98TSCIWCa0UIe4im/3b4128522e354d2fb30f03a6efb49ffb/code_30.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/6pXKfL98TSCIWCa0UIe4im/3b4128522e354d2fb30f03a6efb49ffb/code_30.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;위의 코드처럼 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;의 값을 사용(&lt;strong&gt;get&lt;/strong&gt;)한다면, 위의 코드(target)가 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt; 값에 의존하고 있다는 사실을 기록해두고 싶다. 그렇게 해 둔다면 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt; 값의 변경이나 새로운 값의 할당이 저장해둔 코드를 재실행할 수 있는 계기(trigger)가 되도록 만들 수 있다. 우리의 애플리케이션은 어떤 코드가 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;에 의존하고 있는지 알고 있기 때문이다. 따라서 getter와 setter를 아래처럼 동작하도록 생각할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get&lt;/strong&gt; =&gt; 이 익명 함수를 기억해라, 값이 변경될 때 다시 실행할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Set&lt;/strong&gt; =&gt; 저장된 익명 함수를 실행해라. 값은 방금 변경되었다.&lt;/p&gt;
&lt;p&gt;Dep Class의 경우에는&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Price가 사용되었다 (get)&lt;/strong&gt; =&gt; &lt;code class=&quot;language-text&quot;&gt;dep.depend()&lt;/code&gt; 함수를 호출해서 현재 &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;을 저장해라.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Price가 할당되었다 (set)&lt;/strong&gt; =&gt; price의 &lt;code class=&quot;language-text&quot;&gt;dep.notify()&lt;/code&gt;를 해서 모든 &lt;code class=&quot;language-text&quot;&gt;targets&lt;/code&gt;를 재실행해라.&lt;/p&gt;
&lt;p&gt;이 두가지 아이디어를 조합한 최종 코드를 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; price&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; quantity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 앞에서 선언한 Dep 클래스와 동일하다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dep&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscribers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;depend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscribers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// target이 존재하며 subscribers에 포함되어 있지 않을 때만 추가한다.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscribers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subscribers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sub &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// data 객체가 가지고 있는 모든 속성에 적용한다&lt;/span&gt;
Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; internalValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 각각의 속성은 자기만의 Dep 클래스 인스턴스를 가진다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dep &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// data 객체 속성의 getter, setter를 재설정한다.&lt;/span&gt;
  Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defineProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// getter가 호출된 시점의 target을 기억하도록 한다.&lt;/span&gt;
      dep&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;depend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; internalValue
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newVal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      internalValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newVal
      dep&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 저장된 함수(target)를 재실행&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// watcher는 더 이상 dep.depend를 호출하지 않는다.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 왜냐하면 get 메소드 안에서 자동으로 호출되기 때문이다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myFunc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; myFunc
  &lt;span class=&quot;token function&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 할당된 코드는 무조건 1번은 실행되어야 한다.&lt;/span&gt;
  target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// watcher에 전달된 이 익명함수에서 price와 quantity의 getter 함수가 실행된다.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// watcher가 실행된 시점에서 전역 변수 target에는 watcher의 인자로 전달된 익명 함수가 할당된다&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;watcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;total &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;price &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;quantity
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 코드를 실행하면 콘솔에 무엇이 출력되는지 확인해보자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4hdAI33Twkc0Yq8qQiSSGo/004811cdfae54cac304585f33a5bb986/code_32.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 33.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAOCAMAAABq1a50AAAAulBMVEX4+Pj09PXZ2tvq6uvn6Ojf4OHy8vL9/f34+Prp6PDz9PT29vf09fX7+/v////49/rb2PD39/fz8/TP0NLg4eLd3t/p6er28u7Hwuv8/Pz6+fzg3fT+/v76+vr29frv7vjW19nv7/D5+fn19PrQzO77+/zw8PH29vb9/Pz19Pn19fXb3N3k5ebe3+Da29zo6enj5OT38/Dw7vnh4uPr7Ozm5+f09PT4+Pnn5+vt7u7y8/Pu7u/u7PfV0e++mwHXAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAi2t97KAAAAHNJREFUGBmNwTELglAYQNF744ENoklLrQ0O/Yb+/94eNUZFBgVKEFTg7vfOEUcELBwRkNqBDDb+fWfaMynN9VU9CaWk7xsxG5XSOwE3eiWD69IPl77VI1Nk66h7MMWN9YEMiW6xqnRPIA2tSz0XBGTniQw/4ycVnzCOnboAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 32&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4hdAI33Twkc0Yq8qQiSSGo/004811cdfae54cac304585f33a5bb986/code_32.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4hdAI33Twkc0Yq8qQiSSGo/004811cdfae54cac304585f33a5bb986/code_32.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/4hdAI33Twkc0Yq8qQiSSGo/004811cdfae54cac304585f33a5bb986/code_32.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/4hdAI33Twkc0Yq8qQiSSGo/004811cdfae54cac304585f33a5bb986/code_32.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;우리가 하고 싶었던 것과 정확히 일치한다! &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;quantity&lt;/code&gt; 모두 확실히 반응한다! watcher 함수에 전달한 익명 함수는 &lt;code class=&quot;language-text&quot;&gt;price&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;quantity&lt;/code&gt; 값이 변경될 때마다 재실행되고 있다.&lt;/p&gt;
&lt;p&gt;Vue 문서에서 가져온 이 그림은 이제 이해가 될 것이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/ZaMyCGQCYwsesQeKAY4S4/329e5426c21a5c7a7d83e0339782ed0f/code_33.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAZCAMAAAB0BpxXAAACbVBMVEX////9/v7k7vjh7fj6/P7w8vP19vf5+fr8/f3S5PR+seBvqNx4rd7B2vD///3/4Yf/23D/3HL/88/x8/TCyM67wsnp6+7b6fZxqdzF3PH//fb/2Wbmx2XVu2TXvGTYvWT+2Gb/66/9/f7P1NnO09jEytDIztP+/v+SveR8sN/9/v/302bWvGTuzWX45Kfv7+/4+Pj39/f5+fn6+vp0q92FteGBsuCEtOGAsuDr7vX202bhw2TewWTZvmT51Wb+6q3+/v56rt+GteGBs+Dq0tf//v7wz2XYvmTdwWSlyemOuuP88vL///7/6KH/4or834f/4oz/9t3x9/x3rd7j7vjz+PyszeuEteGjx+jq8vr99fX65ub++fn+/PzIzdPByM7Q1dr//f399PT++vr99/f++/vV2t7M0dbh5Of88/Pp6en9+Pj99vbt7/HV2d3Kz9XT2NzR1dr89PTl6OrU2Nzy8Pji3e/u6/bp9/Ho9e/W29/8/Pz88PD49/uzp9eOfcOOfMOpm9Hy7/ih1r7B49SuodSlls+toNOik82gkcz8+/388fH3+vjn6eja3dzp6+rl6Of6/Pvn3OqrdKKvc56uc56PfMHYx92B0KzI5Nh3zKbR6N2Q1ba43s3WzuejdqrYaG/njIzmhITlgYHgZmaYebfEut7y9/XS2db3+/nc4+DN1NHg6OT7/fzf2u6ndaerdKGPfMLNxuX+/f2F0q9evZK+4dFgwJVdvpLW6uGbi8rmg4Odfrny5+3n4/Kjg7qceLOgfbXb1evJztTHzdK+xcvN0te8w8rQ1Nnp6+339fvOxuW9s9zJwePx7/hPSwcwAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAbNJREFUGBkFwTtI1AEAB+Dvl3/t7Dz1aBAJGhwaGtRs0Bx6QVBIQURBlzWklILRZLXUFAXRKNRQTaFbbS1FBEEPIYSIIAgUhdwkuh6KkX0fAAAAAIAAAABAkvwFBACAxiSr0JxI6kBBawIkK9CaZBUdqbOx0bkMCloikWS9aQVdSVL9QnUJtFc/QwHaot5aV+lJCP/0NnxqsTPJwmI3KEBbUs2K6s8PYD/sicrvsrryoecoQMufLfnF4jzwChzxHvY9G4KCvkQpG027N2UeAJoH3uFwgAB2zQEAcDKZ78qM2jQAegEAwNAZGD6LArDJuSTJQwDQGSitAaAPGDkP4MLFMeNgHBoAx8sLMDfYPwsuDTQ2/jjY+BY6jnUfeAPQC1ADk1euQg3UIMC13IaxF6eSm64nkdwYXX2M8XtQAOWA+24ZGX1QWm9KVkt3MomJ7yBwN0lyGTCxY2VrSDKO4fYpUEBLkgRg6lElSZJgaPMUEDCTJMkJ4MnHnrC0PU/XnD4KKEAlSQTwdfDbtuVUy8m0aYAAAMDLhGQvAAReJ0lmav0As0nSBwAAAAAAAAAAAAAA+A+kDl0gxg/wvQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 33&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/ZaMyCGQCYwsesQeKAY4S4/329e5426c21a5c7a7d83e0339782ed0f/code_33.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/ZaMyCGQCYwsesQeKAY4S4/329e5426c21a5c7a7d83e0339782ed0f/code_33.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/ZaMyCGQCYwsesQeKAY4S4/329e5426c21a5c7a7d83e0339782ed0f/code_33.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/ZaMyCGQCYwsesQeKAY4S4/329e5426c21a5c7a7d83e0339782ed0f/code_33.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;Data와 getter, setter를 가지고 있는 아름다운 보라색 동그라미가 보이는가? 아마 익숙하게 보일 것이다! 모든 컴포넌트 인스턴스는 getter를 통해 종속물을 수집하는(붉은 점선) &lt;code class=&quot;language-text&quot;&gt;watcher&lt;/code&gt; 인스턴스(파란색)를 가지고 있다. setter가 호출되면 Data는 watcher에게 &lt;strong&gt;알림(Notify)&lt;/strong&gt;을 보내서 컴포넌트 re-render로 이어지게 한다. 아래는 내가 주석을 추가한 그림이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/IIEJACvFoyEQSUWEQAOqY/824704788b9a33251af060aab3b77209/code_34.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 50.625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAUCAIAAABwJOjsAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAABLpJREFUSMellXtMU3cUx4FsEwmsaqFAmGRbwVnlKdQhxm3/jJA5F8yWLMbMJWab+8M/2qAFXBDs0I3RQYUNGL4xkZjhkJcPYBQtLVgDSOVRWqi2FtpCS9/tfbXdr7S9dm1hLJ58c3Pu7/7u73PP+Z3zuyFRUVF0Go1O94rmvfoO+j5a38y8vLyQte3kiRPOV7A+6VD18PX2mX6/cZvV+h/gIgbDM9dhdzrQf8np8F1rWWcwGE0oiuIjCp3uyljz96zSqyN/TShlvpNhGHKvH7El7p2c/dHkDH8wYwXssNtgSRk0UwLPFHtVgsgb8YUgGJ4Sz3XeH5ApFjwxIdi529P1gtqzXWd+E5wvuzWmMcKB4JTMHO6ksuj0uT27d+dQsykUCpVKjY2NBeCTLjBqBDDNGGO4izZyj/6kh24TFcNSFgzDdrvd4XBgGAbYJrMFgmDHigllhlMtoop2QfXfrRXtPOAPTCwFgjdHxx45eiw96/2N4eGbCITIyMjQ0NCXqXaDTRNFo/fpj7poEm6hK2hpldVqRRAEXTG3g98Kn+sAjNU5W9kuqeqYBX4fT4LZbH5g3BITE0kkEplMJhAIPmCQanEpiFIvZOjGXQJgRFa/RlkhqN3Nc4vZKtZbENRm4wpGO3s4bXd6N24i4Yokxm9NogCch+pbXA474sBsQJDV4HacDmztkl42wy2DClbH7NUB+fyyK1aDVnvo04LbX+fH7MwkpWaRtlNjU3KB3krLTdiRDXCu3fWvajwUBAHVq1QvgU1dT0dZVSqz2Qw2BRSEXqM58s1x2mf70z/6JIayi7QtM+a97OjkjBigpIywsLD4+PhVweB9o8msXtKC3cQgCLVYgvLEc7J5pRqCIIvBgA+C+fgiga2bkJDgqaygYL1e7zfixwZ1LpUpJFKZeO65YkGFw1wzrdYXC6rZZ3KQg7S0tLi4OJxKJBIjIiKCHSBeA62C+6B/zBarzWLBEE/axZPq7tbJ3k7RosrkChGGQKt5PghF7TB84Gjh3u9q3s36gEhOi92WSUxKj07eBVJNSs7YELV5VfBdEV9t1L48+SBItahBUQyEAj7oMU/GOt3f3PDoIpvPZg6o5g2+yXD7Hx5mfFn1IG5n1ts5exN3ULem5iZspyam7iGRU8LfJAYBwxj6RCYalowvLKmmlc8CNxWse/ns3a4idu+Zxp6y328ymrpbRrwdYce/YOTpNIcv6H3ADydE+ynstdeDgG0oPKtVPJx6bIKtnFG+JaCmQMQ1TE5bSWNn6R8dPzRcL77WdmPc/UH4ueFboev+STidE4vSuuE/eTIhuF4QtAcGfefWpKCwRMisEJYz7xVWTo3IA5H/GwyaB7QQ6GD1ogbsq95gDHJawVhPh6i+knvxPF8wKDOb4NWk05qiIrcEasMbEQF7jCBL2mVwbgA8+AMq1Zo1Dg3QLX3d0z+z+eU3ntJ/4v5a1r8iTnU5B1QfELh1+/gIEJvJ+fZwpQecn5+vULzwajXzfySXy282P/yxQcDiKo7XCkAOrtQNXa4bulY/3FTDa/iFe6mW31Q9CBJzoYYHHrkcNq+hinvsK1bIK1oqZV9BQfHHX5w6cLD40MGSdWpfzufu1/8B9tkAKBTPgooAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;code 34&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/IIEJACvFoyEQSUWEQAOqY/824704788b9a33251af060aab3b77209/code_34.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/IIEJACvFoyEQSUWEQAOqY/824704788b9a33251af060aab3b77209/code_34.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/IIEJACvFoyEQSUWEQAOqY/824704788b9a33251af060aab3b77209/code_34.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/IIEJACvFoyEQSUWEQAOqY/824704788b9a33251af060aab3b77209/code_34.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;어떤가, 이쪽이 우리가 지금까지 살펴본 코드를 떠올리게 하면서 더 잘 와닿지 않는가??&lt;/p&gt;
&lt;p&gt;사실 Vue의 반응성 엔진은 이것보다 훨씬 더 복잡하다. 하지만 당신은 이제 기초를 알게 되었다.&lt;/p&gt;
&lt;h2 id=&quot;-지금까지-학습한-내용&quot;&gt;&lt;a href=&quot;#-%EC%A7%80%EA%B8%88%EA%B9%8C%EC%A7%80-%ED%95%99%EC%8A%B5%ED%95%9C-%EB%82%B4%EC%9A%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;⏪ 지금까지 학습한 내용&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;종속물(종속된 코드, dependencies)를 수집하는(&lt;code class=&quot;language-text&quot;&gt;depend&lt;/code&gt;) &lt;strong&gt;Dep class&lt;/strong&gt;를 어떻게 선언하고, 어떻게 의존 코드를 재실행하는지(&lt;code class=&quot;language-text&quot;&gt;notify&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;종속물로 추가될 수 있는 실행 코드(&lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt;)를 관리하기 위해 &lt;strong&gt;watcher&lt;/strong&gt;를 어떻게 생성하는지.&lt;/li&gt;
&lt;li&gt;getter와 setter를 생성하기 위해  &lt;strong&gt;Object.defineProperty()&lt;/strong&gt;를 어떻게 사용하는지&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;이-다음은&quot;&gt;&lt;a href=&quot;#%EC%9D%B4-%EB%8B%A4%EC%9D%8C%EC%9D%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이 다음은?&lt;/h2&gt;
&lt;p&gt;이 글을 통해 배운 내용에 흥미를 느꼈다면 다음 단계는 &lt;a href=&quot;https://www.vuemastery.com/courses/advanced-components/evan-you-on-proxies/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Reactivity with Proxies&lt;/a&gt;다. 그리고 내가 Vue 개발자인 Evan You와 함께 이 주제에 대해서 이야기하는 &lt;a href=&quot;https://www.vuemastery.com/courses/advanced-components/evan-you-on-proxies/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;무료 동영상&lt;/a&gt;도 VueMastery.com에서 꼭 확인해 보길 바란다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[MongoDB 원격 접속 설정]]></title><description><![CDATA[Amazon Lightsail Amazon Lightsail 은 저렴한 가격으로 클라우드 서버를 사용할 수 있다. 메모리 512MB의 최소 사양 인스턴스는 3.5달러밖에 하지 않기 때문에 테스트나 학습용으로 사용하기 좋다. 운영체제는 Ubuntu 16.04 버전을 선…]]></description><link>https://blog.rhostem.com//posts/2018-08-31-mongodb-connection</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-08-31-mongodb-connection</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Thu, 30 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;amazon-lightsail&quot;&gt;&lt;a href=&quot;#amazon-lightsail&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Amazon Lightsail&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://lightsail.aws.amazon.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Amazon Lightsail&lt;/a&gt;은 저렴한 가격으로 클라우드 서버를 사용할 수 있다. 메모리 512MB의 최소 사양 인스턴스는 3.5달러밖에 하지 않기 때문에 테스트나 학습용으로 사용하기 좋다. 운영체제는 Ubuntu 16.04 버전을 선택해서 사용했다.&lt;/p&gt;
&lt;h2 id=&quot;mongodb-설치&quot;&gt;&lt;a href=&quot;#mongodb-%EC%84%A4%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;MongoDB 설치&lt;/h2&gt;
&lt;p&gt;공식 홈페이지의 설치 가이드를 단계별로 따라가면 쉽게 설치할 수 있다. 나는 &lt;a href=&quot;https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#using-deb-packages-recommended&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;.deb&lt;/code&gt; 패키지를 사용하는 방법&lt;/a&gt;으로 진행했다. 설치한 MongoDB 버전은 4.0.2였다.&lt;/p&gt;
&lt;h3 id=&quot;데이터베이스-폴더-설정&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%8F%B4%EB%8D%94-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 폴더 설정&lt;/h3&gt;
&lt;p&gt;데이터베이스 사용을 위해서는 데몬 프로세스 &lt;a href=&quot;https://docs.mongodb.com/manual/reference/program/mongod/index.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;mongod&lt;/a&gt;를 실행해야 하는데, 그 전에 데이터를 저장할 폴더를 만들어야 한다. MongoDB의 기본 데이터베이스 폴더는 &lt;code class=&quot;language-text&quot;&gt;/data/db&lt;/code&gt;로 지정되어 있으니 루트 권한으로 폴더를 만들어야 한다. 폴더가 만들어지면 MongoDB를 설치하면서 등록된 시스템 서비스를 실행한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; /data/db
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt; mongod start&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;서비스 실행 후 &lt;a href=&quot;https://docs.mongodb.com/manual/mongo/index.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;mongo 쉘&lt;/a&gt;을 실행할 수 있으면 로컬에서 데이터베이스를 사용할 준비가 된 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;mongo&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;원격-접속-설정&quot;&gt;&lt;a href=&quot;#%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;원격 접속 설정&lt;/h2&gt;
&lt;p&gt;설치 직후의 데이터베이스는 인증 없이 사용할 수 있고, 설치된 기기에서만 접속 가능하다. 원격 접속이 가능하도록 하려면 사용자를 추가하고 네트워크 관련 설정을 수정해야 한다.&lt;/p&gt;
&lt;h3 id=&quot;사용자-생성&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%83%9D%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사용자 생성&lt;/h3&gt;
&lt;p&gt;사용자 생성은 mongo 쉘에 접속해서 한다. mongo 쉘은 MongoDB의 자바스크립트 인터페이스로서 명령어 실행과 함수 호출을 통해 데이터베이스를 수정할 수 있는 기능을 제공한다.&lt;/p&gt;
&lt;p&gt;사용자 정보는 기본적으로 admin 데이터베이스에 저장한다. MongoDB는 사용자 정보가 저장된 데이터베이스가 아닌 다른 데이터베이스에 대한 권한을 부여할 수도 있다. 이를 &lt;a href=&quot;https://docs.mongodb.com/manual/core/authorization/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Role-Based Access Control&lt;/a&gt;이라고 한다.&lt;/p&gt;
&lt;p&gt;데이터베이스를 선택하고, &lt;a href=&quot;https://docs.mongodb.com/manual/reference/method/db.createUser/index.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;사용자를 생성&lt;/a&gt;하는 함수를 호출하도록 하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;use admin &lt;span class=&quot;token comment&quot;&gt;# admin 데이터베이스 선택&lt;/span&gt;

db.createUser&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  user: &lt;span class=&quot;token string&quot;&gt;&quot;dbadmin&quot;&lt;/span&gt;,  &lt;span class=&quot;token comment&quot;&gt;# 계정 이름&lt;/span&gt;
  pwd: &lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;,  &lt;span class=&quot;token comment&quot;&gt;# 비밀번호&lt;/span&gt;
  roles: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;          &lt;span class=&quot;token comment&quot;&gt;# 사용자에게 주어진 권한 목록. 여러 데이터베이스에 대한 권한을 할당할 수 있다.&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      role: &lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;, &lt;span class=&quot;token comment&quot;&gt;# built-in 권한인 root. 문자 그대로 모든 데이터베이스를 관리할 수 있다.&lt;/span&gt;
      db: &lt;span class=&quot;token string&quot;&gt;&quot;admin&quot;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;# 어떤 데이터베이스에 대한 권한인지 명시&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 관리자가 아닌 사용자를 생성해서 실제 작업에 사용하도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;db.createUser&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  user: &lt;span class=&quot;token string&quot;&gt;&quot;rhostem&quot;&lt;/span&gt;,
  pwd: &lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;,
  roles: &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      role: &lt;span class=&quot;token string&quot;&gt;&quot;readWrite&quot;&lt;/span&gt;, &lt;span class=&quot;token comment&quot;&gt;# 읽기, 쓰기 권한&lt;/span&gt;
      db: &lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;         &lt;span class=&quot;token comment&quot;&gt;# 위의 권한을 부여할 데이터베이스로 test를 지정&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;code-classlanguage-textmongodconfcode-파일-설정&quot;&gt;&lt;a href=&quot;#code-classlanguage-textmongodconfcode-%ED%8C%8C%EC%9D%BC-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;mongod.conf&lt;/code&gt; 파일 설정&lt;/h3&gt;
&lt;p&gt;사용자를 생성했으니 데이터베이스에 접속할 때 인증이 필요하도록 수정해야 한다. 서비스가 사용하고 있는 &lt;a href=&quot;https://docs.mongodb.com/manual/reference/configuration-options/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;설정&lt;/a&gt; 파일은 아래의 명령어로 확인할 수 있다. 아래 로그에서 확인할 수 있듯이 Ubuntu에서는 &lt;code class=&quot;language-text&quot;&gt;/etc/mongod.conf&lt;/code&gt; 파일을 수정하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt; mongod status

● mongod.service - MongoDB Database Server
   Loaded: loaded &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/lib/systemd/system/mongod.service&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; disabled&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; vendor preset: enabled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
   Active: active &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;running&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; since Thu 2018-08-30 00:00:00 UTC&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 2s ago
     Docs: https://docs.mongodb.org/manual
 Main PID: 4376 &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mongod&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    Tasks: 26
   Memory: 138.1M
      CPU: 1.053s
   CGroup: /system.slice/mongod.service
           └─4376 /usr/bin/mongod --config /etc/mongod.conf
           &lt;span class=&quot;token comment&quot;&gt;# MongoDB 데몬 프로세스의 옵션 파일을 지정하는 --config가 사용되어 있다.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;데이터베이스 접속이 인증이 필요하도록 하려면 &lt;strong&gt;security&lt;/strong&gt; 섹션을 아래처럼 수정한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;security:
  authorization: enabled&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 &lt;strong&gt;net&lt;/strong&gt; 섹션의 &lt;code class=&quot;language-text&quot;&gt;bind_ip&lt;/code&gt; 항목도 수정해야 한다. 기본 설정은 로컬에서만 접속할 수 있도록 &lt;code class=&quot;language-text&quot;&gt;127.0.0.1&lt;/code&gt;로 되어 있는데, 모든 아이피에서 접속할 수 있도록 &lt;code class=&quot;language-text&quot;&gt;0.0.0.0&lt;/code&gt;으로 변경한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;net:
  port: 27017
  bindIp: 0.0.0.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;tcp-포트-개방&quot;&gt;&lt;a href=&quot;#tcp-%ED%8F%AC%ED%8A%B8-%EA%B0%9C%EB%B0%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TCP 포트 개방&lt;/h3&gt;
&lt;p&gt;설정에서도 확인할 수 있지만 MongoDB는 기본적으로 &lt;strong&gt;27107&lt;/strong&gt;번 포트를 사용한다. 따라서 외부에서 접속할 수 있도록 방화벽 설정을 통해 27017번 포트를 개방해야 한다. 나는 Amazon Lightsail 인스턴스를 사용했기에 웹에서 간편하게 설정할 수 있었다. 인스턴스의 네트워킹 탭에 항목을 추가하면 된다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/gcQ908FtBuKyQceoOqCoi/cdda34ddb561d6a95b2b8358cb8f1228/lightsail_firewall.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 39.9734395750332%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;lightsail firewall&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/gcQ908FtBuKyQceoOqCoi/cdda34ddb561d6a95b2b8358cb8f1228/lightsail_firewall.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/gcQ908FtBuKyQceoOqCoi/cdda34ddb561d6a95b2b8358cb8f1228/lightsail_firewall.png?w=377 377w,
https://images.ctfassets.net/rpmifyuylbfw/gcQ908FtBuKyQceoOqCoi/cdda34ddb561d6a95b2b8358cb8f1228/lightsail_firewall.png?w=753 753w,
https://images.ctfassets.net/rpmifyuylbfw/gcQ908FtBuKyQceoOqCoi/cdda34ddb561d6a95b2b8358cb8f1228/lightsail_firewall.png?w=1506 1506w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;TCP 프로토콜 27017 포트를 개방했다&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;접속-명령어&quot;&gt;&lt;a href=&quot;#%EC%A0%91%EC%86%8D-%EB%AA%85%EB%A0%B9%EC%96%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;접속 명령어&lt;/h3&gt;
&lt;p&gt;우선 사용자가 설정되었는지 확인하기 위해 서버에서 mongo 쉘에 접속을 시도해본다. &lt;code class=&quot;language-text&quot;&gt;-p&lt;/code&gt; 옵션 다음에는 비밀번호를 입력해야 하지만 비워두면 입력을 위한 프롬프트가 뜬다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;authenticationDatabase&lt;/code&gt; 옵션은 사용자의 정보가 저장된 데이터이스를 가리키기 위한 것이다. 앞서 사용자를 추가할 때 사용한 admin 데이터베이스를 입력한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; mongo -u dbadmin -p --authenticationDatabase admin

MongoDB shell version v4.0.2
Enter password: &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;패스워드 입력&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;접속에 성공했다면 이제 서버에서 로그아웃한 후 원격 접속이 가능한지 시도해보자. 앞의 명령어에 &lt;code class=&quot;language-text&quot;&gt;host&lt;/code&gt; 옵션만 추가하면 된다. &lt;code class=&quot;language-text&quot;&gt;host&lt;/code&gt;에는 서버의 IP 주소 또는 도메인을 입력하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;mongo -u dbadmin -p --host &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;아이피 또는 도메인&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; --authenticationDatabase admin&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;접속이 성공했다면 원격으로 데이터베이스를 사용할 준비가 된 것이다. 이제 node.js 서버 앱에서 &lt;a href=&quot;https://mongoosejs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Mongoose&lt;/a&gt; 같은 모듈을 사용해 데이터베이스를 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;(Mac 또는 Windows의 커맨드라인에서 데이터베이스에 원격 접속하기 위해서는 MongoDB가 설치되어 있어야 한다. 그렇지 않다면 MongoDB를 위한 &lt;a href=&quot;https://www.guru99.com/top-20-mongodb-tools.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;GUI 도구&lt;/a&gt;를 사용하면 된다.)&lt;/p&gt;
&lt;h2 id=&quot;기타-설정&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%ED%83%80-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기타 설정&lt;/h2&gt;
&lt;h3 id=&quot;시스템-재시작-시-mongodb를-자동으로-실행하도록-설정&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EC%8A%A4%ED%85%9C-%EC%9E%AC%EC%8B%9C%EC%9E%91-%EC%8B%9C-mongodb%EB%A5%BC-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-%EC%8B%A4%ED%96%89%ED%95%98%EB%8F%84%EB%A1%9D-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시스템 재시작 시 MongoDB를 자동으로 실행하도록 설정&lt;/h3&gt;
&lt;p&gt;서버를 재시작하면 실행 중인 mongod 프로세스도 종료된다. 시스템 시작과 함께 서비스가 실행되도록 crontab을 사용해서 작업을 등록할 수 있다. crontab은 Linux 운영체제가 특정 시점에 특정 명령어를 실행하도록 할 수 있는 기능이다. 아래의 명령어로 crontab 파일을 연다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;crontab&lt;/span&gt; -e&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;맨 아래쪽에 재부팅 시점에 실행할 명령어를 추가한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;@reboot &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt; mongod start&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;파일을 저장하고 나왔을 때 아래의 메시지가 표시되면 등록된 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;crontab: installing new &lt;span class=&quot;token function&quot;&gt;crontab&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;데이터베이스-접속을-위한-쉘-스크립트-작성&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%A0%91%EC%86%8D%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%89%98-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%91%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데이터베이스 접속을 위한 쉘 스크립트 작성&lt;/h3&gt;
&lt;p&gt;git 저장소에서 작업하고 있다면 쉘 스크립트에 비밀번호가 노출되면 안 되므로 별도의 파일에 저장해서 관리하는 편이 보안상 좋다. 예를 들어 비밀번호를 .dbpasswd 파일에 저장했다면 해당 파일은 .gitignore 파일에 등록하고 쉘 스크립트를 아래와 같이 작성한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# connect-db.sh&lt;/span&gt;

passwd&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;.dbpasswd&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 변수 passwd에 .dbpasswd 파일의 내용을 할당.&lt;/span&gt;
mongo -u rhostem -p &lt;span class=&quot;token variable&quot;&gt;$passwd&lt;/span&gt; --host db.host.name --authenticationDatabase admin&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;참고자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;The MongoDB 4.0 Manual — MongoDB Manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://optimalbi.com/blog/2017/09/14/how-to-install-mongodb-with-aws-ec2/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;How to Install MongoDB with AWS EC2 | OptimalBI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kompulsa.com/run-a-program-on-startup-console-on-ubuntu-18-04/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Run A Program On Startup (Command Line On Ubuntu 18.04)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.guru99.com/top-20-mongodb-tools.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;20 Best MongoDB GUI Tools in 2018&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lightsail.aws.amazon.com/ls/docs/ko/articles/understanding-firewall-and-port-mappings-in-amazon-lightsail&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Amazon Lightsail에서 퍼블릭 네트워크 포트와 방화벽 설정의 이해&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[블로그 검색 기능 개발 과정]]></title><description><![CDATA[블로그를 만든 지 거의 2년이 다 되어가고 작성한 포스트도 어느덧 20개를 넘었다. 콘텐츠가 쌓이니 자연스럽게 ‘검색’ 기능을 붙이고 싶은 욕심이 들었다. Gatsby를 사용해서 만든 블로그라서 플러그인을 검색해 보니 역시 검색 기능을 구현한 것이 있었다. 자바스크립…]]></description><link>https://blog.rhostem.com//posts/2018-08-23-blog-search</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-08-23-blog-search</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Thu, 23 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;블로그를 만든 지 거의 2년이 다 되어가고 작성한 포스트도 어느덧 20개를 넘었다. 콘텐츠가 쌓이니 자연스럽게 ‘검색’ 기능을 붙이고 싶은 욕심이 들었다. Gatsby를 사용해서 만든 블로그라서 플러그인을 검색해 보니 역시 검색 기능을 구현한 것이 있었다. 자바스크립트 기반의 검색 엔진인 &lt;a href=&quot;http://elasticlunr.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Elasticlunr.js&lt;/a&gt;를 사용한 플러그인이었는데, 테스트해 보니 안타깝게도 한글 문장은 인덱싱되지 않았다. 그래서 공부도 할 겸 직접 구현하기로 했다.&lt;/p&gt;
&lt;h2 id=&quot;자체-검색-엔진-구현&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EC%B2%B4-%EA%B2%80%EC%83%89-%EC%97%94%EC%A7%84-%EA%B5%AC%ED%98%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자체 검색 엔진 구현&lt;/h2&gt;
&lt;p&gt;우선 검색 기능에 대한 요구 사항을 정리해 보았다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pages.github.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Github pages&lt;/a&gt;에 호스팅 된 정적인(static) 사이트라서 검색은 브라우저에서 진행되어야 한다.&lt;/li&gt;
&lt;li&gt;클라이언트 사이드 검색이므로 인덱싱 파일은 크기가 가능한 한 작아야 한다.&lt;/li&gt;
&lt;li&gt;영어 및 한글도 검색 가능해야 한다.&lt;/li&gt;
&lt;li&gt;검색 결과에는 매칭된 포스트 제목과 태그를 표시해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;nodejs에서-검색을-위한-콘텐츠-인덱싱-진행&quot;&gt;&lt;a href=&quot;#nodejs%EC%97%90%EC%84%9C-%EA%B2%80%EC%83%89%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%BD%98%ED%85%90%EC%B8%A0-%EC%9D%B8%EB%8D%B1%EC%8B%B1-%EC%A7%84%ED%96%89&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;node.js에서 검색을 위한 콘텐츠 인덱싱 진행&lt;/h2&gt;
&lt;p&gt;콘텐츠를 단어를 추출한 후 단어와 그 단어를 포함한 페이지를 매칭시킨 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%97%AD%EC%83%89%EC%9D%B8&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;역색인&lt;/a&gt;을 구성하는 방법을 사용했다. 처음에는 문장 검색을 위해 글자를 하나하나 분리하고 인접한 단어 정보를 저장해서 검색 결과와 함께 문장을 다시 구성할 수 있도록 하려고 했었다. 그런데 그 과정에서 인덱싱 파일의 크기가 너무 커져 버렸고 효율도 좋지 않았다. 결국, 단어 단위의 역 인덱스로 내가 할 수 있는 구현을 하자는 쪽으로 방향을 틀었다.&lt;/p&gt;
&lt;p&gt;인덱싱 데이터 구성은 node.js 환경에서 아래의 과정을 통해 진행된다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;콘텐츠가 포함된 모든 마크다운(md) 파일을 가져온다.&lt;/li&gt;
&lt;li&gt;마크다운에서 YAML 형식으로 작성된 front matter 영역과 본문을 분리한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/remarkjs/remark&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;remark&lt;/a&gt; 라이브러리로 콘텐츠 본문을 텍스트로 변환한다.&lt;/li&gt;
&lt;li&gt;본문 텍스트에서 단어를 분리하고 포스트 경로로 매핑하는 단어 맵을 만든다.&lt;/li&gt;
&lt;li&gt;front matter에서는 제목, 태그 정보를 추출한다.&lt;/li&gt;
&lt;li&gt;검색에 필요한 데이터를 JSON 파일로 저장한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;인덱싱 파일을 생성하면 아래과 같은 형태가 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;wordList&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;[번역]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;자바스크립트&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;피로감을&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ...
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;wordMap&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;@_[번역]&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;@_자바스크립트&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;@_피로감을&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ...
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;routeMap&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;route&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/posts/2016-12-19-A-Study-Plan-To-Cure-JavaScript-Fatigue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[번역] 자바스크립트 피로감을 줄여주기 위한 학습 계획&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;번역&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;JavaScript&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;React&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Redux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Gatsby&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ES&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ...
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;tagList&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;번역&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;JavaScript&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;React&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ...
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;사용자의 검색어를 입력받으면 &lt;code class=&quot;language-text&quot;&gt;wordList&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;tagList&lt;/code&gt;에서 매칭되는 단어를 찾은 후 &lt;code class=&quot;language-text&quot;&gt;routeMap&lt;/code&gt;에 저장된 정보를 활용해서 결과를 보여주는 방식이다. 실제 검색 로직에서는 결과 정렬을 위한 랭킹 점수 부여 등의 과정이 추가된다.&lt;/p&gt;
&lt;p&gt;콘텐츠의 총글자 수가 약 35만 자인 상태에서 인덱싱 파일을 생성하니 그 크기가 350KB 정도로 브라우저에서 충분히 감당할 수 있을 정도였다. gzip으로 인코딩하면 100KB까지 줄어든다. 하지만 그 크기는 점점 커질 테니, 인덱싱 데이터를 서비스 워커를 사용해서 미리 불러오도록 했다.&lt;/p&gt;
&lt;h2 id=&quot;서비스-워커service-worker를-사용한-검색-데이터-캐싱&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%9B%8C%EC%BB%A4service-worker%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EA%B2%80%EC%83%89-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%BA%90%EC%8B%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서비스 워커(Service Worker)를 사용한 검색 데이터 캐싱&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Service_Worker_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;서비스 워커&lt;/a&gt;는 브라우저의 백그라운드에서 동작하면서 웹앱과 브라우저, 네트워크 사이의 프락시 역할을 한다.
원격 데이터를 호출하는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;fetch&lt;/a&gt; 이벤트를 감지해서 특정 네트워크 요청에 대한 응답을 캐싱하는 등의 작업이 가능하다. 그리고 웹앱에 필요한 리소스를 내려받아서 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CacheStorage&lt;/a&gt;에 저장해서 재방문 시 빠르게 가져올 수 있도록 한다. (서비스 워커를 사용하면 &lt;a href=&quot;https://wicg.github.io/BackgroundSync/spec/#sync-manager-interface&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;백그라운드 동기화&lt;/a&gt;도 가능하지만 아직은 크롬 브라우저에서만 가능한 기술이다) 이를 바탕으로 네트워크가 불안하거나 오프라인인 상태에서도 앱이 구동할 수 있도록 할 수 있다. 네트워크 요청을 하지 않는 앱이라면 네이티브 앱과 다를 바가 없게 되는 것이다.&lt;/p&gt;
&lt;p&gt;검색 기능을 구현하면서 서비스 워커를 사용해서 덩치가 큰 인덱싱 파일을 CacheStorage에 저장했다. 이런 기능을 간편히 사용하기 위한 라이브러리에는 &lt;a href=&quot;https://github.com/GoogleChromeLabs/sw-precache&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;sw-precache&lt;/a&gt;가 있다. Gatsby에는 이를 바탕으로 한 플러그인이 이미 존재하고 있으며, 나도 사용하고 있었다. &lt;a href=&quot;https://www.gatsbyjs.org/packages/gatsby-plugin-offline/?=offline&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;gatsby-plugin-offline&lt;/a&gt;이 그것인데, 기본 옵션만 사용해도 웹사이트 데이터를 잘 캐싱해준다. 이 플러그인의 추가 옵션을 살짝 수정해서 덩치가 큰 인덱싱 파일도 저장하도록 했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rootDir &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;public&apos;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  resolve&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;gatsby-plugin-offline&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  options&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    staticFileGlobs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rootDir&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/search/*.json`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 검색 인덱싱 파일 추가&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rootDir&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/**/*.{woff2}`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rootDir&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/commons-*js`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rootDir&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/app-*js`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rootDir&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/index.html`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rootDir&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/manifest.json`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rootDir&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/manifest.webmanifest`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;rootDir&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/offline-plugin-app-shell-fallback/index.html`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/QpJIMy6Xuu80iWwW0a0Kw/38eec6b87793439f85d7261ce4bec76b/precache_1.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 527px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 20.30360531309298%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAICAMAAAC8jE1pAAABcmlDQ1BpY2MAAHjaY2BgKkksKMhhYWBgyM0rKQpyd1KIiIxSYL/DwM3AwyDEYMUgnphcXOAYEODDgBN8u8bACKIv64LMSvP8edOmtXz+FjavmXJWJToM+AF3SmpxMgMDIweQnZxSnJwLZOcA2TrJBUUlQPYMIFu3vKQAxD4BZIsUAR0IZN8BsdMh7A8gdhKYzcQCVhMS5AxkSwDZAkkQtgaInQ5hW4DYyRmJKUC2B8guiBvAgNPDRcHcwFLXkYHKIDenFGYHKLR4UvNCg0HuAGIZBg8GFwYFBnMGAwZLBl0Gx5LUihKQQuf8gsqizPSMEgVHYMimKjjn5xaUlqQW6Sh45iXr6SgYGRgagNSB4gxi9OcgsOmMYucRYvkLGRgslRkYmHsQYknTGBi272FgkDiFEFOZx8DAb83AsO1cQWJRItzhjN9YCPGL04yNIGweJwYG1nv//39WY2Bgn8TA8Hfi//+/F/3//3cx0H5gnB3IAQAkd2ngGBDbwwAAAb9QTFRF2Nna19jZ4OHh7u7u6+vr1dbX4uLj2tvb7Ozs3N3d0tPU7e3t0dPU4eLi1tfY4uPj3+Dg5OTl4OHi2tvc4+Tk4+Pj7evr6bCn3q+o4rOs8cG677+46Lix8MG56rqz47St8Lmx4uPkys3QxsjKzs/RztDRyMrLz9HSzc/Q7e3u8fHxz9DS8/Pz0NHTyszN8vLy8+/vzKGbu77Ax8nLwcPFvL7Bubu909XWzc7Q8PDw3a2m7O3t5ufo5ebn6+vs6erq4+Tl7u7v+fn55+jp/Pz87Ozt9fX1+/n58buy6ry16bqz7L2278C55bew98jA+8vE/c3G9ca+5bav5rix+MjB7b6377y17+/v6Onq8fHy9fX26urr7e7v9fb2+vr629zd2drc09TW9/f36+zs9vb25ubn7/Dw6enq3N3excfJxMbIy83OyszO2dnZ3d3d5+foz9DRw8TGv8HDt7m72drb39/f2tra0dHR3t7e1dXV6Ojp9PT06err3t/g2Nrb3N3f5OXm6Ojo6urq/f39+/v78/P04eLj3+Hi5+fn6enp4ODg5eXl8/T05+jo+Pj48fLy+vv7////+fn6/v7+7u/v8fP3EAAAAAlwSFlzAAAXEQAAFxEByibzPwAAAAd0SU1FB+YMGw8eHIU9Sw0AAAEXSURBVBgZPcG/SwJhAAbg9/0+f52H5qWcF0iYS4NXThbRUgRSRI79BdLY3trc2Nw/YUtIjhFaFv1c2hIKijIzO/T86hTqeQiSGCH7ASV7An7yO+wADLELT8gNdsExso0RRsmW0XYRpwcABz4FjyRdP3WT/x4nlSQHPg79iI6AR6/N81rYXBDErc2RfkBJKkj+gYee+hwFUqm7nBRXtCxLAskkkKyZZuIw8RbvDGnGa6xgGPSVKgWSFfWwabKq7rePIZdJBxrbJ2uORnJwtL6/JXd0Y0I/G6+Xlj5Tuj3bz2Xtl0xMP32aLi9m94rh8O6G9jzznmdh9cLNK0l1YKYzkXLxIwpx+dUM9lYSrch5E2n7BlORRuMXVXxWK3Iw+wIAAAAydEVYdGljYzpjb3B5cmlnaHQAQ29weXJpZ2h0IEFwcGxlIENvbXB1dGVyLCBJbmMuLCAyMDEwHZO5YgAAABh0RVh0aWNjOmRlc2NyaXB0aW9uAEhEIDcwOS1Bstta8QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;precache 1&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/QpJIMy6Xuu80iWwW0a0Kw/38eec6b87793439f85d7261ce4bec76b/precache_1.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/QpJIMy6Xuu80iWwW0a0Kw/38eec6b87793439f85d7261ce4bec76b/precache_1.png?w=132 132w,
https://images.ctfassets.net/rpmifyuylbfw/QpJIMy6Xuu80iWwW0a0Kw/38eec6b87793439f85d7261ce4bec76b/precache_1.png?w=264 264w,
https://images.ctfassets.net/rpmifyuylbfw/QpJIMy6Xuu80iWwW0a0Kw/38eec6b87793439f85d7261ce4bec76b/precache_1.png?w=527 527w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;최초 방문&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/64E4IwQ13iqUoq88EYMUKS/eca28dd7185c40cd982454e29a510b97/precache_2.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 527px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 19.35483870967742%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAICAMAAAC8jE1pAAABcmlDQ1BpY2MAAHjaY2BgKkksKMhhYWBgyM0rKQpyd1KIiIxSYL/DwM3AwyDEYMUgnphcXOAYEODDgBN8u8bACKIv64LMSvP8edOmtXz+FjavmXJWJToM+AF3SmpxMgMDIweQnZxSnJwLZOcA2TrJBUUlQPYMIFu3vKQAxD4BZIsUAR0IZN8BsdMh7A8gdhKYzcQCVhMS5AxkSwDZAkkQtgaInQ5hW4DYyRmJKUC2B8guiBvAgNPDRcHcwFLXkYHKIDenFGYHKLR4UvNCg0HuAGIZBg8GFwYFBnMGAwZLBl0Gx5LUihKQQuf8gsqizPSMEgVHYMimKjjn5xaUlqQW6Sh45iXr6SgYGRgagNSB4gxi9OcgsOmMYucRYvkLGRgslRkYmHsQYknTGBi272FgkDiFEFOZx8DAb83AsO1cQWJRItzhjN9YCPGL04yNIGweJwYG1nv//39WY2Bgn8TA8Hfi//+/F/3//3cx0H5gnB3IAQAkd2ngGBDbwwAAAc5QTFRF1dbX0tPV3t/g8fHx7u7u0dLU09TV3+Dg19jZ0dPU7+/v2tvcy8zO0tPU8PDwzM3P4OHh2dra7O3t5+fo5ebm6Ojp6+vr5ubm4eLi6Ojo5+fn4eHi7e3t6enp4+Tk5eXm8MjC5rWt7buz88G58sC47ry067my8ras4eLjycvOxMbIzM7PzM7QxsjJzs/Ry83O19fX3Nzc8/PzztDRyMrL8vLyx8nLubu9xcfJv8HDuby+tri79MS81NTU2NjY1tbWz8/P29vb29zd1tfY3q6o7e3u6enq6Onq5ufo5+jo6+zs5OXm7+/w+fn56Onp6+zt+/v79fb2+vr67/Dw6+vs9/f3+9PN/MrC9MO757au98W95LOr7Luz8bWs3d7f3+Dh4OHi7Ozt8PDx9vb2/Pz89fX119na1tfZ1dbYz9DS+Pj49fX26erq7e7u7Ozs3Nzdw8XHwcPFycvNyMrM1dfY5ufnzc7QwMLEvb/BwsTGtLa52Nna3t7e2dnZ0NDQ3d3d4+Tl5+jp4uPk5ebn5ubn7u/v6urr9vb36uvr9PT06uvs4OLj8vLz+Pj5////7/Dx/v7+29ze2tvd293e2Nrb09TW+vr7/f39+fn68fHy6I1otgAAAAlwSFlzAAAXEQAAFxEByibzPwAAAAd0SU1FB+YMGw8eHIU9Sw0AAAEPSURBVBgZBcHPK4NxAMfxz/t5Hi379dR2mMNqiVwkTn48KJaUlYsb+Q+UkqOLHZ38Aw5y2D/AQS1O6BGXXZRyUsPYyEZm9n0erxcCJEkCMA4dRQC+JWK0JClmt3ubpOFDkqQUYBxq6gOqkrKAJAGBTTYOADSeR6AyBsYGILQAFNjG+UoAyQmAbg8AmHY8lAWtG6+bAEniehIAe8kvIAS8Xay8u06ouhshtHjMdSQp8nCbz1Bik/IiUDazR0NQ2eJM9jwgqDwVTpbhfO70tbaNnZ8G38yA33+wVrtc/VW96+1744FzlSmlXlK5qcOF9ECVvfvjjehPFH80WhxcvxsOkNV0AXaL7b+i434mtQONf9glVFZsnM1dAAAAMnRFWHRpY2M6Y29weXJpZ2h0AENvcHlyaWdodCBBcHBsZSBDb21wdXRlciwgSW5jLiwgMjAxMB2TuWIAAAAYdEVYdGljYzpkZXNjcmlwdGlvbgBIRCA3MDktQbLbWvEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;precache 2&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/64E4IwQ13iqUoq88EYMUKS/eca28dd7185c40cd982454e29a510b97/precache_2.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/64E4IwQ13iqUoq88EYMUKS/eca28dd7185c40cd982454e29a510b97/precache_2.png?w=132 132w,
https://images.ctfassets.net/rpmifyuylbfw/64E4IwQ13iqUoq88EYMUKS/eca28dd7185c40cd982454e29a510b97/precache_2.png?w=264 264w,
https://images.ctfassets.net/rpmifyuylbfw/64E4IwQ13iqUoq88EYMUKS/eca28dd7185c40cd982454e29a510b97/precache_2.png?w=527 527w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;재방문&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;스크린 샷을 살펴보면 &lt;code class=&quot;language-text&quot;&gt;searchIndex.json&lt;/code&gt; 파일을 가져오는 데 걸리는 시간이 처음에는 118ms지만 재방문했을 때는 서비스 워커를 통해 10ms가 걸렸다. 1/10 정도로 많이 줄어들었다는 사실을 확인할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;웹-워커web-worker를-사용한-검색&quot;&gt;&lt;a href=&quot;#%EC%9B%B9-%EC%9B%8C%EC%BB%A4web-worker%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EA%B2%80%EC%83%89&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;웹 워커(Web Worker)를 사용한 검색&lt;/h2&gt;
&lt;p&gt;자바스크립트는 싱글 스레드라고 알고 있는 사람들이 많지만, 웹 워커(&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Web Workers&lt;/a&gt;)를 사용하면 멀티 스레드 프로그래밍이 가능하다. 그리고 서비스 워커보다는 브라우저 지원 폭이 넓어서 인터넷 익스플로러 9를 반드시 지원해야 하는 웹사이트가 아니라면 사용해도 된다. 웹 워커를 사용하면 좋은 사례에는 다음과 같은 것들이 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ray tracing(이미지 렌더링 기술)&lt;/li&gt;
&lt;li&gt;암호화&lt;/li&gt;
&lt;li&gt;데이터 prefetching&lt;/li&gt;
&lt;li&gt;Progressive Web App&lt;/li&gt;
&lt;li&gt;문법 검사&lt;/li&gt;
&lt;li&gt;타이머&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;상대적으로 큰 데이터를 처리해야 해서 검색 로직 구현에도 웹 워커를 사용하기로 했다. 하지만 웹 워커와 메인 스레드 사이의 통신을 위해서는 &lt;code class=&quot;language-text&quot;&gt;postMessage&lt;/code&gt;를 사용해서 데이터를 보내고, &lt;code class=&quot;language-text&quot;&gt;onmessage&lt;/code&gt; 이벤트 핸들러에서 데이터를 받아서 처리해야 한다. 워커에 의미 있는 이름을 가진 메소드를 추가해서 보다 가독성 있는 방법으로 통신할 방법은 아직은 없다. 이를 개선하기 위한 &lt;a href=&quot;https://github.com/GoogleChromeLabs/comlink&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;comlink&lt;/a&gt;라는 라이브러리가 있긴 하다. 하지만 나는 redux에서 하는 것처럼 정형화된 데이터를 주고받는 방식으로 구현했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 메인 스레드 소스 코드&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; searchWorker
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Worker &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;searchWorker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 웹 워커 등록&lt;/span&gt;
  searchWorker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/js/search-worker.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; searchService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 검색&lt;/span&gt;
  find&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;searchInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    searchWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;SEARCH_REQUEST&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        searchInput&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 웹 워커 소스 코드&lt;/span&gt;
&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; action &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;action
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; payload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload

  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;SEARCH_REQUEST&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이런 식으로 스레드 간에 데이터를 주고받을 수 있다. 위의 코드에서는 메인 스레드에서 메시지를 보내고, 웹 워커에서 메시지를 받는 과정이 표현되어 있지만 당연히 반대로도 가능하다.&lt;/p&gt;
&lt;p&gt;웹 워커를 사용해서 350KB 크기의 데이터를 검색하는 데 보통 0.15~0.2초가 걸렸다.&lt;/p&gt;
&lt;h2 id=&quot;nodejs-브라우저-웹-워커에서-함께-사용할-모듈-작성&quot;&gt;&lt;a href=&quot;#nodejs-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%9B%B9-%EC%9B%8C%EC%BB%A4%EC%97%90%EC%84%9C-%ED%95%A8%EA%BB%98-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%AA%A8%EB%93%88-%EC%9E%91%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Node.js, 브라우저, 웹 워커에서 함께 사용할 모듈 작성&lt;/h2&gt;
&lt;p&gt;검색 기능을 구현하는 과정에서 node.js, 브라우저, 웹 워커에서 공통으로 사용해야 하는 로직이 발견되었다. 인덱싱과 검색 과정에서 문장과 단어의 불필요한 문자를 제거하는 기능이 대표적이다. 그리고 웹 워커를 사용하니 스레드 간의 메시지를 구분할 때 사용하는 enum 역할을 하는 문자열도 일치시킬 필요가 생겼다.&lt;/p&gt;
&lt;p&gt;하지만 세 환경에서는 모듈을 사용하는 방식이 서로 다르다. node.js에서는 모듈을 사용하기 위해 자체 모듈인 &lt;code class=&quot;language-text&quot;&gt;module&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;require&lt;/code&gt;를 사용하지만, 브라우저와 웹 워커에서는 사용할 수 없다. 브라우저에서는 Webpack과 ES6의 &lt;code class=&quot;language-text&quot;&gt;export&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;import&lt;/code&gt;를 사용한다. 그리고 웹 워커에서는 &lt;code class=&quot;language-text&quot;&gt;importScripts&lt;/code&gt; 함수를 통해 외부 스크립트 내부에서 할당한 전역 객체를 사용해야 한다.&lt;/p&gt;
&lt;p&gt;이 3가지 환경에서 모두 사용할 수 있는 모듈 선언 방식이 &lt;a href=&quot;https://github.com/umdjs/umd&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;UMD (Universal Module Definition)&lt;/a&gt; 패턴이다. 링크된 Github 저장소의 &lt;code class=&quot;language-text&quot;&gt;templates&lt;/code&gt; 폴더에는 여러 사례에 대한 예제 코드가 작성되어 있다. 검색 엔진을 위한 모듈 작성에는 &lt;a href=&quot;https://github.com/umdjs/umd/blob/master/templates/returnExports.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;returnExports.js&lt;/a&gt; 예제를 활용했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// searchUtil.js&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// 즉시 실행 함수(IIFE) 형태로 선언한다. factory는 모듈 객체를 반환하는 함수다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; factory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// webpack을 위한 AMD 모듈 선언 방식,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// webpack은 AMD, CommonJS 방식 둘 다 지원한다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; define &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;function&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; define&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;amd&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; factory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// node.js에 해당하는 케이스&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; module &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;object&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 모듈 객체를 전역 컨텍스트에 할당. 웹 워커와 브라우저에서 사용할 수 있다.&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    root&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchUtil &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 모듈 객체&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    actions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token constant&quot;&gt;SEARCH_REQUEST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;SEARCH_REQUEST&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    trimText&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;str&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 선언된 모듈은 각각의 환경에 맞는 방식으로 불러와서 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// node.js에서 사용&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; actions &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;../static/js/searchUtil&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ES6 모듈에서 사용&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; actions &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../static/js/searchUtil&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 웹 워커에서 사용&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;importScripts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./searchUtil.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;searchUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;actions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SEARCH_REQUEST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;향후-개선-방향&quot;&gt;&lt;a href=&quot;#%ED%96%A5%ED%9B%84-%EA%B0%9C%EC%84%A0-%EB%B0%A9%ED%96%A5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;향후 개선 방향&lt;/h2&gt;
&lt;p&gt;검색 기능을 구현하긴 했지만 말 그대로 동작할 정도로만 만들어 두었을 뿐이다. 검색어가 입력되면 단어 목록 전체를 살펴보기 때문에 현재 O(n)의 검색 시간이 걸리는 상태다. 이를 개선하기 위해서는 당장 생각하는 방법으로는 단어를 초성이나 글자별로 그룹화해서 일부만 찾아보는 방식이 있겠다. 검색 결과 캐싱, 랭킹 부여 방식도 개선 등 고칠 점이 많으니 천천히 시도해 볼 생각이다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[[번역] 웹 아키텍쳐 입문]]></title><description><![CDATA[이 글은  Jonathan Fulton 의  Web Architecture 101 을 번역한 글입니다. 
 모던 웹 애플리케이션 아키텍쳐 개요 위의 다이어그램은 우리 Storyblocks의 아키텍쳐를 꽤 잘 나타낸다. 당신이 경험 많은 웹 개발자가 아니라면 아마도 꽤…]]></description><link>https://blog.rhostem.com//posts/2018-07-22-web-architecture-101</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-07-22-web-architecture-101</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 28 Jul 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://engineering.videoblocks.com/@jonathan_fulton&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Jonathan Fulton&lt;/a&gt;의 &lt;a href=&quot;https://engineering.videoblocks.com/web-architecture-101-a3224e126947&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Web Architecture 101&lt;/a&gt;을 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6Qv56PUiQwmgYEokCWKGgW/d7d470aef20abadf6dd29c5de023e529/main.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 665px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 63.308270676691734%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;main&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6Qv56PUiQwmgYEokCWKGgW/d7d470aef20abadf6dd29c5de023e529/main.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6Qv56PUiQwmgYEokCWKGgW/d7d470aef20abadf6dd29c5de023e529/main.png?w=166 166w,
https://images.ctfassets.net/rpmifyuylbfw/6Qv56PUiQwmgYEokCWKGgW/d7d470aef20abadf6dd29c5de023e529/main.png?w=333 333w,
https://images.ctfassets.net/rpmifyuylbfw/6Qv56PUiQwmgYEokCWKGgW/d7d470aef20abadf6dd29c5de023e529/main.png?w=665 665w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;모던 웹 애플리케이션 아키텍쳐 개요&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;위의 다이어그램은 우리 Storyblocks의 아키텍쳐를 꽤 잘 나타낸다. 당신이 경험 많은 웹 개발자가 아니라면 아마도 꽤 복잡하다고 생각할 것이다. 각각의 컴포넌트를 자세히 살펴보기 전에 아래의 글을 살펴보면 조금 더 다가서기 쉬워질 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;사용자가 구글에서 “짙고 아름다운 안개와 숲 속의 햇살”을 검색한다. &lt;a href=&quot;https://www.storyblocks.com/stock-image/strong-beautiful-fog-and-sunbeams-in-the-forest-bxxg0dvxdzj6gt9ini&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;첫 번째 결과&lt;/a&gt;는 Storyblocks, 우리의 메인 사진 저장 및 벡터 사이트에서 나온다. 사용자는 결과를 클릭하고 브라우저는 사진 상세 페이지로 이동한다. 보이지 않지만, 브라우저 내부에서는 DNS 서버에 Storyblocks에 어떻게 접속할 수 있는지 물어본 후 Storyblocks에 접근을 시도한다.&lt;/p&gt;
&lt;p&gt;브라우저의 요청은 우리의 로드 밸런서에 도착하고, 서비스를 운영하기 위해 동작 중인 10여 개의 서버 중 하나를 랜덤하게 선택해서 요청을 처리한다. 웹 서버는 캐싱 서비스에서 필요한 이미지 정보를 가져온 후 더 필요한 정보는 데이터베이스에 요청한다. 사용자에게 전달한 이미지의 컬러 프로필이 아직 만들어지지 않았음을 인지한 후 “컬러 프로필” 잡(job)을 잡 큐에 보낸다. 잡 서버는 큐에 추가된 것들을 비동기적으로 처리한 후 데이터베이스에 결과를 적절히 업데이트한다.&lt;/p&gt;
&lt;p&gt;다음, 우리는 전체 텍스트(full-text) 검색 서비스에 사진의 제목을 전달해서 비슷한 사진들을 찾으려고 한다. 사용자가 Storyblocks의 멤버로 로그인했다면 그의 계정 정보를 계정 서비스에서 가져온다. 일련의 작업들이 끝난 후, 데이터 &lt;a href=&quot;https://www.pubnub.com/blog/2014-11-14-what-is-a-data-firehose-api/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;firehose&lt;/a&gt;에 페이지 뷰 이벤트를 발생시켜서 우리의 클라우드 스토리지 시스템에 기록하고, 그 정보는 분석가들이 비즈니스와 관련된 질의에 답하는 데 사용할 수 있도록 데이터 저장소(warehouse)에서 사용된다.&lt;/p&gt;
&lt;p&gt;서버는 이제 HTML로 화면을 렌더링한 후 로드 밸런서를 통해서 사용자의 브라우저로 보낸다. 페이지는 CDN에 연결된 우리의 클라우드 스토리지 시스템에서 가져오는 자바스크립트와 CSS 파일을 포함하고 있다. 그래서 브라우저는 그 콘텐츠를 가져오기 위해 CDN에 접속한다. 마지막으로, 브라우저는 사용자가 볼 수 있는 콘텐츠를 렌더링한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이제 나는 각각의 컴포넌트들을 하나씩 소개할 것이다. 이는 당신이 앞으로 웹 아키텍쳐에 대해 생각해 볼 때 도움이 될 수 있는 좋은 심리 모델을 선사할 것이다. 나중에는 다른 시리즈의 글을 통해서 Storyblocks에서 얻은 경험을 바탕으로 추천하는 구체적인 구현 방법을 제공할 생각이다.&lt;/p&gt;
&lt;h2 id=&quot;1-dns&quot;&gt;&lt;a href=&quot;#1-dns&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. DNS&lt;/h2&gt;
&lt;p&gt;DNS는 “Domain Name Server”의 약자며 월드 와이드 웹(WWW)이 가능하도록 만드는 기반 기술이다. 가장 기본적인 레벨의 DNS는 도메인 이름(예. google.com)에서 IP 주소(예. 85.129.83.120)로의 키/값 조회를 제공한다. 이는 당신의 컴퓨터가 알맞은 서버에 요청을 보낼 수 있는 경로를 찾는 데 필요하다. 전화번호에 비유하자면 도메인 이름과 IP 주소의 차이는 “Jone Does에게 전화하기”와 “201-867–5309에 전화하기”의 차이다. Jone의 전화번호를 찾기 위해 전화번호부가 필요한 것처럼 도메인에서 IP 주소를 찾기 위해서는 DNS가 필요하다. DNS를 인터넷의 전화번호부라고 생각하면 된다.&lt;/p&gt;
&lt;p&gt;여기서 더 깊게 들어갈 수 있는 많은 내용이 있지만, 입문 수준의 소개에는 크게 중요하지 않기 때문에 이쯤에서 다음으로 넘어가도록 하자.&lt;/p&gt;
&lt;h2 id=&quot;2-로드-밸런서&quot;&gt;&lt;a href=&quot;#2-%EB%A1%9C%EB%93%9C-%EB%B0%B8%EB%9F%B0%EC%84%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 로드 밸런서&lt;/h2&gt;
&lt;p&gt;로드 밸런싱에 대해 자세히 알아보기 전에, 한발 뒤로 물러나서 수평적 vs 수직적 애플리케이션 확장(scaling)에 관해서 얘기해보자. 이들은 어떤 의미이며 어떤 차이가 있는가? 간단히 &lt;a href=&quot;https://stackoverflow.com/questions/11707879/difference-between-scaling-horizontally-and-vertically-for-databases&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;StackOverflow의 답변&lt;/a&gt;에 따르면 &lt;em&gt;수평적&lt;/em&gt; 확장은 더 많은 장치를 새로 추가하는 것이고, &lt;em&gt;수직적&lt;/em&gt; 확장은 이미 사용하고 있던 장치의 성능(예. CPU, RAM)을 높이는 것이다.&lt;/p&gt;
&lt;p&gt;웹 개발을 한다면 당신은 아마 언제나 수평적 확장을 원하게 될 텐데, 그 이유는 간단히 말해 서비스 중단을 막기 위해서다. 서버는 언제든지 고장이 날 수 있다. 네트워크는 속도가 느려질 수 있다. 데이터 센터에 정전이 발생할 수도 있다. 1대 이상의 서버를 가짐으로써 이러한 상황에 대비할 수 있으며 서비스는 멈추지 않고 계속 제공된다. 달리 말하면 ‘오류 내성’이 생긴다. 두 번째로, 수평적 확장은 애플리케이션 백엔드(웹 서버, 데이터베이스, 서비스 X, 기타 등등)들을 각각 다른 서버에서 돌림으로서 서로가 최소한으로 결합할 수 있도록 한다. 마지막으로, 수직적 확장은 언젠가 한계에 부딪힌다. 세상에는 당신의 앱이 하는 일을 모두 처리할 수 있을 만큼 강력한 성능을 가진 컴퓨터는 없다. 전형적인 사례로 구글의 검색 플랫폼을 생각하면 된다. 물론 구글 검색은 매우 큰 규모지만 수직적 확장의 한계는 작은 회사에도 적용될 수 있다. Storyblocks를 예로 들자면 서비스를 위해 150에서 400개의 AWS EC2 인스턴스를 동시에 구동하고 있다. 이 정도 규모를 수직적 확장으로 가능하게 하려 한다면 무척 힘든 일이 될 것이다.&lt;/p&gt;
&lt;p&gt;좋다, 이제 로드 밸런서로 돌아가자. 그들은 수평적 확장이 가능하도록 만드는 마술이다. 그들은 들어오는 요청을 복제/미러링 된 수많은 애플리케이션 서버 중의 하나로 연결하고 서버의 응답을 다시 클라이언트로 보낸다. 모든 서버는 특정 요청을 같은 방식으로 처리해야 하며, 로드 밸런서는 이들 서버에 과부하가 걸리지 않도록 들어오는 요청을 적절히 분배해주는 일을 한다.&lt;/p&gt;
&lt;p&gt;그게 전부다. 개념적으로 로드 밸런서는 꽤 직관적이다. 그 이면은 확실히 복잡하지만 입문에서는 거기까지 들어갈 필요는 없다.&lt;/p&gt;
&lt;h2 id=&quot;3-웹-애플리케이션-서버&quot;&gt;&lt;a href=&quot;#3-%EC%9B%B9-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EC%84%9C%EB%B2%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 웹 애플리케이션 서버&lt;/h2&gt;
&lt;p&gt;고수준의 웹 애플리케이션 서버는 상대적으로 설명하기 간단하다. 그들은 사용자의 요청이 들어오면 핵심 비즈니스 로직을 실행하고 그 결과를 HTML에 담아 브라우저로 보낸다. 이 일을 하기 위해서는 보통 데이터베이스, 캐싱 계층, 잡 큐, 검색 서비스, 기타 마이크로 서비스, 데이터/로그 큐(queue) 등 무척 다양한 백엔드 인프라와 데이터를 주고받아야 한다. 앞서 언급했듯 당신은 사용자의 요청을 처리하기 위해 최소 2번, 보통 그보다 많은 횟수로 로드 밸런서에 연결될 것이다.&lt;/p&gt;
&lt;p&gt;앱 서버 구현은 특정 언어(Node.js, Ruby, PHP, Scala, Java, C# .NET, …)를 선택하고 그에 맞는 웹 MVC 프레임워크(Express for Node.js, Ruby on Rails, Play for Scala, Laravel for PHP, …).를 선택해야 한다는 사실을 알아야 한다. 하지만 이들 언어와 프레임워크에 대해 깊이 알아보는 것은 이 글의 범위를 뛰어넘는다.&lt;/p&gt;
&lt;h2 id=&quot;4-데이터베이스-서버&quot;&gt;&lt;a href=&quot;#4-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%9C%EB%B2%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 데이터베이스 서버&lt;/h2&gt;
&lt;p&gt;모든 모던 웹 애플리케이션은 정보를 저장하기 위해 한 개 이상의 데이터베이스를 사용한다. 데이터베이스는 데이터 구조를 정의하고, 새로운 데이터를 삽입하고, 데이터를 찾고, 데이터를 수정하거나 삭제하고, 데이터로 연산을 수행하는 등의 일을 한다. 대부분은 웹 앱 서버는 잡 서버의 역할을 하는 데이터베이스 서버와 직접 통신한다. 거기에 더해서 각각의 백엔드 서비스는 애플리케이션의 다른 영역과 분리된 자신만의 데이터베이스를 가지고 있을 수 있다.&lt;/p&gt;
&lt;p&gt;나는 이 글에서 각각의 아키텍쳐 요소와 관련된 기술을 깊이 살펴보는 일을 가능한 삼가려 하지만, 데이터베이스의 다음 단계 주제를 얘기하지 않으면 당신에게 해가 될 것 같다. 그것은 SQL과 NoSQL이다.&lt;/p&gt;
&lt;p&gt;SQL은 “Structured Query Language”의 약자이며, 1970년대에 더 많은 사람들이 사용할 수 있도록 관계형 데이터 세트 질의(query)의 표준으로 만들어졌다. SQL 데이터베이스는 공통 ID(보통 숫자를 사용)로 연결된 테이블에 데이터를 저장한다. 간단한 예제로 사용자의 과거 주소 정보를 저장하는 과정을 살펴보자. 아마 사용자의 아이디로 연결된 user와 user&lt;em&gt;address라는 2개의 테이블을 사용할 것이다. 아래의 이미지를 보자. 두 테이블은 user&lt;/em&gt;adress 의 user_id 컬럼이 user의 id 컬럼을 참조하고 있는 외래 키(foreign key)이기에 연결된 것이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/HKZIaK6SE86UKwC8EcwSc/bad4ee3ffb37031c47b61a7463ae8fd5/01.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 451px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 37.91574279379157%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAPCAQAAAA8XkVoAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4chT1LDQAAAWhJREFUOMul0k1vElEYBeAHGAYoEWJqw0cNpKYmGhNdalz5j9269GfYmBgTTIRQkVEKQwdmXIjtUBoj9azuuee95/26hbfZUqiv5DYsvWz6kbtovIu+65pZSFQkDj3IyRNBR0OooYRMQaaQC4h3UjzUc2BhrWKFmns5tSqIVT0F750YONyw31jsGM4d6PvgUizx6oaaCJ5obcgzS6dXLFfSmfU1baqgL1KVGsqkjnPhxXx7sdlWw2C1Fa8htRYZu3QhleazIbg+DhQkTm8aDtXytKmNb4Ym6BhbO1fyYtfwtX/HYz2BRFlLqJybdXC++tvDmMxUW7qd9auxrqGKqUc6uU0X7IfGPKrhk5mlnoG6TMf9jTzKt7wPWppSVSdqpqq3L2UffBGpm6ibeb61tTsaNoSK2kaOxM6sHWv+j2EoUZOoqyBUvvrQdzQc+enIR0VdkbnAm42y95Y/R38mthIgkylKNxWO/QLntGlqakxiXAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;01&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/HKZIaK6SE86UKwC8EcwSc/bad4ee3ffb37031c47b61a7463ae8fd5/01.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/HKZIaK6SE86UKwC8EcwSc/bad4ee3ffb37031c47b61a7463ae8fd5/01.png?w=113 113w,
https://images.ctfassets.net/rpmifyuylbfw/HKZIaK6SE86UKwC8EcwSc/bad4ee3ffb37031c47b61a7463ae8fd5/01.png?w=226 226w,
https://images.ctfassets.net/rpmifyuylbfw/HKZIaK6SE86UKwC8EcwSc/bad4ee3ffb37031c47b61a7463ae8fd5/01.png?w=451 451w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;만약 SQL에 대해서 잘 알지 못한다면 나는 Khan Academy에서 &lt;a href=&quot;https://www.khanacademy.org/computing/computer-programming/sql&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;찾을 수 있는 것&lt;/a&gt;같은 같은 튜토리얼을 공부하길 강력히 권한다. SQL은 모든 웹 개발에서 사용되기 때문에 애플리케이션을 제대로 설계하기 위해서는 최소한 기본은 알아야 한다.&lt;/p&gt;
&lt;p&gt;NoSQL, “Non-SQL”의 약자인 이것은 대규모 웹 애플리케이션에 의해 생성되는 많은 양의 데이터를 처리하기 위해 등장한 최신 데이터베이스 기술의 집합이다(SQL에서 파생된 기술 대부분은 수평적 확장이 어려우며 특정 지점에 도달하면 수직적 확장만이 가능하다). NoSQL에 대해서 전혀 알지 못한다면 다음과 같은 고수준의 안내서와 함께 시작하길 추천한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3resource.com/mongodb/nosql.php&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://www.w3resource.com/mongodb/nosql.php&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.kdnuggets.com/2016/07/seven-steps-understanding-nosql-databases.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://www.kdnuggets.com/2016/07/seven-steps-understanding-nosql-databases.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://resources.mongodb.com/getting-started-with-mongodb/back-to-basics-1-introduction-to-nosql&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://resources.mongodb.com/getting-started-with-mongodb/back-to-basics-1-introduction-to-nosql&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;나는 당신이 SQL은 반드시 공부해야 한다는 사실을 명심하고 있었으면 한다. 왜냐하면, 이 소프트웨어 업계는 NoSQL 데이터베이스조차도 SQL 인터페이스로 데이터를 조정하고 있기 때문이다. 아직은 SQL의 지배에서 벗어날 방법은 없다.&lt;/p&gt;
&lt;h2 id=&quot;5-캐싱-서비스&quot;&gt;&lt;a href=&quot;#5-%EC%BA%90%EC%8B%B1-%EC%84%9C%EB%B9%84%EC%8A%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 캐싱 서비스&lt;/h2&gt;
&lt;p&gt;캐싱 서비스는 정보를 거의 &lt;a href=&quot;https://stackoverflow.com/a/697935&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;O(1) 시간&lt;/a&gt; 안에 찾을 수 있는 단순한 키/값 데이터 저장소를 제공한다. 애플리케이션은 캐싱 서비스를 통해 자원이 많이 소모되는 연산의 결과를 다시 계산하지 않고 캐시에서 가져옴으로써 효율을 높인다. 애플리케이션은 데이터베이스의 쿼리 결과, 외부 서비스 호출 결과, 주어진 URL의 HTML 등을 캐시에 저장한다. 다음은 실무에서 사용되는 몇몇 예제다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;구글은 일반적인 검색어인 ‘dog’나 ‘Taylor Swift’를 사용한 검색을 매번 실행하기보다는 결과를 캐시 한다.&lt;/li&gt;
&lt;li&gt;페이스북은 당신이 로그인할 때 포스트 데이터, 친구 목록 등 많은 데이터를 캐시 한다. 페이스북의 자세한 캐시 기술은 &lt;a href=&quot;https://medium.com/@shagun/scaling-memcache-at-facebook-1ba77d71c082&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;여기&lt;/a&gt;에서 읽어볼 수 있다.&lt;/li&gt;
&lt;li&gt;Storyblocks는 React 서버 사이드 렌더링으로 생성된 HTML, 검색 결과, 검색어 입력 자동완성 결과 등을 캐시 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;가장 널리 사용되는 캐싱 서버 기술 2개는 Redis와 Memcache다. 나는 다른 포스트에서 이들에 대해 더 자세히 다룰 예정이다.&lt;/p&gt;
&lt;h2 id=&quot;6-잡-큐job-queue--서버&quot;&gt;&lt;a href=&quot;#6-%EC%9E%A1-%ED%81%90job-queue--%EC%84%9C%EB%B2%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. 잡 큐(job queue) &amp;#x26; 서버&lt;/h2&gt;
&lt;p&gt;거의 모든 웹 애플리케이션은 사용자의 요청에 대한 응답과는 직접적인 관련이 없는 작업을 백그라운드에서 비동기적으로 실행할 필요가 있다. 예를 들어 구글은 검색 결과를 얻기 위해 인터넷에서 데이터를 크롤링하고 인덱싱할 필요가 있다. 이는 당신이 검색을 요청할 때마다 실행되지 않으며 구글의 검색 엔진은 비동기적으로 웹을 크롤링하고 있다.&lt;/p&gt;
&lt;p&gt;비동기적인 작업을 가능하게 하는 여러 가지 아키텍쳐가 있지만 가장 널리 사용되는 것은 “잡 큐” 아키텍처다. 이는 2개의 컴포넌트로 구성된다. “잡”으로 이루어진 큐, 그리고 큐에 들어있는 잡을 실행하는 1개 이상의 잡 서버가 그것이다.&lt;/p&gt;
&lt;p&gt;잡 큐는 비동기적으로 실행될 잡 목록을 저장하고 있다. 대부분 애플리케이션이 결국에는 우선순위가 적용된 큐가 필요하게 되지만 가장 단순한 것은 first-in-first-out(FIFO) 큐다. 앱이 정기적인 일정이나 사용자에 의해 발생한 잡을 실행할 필요가 생기면, 그에 알맞은 잡을 큐에 추가하기만 하면 된다.&lt;/p&gt;
&lt;p&gt;Storyblocks를 예로 들자면, 우리의 마켓플레이스를 지원하는 데 필요한 내부 작업에 잡 큐를 사용해서 힘을 실어주고 있다. 잡 큐를 통해 영상과 사진을 인코딩하고, 메타데이터 태그를 붙이기 위해 CSV를 처리하고, 사용자 통계를 취합하고, 비밀번호 재설정을 위한 이메일을 전송하는 등의 일을 한다. 우리는 간단한 FIFO 큐로부터 시작했지만 빠른 처리 속도가 중요한 민감한 비밀번호 재설정과 같은 작업을 위해 우선순위 큐로 업그레이드하게 되었다.&lt;/p&gt;
&lt;p&gt;잡 서버는 잡을 처리 한다. 그들은 잡 큐를 가져와서 할 일이 있는지 확인하고, 있다면 큐에서 잡을 뽑아내서 실행한다. 잡 서버로 사용할 수 있는 언어와 프레임워크는 너무나 다양해서 이 글에서는 자세히 다루지 않겠다.&lt;/p&gt;
&lt;h2 id=&quot;7-전체-텍스트-검색-서비스&quot;&gt;&lt;a href=&quot;#7-%EC%A0%84%EC%B2%B4-%ED%85%8D%EC%8A%A4%ED%8A%B8-%EA%B2%80%EC%83%89-%EC%84%9C%EB%B9%84%EC%8A%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. 전체 텍스트 검색 서비스&lt;/h2&gt;
&lt;p&gt;대부분은 아니지만 많은 웹 앱이 사용자가 텍스트 입력(보통 ‘쿼리’라고 불리는)을 입력을 하면 검색을 하고 가장 ‘관련 있는’ 결과를 보여주는 기능을 제공한다. 이 기능을 가능하게 하는 것은 보통 &lt;a href=&quot;https://en.wikipedia.org/wiki/Full-text_search&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“전체 텍스트 검색”&lt;/a&gt;이라고 불린다. 전체 텍스트 검색은 쿼리 키워드를 포함하는 문서를 빠르게 찾기 위해 역 인덱스(inverted index)를 활용한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5gJakNIGvuS42easQcMeGi/e40692a0b6a26aa9a794b9df34c5afac/02.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 561px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 33.68983957219251%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAANCAMAAADsQdzaAAABoVBMVEUAAAC4VFDE1vDK2/PD1e/XuFnR4fft2Jnhxnbg6PX25bL0462rTkrz9vr66r3i5en87cLYuFvmzoTYuVvhx3ba4u/i6fLg5/HU2uPZ4u64VFDs4eHTy8vXz8/Xz8737+/58fHo4OD15uXyw8D0xsPit7XWravPp6bSqqjQqKbovLrvvrvzycj1zcvowsDbt7bWs7LYtbPdubjVsrHpw8HwxMLS4vja6PzR3vKdp7aos8Kjrr2irLust8egq7mirbytuMebpbSgqrnd29rMzMzKysrR0dH////t7e3W1tbNzc3b29vS0tLo6Oj68/Lh6fXq8Pnq8Prp7/jf5e7p8Pnl4+Pc3Nzg4ODj4+Pd3d3e3t7i4uLh4eHm5ubp4uLT1tnr6+v9/f3Jycm3t7fs6en7+/v09PTk5uns7Oz49fXu7u74+Pj29vbm6ezQ0NDf39/T09PX19fV1dXIyMj/8cvm5OPPz8/k5OTt7/P39/f+/v79+vrg4+ba2trY2NjU1NTq6urn5OTOzs7x8/f8/Pzd4OP6+vrg3d3n5+fx7u7z8/N0rRT5AAAAI3RSTlMAC3x8bwrlRwnlUkkL5ZXl3wEaAQlZWVlZUArc3Nzc3Nzc3K1PtkMAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmDBsPHhyFPUsNAAABH0lEQVQYGXXBu07CYACG4ff7+7fUQGIxGLQREg+JgoMLm4M34wV5AV6Mi5uoYWFU8ZiokcSgiD1YWiJx8HnEP7QhUCZxNIGSNeIvRbjfwIJABQd82xGaG8EFGH8MdWVQbkLJrmoqprAIPXAE5SVJQ6pS5OqZwAZ6DKU7flnAgdHiR3kAb81BSmZo/CeVxveaM4CpghtEZNwtcsZL069auL1jWmol7V0nTYBDQcVf3gTSMjlbBq6aH9T9lWGnS+HYAe8stEDQJWcsELmPN2ufjabHTPIKXrpe9eC8Ss6WgHqtr7hP+MBMDEx0SqYxIGcMEJzstPy2c7t3QC5eACrbHplNCvZIXCrpMdUjU3kneQFMtH+91MVQEP8IVSBlyvwAzrtI7oTjN5AAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;02&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5gJakNIGvuS42easQcMeGi/e40692a0b6a26aa9a794b9df34c5afac/02.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5gJakNIGvuS42easQcMeGi/e40692a0b6a26aa9a794b9df34c5afac/02.png?w=140 140w,
https://images.ctfassets.net/rpmifyuylbfw/5gJakNIGvuS42easQcMeGi/e40692a0b6a26aa9a794b9df34c5afac/02.png?w=281 281w,
https://images.ctfassets.net/rpmifyuylbfw/5gJakNIGvuS42easQcMeGi/e40692a0b6a26aa9a794b9df34c5afac/02.png?w=561 561w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;3개의 문서의 제목이 어떻게 역 인덱스로 변환되어서 특정 키워드를 포함한 제목을 가진 문서를 찾을 수 있도록 활용하는지를 보여주는 예제다. 보통 ‘in’, ‘the’, ‘with’ 같은 일반적인 단어(stop 단어라고 불린다)는 역 인덱스에 포함되지 않음을 주의 깊게 보라.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;어떤 데이터베이스에서는 전체 텍스트 검색을 바로 사용할 수 있지만(MySQL은 전체 텍스트 검색을 지원한다), 보통 역 인덱스를 연산하고 쿼리 인터페이스를 제공하는 “검색 서비스”를 분리해서 운영하는 사례가 많다. 오늘날 가장 인기 있는 전체 텍스트 검색 플랫폼은 &lt;a href=&quot;https://www.elastic.co/products/elasticsearch&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Elasticsearch&lt;/a&gt;지만 &lt;a href=&quot;http://sphinxsearch.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Sphinx&lt;/a&gt; 또는 &lt;a href=&quot;http://lucene.apache.org/solr/features.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Apache Solr&lt;/a&gt; 같은 다른 선택지도 있다.&lt;/p&gt;
&lt;h2 id=&quot;8-서비스&quot;&gt;&lt;a href=&quot;#8-%EC%84%9C%EB%B9%84%EC%8A%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. 서비스&lt;/h2&gt;
&lt;p&gt;앱이 특정 규모에 도달하면 별도의 애플리케이션으로 분리해서 운영하기 위핸 ‘서비스’가 생기게 된다. 외부에는 노출되지 않지만, 앱과 다른 서비스와는 연동된다. Storyblocks을 예로 들자면 몇 개의 운영, 계획 서비스를 하고 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;계정 서비스&lt;/strong&gt;는 우리의 모든 사이트의 사용자 정보를 저장해서 교차 판매 기회를 더 쉽게 제공하고, 더 일관적인 사용자 경험을 가능하게 한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;컨텐츠 서비스&lt;/strong&gt;는 우리의 모든 비디오, 오디오, 이미지의 메타데이터를 저장한다. 또 콘텐츠 다운로드 인터페이스와 다운로드 이력을 보여주는 기능을 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;결제 서비스&lt;/strong&gt;는 고객이 카드로 결제할 수 있는 인터페이스를 제공한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTML → PDF 서비스&lt;/strong&gt;는 HTML을 PDF로 변환하는 간단한 인터페이스를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;9-데이터&quot;&gt;&lt;a href=&quot;#9-%EB%8D%B0%EC%9D%B4%ED%84%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9. 데이터&lt;/h2&gt;
&lt;p&gt;오늘날, 기업은 데이터를 어떻게 다루느냐에 따라 살고 죽는다. 최근 거의 모든 앱은 특정 규모에 도달하면 데이터를 제어, 저장, 분석하기 위해 데이터 &lt;a href=&quot;https://en.wikipedia.org/wiki/Pipeline_(computing)&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;파이프라인&lt;/a&gt;을 사용한다. 전형적인 파이프라인은 3개의 주요 단계를 가진다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;앱은 보통 사용자 상호작용으로 발생한 데이터를 데이터 ‘firehose’라 불리는 곳으로 전달한다. 그것은 데이터를 받아들이고 처리할 수 있는 스트리밍 인터페이스를 제공한다. 가공되지 않은 원시 데이터는 변형되거나(transformed) 추가 정보와 함께(augmented) 다른 firehose로 전달된다. AWS Kinesis와 Kafka는 이러한 작업을 위한 대표적인 기술이다.&lt;/li&gt;
&lt;li&gt;원시 데이터와 최종 데이터는 모두 클라우드 스토리지에 저장된다. AWS Kinesis는 원시 데이터를 AWS의 클라우드 스토리지(S3)에 저장할 수 있도록 매우 쉽게 사용할 수 있는 ‘firehose’로 불리는 설정을 제공한다.&lt;/li&gt;
&lt;li&gt;변형/추가된 데이터는 종종 분석을 위해 데이터 웨어하우스(DW)에서 로드된다. 우리는 AWS Redshift를 사용한다. 큰 기업에서는 Oracle이나 기타 독점적인 웨어하우스 기술을 사용하고 있지만, 스타트업 업계에서는 RedShift를 많이 사용하고 있으며 점유율도 계속 오르고 있다. 만약 데이터가 충분히 축적되었다면 Hadoop같은 NoSQL MapReduce 기술이 분석을 위해 필요하게 될 것이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;처음에 제시한 아키텍쳐 다이어그램에서는 빠진 단계가 있다. 데이터 웨어하우스에서 앱과 서비스에서 사용중인 데이터베이스에서 데이터를 불러오는 과정이다. 예를 들어 Storyblocks에서 우리는 VideoBlocks, AudioBlocks, Storyblocks, 계정 서비스, 그리고 컨트리뷰터 포털 데이터베이스를 매일 밤 Redshift로 불러온다. 이는 사용자 상호작용 이벤트 데이터와 핵심 비즈니스 데이터를 함께 둠으로써 우리의 분석가에게 총체적인 데이터 집합을 제공한다.&lt;/p&gt;
&lt;h2 id=&quot;10-클라우드-스토리지&quot;&gt;&lt;a href=&quot;#10-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10. 클라우드 스토리지&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/what-is-cloud-storage/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;AWS에 의하면&lt;/a&gt; “클라우드 스토리지는 인터넷을 통해 데이터를 저장, 접근, 공유할 수 있는 단순하고 확장성 있는 방법”이다. 당신은 로컬 파일 시스템에 저장할 수 있는 거의 모든 것을 RESTful API를 사용해서 HTTP를 통해 클라우드에 저장하고 접근할 수 있다. 아마존의 S3는 현재로써는 가장 인기 있는 클라우드 스토리지이며 Storyblocks에서는 비디오, 사진, 오디오 데이터, CSS, 자바스크립트, 사용자 데이터 등을 저장하기 위해 S3에 크게 의존하고 있다.&lt;/p&gt;
&lt;h2 id=&quot;11-cdn&quot;&gt;&lt;a href=&quot;#11-cdn&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;11. CDN&lt;/h2&gt;
&lt;p&gt;CDN은 ‘Content Delivery Network’의 약자며 HTML, CSS, 자바스크립트, 이미지 같은 정적인 데이터를 웹을 통해 1개의 원본(origin) 서버를 사용하는 것보다 더 빠르게 제공하기 위한 기술이다. 이는 콘텐츠를 전 세계의 많은 ‘엣지(edge)’ 서버에 분산시킴으로써 동작한다. 그리고 사용자는 데이터를 원본 서버 대신 가장 가까운 엣지 서버에서 다운로드한다. 예를 들어 아래의 이미지처럼 스페인에 있는 사용자가 뉴욕에 있는 원본 서버의 웹페이지에 접근하면 정적인 데이터는 대서양을 가로지르는 매우 느린 HTTP 요청을 하는 대신 영국에 있는 CDN ‘엣지’ 서버에서 빠르게 가져오는 것이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2HAbcN4a7eMYmY22iQCm0M/7f283929fc77876741a5ff2939af210a/03.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 918px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 44.77124183006536%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAASCAMAAAAewWyUAAAC8VBMVEX////+/v/8/f77/P33+fvv9Pjj6/Pm7fTg6fLe6PHb5e/Z4+7c5vD1+Pv6+/32+Pvr8fbk7PPf6PHe5/Ha5O/Y4+7i6vL6/P3y9vnl7fTo7/Xs8ffp7/Xg6fHq8PbZ5O7n7vX+///9/v7l7PPt8vfn7fT+/v7y9fnb5vDd5/Da5e/4+vz09/rh6vLb5fD5+/3w9fn2+fvw9Pj5+/zz9/rx9fnz9vrl7PTs8vfZ5O/5+vzd5vD3/ffq+erp+ena7uHEx8fX4u7X0NrX4u3o7vX3+vzs8/Tb7+To+Ojx+/H+//7759Xh5enW4e3OsrXg0ML138z54s775M/75dH859XGwb3a4+3v8/jy9vrEtbXS6d/l9+fu+u78/vy7t7Pe5/DU4Oz///7//v3++/nV2Nzf5uz8/f3IztTs8fbS3+vT3+vZ4+3o7vTW4ezf5/DR3enQ3enx9fjc5u/1+PrP2+jO2+jb5e7M2ebe1dzvysr0zMz11NT21tb66urj6vLh5+7ix8v43t703d733d376+v++vrxwcHxvr7p2d3KvcnKvsrKrbjKoqzKxNDv8/fT3OXh3+Dv7+/2+Prg8+C/576p1be509Ky0cqhzLaXyarB1drazdDh3OLJy9jKzdrY3Obvw8PmwMDrzMzlu7vKtLy/qrW9oqy/qbPEuMTu8vfm5+fO0dTG0NnJ1uTn7fPx8fHT5dPH48a93b6dwrCpx8CVvqiXwaq+0dn19fXBwcHByM+9y9jE0+HH1eP5+fns7Ozy8vLh4eHS0tLGyczMztDJyszCzNbt8vb98+r76trRx77Kx8XLxL/Mw73Q0dT09PTQ0NDLzc7Iy87Nzs7Iys3Cz9z++fX35tbKwbrHxMLIwr7JwLnGxsjO2ubq7/T8/P3r6+vi4uLm5ubk5OTf39/K09vs8fXt/O3J6dKbzLOjzbyfzLidzLawzsvX4ev39/fd3d3g4ODD0N2/zt29zNzD0d/z9vn4+fvl6/LL1+Tl6/EGjzEHAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAeNJREFUGBk9wc1LkwEAx/Hvb8/z+GzO10ybSkqz9wR7gULp0i2KIILoVnTpFB0KOkUdJPoDukRdgm4SVIfeCIKiIjzMUilzsaJhmwZbqNt0Pm49z7OefT4CBZBrCSO6CCHRpGJUyDNPq4jJl1ZjhEbpB674algrkXRbSyYM1Y5vgLEz6xRilcxalYFo1prLQ2j3prlCqTO/bPZYG+Yd+qeBZkO2ZSz2VsN/5XSmlwGjvdMs2K0LTpfZmuj73Z3JAgcjJlBdtqwSlVwOiHflislt8KelmK2sM04KGGwuZ0zA6kgVqiMf8Gz8ZebiQNQpN29P4josV2K/OQjS5Eg2im+8d6BEaBBUnYruUxGienZcIys6AfLgkStxYGZXAZrkeoDvrL7OmR0wFkvhOy8Xyan5o/A+SeCCnh/DGG54t5Cj5tPERKR7oC/RvcOyX1Jnb96qfu1Zq3yn7qIh0JP21RmHQPxkBQilWafuSrjBsqw3KXvaoc4wLJeuPp6lLnYO16sjljyj+K6v4tINea7BTalsy/fwtKRL+G7rP+7JdWsW7itwZkzS+KFTwCMFXsiFPl/mtQLDH+W7c5eJt0Nt8n1RYAv8lK8no5rRp7PkJ4eWUnslzLzwSI34SvkeVhx8lk1N+R/cEZraVrIRRgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;03&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2HAbcN4a7eMYmY22iQCm0M/7f283929fc77876741a5ff2939af210a/03.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2HAbcN4a7eMYmY22iQCm0M/7f283929fc77876741a5ff2939af210a/03.png?w=230 230w,
https://images.ctfassets.net/rpmifyuylbfw/2HAbcN4a7eMYmY22iQCm0M/7f283929fc77876741a5ff2939af210a/03.png?w=459 459w,
https://images.ctfassets.net/rpmifyuylbfw/2HAbcN4a7eMYmY22iQCm0M/7f283929fc77876741a5ff2939af210a/03.png?w=918 918w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;&lt;a href=&quot;https://www.creative-artworks.eu/why-use-a-content-delivery-network-cdn/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;출처&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;CDN에 대한 더 자세한 안내는 &lt;a href=&quot;https://www.creative-artworks.eu/why-use-a-content-delivery-network-cdn/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;이 글을 살펴보길 바란다&lt;/a&gt;. 일반적인 웹 앱은 CSS, 자바스크립트, 이미지, 비디오 및 다른 정적인 데이터를 제공하기 위해 항상 CDN을 사용해야 한다. 어떤 앱은 정적인 HTML 페이지를 제공할 때도 CDN을 사용하기도 한다.&lt;/p&gt;
&lt;h2 id=&quot;맺음말&quot;&gt;&lt;a href=&quot;#%EB%A7%BA%EC%9D%8C%EB%A7%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;맺음말&lt;/h2&gt;
&lt;p&gt;웹 아키텍쳐 입문은 여기서 끝이다. 이 글이 도움되었으면 좋겠다. 나는 강의를 통해 웹 아키텍쳐의 몇몇 컴포넌트를 깊이 있게 살펴보는 시리즈를 내후년 안에 제공하려 한다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[자바스크립트의 this가 가리키는 것]]></title><description><![CDATA[자바스크립트에서 컨텍스트( context )는 곧 객체 인스턴스다. 그리고 컨텍스트는  라는 키워드를 통해 참조할 수 있다. 자바스크립트에서  는 처음 접했을 때 많은 혼란을 주는 존재다, 이 키워드는 사용되는 위치에 따라 서로 다른 객체를 가리키기 때문이다.   키…]]></description><link>https://blog.rhostem.com//posts/2018-07-20-this-in-javascript</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-07-20-this-in-javascript</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Fri, 20 Jul 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;자바스크립트에서 컨텍스트(&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;context&lt;/a&gt;)는 곧 객체 인스턴스다. 그리고 컨텍스트는 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;라는 키워드를 통해 참조할 수 있다. 자바스크립트에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 처음 접했을 때 많은 혼란을 주는 존재다, 이 키워드는 사용되는 위치에 따라 서로 다른 객체를 가리키기 때문이다. &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 키워드를 파악하기 위해서는 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 사용하고 있는 함수의 객체 생성 여부와 함수와 객체 사이의 관계를 잘 살펴봐야 한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 글에서 언급하는 ‘객체’란 인스턴스를 의미한다. 즉 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드를 통해 실체화 된 객체를 말한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;함수&quot;&gt;&lt;a href=&quot;#%ED%95%A8%EC%88%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;함수&lt;/h2&gt;
&lt;p&gt;먼저 함수 내부에서의 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 살펴보자. 함수는 선언한 후, 호출해서 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;this is&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this is undefined&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 코드를 실행하면 콘솔 창에는 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;가 출력된다. &lt;strong&gt;단순히 선언만 한 함수는 컨텍스트가 없기 때문이다&lt;/strong&gt;. 호출을 통해 내부 프로세스를 사용할 수는 있지만 함수라는 틀만 있고 객체 인스턴스로는 만들어지지 않은 상태다. 실체가 없으므로 함수 내부에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;가 된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이 글의 모든 예제는 전역 공간(브라우저의 콘솔 커맨드라인에서 실행하는 것과 같다.)에서 &lt;a href=&quot;https://msdn.microsoft.com/ko-kr/library/br230269(v=vs.94).aspx&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Strict 모드&lt;/a&gt;로 실행했다. Strict 모드를 사용하면 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 객체가 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;일 때 전역 객체로 자동 변환되지 않는다. 만약 Strict 모드를 사용하지 않았다면 위의 예제에서는 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; 대신 &lt;code class=&quot;language-text&quot;&gt;Window&lt;/code&gt;가 출력되었을 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;함수로 객체를 생성한다&lt;/strong&gt;는 개념이 약간 어색하게 느껴질 수 있지만, 자바스크립트에는 가능한 일이다. 자바스크립트에서는 객체 리터럴 표기법(ex. &lt;code class=&quot;language-text&quot;&gt;var a = { prop: &amp;#39;value&amp;#39; }&lt;/code&gt;)뿐만 아니라 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드와 함수를 사용해서 객체를 만들 수 있다.&lt;/p&gt;
&lt;p&gt;앞서 선언한 &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt; 함수로 객체를 만들어 보자. 그러면 콘솔 로그에는 함수를 직접 호출했을 때와 다른 결과가 나타난다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this is foo {}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞에서는 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;라고 했지만 여기서는 &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt;라고 한다. &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 가리키는 대상이 바뀐 이유는 함수 &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt;를 바탕으로 한 객체가 만들어졌기 때문이다. 따라서 &lt;code class=&quot;language-text&quot;&gt;console.log&lt;/code&gt; 함수에서 참조한 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 가리키는 대상은 이제 &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt;가 된다.&lt;/p&gt;
&lt;h2 id=&quot;메소드method&quot;&gt;&lt;a href=&quot;#%EB%A9%94%EC%86%8C%EB%93%9Cmethod&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;메소드(method)&lt;/h2&gt;
&lt;p&gt;객체의 속성에 할당된 함수를 메소드라고 한다. 메소드 내부에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 무엇을 가리키는지 확인해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;check&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;this is&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this is foo {check: ƒ}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;check&lt;/code&gt; 메소드 내부의 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt;로 나온다. 메소드도 앞서 언급한 것처럼 함수 선언만 하고 객체가 생성되지는 않은 상태다. 하지만 함수 선언과 달리 &lt;strong&gt;메소드는 &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt; 함수의 컨텍스트(&lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;)에 직접 연결되어 있다&lt;/strong&gt;. &lt;code class=&quot;language-text&quot;&gt;foo&lt;/code&gt; 함수로 객체가 만들어지면 메소드도 그 객체에 포함된다. 서로 동일한 컨텍스트를 공유하는 개념이라고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;클래스 문법으로 프로토타입 메소드를 선언하거나 리터럴 문법으로 선언한 객체에도 같은 법칙이 적용된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// === Bar.prototype.check&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;this is&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this is Bar {}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  checkAtObj&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;this is&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checkAtObj&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this is {checkAtObj: ƒ}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;컨텍스트를-가지지-않는-화살표-함수&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EA%B0%80%EC%A7%80%EC%A7%80-%EC%95%8A%EB%8A%94-%ED%99%94%EC%82%B4%ED%91%9C-%ED%95%A8%EC%88%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨텍스트를 가지지 않는 화살표 함수&lt;/h2&gt;
&lt;p&gt;ES6에는 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/%EC%95%A0%EB%A1%9C%EC%9A%B0_%ED%8E%91%EC%85%98&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;화살표 함수&lt;/a&gt; 문법이 추가되었다. 화살표 함수로 생성된 함수는 컨텍스트를 가지지 않으며 객체 생성을 위한 &lt;code class=&quot;language-text&quot;&gt;constructor&lt;/code&gt;도 없어서 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드로 객체를 생성할 수도 없다.&lt;/p&gt;
&lt;p&gt;화살표 함수는 일반적인 함수 선언과 다르게 작동한다. 화살표 함수 내부에서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 그 함수를 포함하고 있는 객체 인스턴스를 가리킨다. 함수처럼 사용할 수 있지만 &lt;strong&gt;화살표 함수 내부의 코드는 중괄호로 둘러싸이지 않은 영역에서 실행된다&lt;/strong&gt;고 생각하면 이해하기 쉽다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;normalFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;this in normalFunc is&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this in normalFunc is undefined&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;normalFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;arrowFunc&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;this in arrowFunc is&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this in arrowFunc is test {}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;arrowFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;this&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this test {}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; testInstance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;컨텍스트를-직접-지정하기&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%A7%81%EC%A0%91-%EC%A7%80%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨텍스트를 직접 지정하기&lt;/h2&gt;
&lt;p&gt;지금까지는 살펴본 것들은 자연스러운 상황이었다. 하지만 자바스크립트는 컨텍스트, 즉 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;가 가리키는 객체를 조작할 수 있는 방법을 제공한다. &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Function&lt;/code&gt;&lt;/a&gt; 함수의 프로토타입에 선언되어 있는 &lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;apply&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;bind&lt;/code&gt;가 그것이다. 이 3가지 메소드는 사용 방식이 조금씩 다를 뿐 목적은 모두 동일하다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt;은 컨텍스트를 지정하는 동시에 호출한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;check&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; testInstance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

testInstance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// test {check: ƒ}&lt;/span&gt;
testInstance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Window {}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;자바스크립트의 모든 함수는 기본 객체인 &lt;code class=&quot;language-text&quot;&gt;Function&lt;/code&gt;으로 만들어진 객체이기에 사용자가 만들지 않은 속성을 프로토타입으로 가지고 있다. &lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt; 메소드에 객체를 파라미터로 전달하면 함수가 즉시 실행되면서 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;만 바뀐다. &lt;code class=&quot;language-text&quot;&gt;apply&lt;/code&gt;는 call과 같지만, 원본 함수에 전달할 파라미터를 배열에 담아서 전달한다는 것만 다르다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;testInstance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;arg1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;args2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
testInstance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;arg1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;args2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞의 두 메소드와 달리 &lt;code class=&quot;language-text&quot;&gt;bind&lt;/code&gt;는 즉시 실행되지 않는다. 대신 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EA%B3%A0%EC%B0%A8_%ED%95%A8%EC%88%98&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;고차 함수&lt;/a&gt;가 하는 것처럼 새로운 함수를 만든다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; calc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  base&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  sum&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;base &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; num
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hundred &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  base&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;calc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 11&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// sum 메소드의 컨텍스트를 교체해서 this.base가 100이 되도록 한다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sumBindToHundred &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; calc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hundred&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sumBindToHundred&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// 101&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;컨텍스트-this-결국-모두-객체다&quot;&gt;&lt;a href=&quot;#%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-this-%EA%B2%B0%EA%B5%AD-%EB%AA%A8%EB%91%90-%EA%B0%9D%EC%B2%B4%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컨텍스트? this? 결국 모두 객체다.&lt;/h2&gt;
&lt;p&gt;자바스크립트는 모든 것이 객체로 구성되어 있으며 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;도 결국 객체를 가리키는 키워드다. 하지만 몇 가지 변형이 있어서 모든 사례에 적용되는 규칙을 정의하기는 어렵다. 그래도 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 관통하는 추상적인 원칙을 하나 들자면 &lt;strong&gt;this는 실체가 있는 대상을 가리켜야 한다&lt;/strong&gt;가 아닐까 한다. 단순 함수 선언은 컨텍스트가 없고, 객체가 만들어진 함수만 컨텍스트가 있기 때문이다. 그리고 메소드로 지정된 함수는 객체로 만들어지진 않았지만 다른 객체에 포함되어 있으니 실체가 있다고 간주할 수도 있지 않을까? 개인적으로는 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 단순 암기식으로 외우는 것보다 이런 식으로 원칙을 세우는 편이 이해하기에 좋았다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[[번역] JavaScript vs. TypeScript vs. ReasonML]]></title><description><![CDATA[이 글은  Dr. Axel Rauschmayer 의  JavaScript vs. TypeScript vs. ReasonML 을 번역한 글입니다. 이 포스트에서는 세 가지 프로그래밍 언어/방언인 자바스크립트(JavaScript), 타입스크립트(TypeScript)와 R…]]></description><link>https://blog.rhostem.com//posts/2018-06-24-javascript-vs-typescript-vs-reasonml</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-06-24-javascript-vs-typescript-vs-reasonml</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 24 Jun 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;http://dr-axel.de/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Dr. Axel Rauschmayer&lt;/a&gt;의 &lt;a href=&quot;http://2ality.com/2018/03/javascript-typescript-reasonml.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;JavaScript vs. TypeScript vs. ReasonML&lt;/a&gt;을 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;이 포스트에서는 세 가지 프로그래밍 언어/방언인 자바스크립트(JavaScript), 타입스크립트(TypeScript)와 ReasonML의 장점과 단점을 평가한다. 이 글은 최근에 타입스크립트와 ReasonML로 진행된 작은 규모의 실제(real-world) 프로젝트 참여 경험과 다년간의 자바스크립트 사용 경험을 기반으로 서술했다.&lt;/p&gt;
&lt;h2 id=&quot;1-정적-타입의-장단점&quot;&gt;&lt;a href=&quot;#1-%EC%A0%95%EC%A0%81-%ED%83%80%EC%9E%85%EC%9D%98-%EC%9E%A5%EB%8B%A8%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 정적 타입의 장단점&lt;/h2&gt;
&lt;p&gt;장점:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;문서화: 거의 모든 코드에서 파라미터의 타입이 문서로 만들어 져 있다면 무척 큰 도움이 된다는 사실을 알았다. 그러면 나는 caller와 callee로서 어떤 값을 기대해야 하는지 알 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;하지만 이 장점은 거기서 더 나아간다. 오래된 자바스크립트 코드를 열어 타입을 추가하려고 하면, 그 코드가 어떻게 동작했는지 기억이 잘 나지 않곤 한다. 하지만 타입이 있으면 어떻게 동작하는지 훨씬 쉽게 파악할 수 있다. 예를 들어 “이 함수는 어떻게 사용되는가”에 대한 정보를 얻기 위해서는 보통 호출 지점으로 가 본다. 만약 함수 파라미터에 타입 주석(annotation)이 추가되어 있다면 그 정보를 더 쉽게 얻을 수 있을 것이다.&lt;/li&gt;
&lt;li&gt;나는 “더 나은 문서화” 역할을 하는 에디터의 자동완성 기능도 고려할 것이다. 자동완성은 API를 사용할 때 문서를 찾아볼 일이 줄어들게 한다(좋은 주석 또한 도움이 된다). 예를 들어, 내가 DOM 프로그래밍을 많이 하기 시작했던 2006년에는 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EA%B5%AC%EA%B8%80_%EC%9B%B9_%ED%88%B4%ED%82%B7&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;GWT&lt;/a&gt;(자바 기반이며, 당시에는 여러 가지 이유로 매력적인 솔루션이었다)를 사용했다. Eclipse의 자동완성 덕분에 DOM API를 찾아보면서 공부하는 일이 즐거웠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수 파라미터의 타입을 확인할 수 있는 빠른 방법이다. 나는 여러 함수에 프로그래밍적인 방법으로 타입을 붙이려고 할 때, 그리고 그 코드들이 불필요한 상용구처럼 느껴졌을 때, 자바스크립트에 정적 타입을 사용할 때가 되었음을 안다.&lt;/li&gt;
&lt;li&gt;리팩토링에 도움이 된다(열거형에 케이스를 붙이는 것 등).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;단점:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;배우는 데 시간이 걸린다.&lt;/li&gt;
&lt;li&gt;추가적인 단계의 복잡도를 추가한다. 정적 타입의 사용은 기본적으로 다른 계층에서 코드를 작성하는 일이다.&lt;/li&gt;
&lt;li&gt;표현의 자유를 제한한다. 제네릭(generic), 공변성과 반공변성(covariance and contravariance, 예: 문자열 타입의 배열은 객체 타입 배열의 부분집합이 아니다), 기타 등등에 들어가면 복잡해진다.&lt;/li&gt;
&lt;li&gt;에러를 막지 못한다. 적어도 이는 &lt;a href=&quot;https://danluu.com/empirical-pl/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;최근의 연구&lt;/a&gt;가 말해준다. 내 경험으로는, 몇 가지 종류의 에러는 잡을 수 있다(예를 들면 null 체크 누락). 하지만 그 에러는 타입 선언 대신 단위 테스트를 작성했다면 더 빨리 발견했을 것이다. 더 심각한 문제를 막기 위해서는 테스트가 필요하다.&lt;/li&gt;
&lt;li&gt;얼마간의 상호 작용을 잃게 되며 컴파일에 시간이 걸린다. 하지만 자바스크립트 생태계(ecosystem)에서 컴파일은 피할 수 없기도 하다. (&lt;em&gt;역주: 2018년 현재 ES6+를 사용하기 위해서는 Babel 같은 도구로 자바스크립트 소스를 ES5 형태로 변환하는 과정이 필수적이다&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2-reasonml&quot;&gt;&lt;a href=&quot;#2-reasonml&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. ReasonML&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;만약 정적 타입이 제공할 수 있는 최선을 경험하고 싶다면, ReasonML(또는 ReasonML의 기반이 되는 OCaml)을 사용해라. 예를 들면, 언어의 거의 모든 기능이 설계되어 있어서 최소한의 타입 주석만 작성하면 된다(&lt;em&gt;타입 추론&lt;/em&gt;에 의해 가능하다).&lt;/li&gt;
&lt;li&gt;이미 모든 것이 준비되어 있다는 점이 놀랍다(특히 에디터 지원). 하지만 몇가지 중요한 기능-더 나은 Promise, 반복과 비동기 반복 지원; 표준 라이브러리 Belt; 향상된 자바스크립트 호환(이미 꽤 괜찮은 수준이지만 내가 기대했던 것보다는 더 복잡하다); 더 나은 유니코드 문자열 지원-은 아직 작업 중이다.&lt;/li&gt;
&lt;li&gt;ReasonML은 빌드 시간이 무척 짧다. 타입스크립트보다 상당히 빠르며 이는 사용성 면에서 확실한 장점이다.&lt;/li&gt;
&lt;li&gt;자바스크립트 라이브러리 바인딩(binding)이 제공되고 있으나 여전히 제한적이다. &lt;a href=&quot;https://redex.github.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Reason Package Index&lt;/a&gt;를 살펴보길 바란다.&lt;/li&gt;
&lt;li&gt;네이티브로 갈 기회가 있다. 예를 들자면, Jared Forsyth는 &lt;a href=&quot;https://jaredforsyth.com/posts/making-a-cross-platform-mobile-game-in-reason-ocaml/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;the game Gravitron&lt;/a&gt;을 ReasonML로 구현했으며 Android, iOS, 웹, 그리고 macOS에서 네이티브로 실행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ReasonML에 대한 더 많은 정보는 내가 쓴 책 &lt;a href=&quot;http://reasonmlhub.com/exploring-reasonml/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Exploring ReasonML and functional programming&lt;/a&gt;(온라인에서 무료로 읽을 수 있다)을 참고하길 바란다.&lt;/p&gt;
&lt;h2 id=&quot;3-타입스크립트&quot;&gt;&lt;a href=&quot;#3-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 타입스크립트&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;타입스크립트의 타입 시스템은 내가 기대했던 것보다는 더 가볍다. 자바(Java)보다는 함수형 언어(FP)를 사용한다는 느낌이 더 난다. 예를 들면 타입은 이름 기반(nominal)이 아니라 구조 기반으로 작동한다. 예를 들어 인터페이스(interface)를 선언해서 사용할 때, 이름만 다르고 구조가 같은 인터페이스를 자바에서는 다른 타입으로 보지만 타입스크립트에서는 동일한 타입으로 본다.&lt;/li&gt;
&lt;li&gt;타입 시스템이 무척 강력하고 직관적이다. union 타입, intersection 타입, discriminated union type을 사용해서 자바스크립트의 많은 관용적인 표현에 타입을 정의할 수 있다.&lt;/li&gt;
&lt;li&gt;에디터 지원(Visual Studio Code, WebStorm, etc)이 무척 뛰어나다.&lt;/li&gt;
&lt;li&gt;많은 npm 패키지가 자체적으로 타입 선언을 포함하고 있거나, 타입 선언 파일을 매우 쉬운 방법으로 설치할 수 있다. 자세한 정보는 &lt;a href=&quot;http://definitelytyped.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;DefinitelyTyped&lt;/a&gt;를 참고하길 바란다.&lt;/li&gt;
&lt;li&gt;당연히, 자바스크립트와의 호환성이 훌륭하다. 하지만 하나의 예외가 있다. &lt;a href=&quot;http://2ality.com/2015/01/es6-destructuring.html#simulating-named-parameters&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;객체 리터럴을 사용해서 named parameter를 흉내냈을 때&lt;/a&gt; 파라미터에 타입을 붙이는 일이 필요없이 복잡하다. (&lt;a href=&quot;https://github.com/Microsoft/TypeScript/issues/7576#issuecomment-370196012&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;추가 정보&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;결론: 타입스크립트는 자바스크립트와 ReasonML의 중간 지점을 잘 차지하고 있다. 나는 대규모 프로젝트에서 얼마나 효과가 있었는지 이야기를 들어보고 싶다.&lt;/p&gt;
&lt;h2 id=&quot;4-자바스크립트&quot;&gt;&lt;a href=&quot;#4-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 자바스크립트&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;개발 생태계 전부가 끊임없는 혁신과 실험의 원천이다.&lt;/li&gt;
&lt;li&gt;npm을 통해 폭넓은 라이브러리 선택권을 가진다&lt;/li&gt;
&lt;li&gt;시스템을 가능한 유동적인 상태로 두면서 원하는 방향으로 빠르게 전환할 수 있다.&lt;/li&gt;
&lt;li&gt;컴파일 단계 없이 소스를 작성하고, 실험할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;자바스크립트에 대한 더 많은 특징에 대해서는 온라인에서 무료로 읽을 수 있는 나의 책 &lt;a href=&quot;http://exploringjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Exploring JS&lt;/a&gt; 시리즈를 참고하길 바란다.&lt;/p&gt;
&lt;h2 id=&quot;5-결론-자바스크립트-생태계는-그-어느-때보다-강력하다&quot;&gt;&lt;a href=&quot;#5-%EA%B2%B0%EB%A1%A0-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%83%9D%ED%83%9C%EA%B3%84%EB%8A%94-%EA%B7%B8-%EC%96%B4%EB%8A%90-%EB%95%8C%EB%B3%B4%EB%8B%A4-%EA%B0%95%EB%A0%A5%ED%95%98%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 결론: 자바스크립트 생태계는 그 어느 때보다 강력하다&lt;/h2&gt;
&lt;p&gt;정적 타입을 사용할지 말지는 감정적인 화제다. 나의 조언은:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;당신을 더 행복하고 생산적으로 만들어주는 것을 선택해라.&lt;/li&gt;
&lt;li&gt;당신이 사용하고 있는 것의 강점과 약점을 인지하고 있어라.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;개인적으로는 프로젝트가 일정 규모 이상으로 커지면(또는 결국에는 그렇게 될 것이라 예상되는 프로젝트에) 정적 타입을 사용한다.&lt;/p&gt;
&lt;p&gt;현재 자바스크립트 생태계의 힘과 다양성은 놀라울 정도다. 당신은 필요에 따라(또, 업무가 변화하는 정도에 따라) 자바스크립트, 타입스크립트, ReasonML을 서로 바꿀 수 있다. 그들은 몇몇 도구, 많은 수의 라이브러리, 많은 문법을 공유한다. 예를 들어 ReasonML을 사용하면서 템플릿 솔루션이 빨리 필요할 때 나는 npm을 통해 자바스크립트 기반의 &lt;a href=&quot;http://ejs.co/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;EJS 라이브러리&lt;/a&gt;를 사용했다.&lt;/p&gt;
&lt;p&gt;마지막으로, 정적 타입에는 많은 다른 좋은 선택지(&lt;a href=&quot;https://flow.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;정적 타입 검사기 Flow&lt;/a&gt;, &lt;a href=&quot;http://elm-lang.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;함수형 프로그래밍 언어 elm&lt;/a&gt; 등)가 존재한다는 사실을 알고 있길 바란다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Git 커밋 상태를 확인하기 위한 쉘 스크립트]]></title><description><![CDATA[
 photo by Ashim D’Silva ( https://unsplash.com/@randomlies ) Git으로 버전 관리를 하고 있지만  CI  도구를 사용하고 있지 않다면, 최소한 앱을 배포하기 전에 커밋을 하고 원격 저장소에 푸시를 해야 한다. 그래야 …]]></description><link>https://blog.rhostem.com//posts/2018-06-12-shellscript-to-check-commit</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-06-12-shellscript-to-check-commit</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Tue, 12 Jun 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/52Cwi0ZTTq2kgUEG2k8K6w/c73c0062ad3c2e4763d33bb40caacc0b/ashim-d-silva-95242-unsplash.jpg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 66.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wAARCAAbACgDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAABAUABgcD/8QALxAAAgEDAgQDBgcAAAAAAAAAAQIDBAURACEGEjFRE0FhBxQikcHwFTJScYGh4f/EABYBAQEBAAAAAAAAAAAAAAAAAAMCBP/EAB0RAAIDAAMBAQAAAAAAAAAAAAECAAMREiExBCL/2gAMAwEAAhEDEQA/AKnbr1YI7ZTVVRULEJE5mTkJxgb4233221ZKO4cH3O0Vb01xpS4iZeWQ+GxYg4ADYJydYm1GstHAkUsbYG4WOTmySMk7Y2x5eWibRZJBX0k0zosaVCB1JIPJnJYHt/etj/Tc3hmYLUozBHnB1inl4xttPMEK1Eoxhs4HK3+a2C9WaxcH2lrreGCwqwVVG7Ox6Ko79flqhVc0MUM09DNJBUxg+6zQuyOBn8xz5jPn137Z1Rrr+N18JirLrVVi8/OVmlLKT+rdjvqaL7qV4p5CtopublYO5ql89rnD1joqaOxUQramaLxJPEwFgJGynHVu4Bx66msXa1TuvIsUYxuSrZOO/U99TUNZYx0tFWtFGAQkVr9AcftomJmaF5JGIGCAB5nH0++ulUJ204qAFkqEGyp8CjsMA4+ZOraw+QVqG7CKe/pWz0kCwxp4S4LKN2O3prlXc6OzRnON2H1+/rpBaSRcE08qHYBHz8QcDPodiNQrEdiI6A/kwb3wgYJxqaGqwEqJUXZVYgD+dTT897gCoT//2Q==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;ashim-d-silva-95242-unsplash&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/52Cwi0ZTTq2kgUEG2k8K6w/c73c0062ad3c2e4763d33bb40caacc0b/ashim-d-silva-95242-unsplash.jpg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/52Cwi0ZTTq2kgUEG2k8K6w/c73c0062ad3c2e4763d33bb40caacc0b/ashim-d-silva-95242-unsplash.jpg?w=450 450w,
https://images.ctfassets.net/rpmifyuylbfw/52Cwi0ZTTq2kgUEG2k8K6w/c73c0062ad3c2e4763d33bb40caacc0b/ashim-d-silva-95242-unsplash.jpg?w=900 900w,
https://images.ctfassets.net/rpmifyuylbfw/52Cwi0ZTTq2kgUEG2k8K6w/c73c0062ad3c2e4763d33bb40caacc0b/ashim-d-silva-95242-unsplash.jpg?w=1800 1800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;photo by Ashim D’Silva (&lt;a href=&quot;https://unsplash.com/@randomlies&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://unsplash.com/@randomlies&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Git으로 버전 관리를 하고 있지만 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%A7%80%EC%86%8D%EC%A0%81_%ED%86%B5%ED%95%A9&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CI&lt;/a&gt; 도구를 사용하고 있지 않다면, 최소한 앱을 배포하기 전에 커밋을 하고 원격 저장소에 푸시를 해야 한다. 그래야 개발 서버든 운영 서버든 간에 현재 배포된 앱이 어떤 버전인지 일 수 있고 버그 수정도 원만하게 할 수 있기 때문이다. 이런 과정을 추가하면 아무리 버그를 급하게 수정해야 하는 상황이라도 자신의 코드를 한번은 돌아보게 만드는 만드는 효과를 줄 수 있다고 본다. 버전 관리를 하면서도 아무런 의미없는 커밋을 남발해도 된다는 마음을 가진 사람은 드물 테니까 말이다.&lt;/p&gt;
&lt;h2 id=&quot;쉘-스크립트&quot;&gt;&lt;a href=&quot;#%EC%89%98-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쉘 스크립트&lt;/h2&gt;
&lt;p&gt;쉘(Shell)은 유닉스 및 유닉스 계열 운영체제의 커맨드라인 인터페이스를 의미하며 리눅스, 맥 OS의 기본 쉘로는 bash가 제공된다. 개인적으로는 더 다양한 기능을 제공하는 &lt;a href=&quot;http://www.zsh.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;zsh&lt;/a&gt;과 &lt;a href=&quot;http://ohmyz.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Oh My Zsh&lt;/a&gt;의 조합을 사용하고 있다.&lt;/p&gt;
&lt;p&gt;그리고 쉘에서는 웹 브라우저의 콘솔에서 자바스크립트 코드를 사용할 수 있는 것처럼 쉘 스크립트 코드를 실행할 수 있다. 쉘 스크립트를 사용하면 파일 입출력, 연속적인 명령어 실행, 사용자 인터페이스 구현 등이 가능해진다. Git 저장소의 커밋 및 푸시 여부도 쉘 스크립트 안에서 Git 명령어의 실행을 조합하는 방식으로 구현할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;쉘-스크립트를-사용해서-현재-브랜치-정보-확인하기&quot;&gt;&lt;a href=&quot;#%EC%89%98-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-%ED%98%84%EC%9E%AC-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%A0%95%EB%B3%B4-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;쉘 스크립트를 사용해서 현재 브랜치 정보 확인하기&lt;/h2&gt;
&lt;p&gt;쉘 스크립트에도 프로그래밍 언어인 만큼 변수를 사용할 수 있다. Git 명령어를 이용해 현재 브랜치의 이름, 현재 작업중인 로컬 브랜치(HEAD)의 커밋 해시 스트링, 그리고 원격 저장소의 커밋 해시 스트링을 저장한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 현재 브랜치 이름을 변수에 할당&lt;/span&gt;
BRANCH&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-parse --abbrev-ref HEAD&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 현재 브랜치의 최종 커밋의 해시값&lt;/span&gt;
HASH&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-parse HEAD&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 원격에 업로드된 현재 브랜치의 커밋 해시&lt;/span&gt;
REMOTE_HASH&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-parse --verify origin/$BRANCH&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;BRANCH&lt;/code&gt;에 현재 브랜치의 이름을 할당한 후 원격 저장소의 커밋 해시를 가져온 후 사용했다. 변수를 사용할 때는 선언할 때와 달리 달러($) 사인을 붙여서 사용해야 한다.&lt;/p&gt;
&lt;p&gt;필요한 값을 가져왔으니 이제 조건문을 사용해 배포가 가능한 상황인지 확인하면 된다. 조건문은 &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt; ~ &lt;code class=&quot;language-text&quot;&gt;fi&lt;/code&gt;로 블록을 형성할 수 있고 &lt;code class=&quot;language-text&quot;&gt;then&lt;/code&gt; 키워드 후에 조건문을 만족했을 때 실행할 코드를 입력한다. 아래 코드에서는 현재 브랜치가 master 브랜치인지 확인한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$BRANCH&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;master 브랜치에서만 배포 가능합니다.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 메시지 출력&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 스트립트 종료&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;다음으로는 커밋되지 않은 변경 사항이 있는지 확인해야 한다. 역시 Git 명령어를 사용해야 한다. &lt;code class=&quot;language-text&quot;&gt;git status --porcelain&lt;/code&gt; 명령어는 저장소의 변경 사항을 아래와 같은 방식으로 간단히 출력한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ?? 기호는 Git index에 추가되지 않은 파일이라는 의미다&lt;/span&gt;
?? src/pages/posts/2018-06-12-shellscript-to-check-commit/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 변경 사항이 없다면 아무것도 출력되지 않는다. 그래서 문자열이 null이 아니라면 true를 반환하는 &lt;code class=&quot;language-text&quot;&gt;-n&lt;/code&gt; 키워드를 사용해서 커밋되지 않은 파일이 있는지 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; -n &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; status --porcelain&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;배포 전 모든 변경 사항을 커밋해야 합니다.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 마지막으로 현재 브랜치의 커밋과 원격 브랜치의 커밋이 일치하는지 확인한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$HASH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$REMOTE_HASH&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;배포 전 모든 커밋을 원격에 푸시해야 합니다.&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 확인 과정이 끝났으므로 스크립트의 나머지 부분에서는 실제 배포를 수행하는 &lt;code class=&quot;language-text&quot;&gt;npm run deploy&lt;/code&gt; 같은 명령어를 실행할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://codewiki.wikidot.com/shell-script&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Shell Scripting (Linux/Unix) - Code Wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.gaerae.com/2015/01/bash-hello-world.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Bash 입문자를 위한 핵심 요약 정리 (Shell Script) - 개발자스럽다&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[PM2로 Node.js 앱 프로세스 배포하기]]></title><description><![CDATA[PM2 는 Node.js 프로세스 관리 도구로서 IBM, Microsoft, Paypal 등의 유명 기업에서도 사용하고 있다. 다양한 기능을 지원하지만 가장 큰 특징으로는 역시  Cluster 모드 일 것이다. 자바스크립트는 싱글 스레드로 앱을 실행하지만 PM2는  …]]></description><link>https://blog.rhostem.com//posts/2018-05-27-pm2-deploy</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-05-27-pm2-deploy</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 27 May 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;http://pm2.keymetrics.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;PM2&lt;/a&gt;는 Node.js 프로세스 관리 도구로서 IBM, Microsoft, Paypal 등의 유명 기업에서도 사용하고 있다. 다양한 기능을 지원하지만 가장 큰 특징으로는 역시 &lt;a href=&quot;http://pm2.keymetrics.io/docs/usage/cluster-mode/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Cluster 모드&lt;/a&gt;일 것이다. 자바스크립트는 싱글 스레드로 앱을 실행하지만 PM2는 &lt;a href=&quot;https://nodejs.org/api/cluster.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Node.js의 Cluster 모듈&lt;/a&gt;을 사용해서 서버 앱에 복수의 인스턴스가 같은 포트를 사용하게 할 수 있도록 해준다. 이를 통해 사용량에 따라 CPU 점유율 늘리고 줄이면서 앱의 안정성을 높일 수 있다.&lt;/p&gt;
&lt;p&gt;이 포스트에서는 Node.js 앱을 PM2를 이용해서 Nginx 서버에 배포하는 과정을 간략히 정리해보고자 한다.&lt;/p&gt;
&lt;!-- 배포에 사용한 앱은 [next.js](https://github.com/zeit/next.js/) 프레임워크에서 만든 React 서버 렌더링 앱이다. --&gt;
&lt;h2 id=&quot;pm2-ecosystem-파일-생성&quot;&gt;&lt;a href=&quot;#pm2-ecosystem-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PM2 ecosystem 파일 생성&lt;/h2&gt;
&lt;p&gt;PM2의 커맨드라인 명령어로 앱 프로세스를 생성하고 관리할 수 있지만 배포를 위해서는 설정 파일을 만들어야 한다. 설정 파일에는 프로세스의 정보와 배포에 필요한 정보가 포함된다. 설정 파일은 JSON 형식으로 만들 수도 있고 Node.js 모듈 형식으로 만들 수도 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;/**
   * 앱 설정
   */&lt;/span&gt;
  apps&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app_name&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      script&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./server.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 앱 실행 스크립트&lt;/span&gt;
      instances&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 앱 인스턴스의 수&lt;/span&gt;
      exec_mode&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cluster&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 실행 모드.&lt;/span&gt;
      env&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 환경변수. 모든 배포 환경에서 공통으로 사용한다.&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;NODE_ENV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;production&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      env_staging&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// staging 배포 환경에서만 사용할 환경 변수&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;API_ROOT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;http://api.server.name&apos;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * 배포 설정
   */&lt;/span&gt;
  deploy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    staging&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      user&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 접속할 계정. SSH를 사용해서 서버에 접속할 수 있어야 한다.&lt;/span&gt;
      host&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;appstaging.server.name&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 서버 도메인 또는 IP&lt;/span&gt;
      ref&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;origin/develop&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 서버에서 clone할 브랜치&lt;/span&gt;
      repo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;git@github.com:user/reponame.git&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Git 저장소 URL&lt;/span&gt;
      ssh_options&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;StrictHostKeyChecking=no&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// SSH 접속 옵션.&lt;/span&gt;
      path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/home/www/project_root&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 앱을 설치할 폴더 위치&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&apos;post-deploy&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// PM2가 배포(git clone)한 후 실행할 명령어&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&apos;npm install &amp;amp;&amp;amp; npm run build &amp;amp;&amp;amp; pm2 reload ecosystem.config.js&apos;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;code-classlanguage-textappscode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textappscode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;apps&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;앱 설정 영역이다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textnamecode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textnamecode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;앱의 이름을 할당한다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textscriptcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textscriptcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;앱을 실행할 수 있는 소스 파일의 경로를 할당한다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textinstancescode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textinstancescode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;instances&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt;로 실행하는 앱이 몇개의 인스턴스를 생성할 것인지를 결정한다. 특히 이 옵션은  &lt;code class=&quot;language-text&quot;&gt;exec_mode&lt;/code&gt; 옵션이 &lt;code class=&quot;language-text&quot;&gt;cluster&lt;/code&gt;일때만 의미가 있다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textexec_modecode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textexec_modecode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;exec_mode&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;실행 모드로 &lt;code class=&quot;language-text&quot;&gt;fork&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;cluster&lt;/code&gt;를 선택할 수 있다. 이는 PM2가 Node.js 의 &lt;code class=&quot;language-text&quot;&gt;cluster&lt;/code&gt; API를 사용할지, &lt;code class=&quot;language-text&quot;&gt;child_process.fork&lt;/code&gt;를 사용할지를 결정한다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textenvcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textenvcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;env&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;환경변수. &lt;code class=&quot;language-text&quot;&gt;deploy&lt;/code&gt; 섹션에서는 여러 개의 배포 환경을 설정할 수 있는데,  &lt;code class=&quot;language-text&quot;&gt;env&lt;/code&gt;에 할당된 값은 모든 환경에서 공통으로 적용된다. 특정 환경에서만 사용하려면 &lt;code class=&quot;language-text&quot;&gt;env_이름&lt;/code&gt;에 값을 설정해야 하며, 앱을 시작할 때 &lt;code class=&quot;language-text&quot;&gt;--env 이름&lt;/code&gt; 옵션을 추가해야 한다.&lt;/p&gt;
&lt;p&gt;하지만 next.js를 사용햐서 만든 &lt;strong&gt;React 서버 렌더링 앱에서는 환경변수를 제대로 사용할 수 없었다&lt;/strong&gt;. 서버 렌더링시에는 환경 변수가 제대로 적용되었지만 브라우저에서는 &lt;code class=&quot;language-text&quot;&gt;process.env&lt;/code&gt;를 참조하지 못하기 때문이었다. 그래서 빌드할 때 환경 변수를 포함할 수 있도록 babel의 inline-dotenv 플러그인을 사용했다.&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-textdeploycode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textdeploycode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;deploy&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;배포 환경과 관련된 설정 영역이다. 배포 환경은 원하는 만큼 만들 수 있다.&lt;/p&gt;
&lt;p&gt;PM2는 서버에 접속해서 지정된 위치에 Git 저장소를 복제한 후 사용자가 지정한 명령어를 사용해 앱을 실행하는 방식을 사용한다. 그래서 서버 접속과 Git 저장소와 관련된 정보가 필요하다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textusercode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textusercode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;user&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;서버 접속에 사용할 계정이다. 접속에는 SSH를 사용한다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-texthostcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-texthostcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;host&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;서버 도메인, 또는 IP에 해당하는 값이다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textrefcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textrefcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;ref&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;서버에서 clone할 Git 저장소의 브랜치 이름이다. &lt;code class=&quot;language-text&quot;&gt;origin/master&lt;/code&gt; 처럼 remote 이름과 브랜치 이름을 함께 입력한다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textrepocode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textrepocode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;repo&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;SSH를 사용해서 Git clone을 가능하게 하려면 PM2에서 제시하는 &lt;a href=&quot;http://pm2.keymetrics.io/docs/usage/deployment/#troubleshooting&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;방법&lt;/a&gt;을 사용하거나 서버의 SSH 공개 키가 Git 서비스에 등록되어 있어야 한다. 만약 Github를 사용한다면 &lt;a href=&quot;https://github.com/settings/keys&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/settings/keys&lt;/a&gt;에서 추가 가능하다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textssh_optionscode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textssh_optionscode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;ssh_options&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;SSH 접속에 사용할 옵션이다.&lt;/p&gt;
&lt;h4 id=&quot;code-classlanguage-textpathcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textpathcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;path&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Git 저장소를 clone할 서버상의 경로에 해당한다. 웹서버에서 설정한 경로로 지정해준다.&lt;/p&gt;
&lt;h2 id=&quot;nginx-virtual-host-설정&quot;&gt;&lt;a href=&quot;#nginx-virtual-host-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nginx Virtual Host 설정&lt;/h2&gt;
&lt;p&gt;배포를 하기 전에는 웹서버 설정이 필요하다. 서버로 들어오는 외부 요청을 특정 프로세스에 연결하기 위해서는 리버스 프록시 설정을 해야 한다. 처음에는 Apache를 사용했지만 PM2의 &lt;code class=&quot;language-text&quot;&gt;reload&lt;/code&gt; 명령어로 프로세스를 재시작할 때 자꾸 서버 오류가 발생해서 Nginx로 바꾸었더니 문제가 발생하지 않았던 경험이 있다.&lt;/p&gt;
&lt;p&gt;Nginx HTTP 프록시 설정은 PM2 홈페이지에서도 &lt;a href=&quot;http://pm2.keymetrics.io/docs/tutorials/pm2-nginx-production-setup&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;문서&lt;/a&gt;로 제공한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;upstream my_nodejs_upstream &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# 서버 locahost IP와 앱의 포트 번호를 입력한다.&lt;/span&gt;
    server 127.0.0.1:3001&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    keepalive 64&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

server &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# 포트번호 없이 접속 가능하도록 80번 사용&lt;/span&gt;
    listen 80&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# 외부에서 접속 가능한 도메인 네임을 입력한다.&lt;/span&gt;
    server_name myapp.yourhost.com&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# 앞서 언급한 PM2의 ecosystem 파일의 deploy.env_name.path에 해당하는 값이다&lt;/span&gt;
    root /home/www/project_root&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    location / &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      proxy_set_header X-Forwarded-For &lt;span class=&quot;token variable&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      proxy_set_header Host &lt;span class=&quot;token variable&quot;&gt;$http_host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      proxy_set_header X-NginX-Proxy &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      proxy_http_version 1.1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      proxy_set_header Upgrade &lt;span class=&quot;token variable&quot;&gt;$http_upgrade&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      proxy_set_header Connection &lt;span class=&quot;token string&quot;&gt;&quot;upgrade&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      proxy_max_temp_file_size 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# 앱의 localhost URL을 입력한다.&lt;/span&gt;
      proxy_pass http://localhost:3001/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      proxy_redirect off&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      proxy_read_timeout 240s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 설정은 Nginx의 가상 호스트 설정 블록으로 &lt;code class=&quot;language-text&quot;&gt;/etc/nginx/sites-available&lt;/code&gt; 폴더에서 생성한 후 &lt;code class=&quot;language-text&quot;&gt;sites-enabled&lt;/code&gt; 폴더에는 원본 파일에 연결된 symbolic 링크를 생성해둔다. &lt;code class=&quot;language-text&quot;&gt;sites-enabled&lt;/code&gt; 폴더에 직접 만들어도 되지만 가상 호스트를 여러개 관리할 때 좋기에 권장되는 방법이다. 그리고 Nginx의 기본 설정은 &lt;code class=&quot;language-text&quot;&gt;sites-enabled&lt;/code&gt; 폴더에 있는 파일을 불러오도록 되어 있지만, 다른 사용자에 의해 변경되어 있을 수 있으니 &lt;code class=&quot;language-text&quot;&gt;nginx.conf&lt;/code&gt; 파일에 &lt;code class=&quot;language-text&quot;&gt;include /etc/nginx/sites-enabled/*;&lt;/code&gt; 설정이 있는지 확인해야 한다.&lt;/p&gt;
&lt;h2 id=&quot;pm2-프로세스-관리-명령어&quot;&gt;&lt;a href=&quot;#pm2-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B4%80%EB%A6%AC-%EB%AA%85%EB%A0%B9%EC%96%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;PM2 프로세스 관리 명령어&lt;/h2&gt;
&lt;p&gt;PM2는 소스 파일명과 인라인 옵션을 이용해서 프로세스를 실행할 수도 있고, 설정 파일을 이용해서 프로세스를 실행할 수도 있다. 앞서 설정한 ecosystem 파일을 사용한다면 아래의 두 명령어는 같은 내용이다. 프로세스 이름을 &lt;code class=&quot;language-text&quot;&gt;app_name&lt;/code&gt;으로 하고 인스턴스를 4개 생성한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;pm2 start ecosystem.config.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;pm2 start server.js --name &lt;span class=&quot;token string&quot;&gt;&quot;app_name&quot;&lt;/span&gt; -i 4&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앱 재시작에는 &lt;code class=&quot;language-text&quot;&gt;restart&lt;/code&gt;  대신 &lt;a href=&quot;http://pm2.keymetrics.io/docs/usage/cluster-mode/#reload&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;reload&lt;/code&gt;&lt;/a&gt;를 사용하는 편이 낫다. 전자는 프로세스를 즉시 종료시키고 재시작하기에 &lt;strong&gt;접속이 불가능한 시간이 발생할 수 있다&lt;/strong&gt;. 하지만 후자는 그런 간격이 생기지 않도록 해준다.  &lt;code class=&quot;language-text&quot;&gt;reload&lt;/code&gt; 명령어는 앱 프로세스와 관련된 프로세스를 정리한 후 준비된 상태에서 다시 시작하는 &lt;code class=&quot;language-text&quot;&gt;gracefulReload&lt;/code&gt;에 해당된다. 그리고 &lt;code class=&quot;language-text&quot;&gt;reload&lt;/code&gt; 명령어를 사용할 때 현재 실행중인 프로세스가 없다면 자동으로 &lt;code class=&quot;language-text&quot;&gt;start&lt;/code&gt; 명령어로 대체한다.&lt;/p&gt;
&lt;h2 id=&quot;배포&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%ED%8F%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배포&lt;/h2&gt;
&lt;p&gt;앱을 배포할 준비는 모두 되었다. 배포를 하기 위해서는 변경 사항을 모두 commit 한 후 원격 저장소에 push한 후에 가능하다. PM2가 기본적으로 해주는 일은 원격 서버에 저장소를 복제해주는 일까지만이며 그 후의 작업은 사용자가 직접 설정해줘야 한다.&lt;/p&gt;
&lt;p&gt;배포 환경 설정 파일의 &lt;code class=&quot;language-text&quot;&gt;deploy&lt;/code&gt; 영역에는 환경마다 &lt;code class=&quot;language-text&quot;&gt;pre-setup&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;post-deploy&lt;/code&gt; 등 특정 시점에서 실행할 수 있는 명령어를 지정할 수 있다. 하지만 가장 중요한 건 PM2에 의한 배포가 끝난 후 실행되는 &lt;code class=&quot;language-text&quot;&gt;post-deploy&lt;/code&gt;다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  deploy&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    staging&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&apos;post-deploy&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;npm install &amp;amp;&amp;amp; npm run build &amp;amp;&amp;amp; pm2 reload ecosystem.config.js&apos;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 설정에서는 저장소를 내려받는 배포가 과정이 끝난 후 NPM 패키지를 인스톨하고 빌드를 한 후 프로세스를 재시작하도록 했다. 최초 배포를 하기 전에 배포할 환경의 setup을 실행하고 배포한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;pm2 deploy staging setup

pm2 deploy ecosystem.config.js staging&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;작업이 끝나면 현재 실행중인 프로세스의 목록을 보여준다. 여기에서는 프로세스의 이름, 실행 모드, CPU 및 메모리 사용률 등을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5cTBfvyG3KAa6q4SQiSCgC/58b9523a46dfff6bf1a96d22ab1249c4/process_list.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 17.539267015706805%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;process list&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5cTBfvyG3KAa6q4SQiSCgC/58b9523a46dfff6bf1a96d22ab1249c4/process_list.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5cTBfvyG3KAa6q4SQiSCgC/58b9523a46dfff6bf1a96d22ab1249c4/process_list.png?w=382 382w,
https://images.ctfassets.net/rpmifyuylbfw/5cTBfvyG3KAa6q4SQiSCgC/58b9523a46dfff6bf1a96d22ab1249c4/process_list.png?w=764 764w,
https://images.ctfassets.net/rpmifyuylbfw/5cTBfvyG3KAa6q4SQiSCgC/58b9523a46dfff6bf1a96d22ab1249c4/process_list.png?w=1528 1528w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;h2 id=&quot;항상-오류가-발생하는-첫-배포-시도&quot;&gt;&lt;a href=&quot;#%ED%95%AD%EC%83%81-%EC%98%A4%EB%A5%98%EA%B0%80-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EC%B2%AB-%EB%B0%B0%ED%8F%AC-%EC%8B%9C%EB%8F%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;항상 오류가 발생하는 첫 배포 시도&lt;/h2&gt;
&lt;p&gt;새롭게 배포 환경을 구성할 때는 항상 오류에 부딪히는 것 같다. 호스트 서버 접속 오류, Git clone 오류, 웹서버 설정 오류 등 아무리 문서를 보고 따라 해도 작업 중인 환경에서만 생기는 문제가 발생하곤 한다. 하지만 처음 한 번만 고생하면 그다음이 편해지니까 시도할 가치는 충분하다.&lt;/p&gt;
&lt;p&gt;특히 PM2처럼 배포된 앱이 어떤 commit에 해당하는지를 확실히 명시하는 것은 무척 중요하다고 생각한다. 소수가 작업할 때나 많은 인원이 작업 할 때나 운영 중인 시스템에 문제가 발생했을 때 무엇이 문제인지 추적하는 데 큰 도움이 되기 때문이다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Gulp로 구성한 퍼블리싱 개발 환경]]></title><description><![CDATA[본 포스트에서 사용된 전체 소스 파일은 아래 링크에서 확인할 수 있다. startkits/gulp-static-site 최근 만들어지는 웹사이트는 싱글 페이지 형식으로 개발하는 형태가 일반적이다. 하지만 퍼블리싱이라고도 불리는 정적인 페이지 개발 작업에 대한 수요는 …]]></description><link>https://blog.rhostem.com//posts/2018-05-20-gulp-static-site</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-05-20-gulp-static-site</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 20 May 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;본 포스트에서 사용된 전체 소스 파일은 아래 링크에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rhostem/startkits/tree/master/gulp-static-site&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;startkits/gulp-static-site&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;최근 만들어지는 웹사이트는 싱글 페이지 형식으로 개발하는 형태가 일반적이다. 하지만 퍼블리싱이라고도 불리는 정적인 페이지 개발 작업에 대한 수요는 여전히 많다. 작업을 할때는 Gulp를 기반으로 개발 프로세스를 구성하는데, 새로운 일을 시작할 때 편하게 clone해서 사용할 수 있도록 별도로 저장소를 만들어 보았다.&lt;/p&gt;
&lt;h2 id=&quot;퍼블리싱을-위한-언어&quot;&gt;&lt;a href=&quot;#%ED%8D%BC%EB%B8%94%EB%A6%AC%EC%8B%B1%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%96%B8%EC%96%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;퍼블리싱을 위한 언어&lt;/h2&gt;
&lt;p&gt;개인적으로는 마크업에는 &lt;a href=&quot;https://pugjs.org/api/getting-started.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Pug&lt;/a&gt;를 사용하고 스타일시트 작성에는 &lt;a href=&quot;https://sass-lang.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Sass&lt;/a&gt;를 선호한다.&lt;/p&gt;
&lt;p&gt;Pug를 사용하면 html보다 태그를 훨씬 간편하게 작성할 수 있어서 좋다. 무엇보다 block으로 템플릿(&lt;a href=&quot;https://pugjs.org/language/inheritance.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Template Inheritance – Pug&lt;/a&gt;)을 작성해두면 중복된 부분을 컴포넌트화 시킬 수 있음과 동시에 여러 페이지를 빠르게 만들 수 있어서 무척 편리하다.&lt;/p&gt;
&lt;p&gt;Sass는 partial 파일을 사용한 컴포넌트화, nesting, mixin, 변수, CSS에서 지원하지 않는 연산자, 조건문, 반복문 등의 기능을 사용할 수 있다. Sass를 한 번이라도 사용해 본다면 CSS를 직접 작성하던 시절로는 돌아가고 싶지 않을 것이다.&lt;/p&gt;
&lt;h2 id=&quot;gulp&quot;&gt;&lt;a href=&quot;#gulp&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Gulp&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://gulpjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Gulp&lt;/a&gt;는 Node.js 기반으로 개발 프로세스를 자동화시킬 수 있는 툴이다. Node.js의 &lt;a href=&quot;https://nodejs.org/api/stream.html#stream_stream&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Stream&lt;/a&gt; 인터페이스를 이용해서 특정 소스 파일에 대한 작업을 단계적으로 진행할 수 있다. 간단히 풀어내자면&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sass 소스 파일을 불러와서&lt;/li&gt;
&lt;li&gt;CSS 파일로 변환한 후&lt;/li&gt;
&lt;li&gt;원하는 위치에 저장한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이런 식으로 순차적으로 작업을 진행할 수 있다. 그리고 이어서&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;소스 파일에 새로운 내용이 저장되면&lt;/li&gt;
&lt;li&gt;앞선 1~3의 과정을 반복한다.&lt;/li&gt;
&lt;li&gt;브라우저를 새로고침시켜서 변경된 사항을 확인할 수 있도록 만든다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이런 식의 작업이 가능하다. webpack 으로도 정적인 사이트 작업의 가능하지만(&lt;a href=&quot;https://github.com/rhostem/webpack-static-site&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;rhostem/webpack-static-site&lt;/a&gt;), 기본 목적이 분리된 모듈을 하나로 만드는데 있기 때문에 작업 결과물로 여러 개의 마크업 파일을 만들어내기에는 다소 불편하다. 그에 비교해 Gulp는 간단하게 소스 파일을 결과 파일로 변환할 수 있기 때문에 퍼블리싱 작업에 더 적합하다고 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;pug-소스파일-처리&quot;&gt;&lt;a href=&quot;#pug-%EC%86%8C%EC%8A%A4%ED%8C%8C%EC%9D%BC-%EC%B2%98%EB%A6%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Pug 소스파일 처리&lt;/h3&gt;
&lt;p&gt;Gulp는 task를 선언해서 사용하며, Pug 파일을 처리하기 위한 task는 아래와 같이 작성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;pug&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buildHTML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pugErrHandler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;errorHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;pug&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; gulp
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paths&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pug&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 소스 파일 불러오기&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; pretty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;error&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pugErrHandler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// HTML로 변환&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paths&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pug&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dist&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 원하는 위치에 저장&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browserSync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; stream&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 브라우저 새로고침&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;pipe 메소드 덕분에 작업 과정이 무척 직관적이다. 사용자가 신경 써야하는 부분은 pug 모듈을 실행할 때 전달해야 하는 옵션, 소스와 아웃풋 파일의 위치 정도다.&lt;/p&gt;
&lt;h3 id=&quot;sass-소스파일-처리&quot;&gt;&lt;a href=&quot;#sass-%EC%86%8C%EC%8A%A4%ED%8C%8C%EC%9D%BC-%EC%B2%98%EB%A6%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Sass 소스파일 처리&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sass&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sassErrHandler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;errorHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sass&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  gulp
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paths&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 소스 파일 불러오기&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sourcemaps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 소스맵 초기화&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;error&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sassErrHandler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// CSS로 변환&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;autoprefixer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;autoprefixer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 자동으로 vendor prefix 붙이기&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sourcemaps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./map&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 소스맵 파일 생성&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paths&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dist&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browserSync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; stream&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pug와 거의 유사하다. Sass에서는 소스맵(관련 문서: &lt;a href=&quot;http://thesassway.com/intermediate/using-source-maps-with-sass&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Using source maps with Sass 3.3&lt;/a&gt;)을 생성하는 과정이 필요하다. 브라우저가 실제로 불러오는 것은 CSS 파일이기에 스타일이 Sass 소스 파일의 어느 부분에서 작성된 것인지 연결할 필요가 있기 때문이다.&lt;/p&gt;
&lt;p&gt;아래와 같이 개발자 도구에서 특정 요소의 스타일을 검사하면 CSS 파일이 아닌 Sass 파일을 가리키는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1IuzC61oneIiwwmmUQAkuK/c74653ab0e842290a8b8cdd8bb3b965a/scss_sourcemap.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 52.16763005780347%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;scss sourcemap&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1IuzC61oneIiwwmmUQAkuK/c74653ab0e842290a8b8cdd8bb3b965a/scss_sourcemap.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1IuzC61oneIiwwmmUQAkuK/c74653ab0e842290a8b8cdd8bb3b965a/scss_sourcemap.png?w=346 346w,
https://images.ctfassets.net/rpmifyuylbfw/1IuzC61oneIiwwmmUQAkuK/c74653ab0e842290a8b8cdd8bb3b965a/scss_sourcemap.png?w=692 692w,
https://images.ctfassets.net/rpmifyuylbfw/1IuzC61oneIiwwmmUQAkuK/c74653ab0e842290a8b8cdd8bb3b965a/scss_sourcemap.png?w=1384 1384w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;h3 id=&quot;로컬-서버-생성&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EC%BB%AC-%EC%84%9C%EB%B2%84-%EC%83%9D%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로컬 서버 생성&lt;/h3&gt;
&lt;p&gt;정적인 페이지는 서버를 실행하지 않고 작성해도 상관없지만 그렇게 할 경우 리소스를 불러올 때 무조건 현재 소스 파일의 위치를 기준으로 참조해야 한다는 문제가 생긴다. 웹에서 흔하게 사용하는 루트(&lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt;) 기호를 사용할 수 없다는 의미이다. 서버에 올라가지 않은 HTML 파일에서 저 루트 기호는 운영체제 파일 시스템의 루트를 가리킬 테니 말이다. 이런 문제도 있고 뒤에서 작성할 브라우저 자동 새로 고침을 위해서도 로컬 서버를 사용하는 편이 좋다.&lt;/p&gt;
&lt;p&gt;로컬 서버로는 &lt;a href=&quot;https://browsersync.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Browsersync&lt;/a&gt;를 사용한다. Browsersync는 Gulp, Grunt 같은 빌드 도구와 호환성이 좋아서 많이 사용된다. 몇 개의 옵션만 할당해주면 간단히 서버를 생성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;serve&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  browserSync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; browserSync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    startPath&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    server&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      baseDir&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paths&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dist&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 루트 폴더 위치&lt;/span&gt;
      directory&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 디렉터리 구조를 기반으로 라우트를 구성한다&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    port&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 포트&lt;/span&gt;
    open&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 서버 시작시 웹페이지를 기본 브라우저에서 자동으로 열 것인지&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 Pug, Sass 태스크에서 등장했듯이 browserSync 모듈은 다른 태스크에서 소스 파일 변경 시 브라우저 새로 고침에 사용된다. 그러기 위해서는 같은 서버를 사용해야 하므로 browserSync 모듈의 &lt;code class=&quot;language-text&quot;&gt;instance&lt;/code&gt; 속성에 serve 태스크 내부에서 생성한 인스턴스를 할당해서 다른 태스크의 콜백 함수 내부에서도 사용할 수 있도록 한다.&lt;/p&gt;
&lt;p&gt;그리고 인스턴스를 생성할 때 directory 옵션을 사용하면 디렉터리 내부의 파일을 탐색할 수 있는 UI를 제공한다. 웹 브라우저에서 마치 파일 탐색기를 사용하는 것과 같으며 간단한 검색 기능도 제공하기에 작업이 진행됨에 따라 파일 개수가 늘어나면 무척 유용할 것이다.&lt;/p&gt;
&lt;h3 id=&quot;파일-변경-탐지&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%BC-%EB%B3%80%EA%B2%BD-%ED%83%90%EC%A7%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파일 변경 탐지&lt;/h3&gt;
&lt;p&gt;Gulp에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;watch&lt;/code&gt; 메소드를 사용하면 파일이 변경되었을 때 특정 태스크를 실행하도록 구성할 수 있다. 이를 통해 파일이 변경될 때마다 새로운 HTML, CSS 파일이 생성되도록 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;소스 파일 경로&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;태스크 목록&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 파일 변경을 감지해서 pug, sass,태스크를 실행하도록 한다.&lt;/span&gt;
gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;watch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paths&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/**/*.pug&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;pug&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paths&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/**/*.scss&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paths&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/**/*.sass&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sass&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;다양한-gulp-플러그인의-활용&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%96%91%ED%95%9C-gulp-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8%EC%9D%98-%ED%99%9C%EC%9A%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다양한 Gulp 플러그인의 활용&lt;/h2&gt;
&lt;p&gt;글에서는 Gulp로 가능한 최소한의 작업만 소개했을 뿐이다. Gulp는 수많은 개발자의 참여를 통해 만들어진 다양한 &lt;a href=&quot;https://gulpjs.com/plugins/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;플러그인&lt;/a&gt;을 통해 더 많은 일이 가능하다. 글을 작성하는 시점에 3500여 개가 넘는 플러그인이 등록되어 있으니 만약 사용하고 싶은 기능이 있다면 이미 구현되어 있을 가능성이 매우 높다. 그리고 Gulp를 잘 활용하려면 Javascript는 물론 Node.js의 파일 시스템과 스트림에 대한 이해가 조금 필요하긴 하지만 학습에 투자할 가치는 충분하다고 생각한다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[[번역] 2018년에 배워야 할 최고의 자바스크립트 라이브러리와 기술]]></title><description><![CDATA[이 글은  Eric Elliot 의  Top JavaScript Libraries & Tech to Learn in 2018 을 번역한 글입니다. 
 Alex Proimos — New York Public Library Grand Study Hall (CC BY 2.…]]></description><link>https://blog.rhostem.com//posts/2018-01-25-top-java-script-libraries-tech-to-learn-in-2018</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-01-25-top-java-script-libraries-tech-to-learn-in-2018</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Thu, 25 Jan 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://medium.com/@_ericelliott?source=post_header_lockup&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Eric Elliot&lt;/a&gt;의 &lt;a href=&quot;https://medium.com/javascript-scene/top-javascript-libraries-tech-to-learn-in-2018-c38028e028e6&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Top JavaScript Libraries &amp;#x26; Tech to Learn in 2018&lt;/a&gt;을 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3uDgc0AZeE6ySgow0eAkAI/270961ace48f47570f73b9422008eaf1/main.jpeg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 66.64999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAlgCWAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAAbACgDASIAAhEBAxEB/8QAGQAAAwEBAQAAAAAAAAAAAAAAAAUHBAYD/8QALhAAAQQBBAEDAQcFAAAAAAAAAQIDBBEFAAYSIQcTMVFBCBUiIzJhgRQWQqHB/8QAFwEBAQEBAAAAAAAAAAAAAAAABgQFAv/EACIRAAICAgIABwAAAAAAAAAAAAECAxEABBIhFCIxMlGBof/aAAwDAQACEQMRAD8AT758PY7L+WomMwSo6I+ZbbTGfjLS82rk+8hspKSBRAQPfod/tqWeTvD8naOQlIca5LjOGO/6YUlDn4AoEEj3AUnrv2FGqvi9ob/e2m7FXi8y9ClNqEmPIStBV6tHgEkKHFQsHuqNfGqzjftIZXMxZsXMP4XOgygmT95NONrcdCEJolqz2ltKehdg/W9BZEkdy9EWfqsTREIoUG6H7koyOV3YnYqoRzcdWDQ8WwzQ5c/1EDrlX1+O/wB9PfEnh97LyY82ahLUiSr044kA8G+SelqodX/3v61RXPG+0shiJW64j2JSIr4fdx0qc6mjxJtP5XIthQqqvv3+pRZTzVksZGYxzK4EB6Oj9cFlx5YUgKFjkASDzIFizYq61KgLEqooX3VZXsOxAv46u807r8bRcfvVnBz1NqTFcUzKcFoSn8xtKx38WR/BOjUi35vqbuCU6xl8hIdfeH9S7IU1ycWohPMqsknvs1dknRpHoyeHj4FL7wtuwGaTkGrrIu35Azk6axJdybzs5u0MPrWoeiCbJB+ntqxR97OZ7GT8ZurNpyy2+S4qI6SqO4ukJ5EUg1xR1yB7Ht3qVZfGRWNzzWG2EIZQDxQkUBSdafQRGmoS2nin0kGrvspBOudsL7AKIyzT5Hzk2D65S/79LUtLzbjxAXy7dUSe77N3/OkGX3hKdz6V49+DGYKCt1D7YI9YkqWqnAsUb9x3Y9vlA8spYKgaPzrxgwmZ2TZbfR6iFGiLIvo/GsuJEjJY9jNradpgADRGc7P3jlvv6RlFyEJnvKK1OJbRxJIo/hACfb9v96NaIkFhzIsBbYWPXbFK7/yGjSqGBJl5UMHyyvC1A5//2Q==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;main&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3uDgc0AZeE6ySgow0eAkAI/270961ace48f47570f73b9422008eaf1/main.jpeg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3uDgc0AZeE6ySgow0eAkAI/270961ace48f47570f73b9422008eaf1/main.jpeg?w=500 500w,
https://images.ctfassets.net/rpmifyuylbfw/3uDgc0AZeE6ySgow0eAkAI/270961ace48f47570f73b9422008eaf1/main.jpeg?w=1000 1000w,
https://images.ctfassets.net/rpmifyuylbfw/3uDgc0AZeE6ySgow0eAkAI/270961ace48f47570f73b9422008eaf1/main.jpeg?w=2000 2000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Alex Proimos — New York Public Library Grand Study Hall (CC BY 2.0)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;작년 나는 &lt;a href=&quot;https://medium.com/javascript-scene/top-javascript-frameworks-topics-to-learn-in-2017-700a397b711&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;2017년에 배워야할 최고의 기술들을 정리한 글&lt;/a&gt;을 작성했다. 올해에는 놀라운 현상이 있었다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“어떤 기술이 투자 대비 학습 효과가 가장 높은가?”라는 질문에 대답이 나오기 시작했다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/7qlGoBDfxK800qkiKIQuay/cd7cbf2e3150ec2a3d37f833693369c5/1-modulecounts.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 591px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 71.91201353637902%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAdCAIAAABXK7kkAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAABIFJREFUSMfFV9tTGmcU99/qQ587nelM89S+pCa2T00aYzKd6SRpU6bT1PZBE6vG1tQQL2g0kDFBBbkISARF2DXrcndhuSy7IHdYFnYRe3QZJBRnWiHTMwNzzvd9fL9z/w490XBoe8dWrvBHR0dRiqJjMV4QKmVOpdYIglCr1WA9EU8Af9xV6llfW7n06aXbAwM3bt7s7f3y2vUbvZe/MBmNE5NP+7/5WiKR9PcPXL1y9au+Poo57CZwtVotl8ulElepgJ0cVz75go0SLHEniyKxRbbLFsMnnU4TBBEMBhOJROz9UzwerwODxdlTikajRXozm/CBEpFIpKEdRLrBg4eaFW8Wz+NbREijOnCDQB2awuMMmUqlQA/wNs9DqgmgGaSY+ONOgWs1fH6+DbAvXgwxKY/H4/P5wPN+vz8UCoEDQAQ9OgdmjCYCx+vAAIAgyN7e3sHBQWBujrLZcBwHvP39fa/HyzCM6GrRaEi0ahM1i+fxDbFE0xGDgaKoNhbLHLJdYhfDsEAg4PV63W43y7LnmfhfLSblCrAh1jbGqDLjRxPgW8hzwIYUA29nMpnOgdMOpHBqa3vgA7uDOsVLJpOAB2klnFLnwJRK3UCpA0MOowhaj7Frgwq5IAwA2VJOncSYsViyoZDIt4+xZT/oJsIulwtFUafTCQ6HSHdYx5VUilSrm1HaAHfSGdrztVrghbw5WGfA0JOTp9TcrboFTK2vs4eHzVtnwPl8HqoWUCGZL1ag5/FJBGFQtGWrfYy7aDEL1mg0/9x6vzHmi0VCrmi7dQYM0YVWBTkM5dQVVwsc556Z4Vi2ZatYru6H0+Y9Xx345KggwEPUFYtrPO+dnWVzuca6cHTsjGS23VE8SBdZlvTEu+9qIZdzT89whQLwUPt0XtjxULteKp3J8lwNW89opAH9a6zLwGkcdy0uVXghmuVNWFDrIF4ZdvXb2NKiXSpbf7mMLK9oZLPDz2UjdeBCodB4Fi8W42I6a5a9lI5LV8yowogqTXYr4iTeZpbHsNcjxjXpH7aNKbvhrx3L4jPtE4VZcUGLwYf5Eh9gslsYoVw1Tj+Tr6o29zAcHlCey6Wi6I5y6cWvk8+Hht5oJl7pJqb10nnz/MLWggpRpbKpf1tOZb5Kp/JuMm604frtt1oLqtmy6ww6w8Jj3eA1+Q+XzcrHa39OPbnzYPjewMjErTH5L1OaR8ubo3NqyaJasmubzkQt5STCJ3cqh5ZK4g1DGN4BhsfH7XaYTXIcbt5Sj0+OjY4Pjjz6fmTo1vi3nzy888Hkbx+NDX78890P713/7P6VH+9+/t2DvvsPb/8+/NPC6FPpnGF21baqt+owhyHst0T8VtJnDxNIjMToiIsknKGAl6aCsWjA43o3uWCkEqcOmDVhwoJ3SZxGSZK0qtUr5hWtVbuxtaHRa8wmCzxfsAVnIC3EY9Bx4VfQrEQR7mncII5s4XBYFGmabnV1s89hEBP5XC4H3RX6uSjC1TCF1Sc3hoHrxEcT9AYeZtPGsRhVDxw0KDjZ8ieo5/h/or8BurywbYPFkIsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;1-modulecounts&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/7qlGoBDfxK800qkiKIQuay/cd7cbf2e3150ec2a3d37f833693369c5/1-modulecounts.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/7qlGoBDfxK800qkiKIQuay/cd7cbf2e3150ec2a3d37f833693369c5/1-modulecounts.png?w=148 148w,
https://images.ctfassets.net/rpmifyuylbfw/7qlGoBDfxK800qkiKIQuay/cd7cbf2e3150ec2a3d37f833693369c5/1-modulecounts.png?w=296 296w,
https://images.ctfassets.net/rpmifyuylbfw/7qlGoBDfxK800qkiKIQuay/cd7cbf2e3150ec2a3d37f833693369c5/1-modulecounts.png?w=591 591w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;landslide에 의하면 자바스크립트가 가장 많은 수의 패키지를 가지고 있다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;먼저 소프트웨어가 세계를 지배했고, 웹이 소프트웨어를 지배했으며, 그리고 자바스크립트가 웹을 지배했다. 2018년에는 React가 자바스크립트를 지배하고 있는 중이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;2018-react의-해&quot;&gt;&lt;a href=&quot;#2018-react%EC%9D%98-%ED%95%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2018: React의 해&lt;/h2&gt;
&lt;p&gt;React가 2017년 인기 경쟁에서 승리했다.&lt;/p&gt;
&lt;p&gt;구글 트렌드가 보여주는 것처럼 Angular로 작업중인 개발자들이 여전히 많이 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1kYiz9zgYsW8mCcWG88CEg/f8a36933209cba84617dfbe71bccf3cc/2-interest-over-time.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 31.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAANCAMAAADsQdzaAAABnlBMVEX09PTj4+Po6Ojr6+v4+Pj7+/v////v7+/6+vr9/f38/Pz+/v799fX+/f384N/70tD83dz85eT84+L2xMH/+/v+9vb94N793dv82df81dP80tH60c/07u7y8fHw7+/v7e366+r9+Pf9+vn729r84eD85+b71tT6xcP9+fn+/Pz99vX29PTw8fHy9PXy9Pbt9PvL4vb+8vL6vbr96Of60M79/Pz86Of19fXt7/D5+/z+/v/y9Pr+8fD6tbL+7u795OL95OP/+Pj+9/b+8PD9/v/p9P3U6frq6ur+9fLk5O799vb95uX72Nb99/f2+v3m8vzT6fvl8vzk8fz1+v35+/abwOn++Pf96ej95+b+///4+/73+/7u9/7n8/3m8/3i8f3k8v3l8v3h5+v5+fn29vb69vr9/fzi6/Tv39zz9PPt7u7e2t7k3uTi4uXj3+Pj4OXb3OrW3evU3evc3+zc4Oza3OLi4OTm5Ofr6e3t6u7t6u/u6/D17vf37/n27vf37vf07PPw5ejv4+P26uv26+r8+/r8+vr7+vn48/Hq2dq0Gg7tAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4eazMqIQAAANRJREFUGBmNwTFLAmEAgOH39e67EC1yihahpQJzjJoK8gcUNDXUf4t+RLS0NDkVmBC5JC6NFS5xeNddGUWQ+DyiZpEyi2JVcskrzPIeMGE+FeYUU4qUfzgVU5qElL+W/IHGfMoCv6y8NCyNaPq01t+ylLS8J+TybfN1+XHjIYNWn0LetnBLTE1ImdpVe83tGwp3wJ4+r14lBxizOIaQC/uDdb2G0CUAHQt42WanVyeLqY8hPdLB23DI8aFfJlGaeAFE7VG31uDU6pmenyw4S05FE+bzARcRMPQA/Y59AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2-interest-over-time&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1kYiz9zgYsW8mCcWG88CEg/f8a36933209cba84617dfbe71bccf3cc/2-interest-over-time.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1kYiz9zgYsW8mCcWG88CEg/f8a36933209cba84617dfbe71bccf3cc/2-interest-over-time.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1kYiz9zgYsW8mCcWG88CEg/f8a36933209cba84617dfbe71bccf3cc/2-interest-over-time.png?w=800 800w,
https://images.ctfassets.net/rpmifyuylbfw/1kYiz9zgYsW8mCcWG88CEg/f8a36933209cba84617dfbe71bccf3cc/2-interest-over-time.png?w=1600 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;하지만 React는 소비자 만족도 설문에서 꾸준히 우위를 점하고 있으며 React의 성장세는 Angular 및 그 외의 라이브버리의 그것을 완전히 제쳐 버렸다.&lt;/p&gt;
&lt;h3 id=&quot;그럼-vuejs는-난-그게-요즘-인기를-끌고-있다고-들었는데&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9F%BC-vuejs%EB%8A%94-%EB%82%9C-%EA%B7%B8%EA%B2%8C-%EC%9A%94%EC%A6%98-%EC%9D%B8%EA%B8%B0%EB%A5%BC-%EB%81%8C%EA%B3%A0-%EC%9E%88%EB%8B%A4%EA%B3%A0-%EB%93%A4%EC%97%88%EB%8A%94%EB%8D%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그럼 Vue.js는? 난 그게 요즘 인기를 끌고 있다고 들었는데?&lt;/h3&gt;
&lt;p&gt;모두가 Vue.js같은 대안들에 대해 립서비스를 하길 좋아한다. 나는 작년 그에 대해 이런 얘기를 했다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://vuejs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Vue.js&lt;/a&gt;는 엄청난 수의 Github Star와 다운로드를 기록하고 있다. 지금처럼 나아간다면 2017년에도 큰 인기를 끌겠지만 나는 React나 Angular(둘 다 높은 성장세를 가지고 있다)를 따라잡을 것이라 생각하지는 않는다. Vue.js는 React나 Angular를 배운 다음에 하길 바란다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Vue.js는 2017년에 무척 성공적이었다. 수많은 헤드라인을 기록했고 많은 사람들이 관심을 가졌다. 그리고 내가 예측한대로 Vue.js는 React를 따라잡지 못했고, 2018년에도 그러리라고 확신한다. 이는 2018년에 Angular를 따라잡을 수는 있다는 말이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6yjxd0Seooo2yYQ0qU6OyA/1dc1a170e867089b20453eb28b487b63/3-vue-js-downloads.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 41.1875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAQCAMAAABTCc2fAAAAzFBMVEX////q6urg4OHi4uLj4uPl5OTo6erg4ODx8fHb3N3W1tXg3+Dc3Nzf3+Dg39/k5OX39/f19fXu7u79/f3+/v799/f99vf//v709PPt7e38+/v68vP69PX9/Pz+/P389PXn5+f7+/v79fb79fX68/T79/jX19f89fbZ2dn8/Pz09PX69PT69fX9+fn++/v+/Pz6+vrz8/Px8fLs7Ozy8vLr7Ozr6+vq6+vz8/T09PT5+vrw8PDv7+/4+Pjv8PD5+fn6+/vt7e74+Pne3t77Yd79AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAMpJREFUKM+NkAcPgjAQhYt7S1sLCjjqwnPWPcD9//+TqDEOlPrl0jbXy7uXh9AHSigcicbiCSQhEU6m0plsLq/6vjChf1BAcpim3+5iKVDKMA1qmWWJYoVV2eNd4z88luuNZuvFY/ubkmYzzdbfex3oUrhimJYB0DOtftMCHwWk0ntiA88Ms7Vgz0Nv0UgakyAU5PwT+JNxCU+meHavOR4vXmqJVwpeK3jjzXEHhAtb73Rg63K643tFcHKgR4GIoO5eJ6cfK5yz8PUuOEIk0U5RlWkAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;3-vue-js-downloads&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6yjxd0Seooo2yYQ0qU6OyA/1dc1a170e867089b20453eb28b487b63/3-vue-js-downloads.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6yjxd0Seooo2yYQ0qU6OyA/1dc1a170e867089b20453eb28b487b63/3-vue-js-downloads.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/6yjxd0Seooo2yYQ0qU6OyA/1dc1a170e867089b20453eb28b487b63/3-vue-js-downloads.png?w=800 800w,
https://images.ctfassets.net/rpmifyuylbfw/6yjxd0Seooo2yYQ0qU6OyA/1dc1a170e867089b20453eb28b487b63/3-vue-js-downloads.png?w=1600 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Vue.js 월별 다운로드 수&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;표에서 볼 수 있는 것처럼 Vue.js의 다운로드는 Angular에 근접해가고 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4DDpnmTX6MMWagkgISGmY4/15de57c01d387185806047cdece08472/4-angular-downloads.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 41.0625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAQCAMAAABTCc2fAAAA4VBMVEX////j4+Tc3Nvc3N3k5OTe3t7l5eXf39/z9PTt7e3g4ODj4+Pe3d7i4uPm5eb7+vvx8PDw8PD8/Pz7+Pn68/P79vf+/v789fb99vf++vr+/Pz89PXv7+/9/f39/Pz8+/v79fb69PX68/T8+/z58PH//v799/f99/j+/f3n6Ofy8vL78/TY2Nj89vbZ2dn39/f29vf69vb7+fn4+Pj99vb++vvv7Oz08fHw7e3u6enz7e329vbu7u/u7u7z8/P09PTs7Oz7+/v39/b39vb19fX6+vr7+/z5+fn5+fro6OjZ2dphCmt5AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAANVJREFUGBmNwYk2w0AUBuC/thq1zcWkyGhSpAwVGku5skw1ivd/IKfHKT1t0vg+zKotLa+srtXXUUHUNuqNzcbW9g5m7UpaaG//gIgUKjjNQxwdY8yVuthJy/Pbp3rsTCsUOQ+cZuAEnQtMuTRS/2n5V77f9vQchWtM6XZQ6ia8pYm7HpVRCKMIE138zz0WCiX96HlUTuHXg4Mq5vGp//zSd9m8sok5iTkRnAhOBacZpxmnGVsAA6PfpB5KHUkaupTLd5vZkbA2zsnNbfxB8QjFPr8w5xtonCVfYCJYPgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;4-angular-downloads&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4DDpnmTX6MMWagkgISGmY4/15de57c01d387185806047cdece08472/4-angular-downloads.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4DDpnmTX6MMWagkgISGmY4/15de57c01d387185806047cdece08472/4-angular-downloads.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/4DDpnmTX6MMWagkgISGmY4/15de57c01d387185806047cdece08472/4-angular-downloads.png?w=800 800w,
https://images.ctfassets.net/rpmifyuylbfw/4DDpnmTX6MMWagkgISGmY4/15de57c01d387185806047cdece08472/4-angular-downloads.png?w=1600 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;angular/core 월별 다운로드 수&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;하지만 React는 선두를 굳건히 지키고 있고 높은 성장률도 가지고 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/HfVmxhNDiwEomEk02Gc4o/cb2c2862a74534eb6d2a87d2ab2a72e6/5-react-downloads.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 39.62500000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAQCAMAAABTCc2fAAAA51BMVEX////+/v7h4OHY2NnX1tbY19jY2Njl5ebX1tfu7u739/f9/f38/Pzh4eHh4uPc3dzl5eXj4+Lh4eLj4+P09PT19vX99/j99vb++/v+/f389PX99vf//v7++vr09PP8+Pn69PT8+vr6+vn99/f9+Pn++/zm5ub+/Pz89fX89fb+/P3X19fx8fH79vb78/T68/T69fXX19j9+Pj19fT++vv//f7w8O/19vbz8/P19fX09fX4+Pj5+fns7Oz29vb6+vr8+/vr6+vs7O3y8vLt7e349/f19PTq6uv5+fjx8vL9/Pzv7+/W1terv78KAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAM1JREFUKM9jYEAFjEzMLKxs7BycDBiAi5sHCXDy8vELCAoJCIvwoABuBgZRBvxATFyCgTCQlJKWYZCVBrPluHlwAnkFMKWoALJaCZeVytJSKlC2hCoDgxqavLq0tIaytKa0uBaahLYONxzo6unrG3BjBwyGjBANRprK0pL4/GXMwGCiLKYpbkowBMy4iQIMtADmFpaWYGQFJEVEoBw0ZM3AYGNrZ29r4eCow8DpxODIye3swuNqa+/mbu3haMnj4unl4s3AaYvDDh9hTDEAXvAd/XD0oEUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;5-react-downloads&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/HfVmxhNDiwEomEk02Gc4o/cb2c2862a74534eb6d2a87d2ab2a72e6/5-react-downloads.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/HfVmxhNDiwEomEk02Gc4o/cb2c2862a74534eb6d2a87d2ab2a72e6/5-react-downloads.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/HfVmxhNDiwEomEk02Gc4o/cb2c2862a74534eb6d2a87d2ab2a72e6/5-react-downloads.png?w=800 800w,
https://images.ctfassets.net/rpmifyuylbfw/HfVmxhNDiwEomEk02Gc4o/cb2c2862a74534eb6d2a87d2ab2a72e6/5-react-downloads.png?w=1600 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;React 월별 다운로드 수&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Vue.js의 성장 속도는 React보다 빠르다. 왜 2017년의 React vs Angular의 구도와는 다른 것일까?&lt;/p&gt;
&lt;p&gt;2016년 말, 자바스크립트 세계는 새로운 프레임워크를 받아들일 준비가 되어 있었다. Angular 유저는 무척 불만족스러운 상황이었고, React의 유저들은 무척 만족스러운 상태였다. 많은 사람들이 React를 공부하려고 했고 소수의 사람들만이 Angular를 배우길 원했다. 2017년 말 시점에 Angular 2+에 대한 만족도는 여전히 절반 이하인 49%다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;React와 Vue.js의 대결은 상황이 무척 다르다.&lt;/em&gt; &lt;a href=&quot;https://stateofjs.com/2017/front-end/results&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React의 유저 만족도는 Vue.js를 근소한 차이로 앞서고 있다&lt;/a&gt;(93% 대 90%). 유저들이 React에서 다른 라이브러리로 넘어가게 된 큰 계기는 2017년 초의 React 오픈소스 라이센스와 관련된 혼란이었다. 페이스북은 유저들의 의견을 받아들인 후 라이센스를 교체했다.&lt;/p&gt;
&lt;p&gt;이 단계에서 나는 시장이 React에서 다른 라이브러리로 이동하고 있다는 합당한 근거를 보지 못했다. Vue.js는 Angular나 jQuery의 유저는 쉽게 데려올 수 있다. 하지만 React의 유저를 뺏어오기는 쉽지 않으므로 꾸준한 성장세는 곧 벽에 부딪힐 것으로 보인다.&lt;/p&gt;
&lt;p&gt;나는 Vue.js가 1~2년에 걸쳐 급속한 성장을 할 것이라 예상한다. 그리고 1등의 자리를 놓고 React와 치열한 경쟁을 벌이겠지만 그 균형을 깨뜨릴 무언가 큰 변화가 없다면 Vue.js는 React에 이어 2번째 자리에 머물 것이다.&lt;/p&gt;
&lt;h2 id=&quot;채용&quot;&gt;&lt;a href=&quot;#%EC%B1%84%EC%9A%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;채용&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;jQuery는 무너졌다&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;채용에서 React는 jQuery를 완전히 넘어섰다–&lt;strong&gt;채용 인기도에서 jQuery를 넘어선 라이브러리는 지난 10년동안 React가 처음이다&lt;/strong&gt;. 우리는 지금 한 시대의 종말을 지켜보고 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2xSa5cE7mcQI2aIUesUgQk/6fdae05ead603e9babee3cdde57aa2ad/6-react-rising.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 59.93930197268589%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAYCAMAAAC/Wk/yAAAAxlBMVEX////09PT8/Py2xeKju+ahueWiueXq7/fh4eHo6OhvkM82acnU3vH6+vr5+fnm6e7t8vr5+vuuv+CYsuOWsOLK1/Hz8/Pi4uLm5uaWseP7+/vt7vD5+v329/r8/f6wwOCas+SYsuLZ4vXe3t6xxers7fD3+f309vqpu96QrOH9/v/x8fHj4+P7/P5xktA6bMqhttyEo97l7Pj4+Pjg4ODk5OTT3vP+/v55mNJFdM3X4fTs7Ozv7+/w8PD29vbp6eny8vLt7e01N1wZAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAANFJREFUKM+VkmsXgUAQhlcuCRFRhNwjtySEcun//ynlMlsdR+P90nN2nzOzuw0h/yRFkSFf+Y3pDCRLMZOjyAZynisUk1IK6vFlpEhIRYCwwleuvsSaCKmLMW7AZSS5+at1C0SGUXCin3YH0u3EWA2LvT5k0I/ykD44N5J/tB6H/oxCkCIhEw0y1aKsRsSZruvzxKFY5JdB6xVmetaGYagYcYGdx41pbpNFa7f3z3jAVLSPp9MZ1Rp9RomuOJQl1/og716e3yts2jda/e7AuuJ5D4Z4Ij52yzWzAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;6-react-rising&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2xSa5cE7mcQI2aIUesUgQk/6fdae05ead603e9babee3cdde57aa2ad/6-react-rising.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2xSa5cE7mcQI2aIUesUgQk/6fdae05ead603e9babee3cdde57aa2ad/6-react-rising.png?w=330 330w,
https://images.ctfassets.net/rpmifyuylbfw/2xSa5cE7mcQI2aIUesUgQk/6fdae05ead603e9babee3cdde57aa2ad/6-react-rising.png?w=659 659w,
https://images.ctfassets.net/rpmifyuylbfw/2xSa5cE7mcQI2aIUesUgQk/6fdae05ead603e9babee3cdde57aa2ad/6-react-rising.png?w=1318 1318w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;React의 성장 - 지난 10년간 jQuery를 넘어선 첫번째 라이브러리(출처: Indeed.com)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;작년의 차트와 비교해보자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2EPOKM3Nt2Ci48gYeq04oy/0eee96c5425fcc490b233da03217a299/7-jquery-is-so-2016.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 605px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 58.18181818181818%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAXCAMAAABODP0nAAAA/1BMVEX////z8/P8/Pza4vV3ltqetOTy8vL+/v5Lb8BAaso/asq8y+36+vrr8Pq3x+vM1/HS0tLFxcU7YrwsW8W1xuuouNquwOmrvebj6ffS2OXg5/bc4/Lo7Pb19fXm5ubr6+vk5OT9/f1+m9z09PTj4+PV1dXc3NzT2ebh6Pfd4/OZrNacsuSZr+Hm7PjExMTIyMj9/PzK1vH5+flMcMBBa8tAa8rP2vLt7e1uisppitagteXJycnKycrDw8N6mNuFnNCFoN2yw+r7+/ve4enq6urLy8vQ0NDd4ejn6ev39/fp6eno6Ojx8fHv7+/w8PDe3t7X19fW1tbh4eHl5eXb29vA+3FMAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAO5JREFUKM+Vk9d2gkAQQCebBGKNGjU27L1GLLGXaGLBGv3/bxFmTRRFXe/LPcA9DHAYgLt4oCKgafL49CyL4wFedIhed24DgNFkfpVDi9VKbG8XscvhYbTDibw7z+1ShW4P4qXy+PanjccS/IFg6GR0WCuMRGNxYAmRRBJJUSV9F8N0Bslmcqor6jCeL3z8jy5eCXlR5EtlpFKuXgkVPkEr0Ahr9XrjZthstZXv2GG6Y7fX6zOFTM8Y+hJhwBIOv3/IiPWtx4whEVATbkoXQ+LooYQrMpsH/tLJAjVdrtDLdRD/q9+YpHizXQl37N8O0mEiYech1rwAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;7-jquery-is-so-2016&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2EPOKM3Nt2Ci48gYeq04oy/0eee96c5425fcc490b233da03217a299/7-jquery-is-so-2016.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2EPOKM3Nt2Ci48gYeq04oy/0eee96c5425fcc490b233da03217a299/7-jquery-is-so-2016.png?w=151 151w,
https://images.ctfassets.net/rpmifyuylbfw/2EPOKM3Nt2Ci48gYeq04oy/0eee96c5425fcc490b233da03217a299/7-jquery-is-so-2016.png?w=303 303w,
https://images.ctfassets.net/rpmifyuylbfw/2EPOKM3Nt2Ci48gYeq04oy/0eee96c5425fcc490b233da03217a299/7-jquery-is-so-2016.png?w=605 605w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;&lt;em&gt;jQuery는 2016년에도 여전했다&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;여기서 정말 흥미로운 점은 다른 라이브러리들이 jQuery가 떨어진 것보다 더 많이 성장했다는 사실이다. 프론트 엔드 프레임워크와 관련된 채용 정보는 지난해 1만개 이상 늘어났다.&lt;/p&gt;
&lt;p&gt;채용 기회의 증가와 함께 평균 연봉의 증가도 확인할 수 있다. 2016년말의 9만3천 달러와 비교하면 &lt;a href=&quot;https://www.indeed.com/salaries/Javascript-Developer-Salaries&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;11만 달러&lt;/a&gt;로 늘어났다. 같은 기간 물가 상승률은 2%로 연봉 상승률보다 훨씬 낮다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;채용 정보 조사는 Indeed.com에서 이루어졌다. 거짓 양성을 가려내기 위해, 나는 검색 결과의 연관성을 높이기 위해 “software” 키워드로 검색했고 결과를 ~1.5의 값으로 곱했다(프로그래밍 관련 채용 정보 중에 “software” 키워드를 사용하는 것과 그렇지 않은 것의 대략적인 차이) 모든 SERP는 날짜와 연관성이 있는 부분으로 정렬되었다. 결과는 100% 정확하지는 않다. 하지만 이 글에서 사용된 근사치로 사용하기에는 충분하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;프레임워크-추천&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EC%B6%94%EC%B2%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프레임워크 추천&lt;/h2&gt;
&lt;p&gt;올해의 수치를 확인한 후 나는 React를 가장 일반적인 앱 개발 방법으로 추천할 준비가 되었다. 여기서 말하는 앱은 모바일 앱(PWA, React Native), 웹 애플리케이션, 사무용 앱, 데스크탑용 미디어 컨텐츠 제작 도구를 포함한다(&lt;a href=&quot;https://electronjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Electron&lt;/a&gt;을 확인해보길 바란다).&lt;/p&gt;
&lt;p&gt;다른 도구를 이용하는 편이 좋은 영역에는 단순한 랜딩 페이지(프레임워크를 완전히 배제하라), 3D 게임, AR/VR이 있다. 3D 컨텐츠를 위해서는 Unity, Unreal, 또는 PlayCanvas를 살펴보길 바란다. 이는 React가 3D 컨텐츠 UI 개발에도 사용되고 있다는 말이다.&lt;/p&gt;
&lt;p&gt;나는 올해 다른 모든 프레임워크를 엄밀히 말하자면 선택 사항으로 분류했다. 이는 그 프레임워크들이 좋지 않다는 의미가 아니며 단지 채용 시장에서 React에 견줄만한 경쟁자가 아니기 때문이다. 이 목록은 학습시간 투자 대비 효과에 관한 것이며 어떤 기술이 “최고”인지에 관한 것이 아님을 기억하길 바란다.&lt;/p&gt;
&lt;h2 id=&quot;왜-이렇게-react는-많은-관심을-받고-있는가&quot;&gt;&lt;a href=&quot;#%EC%99%9C-%EC%9D%B4%EB%A0%87%EA%B2%8C-react%EB%8A%94-%EB%A7%8E%EC%9D%80-%EA%B4%80%EC%8B%AC%EC%9D%84-%EB%B0%9B%EA%B3%A0-%EC%9E%88%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 이렇게 React는 많은 관심을 받고 있는가?&lt;/h2&gt;
&lt;p&gt;React 직업 목록을 살펴보면서 나는 흥미로운 트렌드를 발견했다. 많은 직업들이 우리가 프론트엔드 웹 개발과 관련없다고 생각했던 것이었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React Native(Vue.js와 관련된 직업의 수보다 React Native와 관련된 직업이 더 많을 것이다)&lt;/li&gt;
&lt;li&gt;React for IOT&lt;/li&gt;
&lt;li&gt;React for AR/VR (Oculus Rift가 채용을 주도하고 있다)&lt;/li&gt;
&lt;li&gt;들어본 적이 없는 모호한 컴퓨팅 작업을 위한 React&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;React는 웹이라는 기본 틀에서 벗어났다&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다양한 쓰임새는 React의 큰 강점 중의 하나다. 다른 많은 프레임워크와는 달리 React에 투자하는 것은 단순히 데이터 모델이나 브라우저나 DOM에 한정되지 않는다. 사실 나는 자바스크립트를 언급조차 하지 않는 꽤 많은 수의 직업을 찾아냈다.&lt;/p&gt;
&lt;p&gt;또 React는 사실상의 표준이 되어 다양하고 풍부한 개발 생태계를 제공한다. 이는 jQuery 플러그인이 웹을 지배한 이후로 없었던 일이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;문제는 더 이상 “어떤 프레임워크를 쓸 것인가?”가 아니라 “어떤 기술이 React와 가장 잘 맞는가”이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;어떤 프레임워크도 2018년에는 React의 위치를 넘어설 수 없을 것으로 보인다 (아마도 2019년에도). 당신은 안전하다. 자바스크립트 피로감은 이제 진정될 것처럼 보인다. 우리는 앱 개발에 필요한 멋진 프레임워크를 가지고 있으며 React를 중심으로 훌륭한 개발 생태계가 자리잡고 있다.&lt;/p&gt;
&lt;h2 id=&quot;어떤-주제를-공부해야-하는가&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%96%A4-%EC%A3%BC%EC%A0%9C%EB%A5%BC-%EA%B3%B5%EB%B6%80%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어떤 주제를 공부해야 하는가?&lt;/h2&gt;
&lt;p&gt;지난해처럼 자바스크립트와 웹 개발의 기본에 집중해야하는 것에는 변함이 없다. 하지만 React 앱을 위한 함수형 프로그래밍에 조금 더 중점을 두어야 한다.&lt;/p&gt;
&lt;p&gt;React를 훌륭하게 만드는 데에는 2가지 주요 요소가 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;선언형 뷰 렌더링&lt;/li&gt;
&lt;li&gt;DOM을 직접 조작하지 않고 뷰 레이어를 추상화시키는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 두가지를 염두에 두면서, 당신이 공부해야 하는 주제는 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;기본 ES6 문법&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/why-composition-is-harder-with-classes-c3e627dcd0aa&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Class 문법과 그것의 많은 결점들&lt;/strong&gt;&lt;/a&gt; – React 컴포넌트 작성을 위해 Class를 사용하는 것은 괜찮다. 하지만 직접 만든 클래스를 상속하는 방법은 피해야 하며, &lt;code class=&quot;language-text&quot;&gt;instanceOf&lt;/code&gt;의 사용을 피해야 하고, 다른 사람들이 당신이 작성한 클래스 &lt;code class=&quot;language-text&quot;&gt;new&lt;/code&gt; 키워드와 함께 사용하지 않도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/composing-software-an-introduction-27b72500d6ea&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;함수형 프로그래밍 &amp;#x26; 소프트웨어 컴포지션&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#0355&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Currying&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Closures&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-pure-function-d1c076bec976&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Pure functions&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-promise-27fc71e77261&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Promises&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/7-surprising-things-i-learned-writing-a-fibonacci-generator-4886a5c87710&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Generators&lt;/strong&gt;&lt;/a&gt; &amp;#x26; &lt;a href=&quot;https://medium.com/javascript-scene/the-hidden-power-of-es6-generators-observable-async-flow-control-cfa4c7f31435&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;async functions&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/the-outrageous-cost-of-skipping-tdd-code-reviews-57887064c412&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;TDD&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2015/10/rail-user-centric-model-performance/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Rail 성능 모델&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Progressive 웹 애플리케이션&lt;/strong&gt; (PWA): &lt;a href=&quot;https://medium.com/javascript-scene/native-apps-are-doomed-ac397148a2c0&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“네이티브 앱은 이제 끝났다”&lt;/a&gt;와 &lt;a href=&quot;https://medium.com/javascript-scene/why-native-apps-really-are-doomed-native-apps-are-doomed-pt-2-e035b43170e9&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“네이티브 앱이 끝난 진짜 이유”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GraphQ&lt;/strong&gt;L은 2017년에 많이 성숙해졌으며 REST API를 빠르게 따라잡고 있다. Apollo는 오프라인 우선 캐시 아키텍쳐를 기본 사양으로 추가했으며 이로 인해 2018년에는 Apollo+GraphQL이 Redux의 진지한 대안 또는 보완물이 될 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;라이브러리--개발-도구&quot;&gt;&lt;a href=&quot;#%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC--%EA%B0%9C%EB%B0%9C-%EB%8F%84%EA%B5%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;라이브러리 &amp;#x26; 개발 도구&lt;/h2&gt;
&lt;p&gt;아래는 내가 가장 유용하다고 여기는 라이브러리와 도구들이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;React&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redux.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Redux&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/redux-saga/redux-saga&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;redux-saga&lt;/strong&gt;&lt;/a&gt; – 비동기 I/O 처리와 사이드 이펙트의 분리&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/zeit/next.js/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Next.js&lt;/strong&gt;&lt;/a&gt; – Node와 Express 기반의 서버 사이드 렌더링, 번들 파일 자동 분리, styled-jsx&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.material-ui.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Material UI&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/storybooks/storybook&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Storybook&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cheeriojs/cheerio&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Cheerio&lt;/strong&gt;&lt;/a&gt; – React 컴포넌트 유닛 테스팅(나는 Enzyme보다 선호한다)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lodash.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Lodash&lt;/strong&gt;&lt;/a&gt;. 번들 파일의 사이즈가 크게 늘어나지 않도록 필요한 유틸리티만 불러와서 사용하길 바란다(나는 &lt;code class=&quot;language-text&quot;&gt;lodash/fp&lt;/code&gt;의 유틸리티를 선호한다)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://babeljs.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Babel&lt;/strong&gt;&lt;/a&gt; – ES6를 구형 브라우저에서 사용할 수 있도록 컴파일하는데 사용&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webpack.github.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Webpack&lt;/strong&gt;&lt;/a&gt; – 표준 자바스크립트 위한 가장 인기있는 번들러&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://eslint.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;ESLint&lt;/strong&gt;&lt;/a&gt; – 문법 오류와 스타일 이슈를 빨리 찾아낸다. 코드 리뷰와 TDD 다음으로 버그를 줄이기에 가장 좋은 도구&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ramdajs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Ramda&lt;/strong&gt;&lt;/a&gt; – 대개 함수형 프로그래밍의 lense와 transducer로 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/introduction-to-node-express-90c431f9e6fd#.gl2r6gcnn&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;Node &amp;#x26; Express&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://reactivex.io/rxjs/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;strong&gt;RxJS&lt;/strong&gt;&lt;/a&gt; – JavaScript에서 사용하는 Observable. 최근에 나는 transducer를 더 많이 사용했다. 번들 파일의 사이즈를 키우지 않으려면 &lt;a href=&quot;https://github.com/ReactiveX/rxjs#es6-via-npm&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;patch import&lt;/a&gt;를 사용하는 것을 잊지 말길 바란다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;는 2017년에 많은 관심을 받았다. 하지만 나는 TypeScript가 작업에 방해가 되기도 하며, 앱 개발에 도움을 주는 것 이상으로 복잡도를 높인다는 사실을 확인했다. TypeScript이 주된 단점은 타입 추론보다 타입 명시에 과도하게 의존하고 있다는 점이다. 그리고 고차 함수의 타입 정의는 엄청난 왜곡 없이는 제대로 할 수 없기도 하다. 나는 한동안 온종일 TypeScript을 사용해봤지만, 이 글은 여전히 유효하다: &lt;a href=&quot;https://medium.com/javascript-scene/the-shocking-secret-about-static-types-514d39bf30a3&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“정적 타입에 대한 충격적인 비밀”&lt;/a&gt; &amp;#x26; &lt;a href=&quot;https://medium.com/javascript-scene/you-might-not-need-typescript-or-static-types-aa7cb670a77b&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“당신에게는 TypeScript가 필요없을 수도 있다.”&lt;/a&gt;. Flow도 같은 문제가 있으며 개발 도구가 TypeScript의 그것만큼 훌륭하지 않다. (역주: Flow는 Nuclide, TypeScript는 VS Code)&lt;/p&gt;
&lt;h2 id=&quot;2018년에-주목해야-할-기술&quot;&gt;&lt;a href=&quot;#2018%EB%85%84%EC%97%90-%EC%A3%BC%EB%AA%A9%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B8%B0%EC%88%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2018년에 주목해야 할 기술&lt;/h2&gt;
&lt;p&gt;2018년에 아래 영역의 R&amp;#x26;D는 모두 채용 수요가 늘어나고 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Progressive Web Apps(PWAs)&lt;/li&gt;
&lt;li&gt;블럭체인 &amp;#x26; 핀테크&lt;/li&gt;
&lt;li&gt;의료 기술&lt;/li&gt;
&lt;li&gt;AR/VR – 홀로렌즈, 메타와 ODG는 현재 발매중이다. ODG R-9은 2017년 발매 예정이었지만 2018년에는 가능할 것으로 보인다. MagicLeap는 2018년 출시를 약속했다. AR은 휴대폰이 그랬던 것 이상으로 사용자의 경험을 바꿔놓을 것이다.&lt;/li&gt;
&lt;li&gt;3D 프린팅&lt;/li&gt;
&lt;li&gt;AI&lt;/li&gt;
&lt;li&gt;드론&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;양자 컴퓨팅 또한 세계를 바꿔놓을 준비가 되었다. 하지만 2019년 또는 혼란스러운 과도기를 지난 다음이 될 것 같다. 현재 온라인에는 작동중인 양자 컴퓨터가 있지만 아직 많은 일을 하지는 못한다. 아직은 많은 개발자에게 실험적인 제품을 만들기에도 이르다. 마이크로소프트는 최근 양자 컴퓨팅을 위한 &lt;a href=&quot;https://arstechnica.com/gadgets/2017/12/microsofts-q-quantum-programming-language-out-now-in-preview/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Q# 프로그래밍 언어&lt;/a&gt;를 발표했다. 그리고 IBM와 구글도 클라우드 양자 컴퓨팅 시장을 선점하기 위한 투자를 계속하고 있다.&lt;/p&gt;
&lt;p&gt;만약 양자 컴퓨팅을 배우기 위한 준비를 하고 싶다면, &lt;a href=&quot;https://mitpress.mit.edu/books/quantum-algorithms-linear-algebra&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;선형대수학&lt;/a&gt;을 공부해야 한다. 그리고 &lt;a href=&quot;https://plato.stanford.edu/entries/lambda-calculus/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;람다 대수&lt;/a&gt;에 기반한 양자 컴퓨팅의 기능적 탐구도 해야 한다.&lt;/p&gt;
&lt;p&gt;아마도, AI에서 그랬던 것처럼, 수학적인 기반 지식이 적은 사람들도 양자 컴퓨팅의 능력의 일부를 사용할 수 있도록 클라우드 API가 개발될 것이다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[TypeScript로 작성하는 React-Redux 앱]]></title><description><![CDATA[몇 개의 시스템 관리 도구를 개발하면서 느꼈던 점은 서버에서 내려받은 데이터를 다룰 때 데이터의 형태가 타입으로 정의되어 있지 않다 보니 API 문서나 개발 도구의 네트워크 탭, 어떤 경우에는 콘솔 로그를 찍어야 하는 일이 많아서 불편하다는 점이었다. 특히 앱의 규모…]]></description><link>https://blog.rhostem.com//posts/2018-01-15-react-redux-typescript</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2018-01-15-react-redux-typescript</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 15 Jan 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;몇 개의 시스템 관리 도구를 개발하면서 느꼈던 점은 서버에서 내려받은 데이터를 다룰 때 데이터의 형태가 타입으로 정의되어 있지 않다 보니 API 문서나 개발 도구의 네트워크 탭, 어떤 경우에는 콘솔 로그를 찍어야 하는 일이 많아서 불편하다는 점이었다. 특히 앱의 규모가 커지고 다뤄야 하는 데이터가 많아질수록 그로 인한 오류는 더욱 자주 발생하곤 했다. 자바스크립트가 동적 타입 언어다 보니 생기는 어쩔 수 없는 문제라고 할 수 있다. 물론 특정 데이터의 기본값을 확장하는 방법과 에디터의 도움을 받으면 어느 정도 극복은 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;getDataFromServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_DATA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;updateClientStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 데이터 형태를 넘어 함수 객체의 데이터형, 리턴 타입까지 정의할 수 있고 프로그래밍 오류를 줄일 수 있을 것이라는 기대에 Angular 와 함께 인기를 얻기 시작한 TypeScript에 자연스럽게 관심을 두게 되었고, 몇 개의 앱을 TypeScript로 개발하게 되었다.&lt;/p&gt;
&lt;p&gt;하지만 써 본 결과, 역시 모든 것에는 장단점이 존재한다는 것을 확인할 수 있었다.&lt;/p&gt;
&lt;h2 id=&quot;javascript를-정적-타입-언어-형태로-작성하는-것이-정말로-개발에-도움이-되는가&quot;&gt;&lt;a href=&quot;#javascript%EB%A5%BC-%EC%A0%95%EC%A0%81-%ED%83%80%EC%9E%85-%EC%96%B8%EC%96%B4-%ED%98%95%ED%83%9C%EB%A1%9C-%EC%9E%91%EC%84%B1%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B4-%EC%A0%95%EB%A7%90%EB%A1%9C-%EA%B0%9C%EB%B0%9C%EC%97%90-%EB%8F%84%EC%9B%80%EC%9D%B4-%EB%90%98%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Javascript를 정적 타입 언어 형태로 작성하는 것이 정말로 개발에 도움이 되는가?&lt;/h2&gt;
&lt;p&gt;도움이 되는 요소는 분명히 있다. 모든 데이터와 함수의 파라미터에 자료형을 붙임으로써 숫자 형 변수에 &lt;code class=&quot;language-text&quot;&gt;length&lt;/code&gt;같은 속성을 참조하려다가 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; 값을 돌려받는 일 따위는 확실히 막을 수 있다. 그리고 더 정확한 코드 자동완성이 가능해지므로 한동안 작업을 하지 않다가 다시 시작해도 코드를 파악하는데 시간이 덜 걸리기도 했다.&lt;/p&gt;
&lt;p&gt;하지만 개발에 걸리는 시간은 확실히 늘어난다. 타입 선언을 하지 않았다고 자꾸 오류를 발생시키는 TypeScript 컴파일러 때문에 &lt;code class=&quot;language-text&quot;&gt;tsconfig.json&lt;/code&gt; 파일의 각종 옵션을 &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt; 로 바꿔버리고 &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; 타입을 남발하면서 개발을 진행하고 싶은 충동이 들 수 있는데, 그렇게 할 거라면 정적 언어를 사용하는 의미가 없어진다. 특히 외부 라이브러리는 TypeScript 는 &lt;a href=&quot;https://github.com/DefinitelyTyped/DefinitelyTyped&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;DefinitelyTyped&lt;/a&gt;에 올라온 타입 정의를, flow 는 &lt;a href=&quot;https://github.com/flowtype/flow-typed&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;flow-typed&lt;/a&gt;에 추가된 타입 정의를 사용해야 한다. 하지만 타입 정의의 버전은 라이브러리의 최신 버전보다 이전인 경우가 더 많고 수년 전에 안정화되어서 개발이 중단된 라이브러리는 없는 경우도 많다. 그럴 땐 직접 타입을 선언해서 사용해야 한다. 그리고 어떤 라이브러리들은 콜백 함수에 정말 상세하게 타입을 정의해두곤 하는데, 거기에 맞추기 위해 타입 정의를 작성하다 보면 제법 많은 시간이 지나가곤 한다.&lt;/p&gt;
&lt;p&gt;그리고 정적 타입 언어가 오류 자체를 줄여주는 것은 아니다. 컴파일 오류가 없다고 하더라도 코드 로직에서는 얼마든지 잘못된 결과를 만들어낼 수 있다. 테스트 코드를 작성하지 않는 이상 오류가 발생하지 않는다는 보장은 가질 수 없는 것에는 변함이 없다. 그리고 데이터 타입 측면에서도 서버의 데이터 모델과 프론트엔드 코드의 타입이 언제나 일치한다는 보장은 없으며 서버 코드에 오류가 있는 경우라면 말할 것도 없다.&lt;/p&gt;
&lt;h2 id=&quot;그래도-자동완성-기능은-역시-매력적이다&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9E%98%EB%8F%84-%EC%9E%90%EB%8F%99%EC%99%84%EC%84%B1-%EA%B8%B0%EB%8A%A5%EC%9D%80-%EC%97%AD%EC%8B%9C-%EB%A7%A4%EB%A0%A5%EC%A0%81%EC%9D%B4%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그래도 자동완성 기능은 역시 매력적이다&lt;/h2&gt;
&lt;p&gt;TypeScript, flow 같은 자바스크립트 정적 언어를 통해 자동완성, intelliSense 라고도 불리는 기능을 제대로 활용할 있다는 것은 확실히 좋은 점이다. 특히 Redux 를 사용할 때 reducer 내부에서 특정 케이스 아래에서 action 객체의 속성을 참조하기 위해 점(&lt;code class=&quot;language-text&quot;&gt;.&lt;/code&gt;)을 찍으면 에디터(TypeScript 라면 vscode)가 어떤 타입의 액션인지 알아서 파악한 후 정확한 속성을 표시해 주는 모습을 보면 막혀 있던 가슴이 시원해지는 느낌이 들기도 한다. 이처럼 타이핑은 수많은 액션 타입을 사용해야 하는 Redux 에서 무척 큰 도움이 된다. 사실 내가 TypeScript를 이용해서 해결하려고 했던 것도 Redux 에서 action, reducer, model 의 속성을 확인하기 위해 이 파일 저 파일을 옮겨다니며 확인하는 일을 해결하고 싶어서였다.&lt;/p&gt;
&lt;h2 id=&quot;typescript-react-redux-프로젝트-설정&quot;&gt;&lt;a href=&quot;#typescript-react-redux-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TypeScript React-Redux 프로젝트 설정&lt;/h2&gt;
&lt;p&gt;TypeScript React 앱은 &lt;a href=&quot;https://github.com/facebookincubator/create-react-app&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;create-react-app&lt;/a&gt;을 사용하면 간단히 만들 수 있다. 다만 TypeScript로 사용하기 위해서는 옵션을 추가해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -g create-react-app

create-react-app my-app --scripts-version&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;react-scripts-ts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 해서 프로젝트를 생성하면 앱 실행에 react-scripts 대신 &lt;a href=&quot;https://github.com/wmonk/create-react-app-typescript&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;react-scripts-ts&lt;/a&gt;를 사용하기 때문에 소스를 TypeScript로 작성할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;react-컴포넌트-타입-선언&quot;&gt;&lt;a href=&quot;#react-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;React 컴포넌트 타입 선언&lt;/h2&gt;
&lt;p&gt;컴포넌트로는 함수형의 stateless component, 클래스 형태의 statefull component, Redux 가 연결된 container component 가 있다. higher-order 컴포넌트도 있지만, 여기에서는 다루지 않도록 하겠다. 타입 선언을 위해서는 React의 타입 선언 패키지를 설치 후 진행해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i -D @types/react @types/react-dom @types/react-redux&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;1-stateless-component&quot;&gt;&lt;a href=&quot;#1-stateless-component&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. stateless component&lt;/h3&gt;
&lt;p&gt;React는 Props의 데이터형을 설정하기 위해 컴포넌트 클래스는 &lt;code class=&quot;language-text&quot;&gt;static&lt;/code&gt; 타입의 &lt;code class=&quot;language-text&quot;&gt;PropTypes&lt;/code&gt; 변수 안에 타입을 선언하는 방식을 사용한다. 하지만 이제는 TypeScript를 사용하므로 &lt;code class=&quot;language-text&quot;&gt;interface&lt;/code&gt;나 &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt;을 사용해서 Props의 형태를 선언하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;

type Props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Props 타입 선언&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 컴포넌트의 리턴 타입을 Props 타입과 함께 설정&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TSSFC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SFC&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; = props =&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;restProps &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; props

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;restProps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

export default TSSFC&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;React의 Props와 State의 타입 선언에는 &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/generics.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;generic&lt;/a&gt; 타입을 사용한다. generic 타입은 함수처럼 인자를 받아서 타입 선언을 유동적으로 바꿀 수 있으므로 컴포넌트마다 서로 다른 Props와 State를 선언하는데 활용된다. 컴포넌트 &lt;code class=&quot;language-text&quot;&gt;TSSFC&lt;/code&gt;의 리턴 타입이 &lt;code class=&quot;language-text&quot;&gt;React.SFC&amp;lt;Props&amp;gt;&lt;/code&gt;으로 되어 있는데, 이는 &lt;code class=&quot;language-text&quot;&gt;Props&lt;/code&gt;라는 타입을 전달받아서 내부적으로 타입 선언에 사용한다는 의미이다.&lt;/p&gt;
&lt;h3 id=&quot;2-statefull-component&quot;&gt;&lt;a href=&quot;#2-statefull-component&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. statefull component&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;

type Props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
type State &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TSComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Props&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; State&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; defaultProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; State &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;TSComponent&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; TSComponent&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;클래스 컴포넌트는 &lt;code class=&quot;language-text&quot;&gt;React.Component&lt;/code&gt; 대신 &lt;code class=&quot;language-text&quot;&gt;React.Component&amp;lt;Props, State&amp;gt;&lt;/code&gt;를 상속받는 방식을 사용해서 타입을 추가한다. 그리고 클래스 내부에서 &lt;code class=&quot;language-text&quot;&gt;state&lt;/code&gt; 변수를 선언할 때 타입을 붙여주고 &lt;code class=&quot;language-text&quot;&gt;constructor&lt;/code&gt; 함수의 인자에도 타입을 붙여준다. 이 외에는 Javascript의 React 컴포넌트와 차이가 없다. 오히려 PropTypes를 이용한 타입 선언보다 TypeScript의 타입 선언이 더 간단하기 때문에 더 간결하게 느껴질 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;3-container-component&quot;&gt;&lt;a href=&quot;#3-container-component&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. container component&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; React &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; connect &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-redux&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; bindActionCreators&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; compose&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Dispatch &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;redux&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; RootState &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../../store/rootReducer&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; todoActions &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../../store/todo/actions&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; returntypeof &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-redux-typescript&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;mapStateToProps&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; RootState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  todoList&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;todo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;list&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;mapDispatchToProps&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dispatch&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Dispatch&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;bindActionCreators&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      addTodo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; todoActions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;addTodo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    dispatch
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// 두 함수에 의해 생성될 Props를 객체 형태로 가져온다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; statePropTypes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;returntypeof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mapStateToProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; actionPropTypes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;returntypeof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mapDispatchToProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

type Props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; statePropTypes &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; actionPropTypes &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
type State &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Container&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Props&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; State&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; defaultProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; State &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;compose&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mapStateToProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; mapDispatchToProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;컨테이너 컴포넌트는 외부 라이브러리를 활용한다. &lt;code class=&quot;language-text&quot;&gt;mapStateToProps&lt;/code&gt; 함수와 &lt;code class=&quot;language-text&quot;&gt;mapDispatchToProps&lt;/code&gt; 함수의 리턴값을 &lt;code class=&quot;language-text&quot;&gt;typeof&lt;/code&gt; 명령어로 타입 형태로 바꿀 수 있도록 객체 형태로 바꿔주는 &lt;code class=&quot;language-text&quot;&gt;returntypeof&lt;/code&gt;라는 헬퍼 함수가 그것이다. 두 함수의 리턴 타입을 직접 작성하는 것보다 자동으로 생성해주는 라이브러리를 사용함으로서 컴포넌트의 Props 타입 선언을 무척 간편하게 할 수 있다.&lt;/p&gt;
&lt;p&gt;위의 예제 코드에서 &lt;code class=&quot;language-text&quot;&gt;returntypeof(mapStateToProps)&lt;/code&gt;의 결과는 &lt;code class=&quot;language-text&quot;&gt;{ todoList: state.todo.list }&lt;/code&gt;가 되고 이를 &lt;code class=&quot;language-text&quot;&gt;typeof&lt;/code&gt; 연산자로 타입으로 변경하면 아래와 같은 형태가 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;type StatePropTypes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  todoList&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Array&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Todo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;, // 예시를 위해 임의로 todoList가 Todo 타입의 배열이라고 표현
}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 &lt;code class=&quot;language-text&quot;&gt;type&lt;/code&gt; 키워드로 선언한 타입은 &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&lt;/code&gt; 연산자를 통해 &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/advanced-types.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;intersection&lt;/a&gt; 타입 형태로 합치는 것이 가능하므로 사용자가 직접 정의한 Props와 함께 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;type Props &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; statePropTypes &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; actionPropTypes &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;react-redux의-타입-선언&quot;&gt;&lt;a href=&quot;#react-redux%EC%9D%98-%ED%83%80%EC%9E%85-%EC%84%A0%EC%96%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;React-Redux의 타입 선언&lt;/h2&gt;
&lt;h3 id=&quot;action-객체&quot;&gt;&lt;a href=&quot;#action-%EA%B0%9D%EC%B2%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;action 객체&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_TODO&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ADD_TODO&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; type Actions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;ADD_TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_TODO&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// string&lt;/span&gt;
    content&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; todoActions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  addTodo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Actions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    content&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Javascript로 액션을 설정할 때는 타입 상수만 선언해주면 되었지만 여기서는 &lt;code class=&quot;language-text&quot;&gt;Actions&lt;/code&gt; 타입도 함께 선언해줘야 한다. 그 이유는 액션 타입을 reducer에서 사용하기 위함이다. 액션의 타입이 정의되어 있어야 reducer에서 어떤 액션인지 파악할 수 있다.&lt;/p&gt;
&lt;p&gt;액션의 타입을 직접 선언해줘야 해서 코드의 양이 이 부분에서 많이 늘어난다. 하지만 이 작업을 제대로 해 줘야 reducer의 &lt;code class=&quot;language-text&quot;&gt;case&lt;/code&gt;문 내부에서 어떤 액션인지 제대로 찾을 수 있다. 액션의 타입 형태는 거의 비슷하므로 복사 &amp;#x26; 붙여넣기를 사용하면 작업 시간을 줄일 수 있지만, 작업 시간이 늘어나는 건 어쩔 수 없다.&lt;/p&gt;
&lt;h3 id=&quot;root-action&quot;&gt;&lt;a href=&quot;#root-action&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;root action&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Actions &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; TodoActions &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./todo/todoActions&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Actions &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; AnotherAction &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./todo/anotherAction&apos;&lt;/span&gt;

type RootAction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; TodoActions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;keyof TodoActions&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; AnotherAction&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;keyof AnotherAction&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; RootAction&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;모든 액션 타입을 합친 하나의 루트 타입을 만든다. &lt;code class=&quot;language-text&quot;&gt;RootAction&lt;/code&gt;은 &lt;code class=&quot;language-text&quot;&gt;|&lt;/code&gt; 연산자를 사용해서 만드는 &lt;code class=&quot;language-text&quot;&gt;union&lt;/code&gt; 타입 형태로서 &lt;code class=&quot;language-text&quot;&gt;or&lt;/code&gt; 연산자와 유사한 의미다. 앞서 생성한 Actions 타입을 가져온 후 &lt;code class=&quot;language-text&quot;&gt;keyof&lt;/code&gt; 키워드를 사용해서 &lt;code class=&quot;language-text&quot;&gt;union&lt;/code&gt; 타입 형태로 변환한 후 &lt;code class=&quot;language-text&quot;&gt;RootAction&lt;/code&gt; 타입에 할당한다. 다른 액션 생성자 타입도 같은 방식으로 계속 추가하면 된다.&lt;/p&gt;
&lt;p&gt;추가로 설명하자면 &lt;code class=&quot;language-text&quot;&gt;TodoActions[keyof TodoActions]&lt;/code&gt; 코드의 결과는 아래와 같은 형태가 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;type todoActionUnion &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ADD_TODO&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      content&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;TOGGLE_TODO&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;reducer-함수와-state&quot;&gt;&lt;a href=&quot;#reducer-%ED%95%A8%EC%88%98%EC%99%80-state&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;reducer 함수와 state&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Todo &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./todoModel&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_TODO&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./todoActions&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; RootAction &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../rootAction&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; type TodoState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Readonly&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  list&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Array&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Todo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;,
}&gt;

const initialState: TodoState = &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  list&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

const todo = (state: TodoState = initialState, action: RootAction) =&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 에디터는 action이 어떤 타입인지 case와 RootAction 타입을 사용해서 파악해준다&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

export default todo&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;todo&lt;/code&gt; state의 형태를 표현한 &lt;code class=&quot;language-text&quot;&gt;TodoState&lt;/code&gt; 타입 선언에는 &lt;code class=&quot;language-text&quot;&gt;ReadOnly&lt;/code&gt; 키워드를 사용했다. 이는 컴파일시점에서 이 타입이 할당된 객체가 수정되었는지 검사하는데, redux는 immutable한 객체 상태를 지향하기 때문에 사용하는 것이 좋다.&lt;/p&gt;
&lt;p&gt;그리고 reducer에서는 &lt;code class=&quot;language-text&quot;&gt;RootAction&lt;/code&gt; 타입과 추후 기술할 &lt;code class=&quot;language-text&quot;&gt;RootReducer&lt;/code&gt; 타입을 사용한다. reducer 함수의 파라미터인 &lt;code class=&quot;language-text&quot;&gt;state&lt;/code&gt; 에는 현재 reducer에서만 사용하는 데이터를 표현하는 &lt;code class=&quot;language-text&quot;&gt;TodoState&lt;/code&gt; 타입을 사용하고,  action 에는 &lt;code class=&quot;language-text&quot;&gt;RootAction&lt;/code&gt;을 사용했다. &lt;code class=&quot;language-text&quot;&gt;RootAction&lt;/code&gt; 대신 todoActions 파일에서 선언해둔 타입을 가져와서 &lt;code class=&quot;language-text&quot;&gt;RootAction&lt;/code&gt;을 만들 때 사용했던 union 타입을 만드는 방식을 사용해도 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;todo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; TodoState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; initialState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; TodoAction&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;keyof TodoActionType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 state 함수의 &lt;code class=&quot;language-text&quot;&gt;action&lt;/code&gt; 파라미터에 타입을 추가하면 case 문 내부에서 &lt;code class=&quot;language-text&quot;&gt;action&lt;/code&gt;이 어떤 타입인지 자동으로 파악해준다. 단, 타입을 정확하게 선언해둬야 한다. 수많은 액션 객체를 생성하고 타입을 선언하다 보면(=복사 &amp;#x26; 붙여넣기를 반복하다 보면) 잘못 선언한 타입이 생길 가능성은 얼마든지 있다.&lt;/p&gt;
&lt;h3 id=&quot;root-reducer&quot;&gt;&lt;a href=&quot;#root-reducer&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;root reducer&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; combineReducers &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;redux&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; TodoState &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./todo/todoReducer&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RootState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  todo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; TodoState&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rootState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; combineReducers&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;RootState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  router&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  todo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;);

export default rootState&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;root reducer 는 &lt;code class=&quot;language-text&quot;&gt;rootAction&lt;/code&gt;보다 단순하다. reducer들의 state 구조를 표현한 타입을 가져와서 합치기만 하면 된다. 그리고 &lt;code class=&quot;language-text&quot;&gt;combineReducers&lt;/code&gt; 함수가 리턴해야 하는 객체의 형태가 가변적이므로 generic type으로 &lt;code class=&quot;language-text&quot;&gt;RootState&lt;/code&gt;를 전달하고 있다.&lt;/p&gt;
&lt;h2 id=&quot;이렇듯-장황한-타이핑이-필요한-typescript&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%A0%87%EB%93%AF-%EC%9E%A5%ED%99%A9%ED%95%9C-%ED%83%80%EC%9D%B4%ED%95%91%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-typescript&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이렇듯 장황한 타이핑이 필요한 TypeScript&lt;/h2&gt;
&lt;p&gt;React, Redux에서 TypeScript를 사용하는 방법을 정리해보았다. 개인적으로 TypeScript, Flow를 모두 시도해 본 상황에서 다음 프로젝트에 TypeScript를 사용할지 말지를 고민하고 있다. 동적 타입의 언어는 유연하다는 장점도 있고 개발 진행 속도도 빠르기 때문이다. 인기 있는 언어인 php, python, ruby도 동적 타입 언어이기도 하다. 많은 팀원이 참여하고 있는 프로젝트라면 TypeScript를 도입해볼 만 하지만 타입 선언만 강제할 수 있을 뿐 서로의 코딩 스타일이 달라서 결국 표준 스타일의 정립과 많은 대화의 필요성은 마찬가지일 것 같다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;참고-자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h2&gt;
&lt;h3 id=&quot;react-redux-타입-설정&quot;&gt;&lt;a href=&quot;#react-redux-%ED%83%80%EC%9E%85-%EC%84%A4%EC%A0%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;React, Redux 타입 설정&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/piotrwitek/react-redux-typescript-guide&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/piotrwitek/react-redux-typescript-guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/piotrwitek/react-redux-typescript-starter-kit&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/piotrwitek/react-redux-typescript-starter-kit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;본문에서-사용한-전체-예제-코드&quot;&gt;&lt;a href=&quot;#%EB%B3%B8%EB%AC%B8%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%A0%84%EC%B2%B4-%EC%98%88%EC%A0%9C-%EC%BD%94%EB%93%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;본문에서 사용한 전체 예제 코드&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rhostem/react-typescript-starter&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://github.com/rhostem/react-typescript-starter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[[번역] 지난 5년 동안 소프트웨어 개발자로서 내가 배운 것들]]></title><description><![CDATA[이 글은  James Wright 의  What I’ve Learned Over 5 Years as a Software Developer 를 번역한 글입니다. 이 글을 쓰고 있는 시간은 2017년 7월 3일이다. 굳이 날짜를 언급하는 이유는 오늘이 내가 소프트웨어 엔…]]></description><link>https://blog.rhostem.com//posts/2017-10-29-what-i-ve-learned-over-5-years-as-a-software-developer</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-10-29-what-i-ve-learned-over-5-years-as-a-software-developer</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 29 Oct 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://read.acloud.guru/@jamesseanwright&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;James Wright&lt;/a&gt;의 &lt;a href=&quot;https://read.acloud.guru/what-ive-learned-over-5-years-as-a-software-developer-a5a8bf456b11&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;What I’ve Learned Over 5 Years as a Software Developer&lt;/a&gt;를 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/tfnfEoHGRqegoqcs8gos4/a7f650b49d338269bcbfbb9b59c11f52/main.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 40.68343004513218%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAQCAMAAABTCc2fAAACnVBMVEUeHh4oKCgkLjYmMDsmMj4uMjFPVEpBRD1BQjpFRj1BQjk5OzVAQTlCQzwyOT0qKywhIyQfHyAjJCMfHx8mJiYgICAhJiooNkUkLDM5SVI0Q0wsNDgzOj4yPUQ1RE0tNjsuMjQ0QEg4R1AxPEInLC81OTQnJycpKSkhJSkoN0UjKzE1Q0s2RE0zP0cwNjkzPEEyP0YrLzEyPEM4Rk46SE8tMjUuMjMpKykjIyMfISMiJywgIyUmKy4kKColJygkJSYoLC4oLjImKSsiIiInKCclIiU3LTc0KjMyKicoND8sMjUmLjYsNDsqMTYsMjkuOD4uOT8pMDUmMDovNzsjKTAoMzsvOT8wO0EsNz8wPEIiKS8qNT00QUgwPEMlKS0rN0IxPkUoLzQqKiogHyAfHh4hIiMhISIgISIfICEfICAhIiIfICIfISIgISEkLjglMDorLSxFRDk+PjVAQDdBQDgxPUQzQUkzQUg2RU40QkkyPkUtNjwyMjIhJCgiKC0vMzUyP0czQEg0QkorMTMtNz0yPkQ0Q0s1Q0ouOUA2RU0uNDckLDIzQEYwO0IvOkEtMTQsNTs1REwzQEcwNztBQDY8PDM6OjJAQDlHRztJST07OzI6PTszQEk4QkcuLS0lJSUsMTUsMzc2RU8wOT05OjI7OzY1NTI2NzUxOkAoLjM1RUwyODstLS4nMz8kKjE3RU00NC5DQzhCQjg9PTc2Q0k6SVE2QUg4SFEzP0YxOj8pKCghISEiJyspOEYmLzg7TFZBT1RDRT5KTUNDRkE4NDM5OTkhJSo6TFQxODs+REE3Rk44QkQ4OjYyMiwvLyopKSchJSgqMjYoLjErMzgoLTEnLTEqMjcuLiowMCsqKiglJSM8PDQ0NC06OjQlKCmlTB68AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAASxJREFUKM9jZGD8z8SIDt4yYAAWBuE3wsQqZBBCV8iATSEQCILkQAqu6vxhZWTcg0UdAyODyBtRAyTDbquBqetaX3iBBETssN31E0xAxb67Zc/IfJAGAklJSTtJyUMi4sI2gtLCu2z5tvJyc/ByurLfBSoTYSAKgNyYxzglF2bzXxYg0YJFIcjq5XyMfDAgxLaGl7cLq4lCb4AUL9TAfc68+UBqKsst9RuaQEYSkomVIOrzO2Fh4ad/BEOEhBgEf798y292zGICP38ScvCo3xQVbmVkDFoPNfQwgx0jYySWcFS/uRskn/RuI1idHw5fA2Pkv+jG5Pfr1wcx3rvtAVZ7IRFXzKgoloEUpMxlZPz+n5vR+g1WhWq3RI/ucWVkrGRg/9S/7PSNm9itBgDHfUx9X2+vuQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;main&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/tfnfEoHGRqegoqcs8gos4/a7f650b49d338269bcbfbb9b59c11f52/main.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/tfnfEoHGRqegoqcs8gos4/a7f650b49d338269bcbfbb9b59c11f52/main.png?w=388 388w,
https://images.ctfassets.net/rpmifyuylbfw/tfnfEoHGRqegoqcs8gos4/a7f650b49d338269bcbfbb9b59c11f52/main.png?w=776 776w,
https://images.ctfassets.net/rpmifyuylbfw/tfnfEoHGRqegoqcs8gos4/a7f650b49d338269bcbfbb9b59c11f52/main.png?w=1551 1551w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;이 글을 쓰고 있는 시간은 2017년 7월 3일이다. 굳이 날짜를 언급하는 이유는 오늘이 내가 소프트웨어 엔지니어로서 경력을 시작한 지 5년이 되는 날이기 때문이다.&lt;/p&gt;
&lt;p&gt;그 전에 나는 프리랜서로서 프로젝트도 경험했고 몇 가지 일도 한 적은 있지만 2012년의 오늘 Sky에서 졸업하기 전까지는 상업적인 일은 하지 않았었다.&lt;/p&gt;
&lt;p&gt;방종한 태도를 보이기보다는 지금껏 내가 배운 가장 중요한 5가지를 공유하려고 한다. 난 모든 개발자가 이 5가지 원칙을 받아들여야 한다고 믿는다.&lt;/p&gt;
&lt;h2 id=&quot;1-자기-자신을-깊은-곳까지-밀어넣어라&quot;&gt;&lt;a href=&quot;#1-%EC%9E%90%EA%B8%B0-%EC%9E%90%EC%8B%A0%EC%9D%84-%EA%B9%8A%EC%9D%80-%EA%B3%B3%EA%B9%8C%EC%A7%80-%EB%B0%80%EC%96%B4%EB%84%A3%EC%96%B4%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 자기 자신을 깊은 곳까지 밀어넣어라&lt;/h2&gt;
&lt;p&gt;나는 NOW TV Xbox 360팀에서 일하게 된 첫 번째 날을 기억한다. 나는 7개월간의 Graduate scheme(졸업생을 대상으로 하는 업무와 교육이 결합한 프로그램 - 역주)을 막 끝마쳤고 루비와 자바스크립트로 만든 몇 가지 조그만 프로젝트를 진행하고 있었다. 그에 비해 내가 들어간 새로운 팀에서는 C#, .NET, 그리고 Silverlight를 사용하고 있었다. 나는 졸업 전 마지막 학년에 C#, .NET, XAML을 약간 건드려 봤을 뿐 실무에서 통할 레벨은 아니었기에 대체 어디서부터 시작해야 할지 몰랐다.&lt;/p&gt;
&lt;p&gt;비록 동료들은 무척 친절했고 많은 도움을 주려고 했지만 내가 코드에 제대로 기여하기 위해서는 공부에 속도를 내야 한다는 사실을 깨달았다. 나는 일을 하면서 동시에:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;코드 베이스의 다양한 부분을 공부했고 이해가 안 되는 부분은 온라인에 검색해보았다&lt;/li&gt;
&lt;li&gt;시니어 레벨의 개발자와 함께 작업하며 새로운 기능을 효과적으로 구현하고 리팩토링하는 방법을 찾으려 노력했다&lt;/li&gt;
&lt;li&gt;C#, .NET, Silverlight, 디자인 패턴, 유닛 테스팅, 소프트웨어 개발 생명주기(SDLC)에 관한 다양한 책, 문서, 글을 읽었다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;물론 사람이 배울 수 있는 속도에는 한계가 있다. 이와 관련해서는 나중에도 언급하겠지만, 너무나 많은 정보가 한계에 이를 정도로 머릿속으로 들어왔다. 하지만 사람은 정말 신기한 동물인 것이, 괴물 같은 프로젝트 속에 던져지더라도 자연스럽게 자신이 할 수 있는 만큼 열심히 하게 된다. 그리고 사람은 새로운 환경 속에서 성장한다. 그 환경이 얼마나 어려운가와는 상관없이 말이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;만약 누군가가 당신에게 멋진 기회를 제공했지만, 그 일을 해낼 수 있다는 확신이 없을 때는, 그냥 하겠다고 해라! 그리고 어떻게 할지는 나중에 배우면 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;- Richard Branson&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;2-겸손하라&quot;&gt;&lt;a href=&quot;#2-%EA%B2%B8%EC%86%90%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 겸손하라&lt;/h2&gt;
&lt;p&gt;개발자로서 충분한 경험을 쌓게 되면, 아니면 어떤 다른 방법으로도, 결과적으로 자신감을 느끼게 된다. 그리고 그 사람은 오픈 소스 프로젝트, 다양한 형태의 커뮤니티, 그리고 글 등을 통해서 코드 베이스에서 예전보다 더 복잡한 부분에 일조할 수 있는 수 있는 위치에 서게 된다.&lt;/p&gt;
&lt;p&gt;그런 입장이 되면 자기와 같은 기술을 보유하고 있지 않거나, 자신만큼의 능력을 갖추지 못한 사람들을 깔보는 태도를 쉽게 가지게 될 수 있다. 간단한 질문에도 “아니, 그 쉬운 걸 왜 모르는 거지?!”라거나 “스크립트를 빌드하는 방법도 잊어버린 게 분명하구나” 같은 답변을 하기 쉽다. 하지만 분명히 기억해야 한다. 당신도 예전엔 제대로 못 하던 시절이 있었다. 질문을 던지는 사람들에게는 같은 눈높이에서 친절하고 겸손하게 대답해줘야 한다. 한때는 같은 처지였다는 것을 공감하자!&lt;/p&gt;
&lt;p&gt;겸손의 다른 중요한 한 면은 틀렸을 때 인정하는 일이다. 나는 개념을 잘못 이해했다는 사실을 인정하거나 본인이 만든 문제에 대한 책임을 스스로 지기보다는 그 상황을 회피해버리려는 동료들을 많이 접해 왔다. “너는 내 코드를 이해할 능력이 없어! 내가 틀린 것이 아니야.” 뭐, 그렇게 주장할 수는 있다.&lt;/p&gt;
&lt;h2 id=&quot;3-타인의-얘기를-잘-듣는-열린-자세를-가져라-하지만-자기주장을-펴는-걸-두려워하지는-마라&quot;&gt;&lt;a href=&quot;#3-%ED%83%80%EC%9D%B8%EC%9D%98-%EC%96%98%EA%B8%B0%EB%A5%BC-%EC%9E%98-%EB%93%A3%EB%8A%94-%EC%97%B4%EB%A6%B0-%EC%9E%90%EC%84%B8%EB%A5%BC-%EA%B0%80%EC%A0%B8%EB%9D%BC-%ED%95%98%EC%A7%80%EB%A7%8C-%EC%9E%90%EA%B8%B0%EC%A3%BC%EC%9E%A5%EC%9D%84-%ED%8E%B4%EB%8A%94-%EA%B1%B8-%EB%91%90%EB%A0%A4%EC%9B%8C%ED%95%98%EC%A7%80%EB%8A%94-%EB%A7%88%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 타인의 얘기를 잘 듣는 열린 자세를 가져라. 하지만 자기주장을 펴는 걸 두려워하지는 마라.&lt;/h2&gt;
&lt;p&gt;겸허함의 관점에서는 이미 알고 있는 개념에 대해 다른 관점이나 완전히 새로운 정보에 대해 열린 자세를 가지는 것은 중요하다. 후자가 자연스러운 태도로 보일 수 있지만, 소수의 사람은 기꺼이 새로운 것을 적극적으로 받아들여서 결과적으로는 자기 생각을 완전히 바꾸기도 한다. 나의 온순한 태도는 타인의 생각을 바꾸게 만들 수 있고, 그 사람은 앞으로 만날 비슷한 상황에도 그렇게 나로 인해 변화한 생각으로 대처하게 된다.&lt;/p&gt;
&lt;p&gt;즉, 내가 어떤 분야의 특정한 문제에 어떤 위험과 대처법이 있는지 잘 알고 있다면 의견을 강하게 주장해도 전혀 문제 될 것이 없다는 말이다. 확신을 가지고 제대로 설득한다면 동료에게 새로운 정보에 관심을 끌게 만들 수 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;단정적인 태도는 당신을 재수 없는 인간으로 만들지 않는다.&lt;/p&gt;
&lt;p&gt;- 나&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;4-변화를-받아들여라-하지만-시류에-편승하지는-마라&quot;&gt;&lt;a href=&quot;#4-%EB%B3%80%ED%99%94%EB%A5%BC-%EB%B0%9B%EC%95%84%EB%93%A4%EC%97%AC%EB%9D%BC-%ED%95%98%EC%A7%80%EB%A7%8C-%EC%8B%9C%EB%A5%98%EC%97%90-%ED%8E%B8%EC%8A%B9%ED%95%98%EC%A7%80%EB%8A%94-%EB%A7%88%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 변화를 받아들여라, 하지만 시류에 편승하지는 마라.&lt;/h2&gt;
&lt;p&gt;신입 개발자에게 기술이 변하는 속도는 마치 도저히 따라잡을 수 없을 것처럼 빠르게 느껴진다. 나는 이것이 반드시 그런 것만은 아니라고 주장함으로써 반박하고 싶다.&lt;/p&gt;
&lt;p&gt;자바스크립트 세계에서 새로운 라이브러리와 프레임워크가 주기적으로 등장하는 것은 무척 자명한 일이다. 그리고 어떤 프로그래머(힙스터라고 불리는)들은 그것들을 특정한 상황에 적용할 수 있는지 깊이 생각하지 않고 빠르게 적용해 버린다. 그리고 개발자로서의 가치를 높이기 위해 새로운 기술을 따라잡아야 한다는 의무를 수행했다고 느끼곤 한다.&lt;/p&gt;
&lt;p&gt;처음엔 나도 이런 압박감을 느꼈다. 하지만 다른 시각에서 이걸 바라보면 그런 부담을 완전히 떨쳐버릴 수 있다. 나는 새로운 프로젝트를 시작할 때마다 도움이 될만한 기술을 알아보는데, 다음의 질문들에 대한 답을 바탕으로 조사한다.&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;이건 결코 배타적인 조건들이 아니다. 하지만 나는 이런 마음가짐이 올바른 방향으로 이끌어줬다는 사실을 확인했다. 솔직히 나는 React에 대해 처음에는 무척 냉소적인 태도를 가지고 있었다. “자바스크립트 안에서 XML을 쓴다고?! 이건 분명 DOM의 무의미한 추상화일 뿐이야.” 그리고 나는 새로운 앱 개발을 맡았고, 많은 양의 리스트가 다양한 사용자 입력에 대응해서 새로 렌더링 되게 해야 했다. 그 작업을 통해 나는 React가 이런 종류의 앱 개발에서는 무척 훌륭한 라이브러리라는 사실을 알게 되었고 내가 얼마나 잘못된 생각을 가졌었는지도 깨닫게 되었다.&lt;/p&gt;
&lt;p&gt;누구나 새로운 것을 적극적으로 받아들이고 적절히 사용할 수 있도록 해야 한다. 하지만 단지 새롭고 인기가 있다고 해서 사용해야 할 의무가 있는 것 아니다. 소프트웨어 엔지니어링의 주요한 목표는 &lt;em&gt;소프트웨어를 잘 만들어서 전달하는 일&lt;/em&gt;이다. 그 과정에서 잘못된 기술을 선택하는 것은 그 목표를 달성하는 데 방해가 될 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;5-일과-놀이의-균형&quot;&gt;&lt;a href=&quot;#5-%EC%9D%BC%EA%B3%BC-%EB%86%80%EC%9D%B4%EC%9D%98-%EA%B7%A0%ED%98%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 일과 놀이의 균형&lt;/h2&gt;
&lt;p&gt;대학에서 처음으로 ‘Hello World’를 출력하는 프로그램을 만든 후, 나는 거의 프로그래밍에 중독되다시피 했다. 처음에 내 실력은 별것 아니었고 코드도 무척 지저분했다(하지만 배움의 과정이니까 괜찮다) 업계에서 일하기 시작한 후 시간이 지나면서 내 실력은 향상했다. 하지만 나는 저녁과 주말을 모두 사이드 프로젝트를 진행하는데 할애하곤 했다. 난 그게 무척 좋았다. 난 내가 정말로 하고 싶은 일을 찾았다고 느꼈고 멈출 수가 없었다.&lt;/p&gt;
&lt;p&gt;이런 활동 중 일부는 성공적인 오픈소스 프로젝트가 되었고 코드 베이스에도 많은 이바지를 했다. 그리고 그것은 나에게 큰 자부심을 줬기에 지금까지 해 온 활동을 계속하게 되었다. brilliantJS13games 이나 JS1k 같은 자바스크립트 대회에도 참가했고 SitePoint에 스크린 캐스트도 꾸준히 올렸다. 심지어 몇몇 큰 기술 회사와 면접도 보았기에 많은 연습과 공부가 병행되었다.&lt;/p&gt;
&lt;p&gt;그리고 이 모든 일이 끝나고 난 후, 나는 완전히 탈진(burnout) 상태가 되어버렸다. 프로그래밍을 위한 동기를 완전히 잃어버렸고 여유 시간이 생겨도 프로그래밍을 하고 싶은 마음이 들지 않았다. 게다가 때때로 기술과 프로그래밍에서 벗어나게 해 주었던 나의 취미들은 뒷전으로 밀려나 있었다. 난 그저 코드 작성 기계일 뿐이었다.&lt;/p&gt;
&lt;p&gt;기회가 있을 때 도전하는 것은 중요하다. 이는 내가 개발자로서 성장할 수 있게 할 뿐만 아니라 경력도 키워주었다. 하지만 휴식은 필수다. 당신은 인간이기에 우리의 진화론적인 기원을 생각해 볼 때 충분한 휴식이 없다면 추상적인 지식과 일을 배울 수 있는 능력은 자연스럽게 줄어들게 된다.&lt;/p&gt;
&lt;p&gt;만약 탈진 증후군이 나타날 것 같다면 잠시 일에서 물러서서 쉬어야 한다. 단지 1~2주일을 쉬라는 것이 아니라 한 달에서 두 달 정도의 휴식을 얘기하는 것이다. 믿어보길 바란다. 새로운 직장에 지원할 준비가 되었다면 본인 스스로 알게 될 것이다. 그리고 당신이 지금까지 만들어 왔던 프로젝트는 어디에도 가지 않는다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Intersection Observer API와 lozad.js 로 이미지 지연 로딩하기]]></title><description><![CDATA[이미지가 많은 웹사이트에서는 브라우저 화면상에 표시되지 않은 영역의 이미지의 로딩을 의도적으로 지연시켜서 네트워크 트래픽과 성능에 도움을 주는 방법을 많이 사용한다. 그런데 지연 로딩(lazy loading)이라고 불리는 이 기법을 구현하는 전통적인 방식에는 문제가 …]]></description><link>https://blog.rhostem.com//posts/2017-10-21-lazy-image-loading-with-lodzad-js</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-10-21-lazy-image-loading-with-lodzad-js</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 21 Oct 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이미지가 많은 웹사이트에서는 브라우저 화면상에 표시되지 않은 영역의 이미지의 로딩을 의도적으로 지연시켜서 네트워크 트래픽과 성능에 도움을 주는 방법을 많이 사용한다.&lt;/p&gt;
&lt;p&gt;그런데 지연 로딩(lazy loading)이라고 불리는 이 기법을 구현하는 전통적인 방식에는 문제가 있다. 브라우저 스크롤 이벤트에 리스너를 붙여서 현재 스크롤의 위치와 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Element/getBoundingClientRect&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;getBoundingClientRect&lt;/a&gt;같은 API를 사용해서 얻은 엘레멘트의 위칫값을 주기적으로 비교해야 하기 때문이다.&lt;/p&gt;
&lt;p&gt;최근 브라우저에 추가된 API인 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Intersection Observer API&lt;/a&gt;를 사용하면 이처럼 리소스 소모가 많은 방식을 대체할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;intersection-observer-api&quot;&gt;&lt;a href=&quot;#intersection-observer-api&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Intersection Observer API&lt;/h2&gt;
&lt;p&gt;Intersection Observer API는 이름 그대로 브라우저 뷰포트와 엘레멘트의 교차점(intersection)과 관련된 기능이다. 뷰포트에 엘레멘트가 표시되면 옵저버는 이벤트를 발생시키기 때문에 개발자는 콜백 함수에서 원하는 작업을 수행할 수 있다.&lt;/p&gt;
&lt;p&gt;사용법은 그렇게 복잡하지 않다. 옵션과 함께 옵저버 인스턴스를 생성하고 탐지할 엘레멘트를 지정하면 된다. 상세한 사용법은 MDN의 API 문서를 살펴보면 알 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  root&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;#scrollArea&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// null은 브라우저 전체 영역&lt;/span&gt;
  rootMargin&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;10% 10px&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 요소가 감지되는 영역을 추가하거나 줄일 수 있다. CSS margin 같은 속성에 사용하는 축약형처럼 작성할 수 있다.&lt;/span&gt;
  threshold&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 콜백을 실행시킬 지점. 0은 보이자마자, 1은 모두 표시된 후&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;callback&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; observer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// entry는 탐지된 엘레멘트의 정보를 가지고 있다.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ex)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//    entry.isIntersecting,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//    entry.boundingClientRect&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; observer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IntersectionObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;callback&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 옵저버 인스턴스 생성&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;#listItem&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 탐지할 엘레멘트&lt;/span&gt;

observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;lozadjs로-간단하게-intersection-api-사용하기&quot;&gt;&lt;a href=&quot;#lozadjs%EB%A1%9C-%EA%B0%84%EB%8B%A8%ED%95%98%EA%B2%8C-intersection-api-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;lozad.js로 간단하게 Intersection API 사용하기&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.npmjs.com/package/lozad&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;lozad.js&lt;/a&gt;는 이미지 지연 로딩에 특화된 Intersection API 구현 사례라고 할 수 있다. 복잡한 설정 필요 없이 이미지 태그에 라이브러리가 요구하는 속성을 추가한 후 옵저버 인스턴스를 생성하면 이미지 지연 로딩이 간단히 구현된다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;image&lt;/code&gt; 태그에는  &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt; 속성 대신  &lt;code class=&quot;language-text&quot;&gt;data-src&lt;/code&gt;에 이미지의 경로를 할당하고 &lt;code class=&quot;language-text&quot;&gt;lozad&lt;/code&gt;라는 이름의 클래스 이름을 추가해야 한다. (클래스명은 인스턴스 옵션으로 바꿀 수 있다)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lozad&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; observer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lozad&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 옵션을 지정하지 않으면 lozad라는 클래스가 붙은 이미지 태그를 지연 로딩한다.&lt;/span&gt;
observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이것이 전부다. lozad.js는 앞서 설명한 Intersection Observer를 사용하는 과정에서 타겟 요소를 설정하고 이미지 불러오는 로직을 자동으로 처리해주기에 지연 로딩을 무척 간단히 구현할 수 있다. 물론 Intersection API에서 제공하는 &lt;code class=&quot;language-text&quot;&gt;rootMargin&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;threshold&lt;/code&gt;, 콜백 함수도 사용할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;예제&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%A0%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예제&lt;/h2&gt;
&lt;p&gt;lozad.js를 사용해 간단히 만들어 본 예제 페이지에서는 이미지가 로드된 후 콜백 함수에서 페이드인 효과를 추가하고, 토스트 메시지를 표시하도록 했다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rhostem.github.io/lozad-example&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://rhostem.github.io/lozad-example&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;polyfill-설치&quot;&gt;&lt;a href=&quot;#polyfill-%EC%84%A4%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;polyfill 설치&lt;/h2&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5uuvfSDlscikGk0uokkA0i/a63376ee869fd9461a8f0dea53d4d82b/caniuse-intersection-api.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 35.181451612903224%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAOCAIAAADSackRAAABdWlDQ1BpY2MAAHjarY4xS1tRGIafE2NVDBhKsA4iBxTpcFNSHTTWJSZgIg4SLSTZbm5uonCTHG5OSPsD3Fx0EF0U9S+ILg6O4qCDIAjB3yAIgpRwO1w0U9ul7/R8z/B+LwS0qZQTBKo17WYXF2QuX5B9bQYJ8ZE5PplWQyVWVpb5Y17uEQB3UVMpp5x5fZjfbB2efljaH5vTBn/PYMluWCD6AavUsKogHMCwlKtB7AHRllYaxBUQcXP5Aog2EKn4/AREirl8AQJBIOKuZZMQGAHCRZ8/A+GKz7NA2Fo3SxBIA4a/AYCBdErOxOLRBP85Vaf59kMAIbv2fRUIA6OkSSGZIUacKAlt/9AAybr66W5U1rVMKOXYMlmvqqa2XUNmatYXQ07FvsYAcvmC9KufswhADN92Xf0I4uPQs9V1xV04v4CR666bOIChb3B2o0zXfB8uXoL/uhvl6SmfQwvQ++h5z5PQtwOdbc/7dex5nRPoacOl8xskd2nglz68fAAAAAlwSFlzAAAXEQAAFxEByibzPwAAAAd0SU1FB+YMGw8eHfI6e5sAAANYSURBVDjLrVQ7i11lFN37e53XPXPPPXPnShIimImPJhZCHIsoWmhhK0gQQW3shHT+AQsbCysRBgbEAQsDFv6AOImiiBrFIBITR4mOzn2fe17f22Igo4xJjMyqFt9a7PW99sbZjW8AoKqbJI4AQEo5m5cr/QysK2qptep2U8H5ZDJDQqIoDANRFKUyup/3AGA6nXvv8zxrGtk0jbU2jULpnJYaEQPOkNGirMDBYLk7nBUEibM262WsqNSkaK01dK4FI63UdaMaW2ZcedWA0bUuRyaZzxtEIKTqDaKqbhDRmLFQqiprQnBc1YXzSikAkNqwRYmAhBITBSNfVHWLCDxlo5JWdYuIQUpw/OtXWhsAAABKiHXuIN9Z35h+9gVLElMtHlp/xxgLAISg4MJ5DwCICADeewAghHjn/G0LMkYZ/AfQKGRph3d7QOCwwNrtX0Zbl5ALr1X/mafpcn7Q9O2D4fc8Eswb13ntsILV7nB68VPWWTJlkT229q/B28fE11HSFZ2FKm9VqPnpmm0lAPC0E9x7/M7B4L03FgC8seD9voL71HnnARCJ8RYB9170Hw6Anfc2waFt6/DYkePn7nwvzDvnpEQkTsqP/ry4WawPopXdZvhW+ZTeOB/0+/Nq0p47o7xprZJOfffGm/Or15EQpOSJjXf3N6cUWPBKO61vLnpjrrzyqlju27rKn3x8cPb5/XNdfv/tnzc/ICJwSm6dXrlywgQ0kFau/iYsaThhyqoqSSeZZkiN1c9+OIuLBQBoRq++tOY7jCLaSj96/ofIOgBfhOHy0SOMEPAwRP/JYEfQQDsVuhQeGIRUtFY90jnJgDHW7bI4NnVNPO3xKA+yiZyNw7ZeifIgG7WTeGgiGoQ0KOxCW0uE2Gue6+XvjEeU0LYs15KYUwYAzlo1HLIsN+WiAluczvOgV+tF84dSatILsqmaFarEvcl1EB/vfn5hfDnl6UzNH1468WN1I2JRpcqXL/Hi2jbhnHC29eLJsSkp0oSI11fP3t2vvpUgkMU0TFmkrQqJiGkYEgEsuv+F55w2AIAEv5xeiJwmSGIa3HU73V6WTu0R66wECQA8SW6qZ/BU4xR4HxB+aMG5WFqNj6Y8mavFPUHPgg+IaEzzd8+p9L7/PUD+Am7ByaSGj3P+AAAAMnRFWHRpY2M6Y29weXJpZ2h0AENvcHlyaWdodCBBcHBsZSBDb21wdXRlciwgSW5jLiwgMjAxMB2TuWIAAAAYdEVYdGljYzpkZXNjcmlwdGlvbgBIRCA3MDktQbLbWvEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;caniuse-intersection-api&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5uuvfSDlscikGk0uokkA0i/a63376ee869fd9461a8f0dea53d4d82b/caniuse-intersection-api.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5uuvfSDlscikGk0uokkA0i/a63376ee869fd9461a8f0dea53d4d82b/caniuse-intersection-api.png?w=248 248w,
https://images.ctfassets.net/rpmifyuylbfw/5uuvfSDlscikGk0uokkA0i/a63376ee869fd9461a8f0dea53d4d82b/caniuse-intersection-api.png?w=496 496w,
https://images.ctfassets.net/rpmifyuylbfw/5uuvfSDlscikGk0uokkA0i/a63376ee869fd9461a8f0dea53d4d82b/caniuse-intersection-api.png?w=992 992w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;&lt;a href=&quot;http://caniuse.com/#feat=intersectionobserver&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;http://caniuse.com/#feat=intersectionobserver&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;2017년 10월 현재 크롬 최신 버전에서는 사용할 수 있지만 사파리 브라우저는 데스크탑, 모바일 모두 지원하지 않는다. 많은 웹 브라우저가 지원하지 않기 때문에 &lt;a href=&quot;https://github.com/w3c/IntersectionObserver/tree/master/polyfill&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;polyfill&lt;/a&gt; 설치가 필수적이다.&lt;/p&gt;
&lt;p&gt;polyfill 설치를 위해서는 스크립트 직접 추가하거나, NPM 모듈을 설치한 후 추가하는 방법이 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- 메인 스크립트보다 먼저 불러와야 한다. --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;path/to/intersection-observer.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 가능하면 entry 파일에서 다른 모듈보다 먼저 불러와야 한다.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;intersection-observer&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;참조&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EC%A1%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참조&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Intersection Observer API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/w3c/IntersectionObserver/tree/master/polyfill&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Intersection Observer polyfill&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/lozad-js-performant-lazy-loading-images/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Lozad.js: Performant Lazy Loading of Images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://tech.lezhin.com/2017/07/13/intersectionobserver-overview&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;IntersectionObserver를 이용한 이미지 동적 로딩 기능 개선&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[[번역] 자바스크립트에 대한 애정을 언어의 90%를 쓰레기통에 버리면서 다시 발견했던 과정]]></title><description><![CDATA[이 글은  Joel Thoms 의  How I rediscovered my love for JavaScript after throwing 90% of it in the trash 를 번역한 글입니다. 나와 자바스크립트의 관계 자바스크립트와 함께하는 여행의 시작은 19…]]></description><link>https://blog.rhostem.com//posts/2017-09-how-i-rediscovered-my-love-for-java-script-after-throwing-90-of-it-in-the-trash</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-09-how-i-rediscovered-my-love-for-java-script-after-throwing-90-of-it-in-the-trash</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Fri, 08 Sep 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://hackernoon.com/@joelthoms?source=post_header_lockup&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Joel Thoms&lt;/a&gt;의 &lt;a href=&quot;https://hackernoon.com/how-i-rediscovered-my-love-for-javascript-after-throwing-90-of-it-in-the-trash-f1baed075d1b&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;How I rediscovered my love for JavaScript after throwing 90% of it in the trash&lt;/a&gt;를 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;나와-자바스크립트의-관계&quot;&gt;&lt;a href=&quot;#%EB%82%98%EC%99%80-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EA%B4%80%EA%B3%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;나와 자바스크립트의 관계&lt;/h2&gt;
&lt;p&gt;자바스크립트와 함께하는 여행의 시작은 1997년 Netscape Navigator 3가 있던 시절로 거슬러 올라간다. 그때는 자바스크립트로 할 수 있는 일이 많지 않았다. &lt;strong&gt;자바스크립트로 할 수 있는 가장 멋진 일은 마우스 오버 효과를 만드는 것이었다&lt;/strong&gt;. 그 시절의 그 정도는 제법 고급 기술이었다. 단순히 마우스 포인터를 가져다 대는 것만으로 페이지의 콘텐츠를 바꾼다는 것은 당시로서는 정말로 멋진 기능이었다. 그리고 DHTML이 등장하기 전이었기에 DOM 요소를 숨기고 보여주는 일도 여전히 마법같은 일이었다.&lt;/p&gt;
&lt;p&gt;그 시절에 자바스크립트의 성장은 매우 느렸고, 자바스크립트는 입력 양식(form) 유효성 검사에 주로 사용되고 있었다. 자바스크립트에 향한 관심이나 발전은 지금처럼 크지 않았다. 그저 있으면 좋은 부가적인 요소였다. 자바스크립트는 개발에 있어 주요 고려 대상이 결코 아니었고 애플리케이션이 자바스크립트 없이도 확실히 동작할 수 있도록 보장해야 했다.&lt;/p&gt;
&lt;p&gt;그런 시절이 있은 후에 자바스크립트 프레임워크들이 대거 등장했다. jQuery, Knockout, Angular, React, Vue, 기타 등등. 처음에는 그들에 대한 반응이 그저 그랬었다. 하지만 이제 우리는 매일매일 새로운 프레임워크를 만나는 중이다.&lt;/p&gt;
&lt;p&gt;또한 &lt;strong&gt;자바스크립트 자체의 진화 속도도 점점 빨라지고 있다&lt;/strong&gt;. 우리는 이제 ES6를 앞으로도 계속 사용할 수 있고 사람들은 관심사는 ES7에서 이미 ES8으로 넘어간 상태다!&lt;/p&gt;
&lt;p&gt;그리고 TypeScript, CoffeeScript, ClojureScript, ELM 처럼 자바스크립트의 무한한 대안을 가지고 있다.&lt;/p&gt;
&lt;p&gt;이들의 발전 속도는 너무나 빠르고 압도적이라 모든 것을 따라잡기는 불가능하다.&lt;/p&gt;
&lt;h2 id=&quot;잘못된-길&quot;&gt;&lt;a href=&quot;#%EC%9E%98%EB%AA%BB%EB%90%9C-%EA%B8%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;잘못된 길&lt;/h2&gt;
&lt;p&gt;자바스크립트가 처음 성숙해지기 시작했을 때 객체 지향 프로그래밍(OOP)이 자바스크립트 안으로 슬며시 비집고 들어왔다… 그리고 나는 그걸 무척 좋아했다.&lt;/p&gt;
&lt;p&gt;나는 가능한 모든 방법으로 클래스를 만들며 자바스크립트를 갖고 놀기 시작했다. 그리고 마침내 객체의 상속을 제대로 구현할 수 있었다. 나는 생각했다. 자바스크립트는 마침내 &lt;em&gt;진정한&lt;/em&gt; 언어가 되었다고 말이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;자바스크립트에 OOP를 사용하는 것이 끔찍한 실수라는 사실을 깨닫기까지 그리 오랜 시간이 걸리지 않았다.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;내가 &lt;strong&gt;C#을 통해 알게 된 것들을 자바스크립트에도 강제하려 했고&lt;/strong&gt; 처음에는 그것이 무척 괜찮아 보였다. 하지만 그로 인한 복잡도는 정신이 혼미해질 정도였다.&lt;/p&gt;
&lt;p&gt;왜냐하면, 자바스크립트의 프로토타입 상속은 C#처럼 작동하지 않기 때문이다. 나는 매일매일 &lt;code class=&quot;language-text&quot;&gt;console.log(this)&lt;/code&gt;를 쓰고 있는 나 자신을 발견할 수 있었다. &lt;em&gt;나는 누구, 지금 여기는 어디인가?&lt;/em&gt; 객체지향 자바스크립트로 프로그래밍을 하면서 모든 것을 정확히 하지 않는다면 악몽 같은 일이 벌어진다. Private 메소드와 밸류에는 접두어로 밑줄(_)이 들어가거나 더 심한 경우 클로져 안에 래핑되어야 했다.&lt;/p&gt;
&lt;p&gt;객체지향 자바스크립트는 OOP의 모든 문제를 가져왔을 뿐만 아니라 그 위에 새로운 문제를 추가해버렸다.&lt;/p&gt;
&lt;h2 id=&quot;그리고-나는-함수형-프로그래밍을-발견했다&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EB%82%98%EB%8A%94-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%84-%EB%B0%9C%EA%B2%AC%ED%96%88%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그리고 나는 함수형 프로그래밍을 발견했다&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;처음에는 몰랐다.&lt;/strong&gt; 코드를 읽을 수 있고 이해할 수는 있지만 &lt;em&gt;왜&lt;/em&gt; 그래야 하는지는 알 수 없었다. 결국, 나는 함수형 프로그래밍을 배우도록 나 자신을 강제했다. EDX에서 무료로 &lt;a href=&quot;https://www.edx.org/course/introduction-functional-programming-delftx-fp101x-0&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Introduction to Functional Programming course&lt;/a&gt;를 등록하고 강의에서 배운 기술을 자바스크립트에 접목하려 시도했다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;함수형 프로그래밍은 나에게 새로운 관점을 제시했다. 그것은 프로그래밍을 무척 다른 방식으로 접근하도록 만들어 주었다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;처음에는&lt;/em&gt; 제법 이상해 보여서 익숙해지기까지 시간이 좀 걸렸다. 모든 것은 거꾸로 돌아갔고, 불변(immutable)이었고, 이질적이었다.&lt;/p&gt;
&lt;p&gt;천천히, 코드를 함수형 방식으로 풀어내려 시도했다. 익숙하지 않았기 때문에 시간이 더 걸렸고 더 많은 학습이 필요했다. 하지만 결국 조금씩 파악이 되었고 익숙해지기 시작했다. 그리고 마침내 &lt;em&gt;왜&lt;/em&gt; 그렇게 해야 하는지 알 수 있었다.&lt;/p&gt;
&lt;p&gt;나의 코드는 단순해졌고 재사용이 가능해졌다. 일반적인 언어의 특징이 내 코드에서 천천히 사라지기 시작했다. 내 코드는 완전히 다른 언어처럼 보였다. 내가 작성하고 있는 코드가 여전히 자바스크립트인가?&lt;/p&gt;
&lt;h2 id=&quot;더-이상의-var는-없다&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%EC%9D%B4%EC%83%81%EC%9D%98-var%EB%8A%94-%EC%97%86%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 이상의 ‘var’는 없다&lt;/h2&gt;
&lt;p&gt;나의 모든 &lt;code class=&quot;language-text&quot;&gt;&amp;#39;var&amp;#39;&lt;/code&gt;는 &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt;로 대체되었다. 내 코드가 &lt;strong&gt;불변성&lt;/strong&gt;(immutable)을 가지고 함수가 &lt;strong&gt;순수&lt;/strong&gt;(pure)해 졌을 때 &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;는 마침내 완전히 사라졌다.&lt;/p&gt;
&lt;p&gt;내 코드에서 &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;는 물론이고 &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;이 모두 &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt;로 바뀐 모습을 보며 놀라워하던 순간이 떠오른다. 함수형 프로그래밍에 대한 흥미가 깊어지기 시작했다.&lt;/p&gt;
&lt;h2 id=&quot;for-루프&quot;&gt;&lt;a href=&quot;#for-%EB%A3%A8%ED%94%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;‘for’ 루프&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; 루프는 가장 먼저 해결해야 할 것 중의 하나였다. 코드에서 &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; 루프를 &lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;filter&lt;/code&gt;, 그리고 &lt;code class=&quot;language-text&quot;&gt;reduce&lt;/code&gt;로 대체하기 시작했다. 루프 중에서 특별한 동작이 필요한 것은 재귀를 사용하거나 &lt;a href=&quot;http://danieltao.com/lazy.js/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;lazy.js&lt;/a&gt;를 사용했다. &lt;code class=&quot;language-text&quot;&gt;break&lt;/code&gt; 키워드는 어떻게 하냐는 의문이 들었다면 &lt;a href=&quot;https://hackernoon.com/rethinking-javascript-break-is-the-goto-of-loops-51b27b1c85f8&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;이 글&lt;/a&gt;을 읽어보길 바란다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://hackernoon.com/rethinking-javascript-death-of-the-for-loop-c431564c84a8&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Rethinking JavaScript: Death of the For Loop&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; 루프는 이제 나의 코드 베이스에서 완전히 사라졌다. 만약 발견한다면 내가 없애버릴 수 있게 꼭 알려주길 바란다.&lt;/p&gt;
&lt;h2 id=&quot;if-문&quot;&gt;&lt;a href=&quot;#if-%EB%AC%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;‘if’ 문&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt; 문은 해결해야 할 다음 과제다. 나는 if~else 블럭 안에서 중첩되는 거대한 코드 블럭을 작성하는 일을 그만두었다.(이 또한 OOP에서는 권장되는 방식이다) 로직은 함수로 추출되었고 &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt;를 간단한 &lt;code class=&quot;language-text&quot;&gt;ternary&lt;/code&gt;(삼항) 연산자로 전환할 수 있었다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://hackernoon.com/rethinking-javascript-the-if-statement-b158a61cd6cb&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Rethinking JavaScript: The if statement&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이제 내 코드에서 &lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt;를 찾는 일은 거의 불가능해졌다. 앞으로 다른 프로그래머들의들 가독성을 위해서 거의 사용하지 않을 것이다.&lt;/p&gt;
&lt;h2 id=&quot;삼가-switch에게-조의를&quot;&gt;&lt;a href=&quot;#%EC%82%BC%EA%B0%80-switch%EC%97%90%EA%B2%8C-%EC%A1%B0%EC%9D%98%EB%A5%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;삼가 ‘switch’에게 조의를&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;if&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt;는 사라졌으니 다음 목표는 &lt;code class=&quot;language-text&quot;&gt;switch&lt;/code&gt; 문이다. 자주 사용되지는 않지만 나는 함수형 대안을 사용하고 싶었다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://hackernoon.com/rethinking-javascript-eliminate-the-switch-statement-for-better-code-5c81c044716d&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Rethinking JavaScript: Eliminate the switch statement for better code&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;나는 &lt;code class=&quot;language-text&quot;&gt;switch&lt;/code&gt;를 대체할 수 있는 Ramda의 &lt;a href=&quot;http://ramdajs.com/docs/#cond&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;cond 연산자&lt;/a&gt;를 정말로 좋아한다.&lt;/p&gt;
&lt;h2 id=&quot;더-이상의-this는-없다&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%EC%9D%B4%EC%83%81%EC%9D%98-this%EB%8A%94-%EC%97%86%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 이상의 ‘this’는 없다&lt;/h2&gt;
&lt;p&gt;그렇다. 제대로 읽은 것이 맞다! &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 완전히 사라졌다. 만약 지금까지 내 생각에 동의하지 못했더라도 이제는 나와 함께해야 한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;함수형 자바스크립트는 애플리케이션을 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; 없이 작성할 수 있도록 해준다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이제 데이터와 함수만이 남았고 우리에게 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 완전히 필요없어졌다. 나는 객체를 변경 가능한(mutable) 상태에 함수가 합쳐진 것으로 생각하기 시작했다. 나는 변경 가능한 상태도 필요하지 않으며 객체에 붙어있는 함수도 필요하지 않다. 그래서 나는 그 둘을 떼어내 버렸다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://hackernoon.com/functional-javascript-decoupling-methods-from-their-objects-aa3ca13d7ae8&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Functional JavaScript: Decoupling methods from their objects&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;객체지향-디자인은-필요하지-않다&quot;&gt;&lt;a href=&quot;#%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EB%94%94%EC%9E%90%EC%9D%B8%EC%9D%80-%ED%95%84%EC%9A%94%ED%95%98%EC%A7%80-%EC%95%8A%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;객체지향 디자인은 필요하지 않다&lt;/h2&gt;
&lt;p&gt;돌이켜 보니, 나는 OOP가 복잡도를 필요없이 높인다는 사실을 알게 되었다. 변경 가능한 상태 없이 동일한 작업을 실행할 수 있다는 사실을 알 수 있었다.&lt;/p&gt;
&lt;p&gt;더 이상 무거운 객체를 전달할 필요가 없기 때문에 코드는 한결 가벼워진 느낌이 든다. 코드에는 단지 데이터와 함수만 있을 뿐이다. 그 함수들은 객체에 묶여있지 않기 때문에 이제 예전보다 재사용성이 높아졌다.&lt;/p&gt;
&lt;p&gt;나는 이제 자바스크립트가 제대로 지원하지 않는 전통적인 상속이 가져다주는 문제에 대해 더는 걱정할 필요가 없다.&lt;/p&gt;
&lt;p&gt;자바스크립트가 private, public, internal, 또는 protected 같은 접근 제한 수식어를 제대로 지원하지 않는다는 것은 더 이상 문제가 되지 않았다. 접근 제한 수식어라는 것은 OOP가 만들어내는 문제를 해결하기 위해 만들어졌다. 그 문제들은 함수형 자바스크립트에서는 더 이상 존재하지 않는다.&lt;/p&gt;
&lt;h2 id=&quot;요약&quot;&gt;&lt;a href=&quot;#%EC%9A%94%EC%95%BD&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;요약&lt;/h2&gt;
&lt;p&gt;내 코드는 이제 완전히 다르게 보인다. 많은 &lt;strong&gt;순수 함수&lt;/strong&gt;(pure functions)들의 모임이 있고 그것들은 &lt;strong&gt;ES6 모듈&lt;/strong&gt;로 조직화된다. 나는 이 함수들을 조합해서 더 복잡한 함수를 만든다. 대부분의 함수는 &lt;strong&gt;값을 즉시 반환하는 한줄로 된 람다 표현식&lt;/strong&gt;으로 되어 있다.&lt;/p&gt;
&lt;p&gt;이제 나는 소프트웨어의 입력을 데이터의 스트림으로 간주하며 그 스트림에 반응적으로(reactively) 프로그래밍한다.&lt;/p&gt;
&lt;p&gt;함수형 프로그래밍에 대한 이해는 일반적인 문제를 해결할 수 있는 더 많은 선택지를 제공했다.&lt;/p&gt;
&lt;p&gt;또한 나는 함수형 프로그래밍은 포괄적이기에 현재 진행중인 프로젝트에 충분히 사용하거나, 필요한 만큼만 적게 사용될 수도 있다는 사실을 배웠다. C#의 LINQ는 객체지향 언어에 함수형 디자인을 적용한 매우 훌륭한 사례다.&lt;/p&gt;
&lt;p&gt;함수형 프로그래밍은 아름답다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://hackernoon.com/the-beauty-in-partial-application-currying-and-function-composition-d885bdf0d574&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;The beauty in Partial Application, Currying, and Function Composition.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[redux-saga를 활용한 Redux 비동기 액션 처리]]></title><description><![CDATA[Redux 를 사용하면 React 앱에서 데이터를 처리하는 비즈니스 로직을 컴포넌트로부터 분리할 수 있다. 그리고 비즈니스 로직은 보통  액션  객체를 반환하는 액션 생성자 함수 내부에서 작성하는 방식이 권장된다. 그런데 액션 생성자 함수에서 API 호출 같은 비동기…]]></description><link>https://blog.rhostem.com//posts/2017-09-07-redux-saga-toast-control</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-09-07-redux-saga-toast-control</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Thu, 07 Sep 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;http://redux.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Redux&lt;/a&gt;를 사용하면 React 앱에서 데이터를 처리하는 비즈니스 로직을 컴포넌트로부터 분리할 수 있다. 그리고 비즈니스 로직은 보통 &lt;a href=&quot;http://redux.js.org/docs/basics/Actions.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;액션&lt;/a&gt; 객체를 반환하는 액션 생성자 함수 내부에서 작성하는 방식이 권장된다.&lt;/p&gt;
&lt;p&gt;그런데 액션 생성자 함수에서 API 호출 같은 비동기 프로세스 구현을 위해 Promise를 사용하면 resolve 시점에서 객체를 직전 리턴할 수 없다. 그래서 비동기 처리를 할 때 액션 객체를 반환하는 대신 함수 내부에서 직접 &lt;a href=&quot;http://redux.js.org/docs/api/Store.html#dispatch&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;store.dispatch&lt;/a&gt; 함수를 통해 액션을 발생시킬 수 있도록 하는 &lt;a href=&quot;https://github.com/gaearon/redux-thunk&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;redux-thunk&lt;/a&gt; 미들웨어를 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;fetchAction&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dispatch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/api/data&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;FETCH_ACTION_SUCCESS&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 복수의 Promise를 순차적으로 실행하려면 함수 중첩이 계속 발생해서 if ~ else 만큼이나 가독성이 떨어진다. ES7에서 도입된 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/async_function&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;async 함수&lt;/a&gt;를 사용하면 비동기 프로세스를 보다 간결하게 작성할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dispatch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; res &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/api/data&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;FETCH_ACTION_SUCCESS&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 Redux 미들웨어인 &lt;a href=&quot;https://redux-saga.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;redux-saga&lt;/a&gt;를 사용하면 비동기 액션은 물론 다양한 사이드 이펙트를 발생시킬 수 있는 헬퍼 함수를 활용해서 복잡한 로직을 쉽게 구현할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;redux-saga&quot;&gt;&lt;a href=&quot;#redux-saga&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;redux-saga&lt;/h2&gt;
&lt;p&gt;redux-saga는 ES6에서 도입된 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/function*&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Generator 함수&lt;/a&gt;를 기반으로 한다.&lt;/p&gt;
&lt;p&gt;Generator 함수는 코드 진행 중에 &lt;code class=&quot;language-text&quot;&gt;yield&lt;/code&gt; 키워드를 만나면 일단 멈춘다. 그리고 계속 진행하라는 지시&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Generator/next&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;(&lt;code class=&quot;language-text&quot;&gt;Generator.prototype.next&lt;/code&gt;)&lt;/a&gt;가 전달되어야 다음 &lt;code class=&quot;language-text&quot;&gt;yield&lt;/code&gt; 키워드 까지 코드를 진행시킨다. redux-saga는 이 Generator 함수의 특징을 활용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;takeFetchAction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;takeEvery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;FETCH_ACTION&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dataFetch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dataFetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/api/data&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;FETCH_ACTION_SUCCESS&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 예제에서는 &lt;a href=&quot;https://redux-saga.js.org/docs/api/#takeeverypattern-saga-args&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;takeEvery&lt;/code&gt;&lt;/a&gt; 함수를 사용해서 스토어에 &lt;code class=&quot;language-text&quot;&gt;FETCH_ACTION&lt;/code&gt; 타입의 액션이 전달될 때마다 콜백 함수를 실행한다. 그리고 &lt;a href=&quot;https://redux-saga.js.org/docs/api/#callfn-args&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt;&lt;/a&gt; 함수에 Promise를 반환하는 함수를 인자로 전달해서 실행한 후 대기한다. 그리고 Promise가 resolve되면 redux-saga는 Generator 함수를 진행시킨다. 그렇게 비동기 요청이 끝나고 나면 &lt;a href=&quot;https://redux-saga.js.org/docs/api/#putaction&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;put&lt;/code&gt;&lt;/a&gt; 함수를 사용해서 새로운 액션을 스토어에 전달한다.&lt;/p&gt;
&lt;p&gt;로그인 프로세스도 하나의 redux-saga 함수 안에서 작성 가능하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loginFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;LOGIN&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... perform the login logic&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;LOGOUT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... perform the logout logic&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 코드는 단순화시킨 형태일 뿐이며 실제로는 non-blocking 액션 처리, 진행중인 &lt;a href=&quot;https://redux-saga.js.org/docs/api/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;태스크&lt;/a&gt; 취소 등의 &lt;a href=&quot;https://redux-saga.js.org/docs/advanced/NonBlockingCalls.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;구체적인 로직&lt;/a&gt;이 더 필요하다. 하지만 앱 내부 여기저기서 발생하는 액션들에 의한 프로세스를 하나의 함수 안에서 마치 동기적인 코드처럼 작성할 수 있다는 점은 무척 큰 매력이다. redux-saga는 분리된 비즈니스 로직을 한곳에 모은 &lt;strong&gt;프로세스 명세서&lt;/strong&gt;, 또는 &lt;strong&gt;프로세스 관리자&lt;/strong&gt; 같은 개념으로 받아들여도 될 것 같다.&lt;/p&gt;
&lt;h2 id=&quot;redux-saga로-토스트-메시지-제어하기&quot;&gt;&lt;a href=&quot;#redux-saga%EB%A1%9C-%ED%86%A0%EC%8A%A4%ED%8A%B8-%EB%A9%94%EC%8B%9C%EC%A7%80-%EC%A0%9C%EC%96%B4%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;redux-saga로 토스트 메시지 제어하기&lt;/h2&gt;
&lt;p&gt;redux-saga는 네트워크 입출력뿐만 아니라 비동기적으로 처리되는 UI 구현에도 도움을 줄 수 있다. 예를 들어 토스트 팝업 메시지가 표시되면 3초 후에 자동으로 닫히는 UI를 구현한다고 하자. 그리고 토스트 메시지는 동시에 여러 개가 화면에 표시되어야 한다.&lt;/p&gt;
&lt;p&gt;이를 구현하기 위해서는 토스트 메시지를 가진 객체로 구성된 큐(queue)가 필요하다. 메시지가 발생하면 큐에 메시지를 추가해서 화면에 표시하고 3초 후에 큐에서 제거하여 화면에서 사라지도록 한다. 이 과정을 redux-saga에서 구현해 보았다. 큐는 Redux store에 두고 Saga에서 메시지 객체를 큐에 추가하고 제거하도록 했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; types &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../../actions/actionTypes&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; delay &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;redux-saga&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; all&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; put&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; takeEvery &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;redux-saga/effects&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ShowToastMessageAction &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;../../actions/toast&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// SHOW_TOAST 액션이 발생할 때마다 toastQueueControl 함수를 실행한다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;takeEvery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SHOW_TOAST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; toastQueueControl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toastQueueControl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ShowToastMessageAction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// 토스트 메시지 큐에 메시지를 추가한다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PUSH_TOAST_TO_QUEUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    toast&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toast&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 3초 기다린다&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// 큐에서 메시지를 제거한다.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SHIFT_TOAST_FROM_QUEUE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toast&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;로직은 무척 간단하며 컴포넌트, 리듀서와 완전히 분리되어 있어 입력과 출력이 어떻게 발생하는지 직관적으로 확인할 수 있다. 데이터를 변경하는 로직은 리듀서에서 구현하고, 주어진 데이터로 UI를 구현하는 일은 컴포넌트에서 하고, redux-saga는 이 모든 것을 컨트롤함으로써 역할 분담이 더 명확해졌다.&lt;/p&gt;
&lt;p&gt;실제 구현 예제는 아래의 링크에서 확인 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rhostem.github.io/redux-saga-toaster-control/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://rhostem.github.io/redux-saga-toaster-control/&lt;/a&gt;
&lt;a href=&quot;https://github.com/rhostem/redux-saga-toaster-control&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;(source)&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;redux-thunk에서-redux-saga로&quot;&gt;&lt;a href=&quot;#redux-thunk%EC%97%90%EC%84%9C-redux-saga%EB%A1%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redux-Thunk에서 redux-saga로&lt;/h2&gt;
&lt;p&gt;실무에서 redux-saga를 사용하며 이제 더는 Redux-Thunk를 사용하지 않아도 되겠다는 판단이 들었다. Saga 함수를 작성할 파일이 추가되어서 관리 포인트가 늘어나긴 하지만 액션 생성자의 복잡도와 덩치를 줄일 수 있다는 점이 무척 마음에 들었다. 특히 다양한 사이드 이펙트를 함수를 제공한다는 점도 좋다. 대표적으로 &lt;code class=&quot;language-text&quot;&gt;call&lt;/code&gt; 함수를 사용하면 Promise도 처리할 수 있으니 async/await를 사용하기 위해 별도의 babel 설정을 하지 않아도 된다.&lt;/p&gt;
&lt;p&gt;redux-saga는 ES6의 Generator 함수를 제대로 활용한 멋진 라이브러리다. 공식 홈페이지의 문서와 관련 아티클들을 꼼꼼히 살펴보며 깊이 파고들 가치가 충분히 있어 보인다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[dploy 모듈로 Node.js에서 FTP 배포 프로세스 자동화하기]]></title><description><![CDATA[웹사이트를 서버에 배포할 때 FTP를 통해 직접 업로드하려면 Filezilla처럼 UI를 제공하는 앱을 사용하거나 커맨드라인 명령어를 사용한다. 하지만 Filezilla는 태스크 관리를 통한 자동화를 지원하지 않고 커맨드라인 명령어는 복잡하고 학습 난이도가 있다. 여…]]></description><link>https://blog.rhostem.com//posts/2017-09-01-ftp-deploy-process-with-dploy</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-09-01-ftp-deploy-process-with-dploy</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Fri, 01 Sep 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;웹사이트를 서버에 배포할 때 FTP를 통해 직접 업로드하려면 Filezilla처럼 UI를 제공하는 앱을 사용하거나 커맨드라인 명령어를 사용한다. 하지만 Filezilla는 태스크 관리를 통한 자동화를 지원하지 않고 커맨드라인 명령어는 복잡하고 학습 난이도가 있다. 여러 개의 파일을 폴더 단위로 업로드하려면 더욱 그렇다.&lt;/p&gt;
&lt;p&gt;그래서 Node.js에서 사용할 수 있는 모듈과 npm script를 사용해 자동화할 방법을 찾아보았다. 하지만 배포용 프로세스에 맞는 모듈은 좀처럼 찾기 어려웠다. 맥 OS의 기반이 되는 유닉스와 Node.js에 익숙하지 못해서 제대로 사용하지 못하는 모듈도 여럿 있었다. 그러다 발견한 모듈이 &lt;a href=&quot;https://www.npmjs.com/package/dploy&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;dploy&lt;/a&gt;였다.&lt;/p&gt;
&lt;p&gt;dploy는 FTP와 SFTP를 모두 지원하고 자체적으로 서버에 버전 관리용 파일을 생성해서 변경된 파일만 업로드하는 기능을 가지고 있다. 그리고 환경 설정 파일을 별도로 생성할 수 있고 복수의 업로드 설정을 저장해둘 수 있다. 웹서비스를 개발할 때 테스트 버전, 운영 버전을 구분해서 배포하는 일이 일반적이므로 무척 유용하다.&lt;/p&gt;
&lt;h2 id=&quot;배포-프로세스-구축&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%ED%8F%AC-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B5%AC%EC%B6%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배포 프로세스 구축&lt;/h2&gt;
&lt;h3 id=&quot;dploy-설치&quot;&gt;&lt;a href=&quot;#dploy-%EC%84%A4%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;dploy 설치&lt;/h3&gt;
&lt;p&gt;dploy는 전역에 설치해서 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm i -g dploy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;설정-파일-생성&quot;&gt;&lt;a href=&quot;#%EC%84%A4%EC%A0%95-%ED%8C%8C%EC%9D%BC-%EC%83%9D%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;설정 파일 생성&lt;/h3&gt;
&lt;p&gt;프로젝트 루트에 &lt;code class=&quot;language-text&quot;&gt;dploy.yaml&lt;/code&gt;파일을 만든다. &lt;code class=&quot;language-text&quot;&gt;dploy install&lt;/code&gt; 명령어를 사용해도 자동으로 만들어준다. 이 파일에 FTP 접속 정보와 업로드할 파일의 위치 등을 명시해준다. 아래는 예제 설정 파일이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;env_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sftp&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ftp.host.pathname&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;username&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;pass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;password&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;build/&quot;&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;remote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;www/&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;exclude&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.DS_Store&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;&quot;build/**/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;설정 데이터는 중첩된 키-밸류 해쉬 데이터의 구조체로 되어 있으며 최상위 키의 이름으로 업로드 환경을 구분한다.&lt;/p&gt;
&lt;h4 id=&quot;scheme&quot;&gt;&lt;a href=&quot;#scheme&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;scheme&lt;/h4&gt;
&lt;p&gt;프로토콜이 ftp인지, sftp인지 명시한다.&lt;/p&gt;
&lt;h4 id=&quot;port&quot;&gt;&lt;a href=&quot;#port&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;port&lt;/h4&gt;
&lt;p&gt;명시하지 않으면 ftp는 기본값으로 21, sftp는 22를 사용한다.&lt;/p&gt;
&lt;h4 id=&quot;pathlocal&quot;&gt;&lt;a href=&quot;#pathlocal&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;path.local&lt;/h4&gt;
&lt;p&gt;현재 프로젝트에서 업로드할 폴더를 지정한다. 지정하지 않으면 프로젝트 전체가 업로드된다.&lt;/p&gt;
&lt;h4 id=&quot;pathremote&quot;&gt;&lt;a href=&quot;#pathremote&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;path.remote&lt;/h4&gt;
&lt;p&gt;파일을 업로드할 원격 폴더를 지정한다. 지정하지 않으면 역시 루트에 저장한다.&lt;/p&gt;
&lt;h4 id=&quot;exclude&quot;&gt;&lt;a href=&quot;#exclude&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;exclude&lt;/h4&gt;
&lt;p&gt;업로드할 때 제외할 파일들이다. 맥에서 개발을 진행한다면 운영체제에서 .DS_Store라는 이름의 숨겨진 파일을 자동으로 폴더에 생성하기 때문에 제외하는 편이 좋다. 어떤 FTP 모듈은 저런 숨겨진 파일 때문에 업로드 과정에서 오류가 발생하기도 한다.&lt;/p&gt;
&lt;h4 id=&quot;include&quot;&gt;&lt;a href=&quot;#include&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;include&lt;/h4&gt;
&lt;p&gt;업로드에 포함할 파일이다. exclude는 값으로 배열을 지정할 수 있지만 include는 키-밸류를 지정해야 한다. 키에는 업로드할 파일을 절대 경로를 사용해 직접 지정하거나 &lt;a href=&quot;https://github.com/isaacs/minimatch&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;glob&lt;/a&gt; 패턴을 사용해서 복수의 파일을 지정한다. 그리고 밸류에는 원격 저장소에서 키에 지정한 파일이 위치할 경로를 작성한다. 이때 경로는 path.remote에서 지정한 경로에 대해 상대적인 경로로 지정해야 한다.&lt;/p&gt;
&lt;p&gt;위의 예제 설정에서 키에는 &lt;code class=&quot;language-text&quot;&gt;build/**/*&lt;/code&gt;를 지정하여 build 폴더 내부의 모든 파일을 업로드하도록 했다. 그리고 밸류에는 &lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt;를 지정했는데, path.remote의 값은 &lt;code class=&quot;language-text&quot;&gt;www/&lt;/code&gt;이므로 업로드될 위치는 &lt;code class=&quot;language-text&quot;&gt;www/&lt;/code&gt;가 된다.&lt;/p&gt;
&lt;h3 id=&quot;업로드&quot;&gt;&lt;a href=&quot;#%EC%97%85%EB%A1%9C%EB%93%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;업로드&lt;/h3&gt;
&lt;p&gt;설정 파일 작성을 완료했다면 일단 업로드는 가능하다. dploy 명령어와 이름을 조합해서 업로드를 하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;dploy env_name&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 테스트, 운영 빌드의 업로드 환경을 구분하고 npm script를 사용해서 빌드와 배포를 자동화하는 과정이 남았다.&lt;/p&gt;
&lt;h3 id=&quot;업로드-환경-추가&quot;&gt;&lt;a href=&quot;#%EC%97%85%EB%A1%9C%EB%93%9C-%ED%99%98%EA%B2%BD-%EC%B6%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;업로드 환경 추가&lt;/h3&gt;
&lt;p&gt;키-밸류로 구성된 구조체를 하나 더 작성하면 된다. 테스트와 운영 웹사이트를 동일한 서버에 배포할 경우 path.remote의 값만 다르게 지정하면 될 것이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;test
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ftp.test.pathname&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;username&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;

prod
    &lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ftp.prod.pathname&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;username&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;npm-script-작성&quot;&gt;&lt;a href=&quot;#npm-script-%EC%9E%91%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;npm script 작성&lt;/h3&gt;
&lt;p&gt;프로젝트 빌드를 실행하는 npm script와 dploy 업로드 명령어를 조합해서 배포용 스크립트를 작성할 수 있다. 아래는 &lt;a href=&quot;https://github.com/facebookincubator/create-react-app&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;create-react-app&lt;/a&gt; 으로 생성한 프로젝트에서 환경 변수를 구분해서 빌드하고 배포하는 예제다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sh -ac &apos;. .env.${REACT_APP_ENV}; react-scripts build&apos;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build:test&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;REACT_APP_ENV=development npm run build&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build:prod&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;REACT_APP_ENV=production npm run build&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;upload:test&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dploy test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;upload:prod&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dploy prod&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;deploy:test&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run build:test &amp;amp;&amp;amp; npm run upload:test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;deploy:prod&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run build:prod &amp;amp;&amp;amp; npm run upload:prod&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;p&gt;FTP를 파일을 배포할 일이 있으면 Filezilla 같은 간편한 앱만 사용했다. 업로드할 때마다 파인더에서 폴더를 찾고, 원격 폴더도 테스트인지 운영인지 확인하는 과정이 귀찮아져서 업로드 프로세스를 자동화시켜 보았다. 개발자라면 역시 &lt;a href=&quot;https://en.wikipedia.org/wiki/Don%27t_repeat_yourself&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;DRY 원칙&lt;/a&gt;을 지키고 싶어지는 법이다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[TypeScript로 npm 패키지 만들기]]></title><description><![CDATA[최근 TypeScript를 사용하는 Angular(version>=2.x)로 작업을 진행하면서 만들었던 자동완성 컴포넌트가 있었다. 널리 쓰이는 자동완성 컴포넌트에 비해서 뛰어나다고 할 순 없지만 나름 확장성을 고려해서 만들었고 오류 검증도 된 상태였기 때문에 공부하…]]></description><link>https://blog.rhostem.com//posts/2017-08-14-typescript-npm-package</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-08-14-typescript-npm-package</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 14 Aug 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;최근 TypeScript를 사용하는 Angular(version&gt;=2.x)로 작업을 진행하면서 만들었던 자동완성 컴포넌트가 있었다. 널리 쓰이는 자동완성 컴포넌트에 비해서 뛰어나다고 할 순 없지만 나름 확장성을 고려해서 만들었고 오류 검증도 된 상태였기 때문에 공부하는 셈 치고 NPM 패키지로 만들어보았다. 지금껏 수많은 오픈소스 패키지들을 사용해 왔지만 직접 만들어 본 적은 한 번도 없었기 때문이다.&lt;/p&gt;
&lt;h2 id=&quot;npm&quot;&gt;&lt;a href=&quot;#npm&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NPM&lt;/h2&gt;
&lt;p&gt;NPM은 Javascript 패키지 관리자다. 개발자들이 서로의 코드를 쉽게 공유할 수 있는 시스템을 구축해 둔 덕분에 자바스크립트 커뮤니티는 짧은 시간 안에 큰 양적인 성장을 이룰 수 있었다. 패키지 다운로드도 편리하고 버전, 의존성 관리도 잘 되어있기 때문에 서로가 서로의 패키지를 사용하면서 거대한 자바스크립트 소프트웨어 생태계가 구축된 상태다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6NoGT9IerKcW8eaY8EGWuS/0b31f4381863c86146102cad0b01d742/npm_module_count.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 71.40468227424749%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;npm module count&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6NoGT9IerKcW8eaY8EGWuS/0b31f4381863c86146102cad0b01d742/npm_module_count.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6NoGT9IerKcW8eaY8EGWuS/0b31f4381863c86146102cad0b01d742/npm_module_count.png?w=299 299w,
https://images.ctfassets.net/rpmifyuylbfw/6NoGT9IerKcW8eaY8EGWuS/0b31f4381863c86146102cad0b01d742/npm_module_count.png?w=598 598w,
https://images.ctfassets.net/rpmifyuylbfw/6NoGT9IerKcW8eaY8EGWuS/0b31f4381863c86146102cad0b01d742/npm_module_count.png?w=1196 1196w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;2017년 8월 현재 NPM 패키지는 약 50만개가 존재한다. &lt;a href=&quot;http://www.modulecounts.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;(modulecounts.com)&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3kCrNKEH4kKKqy0q6g4KkS/5bb7fb0fb9b50f788dedd222f49f7eef/rolling-weekly-downloads-of-npm-pagckage.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 807px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 75.8364312267658%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAeCAMAAABpA6zvAAABLFBMVEX////s7Oy7u7u3t7fExMS8vLy5ubnAwMDBwcG9vb3Ozs64uLi2trbo6Oj9/f37+/v09vrn5+fl5eX8/PzU3/T+/v/I1vHO2vPF1PH6+vr5+vqqv+nP2vLt8fm1x+3R3PTf5vXC0fD4+v3B0O6vw+rC0O3Az+7q6uri6fi0xuy9zO3B0fC4yez5+/7G1PG6y+62x+vK1/G3yOyzxenr6+vd5ve5yu66y+3h6Pa8ze7r8Pq5yuy+zu/r7/i2x+y2x+q/zu6/zu3j4+Pm5ub4+Pjj6vi/z++8zO35+vzJ1vK9zu/y9fzi6vjP2/P3+f3u8fns8Pnm6/Tj6PPk6fPZ4O/U3e/X3/Da4fHY3+7h5vL29vb5+fn09PTx8fH+/v7u7u7y8vLp6enz8/P19fUw3IUmAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAQdJREFUOMtjYCAWMDIxs7CysXNwMnOxc3Ez8/AyDFrAx88HBGgEkBTgExQSYkRWKYyFAJIiGEZiVygqRqRCcQkMN0qC3IRG8ElJyxDnRlk5eeKsVlAkTqGSMqZCrG5UUVWT4VMn7EYNTS2irFYS09YhRqGunr6iATFuNDRSUzMGutGEgBtNzRQVFc0JW21hCVSnqEZQoZW1DQ6FqG60tbN3UAMCR6AbnZyFXHCZ6Oqm6K5I2GoPTy+QEgIKvX18dSBKcCj08w8IDAoOCQ0LD48ID4cSQDLSJQpFXbRJDNBD6rEMDHGM8UB+ggkfA4NLYjSGnxnjQIUGUJIhXhJIJCWDNMfSNuMDAMxiL+dM7+wGAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;rolling-weekly-downloads-of-npm-pagckage&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3kCrNKEH4kKKqy0q6g4KkS/5bb7fb0fb9b50f788dedd222f49f7eef/rolling-weekly-downloads-of-npm-pagckage.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3kCrNKEH4kKKqy0q6g4KkS/5bb7fb0fb9b50f788dedd222f49f7eef/rolling-weekly-downloads-of-npm-pagckage.png?w=202 202w,
https://images.ctfassets.net/rpmifyuylbfw/3kCrNKEH4kKKqy0q6g4KkS/5bb7fb0fb9b50f788dedd222f49f7eef/rolling-weekly-downloads-of-npm-pagckage.png?w=404 404w,
https://images.ctfassets.net/rpmifyuylbfw/3kCrNKEH4kKKqy0q6g4KkS/5bb7fb0fb9b50f788dedd222f49f7eef/rolling-weekly-downloads-of-npm-pagckage.png?w=807 807w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;NPM 패키지는 한 주에 20억 회 이상 다운로드되고 있다. &lt;a href=&quot;https://medium.com/npm-inc/npm-weekly-101-how-many-npm-users-are-there-why-use-semver-want-to-work-with-us-3f0171fd7f78&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;(npm weekly #101)&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;packagejson&quot;&gt;&lt;a href=&quot;#packagejson&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;package.json&lt;/h2&gt;
&lt;p&gt;Node.js 환경에서 NPM 패키지 초기화 명령어를 통해 package.json 파일이 생성되면 그 폴더는 일단 NPM 패키지라고 할 수 있다. NPM 패키지는 package.json을 통해 관리된다. 이 파일에는 패키지의 이름은 무엇인지, 현재 버전은 어떻게 되는지, 의존하고 있는 모듈은 어떤 것인지 등의 다양한 정보가 포함된다. 이 정보들 중에는 NPM 패키지 배포를 통해 코드를 공유할 때 반드시 필요한 항목들이 있고, 개인 프로젝트의 경우에는 굳이 작성하지 않아도 되는 항목들이 있다. 대표적으로 패키지를 불러올 때 진입점으로 사용할 파일을 명시하는 &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt; 항목은 배포를 목적으로 하는 경우 꼭 작성해야 한다.&lt;/p&gt;
&lt;p&gt;아래는 이번에 개발한 패키지의 package.json 파일의 일부분이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ng2-simple-autocomplete&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.5.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dist/index.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;typings&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;definitions/index.d.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;engines&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&gt;= 4.2.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;npm&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&gt;= 3&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;license&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MIT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run server:dev&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ...
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;@angular/core&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;~4.1.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ...

  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;devDependencies&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;@angular/compiler-cli&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;~4.0.3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ...
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;name&quot;&gt;&lt;a href=&quot;#name&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;name&lt;/h3&gt;
&lt;p&gt;이름은 중복되지 않는 고유한 이름을 사용해야 한다.&lt;/p&gt;
&lt;p&gt;그런데 패키지의 수가 50만을 향해가고 있기 때문에 적당한 이름을 만들기가 무척 어려워진 상태다. 그래서 패키지 이름 앞에 &lt;a href=&quot;https://docs.npmjs.com/getting-started/scoped-packages&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;scope&lt;/a&gt;를 붙일 수도 있다. 대표적으로는 Javascript로 작성된 패키지를 TypeScript에서 사용하기 위해 타입 정의 파일(&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;declaration file&lt;/a&gt;)을 제공하는 패키지 앞에 붙이는 &lt;a href=&quot;https://www.npmjs.com/search?q=@types&amp;#x26;page=1&amp;#x26;ranking=optimal&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;@types&lt;/a&gt;가 있다. 예를 들어 lodash 패키지의 타입 정의 파일은 @types/lodash 패키지에 배포되고 있다.&lt;/p&gt;
&lt;h3 id=&quot;version&quot;&gt;&lt;a href=&quot;#version&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;version&lt;/h3&gt;
&lt;p&gt;패키지의 버전을 명시한다. 배포를 위해서는 &lt;a href=&quot;http://semver.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;유효한 버전 문자열&lt;/a&gt;이 작성되어 있어야 하고 이전 배포 버전과 같아서는 안된다. &lt;a href=&quot;https://docs.npmjs.com/cli/version&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;npm version 명령어&lt;/a&gt;를 사용하면 패키지의 버전을 major, minor, patch 등의 상황에 맞는 버전 문자열로 자동으로 업데이트하고 배포 관련 npm script도 자동으로 실행할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;main&quot;&gt;&lt;a href=&quot;#main&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;main&lt;/h3&gt;
&lt;p&gt;Javascript 소스에서 패키지를 불러왔을 때 접근하는 파일명이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Ng2SimpleAutocomplete &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ng2-simple-autocomplete&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 코드에서는 &lt;code class=&quot;language-text&quot;&gt;Ng2SimpleAutocomplete&lt;/code&gt;라는 모듈을 불러왔다. main에 지정된 파일에 해당 모듈이 export되어 있지 않다면 자연스럽게 not found 에러가 발생할 것이다.&lt;/p&gt;
&lt;h3 id=&quot;typings&quot;&gt;&lt;a href=&quot;#typings&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;typings&lt;/h3&gt;
&lt;p&gt;타입 정의 파일의 위치를 명시한다. 일반 Javascript 패키지에는 필요하지 않지만 TypeScript로 개발된 패키지는 이 항목이 필요하다.&lt;/p&gt;
&lt;p&gt;NPM은 Javascript로 작성된 모듈을 불러올 수 있으므로 TypeScript로 패키지를 작성했더라도 배포된 파일은 Javascript로 컴파일된 형태여야 한다. 그리고 컴파일된 파일에는 TypeScript에서 작성한 타입 주석이 모두 제외되어 있다. 게다가 interface 처럼 TypeScript에만 있는 값을 export했을 경우에는 해당 모듈을 아예 찾을 수 없다는 에러를 발생시킨다. 그래서 컴파일 시에 타입 정의 파일도 생성시키도록 하고, package.json 파일에도 typings 항목을 작성해야 TypeScript로 작성된 모듈을 NPM을 통해 공유할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;scripts&quot;&gt;&lt;a href=&quot;#scripts&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;scripts&lt;/h3&gt;
&lt;p&gt;NPM 스크립트를 사용하면 패키지에 설치된 모듈을 실행할 수 있다. NPM 스크립트를 사용하지 않더라도 아래와 같은 방법으로 실행할 수 있긴 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./node_modules/typescript/bin/tsc&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 NPM 스크립트를 이용하면 바이너리 파일의 위치를 생략하고 실행할 수 있다는 편리함이 있다. 그리고 자주 사용하는 명령어들을 저장해둘 수 있고 다른 NPM 스크립트를 실행할 수도 있기 때문에 이제는 빌드 및 배포 프로세스 실행을 위해서 많이 사용되고 있다. grunt, gulp도 여전히 강력한 도구지만 NPM 스크립트만으로도 가능한 작업들이 많기 때문에 예전보다는 사용 빈도가 줄어들었다고 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;dependencies--devdependencies&quot;&gt;&lt;a href=&quot;#dependencies--devdependencies&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;dependencies &amp;#x26; devDependencies&lt;/h3&gt;
&lt;p&gt;패키지에 설치된 다른 NPM 모듈들의 이름과 버전이 명시되어 있다. 처음 NPM을 사용했을 땐 왜 저 두 항목으로 구분해 두었는지 이해가 가지 않았지만 패키지 배포의 측면에서 생각해보니 쉽게 이해가 되었다.&lt;/p&gt;
&lt;p&gt;dependencies 항목에 포함된 패키지들은 패키지를 공유받은 사람에게 필요한 패키지들이다. 하지만 devDependencies 항목의 패키지들은 공유받은 사람들에게는 없어도 되는 패키지들이다. dependencies에 @angular/core 같은 필수 패키지가 들어간다면 devDependencies에는 테스트, 배포를 위한 패키지들이 들어간다고 볼 수 있다. 하지만 배포를 하지 않는다면 굳이 구분할 필요는 없고, 배포된다 하더라도 딱히 문제를 발생시키지는 않는다. 다만 패키지의 덩치를 필요없이 키우는 결과를 낳기 때문에 좋은 소리는 듣지 못할 것이다.&lt;/p&gt;
&lt;h2 id=&quot;typescript-파일-컴파일&quot;&gt;&lt;a href=&quot;#typescript-%ED%8C%8C%EC%9D%BC-%EC%BB%B4%ED%8C%8C%EC%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TypeScript 파일 컴파일&lt;/h2&gt;
&lt;p&gt;이번 패키지를 개발할 때 테스트 코드는 작성하지 않았고 브라우저에서 작동되는 모습을 보면서 개발을 진행했다. 그래서 일반 앱처럼대신 프로젝트를 구성하는 대신  배포를 위한 빌드 프로세스만 따로 추가하는 방법을 사용했다. 프로젝트 베이스로는 &lt;a href=&quot;https://github.com/AngularClass/angular-starter&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;angular starter&lt;/a&gt;를 사용했고 빌드 프로세스 구성에는 Gulp를 사용했다. 그래서 tsconfig.json 파일도 개발용 앱을 위한 설정, 빌드를 위한 설정을 가진 파일을 따로 만들어서 사용했다.&lt;/p&gt;
&lt;p&gt;아래 소스는 TypeScript 파일을 Javascript 파일로 변환하는 과정이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; gulp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gulp&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;gulp-typescript&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; merge &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;merge2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; tsProject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createProject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;tsconfig.json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; tsResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tsProject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tsProject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        tsResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./definitions&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        tsResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;js&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tsProject&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;compilerOptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;outDir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;tsconfig.json 파일에는 어떤 소스 파일을 컴파일할지, 컴파일 옵션에는 무엇을 사용할지, 컴파일 결과는 어디에 저장할지 등의 정보가 포함되어 있기 때문에 태스크 내부에서 별도의 설정이 필요하지 않다.&lt;/p&gt;
&lt;p&gt;타입 정의 파일은 &lt;code class=&quot;language-text&quot;&gt;tsResult.dts.pipe&lt;/code&gt; 함수를 사용해서 저장하고 컴파일된 Javascript 파일은 &lt;code class=&quot;language-text&quot;&gt;tsResult.js.pipe&lt;/code&gt;함수를 통해 저장한다.&lt;/p&gt;
&lt;h2 id=&quot;npm-저장소에-배포&quot;&gt;&lt;a href=&quot;#npm-%EC%A0%80%EC%9E%A5%EC%86%8C%EC%97%90-%EB%B0%B0%ED%8F%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;NPM 저장소에 배포&lt;/h2&gt;
&lt;p&gt;배포하기 전에 .npmignore 파일을 생성해서 배포하지 않을 파일들을 지정해야 한다. 각종 설정 파일, 원본 소스 파일 등은 NPM 저장소에 굳이 올라갈 필요가 없기 때문이다. .gitignore 파일을 작성하는 것과 같은 개념이다. 아래는 .npmignore 파일의 일부분이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;.awcache
.vscode
config
src
node_modules
(...)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;배포는 NPM 커맨드라인 명령어를 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; version &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;major &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; minor &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; patch &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; -m &lt;span class=&quot;token string&quot;&gt;&quot;update message&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;npm version 명령어를 사용하면 package.json에 명시된 버전을 자동으로 업데이트 해준다. 그리고 package.json 의 script 항목에 ‘version’이라는 이름의 스크립트가 있을 경우 해당 스크립트도 함께 실행한다.&lt;/p&gt;
&lt;p&gt;아래 NPM 스크립트에서는 npm version 명령을 실행하면 빌드를 실행한 후 npm publish 명령어를 통해 NPM 저장소에 배포되도록 구성했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run build &amp;amp;&amp;amp; npm run npm:publish&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;npm:publish&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm publish&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run build:npm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build:npm&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gulp&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 Git 저장소에서 npm version 명령어를 사용하면 변경된 package.json 파일을 자동으로 커밋해준다. 커밋 메시지에는 버전명이 들어간다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;첫 NPM 패키지 개발을 TypeScript로 진행하다보니 문제도 많이 발생했고 시간도 제법 많이 걸렸다. 그래도 처음부터 끝까지 하나의 과정을 진행하고 마무리 지으며 많은 부분을 경험할 수 있어서 좋았다.&lt;/p&gt;
&lt;p&gt;그리고 내가 배포한 패키지의 다운로드 수가 적다 하더라도 불특정 다수에게 공개된다고 생각하니 코드 작성에 더 신중해지고 오류가 발생해도 빠르게 대처하게 되었다. 이는 자율적으로 하는 작업과 실무처럼 외부의 요구에 의해 하는 작업의 차이에서 왔을 것이다. 구글같은 회사에서 왜 직원들에게 일정 시간은 개인 프로젝트를 진행하게 하는지 그 이유를 제대로 알게 되었다고나 할까.&lt;/p&gt;
&lt;p&gt;이번에 개발한 패키지는 &lt;a href=&quot;https://www.npmjs.com/package/ng2-simple-autocomplete&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ng2-simple-autocomplete&lt;/a&gt;라는 이름을 가지고 NPM 저장소에 배포되어 있다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Gatsby v1에서 달라진 점들]]></title><description><![CDATA[이 블로그는  Gatsby 로 만들었다. 작년 연말 블로그를 만들기로 한 후 정적 사이트 생성기(static site generator)를 둘러보다가 Gatsby의 존재를 알게 되었다. Node.js와 Javascript 언어를 기반으로 하며 React로 페이지 구성…]]></description><link>https://blog.rhostem.com//posts/2017-07-29-migration-to-gatsby-v1</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-07-29-migration-to-gatsby-v1</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 29 Jul 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 블로그는 &lt;a href=&quot;https://www.gatsbyjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Gatsby&lt;/a&gt;로 만들었다. 작년 연말 블로그를 만들기로 한 후 정적 사이트 생성기(static site generator)를 둘러보다가 Gatsby의 존재를 알게 되었다. Node.js와 Javascript 언어를 기반으로 하며 React로 페이지 구성을 할 수 있다는 건 프론트엔드 개발을 하는 나에게 무척 매력적인 요소였다. 원래 유력한 후보는 더 폭넓은 사용자와 성숙도를 가진 &lt;a href=&quot;https://jekyllrb.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;jekyll&lt;/a&gt;이었지만 Gatsby를 선택하게 되었다.&lt;/p&gt;
&lt;h2 id=&quot;서버-렌더링을-이용한-정적인-페이지-생성&quot;&gt;&lt;a href=&quot;#%EC%84%9C%EB%B2%84-%EB%A0%8C%EB%8D%94%EB%A7%81%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%A0%95%EC%A0%81%EC%9D%B8-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%83%9D%EC%84%B1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;서버 렌더링을 이용한 정적인 페이지 생성&lt;/h2&gt;
&lt;p&gt;React는 SPA(Single Page App) 개발을 위한 뷰 라이브러리로서 가상 DOM을 사용하기 때문에 앱 구동시점에는 빈 페이지가 브라우저에 전달된다. SPA는 네이티브 모바일 앱처럼 빠른 화면 전환이 가능하다는 장점이 있지만 검색 엔진에는 컨텐츠를 제대로 제공할 수 없기 때문에 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EA%B2%80%EC%83%89_%EC%97%94%EC%A7%84_%EC%B5%9C%EC%A0%81%ED%99%94&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;검색 엔진 최적화&lt;/a&gt;가 필요한 웹사이트에는 맞지 않다고 여겨진다.&lt;/p&gt;
&lt;p&gt;하지만 서버 렌더링을 사용하면 앱 구동 시점의 페이지를 구성하는 가상 DOM을 서버 상에서 렌더링한 후 정적인 DOM으로 변환해서 브라우저에 전달할 수 있다. 서버 렌더링은 최초 한번만 이루어지기 때문에 SPA의 장점은 유지하면서 검색 엔진에게 데이터도 제공할 수 있다. 그래서 검색 엔진 최적화가 필요한 웹사이트의 프론트엔드 레이어를 SPA로 구성할 경우 서버 렌더링 방식은 거의 필수적으로 사용되고 있다.&lt;/p&gt;
&lt;p&gt;Gatsby도 서버 렌더링 방식을 사용해서 페이지를 만든다. 하지만 블로그는 보통 서버 앱을 따로 구동하지 않고 github-pages나 AWS S3 같은 정적인 저장소에 올리는 것이 보통이다. 그래서 Gatsby는 빌드를 할 때 앱에서 접근 가능한 &lt;em&gt;모든 라우트에 대해 서버 렌더링된 페이지를 미리 만들어 둔다&lt;/em&gt;. 바꿔 말하면 일반적인 SPA 웹사이트의 진입 페이지가 index.html 하나라면 Gatsby로 빌드된 웹사이트는 index.html이 모든 라우트마다 있다는 의미다.&lt;/p&gt;
&lt;h2 id=&quot;gatsby-v0에서-아쉬웠던-점&quot;&gt;&lt;a href=&quot;#gatsby-v0%EC%97%90%EC%84%9C-%EC%95%84%EC%89%AC%EC%9B%A0%EB%8D%98-%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Gatsby v0에서 아쉬웠던 점&lt;/h2&gt;
&lt;p&gt;v1 이전에서 Gatsby는 페이지를 생성하는 방식에 제한이 있었다. 우선 페이지를 만들기 위해서는 &lt;code class=&quot;language-text&quot;&gt;wrapper&lt;/code&gt; 라는 템플릿 컴포넌트가 파일 확장자별로 필요하다. 보통 블로그 컨텐츠는 마크다운 형식으로 작성하기 때문에 템플릿은 컴포넌트는 보통 하나만 필요했다. 그리고 페이지는 소스 파일 단위로 생성된다.&lt;/p&gt;
&lt;p&gt;페이지가 파일별로 생성된다는 점, 이 부분에 다소 문제가 있었다. 포스트 본문은 마크다운 파일이 존재할 것이기에 문제가 없다. 하지만 태그별 페이지를 만드려고 한다면 까다로워진다. 태그를 직접 정리해서 직접 태그 페이지용 파일을 추가하고 삭제하는 건 전혀 스마트하지 않은 방법이다. 태그용 페이지만 하나 만들어서 태그별 포스트 목록을 동적으로 보여줄 수는 있겠지만 역시 반쪽짜리 구현으로 보인다.&lt;/p&gt;
&lt;h2 id=&quot;gatsby-v1에서-달라진-점&quot;&gt;&lt;a href=&quot;#gatsby-v1%EC%97%90%EC%84%9C-%EB%8B%AC%EB%9D%BC%EC%A7%84-%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Gatsby v1에서 달라진 점&lt;/h2&gt;
&lt;p&gt;앞서 언급했던 부분은 v1에서 새로운 API의 제공으로 완전히 해결되었다. 그 외에도 많은 새로운 기능을 제공함으로써 Gatsby v1은 서버 렌더링을 사용해서 정적인 페이지를 만들어낸다는 컨셉을 제외하고는 거의 모든 것이 바뀐 새로운 라이브러리라고 봐도 될 것 같다. 주요 변경점은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GraphQL과 node 인터페이스에 기반한 데이터 관리&lt;/li&gt;
&lt;li&gt;플러그인&lt;/li&gt;
&lt;li&gt;라이프사이클 API 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;graphql&quot;&gt;&lt;a href=&quot;#graphql&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GraphQL&lt;/h3&gt;
&lt;p&gt;Gatsby는 이제 로컬 서버에서 GraphQL을 사용해 데이터를 관리한다. 모든 정보는 &lt;a href=&quot;https://www.gatsbyjs.org/docs/node-interface/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;node interface&lt;/a&gt;라는 데이터 구조를 기반으로 구성되며 GraphQL 쿼리를 사용해서 데이터를 가져와야 한다. GraphQL에 대해 잘 모른다고 하더라도 마이그레이션 가이드, Gatsby에서 GraphQL을 사용하는 방법을 설명하는 &lt;a href=&quot;https://www.gatsbyjs.org/blog/2017-07-19-creating-a-blog-with-gatsby/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;아티클&lt;/a&gt;을 참조하면 대략 어떻게 사용해야 하는지는 파악할 수 있다. 아래는 페이지 템플릿 컴포넌트에서 사용하는 쿼리 예제다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pageQuery &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; graphql&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
  query BlogPostBySlug($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      timeToRead
      fields {
        tagSlugs
      }
      frontmatter {
        title
        tags
        date(formatString: &quot;YYYY-MM-DD&quot;)
        description
      }
    }
  }
`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 쿼리를 간략히 설명하자면 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;BlogPostBySlug&lt;/code&gt;라는 이름의 쿼리는 &lt;code class=&quot;language-text&quot;&gt;$slug&lt;/code&gt;(현재 페이지 경로)에 해당하는 &lt;code class=&quot;language-text&quot;&gt;markdownRemark&lt;/code&gt;(마크다운 파일을 node 인터페이스로 변환한 데이터)를 가져온다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;markdownRemark&lt;/code&gt; 데이터에서 &lt;code class=&quot;language-text&quot;&gt;html&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;timeToRead&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;frontmatter&lt;/code&gt; 등 필요한 필드만 선택해서 가져온다.&lt;/li&gt;
&lt;li&gt;쿼리 결과는 컴포넌트의 props 객체의 필드로 전달된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;v0에서는 쿼리 없이도 필요한 데이터가 기본적으로 제공되었지만 이제는 무조건 쿼리를 작성해야 한다.&lt;/p&gt;
&lt;h3 id=&quot;플러그인&quot;&gt;&lt;a href=&quot;#%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;플러그인&lt;/h3&gt;
&lt;p&gt;v1에서 Gatsby 라이브러리는 GraphQL로 데이터를 구조화하고 서버 렌더링으로 페이지를 생성하는 핵심 기능 외에는 모두 플러그인을 통해 기능을 구현하도록 했다. 예를 들어 마크다운 파일을 Gatsby에서 사용할 수 있는 형태로 변환하는 기능도 v0에서는 gastby에 포함되어 있었지만 v1에서는 별도의 플러그인을 사용해야 한다. 하지만 필요한 대부분의 플러그인이 이미 구현되어 있으니 공식 홈페이지의 플러그인 목록을 참조해서 필요한 플러그인들을 선택해서 사용하면 된다. 하지만 플러그인이 무척 작은 단위로 구분되어 있고 종류가 다양하기 때문에 예제 프로젝트를 참조해서 플러그인을 설치하는 편이 좋을 것이다.&lt;/p&gt;
&lt;p&gt;플러그인을 사용하면 링크된 모든 정적인 파일을 public 폴더로 옮기는 기능, &lt;a href=&quot;https://github.com/MicheleBertoli/css-in-js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;css-in-js&lt;/a&gt; 관련 라이브러리를 사용, 구글 애널리틱스와 태그 매니저 연결, 웹앱 manifest, sitemap, RSS feed 파일 생성 등 매우 다양하고 유용한 기능들을 사용할 수 있다. 다만 플러그인은 모두 독립적인 npm 모듈이라서 Gatsby 라이브러리에는 포함되어 있지 않으며 직접 설치해서 사용해야 한다.&lt;/p&gt;
&lt;h3 id=&quot;라이프사이클-api-제공&quot;&gt;&lt;a href=&quot;#%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4-api-%EC%A0%9C%EA%B3%B5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;라이프사이클 API 제공&lt;/h3&gt;
&lt;p&gt;v0에서도 빌드 시점, 브라우저 시점에서 코드를 실행할 수 있었다. v1에서는 빌드, 서버 사이드 렌더링, 브라우저 시점에서 사용할 수 있는 API를 보다 다양하게 제공한다. 예를 들어 &lt;a href=&quot;https://www.gatsbyjs.org/docs/browser-apis/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;브라우저 API&lt;/a&gt;는 최초 구동 시점, 최초 렌더링 시점, 라우트가 변경되는 시점에 사용할 수 있는 API를 구분해서 제공한다. 이는 마치 React의 컴포넌트 라이프사이클 메소드와 유사하다.&lt;/p&gt;
&lt;p&gt;특히 빌드 시점에서 사용하는 Node API는 &lt;a href=&quot;https://www.gatsbyjs.org/docs/node-apis/#createPages&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;페이지를 직접 생성할 수 있는 API&lt;/a&gt;를 제공한다. 예를 들자면 GraphQL 쿼리로 마크다운 데이터를 불러온 후 쿼리 결과 배열이 가지고 있는 객체별로 페이지를 생성하는 과정을 사용할 수 있을 것이다. 그리고 v0에서는 만들기 어려웠던 태그별 페이지도 마크다운 데이터의 frontmatter 데이터를 사용해서 빌드 시점에서 생성해둘 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;포스트를 작성하는 시점에서 Gatsby는 v1이 배포된지 약 한 달이 지났으며 지속적으로 마이너 버전이 배포되면서 새로운 플러그인도 추가되고 있다. Gatsby 공식 &lt;a href=&quot;https://www.gatsbyjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;홈페이지&lt;/a&gt;에도 초기에는 없었던 &lt;a href=&quot;https://www.gatsbyjs.org/docs/gatsby-starters/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Starters&lt;/a&gt; 메뉴가 추가되었는데, 기본적인 기능이 구현된 데모 사이트와 소스 파일을 제공하고 있다. v1에서는 간단한 사이트를 만들기 위해서도 제법 많은 종류의 플러그인을 사용해야 하기 때문에 빠른 시작을 위한다면 데모 사이트가 큰 도움이 될 것으로 보인다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[[번역] 통합 스타일링 언어]]></title><description><![CDATA[이 글은  CSS Modules 의 공동 개발자 Mark Dalgleish의  A Unified Styling Language 를 번역한 글입니다.   최근 몇년간  React  커뮤니티를 중심으로 한  CSS-in-JS 의 성장이 있었다. 물론 이와 관련한 논쟁 없…]]></description><link>https://blog.rhostem.com//posts/2017-06-24-unified-styling-language</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-06-24-unified-styling-language</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sat, 24 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://github.com/css-modules/css-modules&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CSS Modules&lt;/a&gt;의 공동 개발자 Mark Dalgleish의 &lt;a href=&quot;https://medium.com/seek-blog/a-unified-styling-language-d0c208de2660&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;A Unified Styling Language&lt;/a&gt;를 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5m1E7R4Gn6we04kcGKiaSU/4b8c06de1c08e4dc6e9d41d82b0c8deb/main.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 43.32810047095762%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAARCAIAAAAg6XlfAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAABFxJREFUSMeNVbuOXEUQPae673NeuzPrhcUPDFhYDiwQiAQcEUNAgIRA8EMk/AA/YBIkMBIQEpAQGBvxsrGwtV7WeHfGs565z+4ugju7NkjGrqAf1aXqrtOnqnjwwxeABlV0crSAQh+MCgWU2tkqVBUBqlBdaRCggKpqp1+dqgZAD9dKKAFALYDL125/eun73Tt367qyUUQi+CDGQJWkqjrnVAOAN189+8H77x3011KmqYhC27Y11tR6z+kykc2DsoKf9fsnI4pDkECnyCQsvGZiEDzJ+Lev84gWxMGiuPzrjdSItPVO1eDRsrs/d/lkczghSdJ7vzEcAgi6pQiGdgsKEOAhaADQ1k3c1EHZ4Vk0bR4lFoAiNE2TplGa2F5sggY6tZRW1PsYJs5cUYXQOgey1+v9XevY+jzL4zgeDAZ4nEzbaV1WeS/3PvT7/UKhhO1eZ0SyXr4s67qsO+sGAQBQw9VLAIAYQ4qIbORpOZ8JmPfyI+8hhBBCZ9b5VA1d+NZahZZllWfZdDoDCdB2aAjN/qzwIiRtCJ5UpQ/uP29XVQBF4w0lTmIAVV3v7OyMx+OvLl26ePHi9vb2Rx9/Uowm/X7/lytXbuxNy9Z9/uE7JK01NrKLchABAGz3HSH4rXHPiw01c1c1UbZY3g/WqHoEwmswsIGpNQDWU1tpLCIArDGTyURElkWxu7sLIM8yzXOSs6L8c3pvo5cbY7Isp41CCKfG8V8kSAtQQZDzslqWrfcdwosO7gfiAKByPqjeK6o4dGaga6NyAeDs5uTt114GcGIUL/oBCM9PotbFG70UQF1XAtvL0uCdgsojqJXwdi1FGbQo/4/YJB1tWxxkWSoi9e7tP7787KmNjTPFIp7fTCJ7fXblp/liNBzW7S3Bzs681vAWAKuNqNk+UAsAFJAgnbp4Qo4jWD6Go6qZQWRNWVYAImtOHX8mSeIsjk4eW99cGwwG/Y3JOIrsnft7P+9eL5uqCma/cUK5PdeRLJWriKmAKtrGq9ekZ7yzdekeFS9Aa41GcZLEAMz6seyVNwCoa5MXzwM4f+K5Z00A8NLrp6fLe1mUxnQjQVXXa8Z1OQnSggBJwlpBQleHOLdN5fVB7fyXfHetReIiJo1TY5p3L4zj3pnV2QvnAOTAcQDAueHpVR5Pp0VRjEYjYw2FJajgKo/FmPle1SWMCB91K4ALZ+xU3UhCnmcdsR8rIpKm6aFz0cOICVCC5IOtHiuGhXNYhlYUJTQDK2jtVsgrSOF6ki5mewD6/d6TXGytfbjAKalCq6QCTt1sdnNKBYDQ/YIAaLpyS4KKIKAYYzwwHA6TOPYwT3Jx12lExNoVoxXC/avf3Lk7+/H3W03bOt9CCKcEKSRXDA/eQ0Dl5ng4mhxbVg1BQOtWe6ntqK4dIqs/6vadTp1zdV2LMLI2juzTab3Wz7h39duHc+VoWM2HjlYb1bppgu+ot+qynX7VcXHYj7FqwEfrzkzIODJC/gO3KImf2BOSqAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;main&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5m1E7R4Gn6we04kcGKiaSU/4b8c06de1c08e4dc6e9d41d82b0c8deb/main.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5m1E7R4Gn6we04kcGKiaSU/4b8c06de1c08e4dc6e9d41d82b0c8deb/main.png?w=478 478w,
https://images.ctfassets.net/rpmifyuylbfw/5m1E7R4Gn6we04kcGKiaSU/4b8c06de1c08e4dc6e9d41d82b0c8deb/main.png?w=956 956w,
https://images.ctfassets.net/rpmifyuylbfw/5m1E7R4Gn6we04kcGKiaSU/4b8c06de1c08e4dc6e9d41d82b0c8deb/main.png?w=1911 1911w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;최근 몇년간 &lt;a href=&quot;https://facebook.github.io/react&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React&lt;/a&gt; 커뮤니티를 중심으로 한 &lt;a href=&quot;https://github.com/MicheleBertoli/css-in-js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CSS-in-JS&lt;/a&gt;의 성장이 있었다. 물론 이와 관련한 논쟁 없이 성장하지는 않았다. 많은 사람, 특히 CSS에 친숙한 사람들은 불신의 눈길을 보내왔다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;자바스크립트 안에다 CSS를 작성하고 싶은 사람이 있기나 하겠어?&lt;/p&gt;
&lt;p&gt;CSS를 이미 배웠다면 그건 정말 끔찍한 아이디어야!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;당신이 이런 반응을 가지고 있었다면 이 글을 읽어보길 바란다. 우리가 스타일을 자바스크립트 안에서 작성하는 이유와 그것이 그렇게 끔찍한 아이디어는 아니라는 사실에 대해 살펴보려 한다. 그리고 내가 급속히 성장하고 있는 이 영역에 대해 관심을 두고 있어야 한다고 생각하는 이유에 대해서도 알아볼 예정이다.&lt;/p&gt;
&lt;h2 id=&quot;오해를-받은-커뮤니티&quot;&gt;&lt;a href=&quot;#%EC%98%A4%ED%95%B4%EB%A5%BC-%EB%B0%9B%EC%9D%80-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;오해를 받은 커뮤니티&lt;/h2&gt;
&lt;p&gt;React 커뮤니티와 CSS 커뮤니티는 서로 간에 종종 오해를 사곤 한다. 나는 이 점이 무척 흥미로우며 나는 저 두 영역의 중간 지점 어딘가에 위치하는 입장을 가지고 있다.&lt;/p&gt;
&lt;p&gt;나는 90년대말에 HTML을 배우기 시작했고 테이블 기반의 레이아웃을 사용하던 암흑기부터 CSS와 함께해왔다. &lt;a href=&quot;http://www.csszengarden.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CSS Zen Garden&lt;/a&gt;의 영감을 받아 기존의 코드를 &lt;a href=&quot;https://en.wikipedia.org/wiki/Semantic_HTML&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;의미론적(semantic) 마크업&lt;/a&gt;과 CSS로 옮기는 일의 선두에 서 있었고 얼마 지나지 않아 &lt;a href=&quot;https://www.w3.org/wiki/The_principles_of_unobtrusive_JavaScript&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;겸손한 자바스크립트&lt;/a&gt;를 이용해 서버 렌더링된 마크업을 클라이언트 사이드에서 유저 상호작용을 추가하는 ‘관심사의 분리’에 사로잡히게 되었다. 이와 관련한 작지만 활발한 커뮤니티가 있었고 브라우저 플랫폼이 응당 받아야 했던 가치를 찾아주기 위해 노력했던 우리는 첫번째 세대의 프론트엔드 개발자가 되었다&lt;/p&gt;
&lt;p&gt;이런 웹 중심의 경력을 가지고 있으므로 지금껏 중시하고 있던 원칙에 반하는 React의 &lt;a href=&quot;https://facebook.github.io/react/docs/jsx-in-depth.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;HTML-in-JS&lt;/a&gt; 모델에 내가 격렬하게 반대했을 것으로 생각했을지도 모른다. 하지만 실제로는 반대다. 내 경험상 서버 렌더링과 결합한 React의 컴포넌트 모델은 빠르게 배포 가능하고 접근성이 뛰어나며 꾸준히 발전하는 제품을 사용자들에게 제공할 수 있는 확장성을 가진 복잡한 싱글 페이지 앱을 만들 수 있는 길을 마침내 열어 주었다고 본다. 우리는 &lt;a href=&quot;https://www.seek.com.au/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;SEEK&lt;/a&gt;에서 이 기술을 활용해 가장 중요한 제품을 싱글 페이지 앱으로 만들었으며 핵심 검색 기능은 브라우저의 자바스크립트가 비활성화된 상황에서도 자연스럽게 작동하도록 서버에서 같은 코드를 실행하게 했다.&lt;/p&gt;
&lt;p&gt;그러니 이를 한 커뮤니티에서 다른 커뮤니티로 뻗어 있는 올리브나무 가지라고 생각해보자. 이 움직임이 무엇인지 함께 생각해보자. 완벽하지 않을 수 있고 당신이 만들려고 하는 제품에서는 사용하려는 기술이 아닐 수도 있으며 당신을 확실하게 납득시킬 수 없을지도 모른다. 하지만 최소한 고민해 볼 가치는 있다.&lt;/p&gt;
&lt;h2 id=&quot;왜-css-in-js인가&quot;&gt;&lt;a href=&quot;#%EC%99%9C-css-in-js%EC%9D%B8%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 CSS-in-JS인가?&lt;/h2&gt;
&lt;p&gt;만약 당신이 내가 최근 React와 함께 작업한 &lt;a href=&quot;https://github.com/css-modules/css-modules&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CSS Modules&lt;/a&gt;에 친숙하다면 CSS-in-JS를 지지한다는 사실에 놀랄지도 모르겠다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6jzZMLvVUAkuIMyIowiCgU/881970487ad4fb9763eaffe4876e7145/css_modules.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 55.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAWCAQAAAAYhxY7AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4chT1LDQAAARBJREFUOMvt1D0vg2EUxvFfq8ODVKIpgmqoR7xEw1LRgUSiMUnEJrH6asLiCxjFIiExNcYKk5QYEFEvM9rqE4YOrvHc1/mflzs5tLtidWJxobemWVV3UYoEMj84wsZP8b8e+R/YhsCOOrGELkVF1zaMiln17uaTI+U2Wofd+uwZllMz661u2UjAF0kDQgfm3Tq08rs1BDKmrIkpKUjZ1P/FEUYHNlcTYKNP2XJqR2DJoLw5HSZVLMu6tG1O1bpx5VZ3SMmCjLxdY1L2LQoxIUROTUzaa5QOAyXnkq7lxT0a0ulerxFXsi68uFJw/D253j0MpD1IqHlScCalR1nStBOzntXMOJJTVWkN+OcHtr30AUFzNUfTvTbmAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;css modules&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6jzZMLvVUAkuIMyIowiCgU/881970487ad4fb9763eaffe4876e7145/css_modules.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6jzZMLvVUAkuIMyIowiCgU/881970487ad4fb9763eaffe4876e7145/css_modules.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/6jzZMLvVUAkuIMyIowiCgU/881970487ad4fb9763eaffe4876e7145/css_modules.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/6jzZMLvVUAkuIMyIowiCgU/881970487ad4fb9763eaffe4876e7145/css_modules.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;CSS Modules는 CSS-in-JS를 사용하지 않고 로컬 영역의 스타일시트를 작성하고 싶은 개발자들에게 주로 선택된다. 사실 나는 개인 작업에서 CSS-in-JS를 사용하지도 않는다.&lt;/p&gt;
&lt;p&gt;그럼에도 불구하고 나는 CSS-in-JS 커뮤니티에 깊은 관심을 꾸준히 기울이고 있으며 그들이 끊임없이 만들어내는 혁신을 계속 지켜보고 있다. 그뿐만 아니라 &lt;em&gt;나는 지금보다 더 많은 CSS 커뮤니티들이 관심을 가져야한다고 생각한다.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;왜 그래야 하는가?&lt;/p&gt;
&lt;p&gt;사람들이 스타일을 자바스크립트 안에서 작성하기를 원하는 이유를 명확히 이해하기 위해서는 이 접근법을 사용했을 때 얻을 수 있는 실질적인 이익에 대해 살펴봐야 할 것이다.&lt;/p&gt;
&lt;p&gt;나는 그 장점을 다섯 가지 영역으로 구분했다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;범위가 지정된(scoped) 스타일&lt;/li&gt;
&lt;li&gt;필수적인(critical) CSS&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;CSS-in-JS가 각각의 영역에서 무엇을 제공하는지 자세히 살펴보도록 하자.&lt;/p&gt;
&lt;h2 id=&quot;1&quot;&gt;&lt;a href=&quot;#1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.&lt;/h2&gt;
&lt;h3 id=&quot;범위가-지정된scoped-스타일&quot;&gt;&lt;a href=&quot;#%EB%B2%94%EC%9C%84%EA%B0%80-%EC%A7%80%EC%A0%95%EB%90%9Cscoped-%EC%8A%A4%ED%83%80%EC%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;범위가 지정된(scoped) 스타일&lt;/h3&gt;
&lt;p&gt;CSS를 효율적으로 구조화하는 일이 무척 어렵다는 것은 공공연한 사실이다. 오랫동안 유지된 프로젝트에 참여했을 때 CSS가 가장 파악하기 어려운 부분이라는 건 친숙한 사실이다.&lt;/p&gt;
&lt;p&gt;이 문제를 해결하기 위해 CSS 커뮤니티는 많은 노력을 했다. Nicole Sullivan의 &lt;a href=&quot;https://github.com/stubbornella/oocss/wiki&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;OOCSS&lt;/a&gt;나 Jonathan Snook &lt;a href=&quot;https://smacss.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;SMACSS&lt;/a&gt; 같은 방법론을 이용해 스타일의 유지보수를 보다 쉽게 만들려는 시도를 해 왔다. 하지만 현재 시점에서 인기도에 의하면 확실한 승자는 Yandex의 Block Element Modifier라고도 불리는 &lt;a href=&quot;http://getbem.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;BEM&lt;/a&gt;이다.&lt;/p&gt;
&lt;p&gt;궁극적으로 BEM은 (CSS에만 적용되었을 경우) 스타일의 클래스를 &lt;code class=&quot;language-text&quot;&gt;.Block__element--modifier&lt;/code&gt; 패턴으로 제한함으로써 최적화를 이루려고 하는 이름 만들기 규칙(naming convention)이다. BEM 스타일의 코드베이스에서 개발자는 항상 BEM의 규칙을 따르려고 해야 한다. 엄격하게 규칙을 따랐을 경우 BEM은 아주 잘 작동한다. 그런데작업 왜 범위 지정이라는 근본적인 작업이 단지 &lt;em&gt;규칙&lt;/em&gt; 하나에만 의존해야 하는가?&lt;/p&gt;
&lt;p&gt;그들이 확실히 말하든 아니든 대부분의 CSS-in-JS 라이브러리들은 BEM 기반의 사고방식을 따르면서 각각의 UI에 스타일을 지정하려고 한다. 하지만 내부적으로는 서로 다른 방식으로 구현하고 있다.&lt;/p&gt;
&lt;p&gt;실제로 어떻게 구현하고 있을까? Sunil Paid의 &lt;a href=&quot;https://github.com/threepointone/glamor&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;glamor&lt;/a&gt;를 사용할 경우 코드는 아래와 같이 작성한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; css &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;glamor&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  fontSize&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1.8em&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  fontFamily&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Comic Sans MS&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;blue&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &apos;css-1pyvz&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 코드에서 &lt;em&gt;CSS 선택자(selector)는 어디에도 존재하지 않는다&lt;/em&gt;는 사실을 확인할 수 있다. 직접 선언된 선택자 레퍼런스는 존재하지 않는 대신 라이브러리가 자동으로 만들어준다. 라이브러리가 레퍼런스를 관리하므로 전역에서 클래스 이름이 충돌할 일이 없다는 것은 우리가 직접 클래스에 BEM과 같은 규칙을 바탕으로 접두어를 붙일 필요가 없다는 말과 같다.&lt;/p&gt;
&lt;p&gt;이 선택자의 범위(scope)는 주변 코드의 범위 규칙을 따른다. 만약 코드가 모듈화되어 있고 이 선택자들을 앱의 다른 영역에서 사용하고 싶다면 자바스크립트 모듈로 변환한 후 필요한 곳에서 import로 불러오면 된다. 모듈화된 선택자를 사용해서 &lt;em&gt;어떤 코드에서도 주어진 스타일을 쉽게 추적할 수 있다는 사실&lt;/em&gt;은 코드 베이스를 지속해서 유지 보수성이 있게 한다는 측면에서 무척 강력하다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;단순한 규칙의 사용에서 스타일의 범위가 기본적으로 지정되도록 강제하는 방향으로 나아감으로써 스타일 코드의 기본 품질을 향상했다. 이로써 BEM은 선택 가능한 옵션(opt-in)에서 시스템에 포함된(baked-in) 규칙이 되었다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;–&lt;/p&gt;
&lt;p&gt;여기서 더 진행하기 전에 명확하게 하고 넘어가야 할 매우 중요한 사항이 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;인라인(inline) 스타일이 아닌 CSS 직접 생산한다는 점이다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;초기에 대부분의 CSS-in-JS 라이브러리들은 각각의 요소에 스타일을 직접 붙이는 방식을 사용했다. 하지만 이 방식의 큰 결점은 그 ‘스타일’ 속성이 CSS가 할 수 있는 모든 것을 할 수 없다는 점이다. 새로운 라이브러리들은 과거의 방식을 사용하는 대신 런타임에서 전역에 스타일을 추가하고 제거하는 &lt;em&gt;동적인 스타일 시트&lt;/em&gt;의 사용에 집중하고 있다.&lt;/p&gt;
&lt;p&gt;한 예로서 CSS를 생성하는 초기의 CSS-in-JS 라이브러리 중 하나인 Oleg Slobodskoi의 &lt;a href=&quot;https://github.com/cssinjs/jss&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;JSS&lt;/a&gt;를 살펴보자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tHNBF9CDmgmEMsuouMkmk/12253f110cbe98f5b690db9c575f90ab/jss.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 53.87500000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAWCAMAAACFUC6CAAABJlBMVEUAAABtYg1xZw1qYAwAAADn0RzeyRsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACbjBMAAAAAAAAAAAAAAAAAAAAAAADEsRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBrhcAAAAAAAAAAAAAAAB7bw6Acw94bA7t1h333x5uZA2PgRHHsxhUTApKQwmrmhUmIgVYTwvy2h0WFAMrJwVDPAggHQTs1R1tYg3jzRwAAAB9cQ/03R7r1BxJQglDPQg/OQi8qhfv2B2EdxAZFwMCAgCxoBX23h7dyBskIARZUAu9qxcTEQJhVwxqYA0YFgNmXAwDAgCBdBComBRPRwlhWAwCAQB7bw8XFQPdxxtWTgovKwaYiRI8NgcyLQZ2ag7x2h0WSFsgAAAAKXRSTlMAGhoYBePqirmFCPWh8U3VD/z+43FEwKXuFZj5AdiM4tbwqc+sKB8fHF9QanEAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmDBsPHhyFPUsNAAAAo0lEQVQoz2NgoCFgZEIBzLjUsWhqoQDWUYXYFGrraOnq6RtosbFzcHLhU2hoZGxiamZuYcnNw8uHX6GVtY2tnZY9P5cAA36FWg7Wjk5agkLCIiwEFDq7uLq5s4qKWYvjVOjhqeXl7eGjZeHrJyHJICUtg0uhf0BgUHCIdWhYeESkrJy8Ak4To6JjAjW1YuPiExIVlZRVVCkKcBk1dRSgwUBjAACb7zSGypltCAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;jss&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tHNBF9CDmgmEMsuouMkmk/12253f110cbe98f5b690db9c575f90ab/jss.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tHNBF9CDmgmEMsuouMkmk/12253f110cbe98f5b690db9c575f90ab/jss.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1tHNBF9CDmgmEMsuouMkmk/12253f110cbe98f5b690db9c575f90ab/jss.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1tHNBF9CDmgmEMsuouMkmk/12253f110cbe98f5b690db9c575f90ab/jss.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;JSS를 사용할 경우 hover나 미디어쿼리같은 표준 CSS 규칙으로 대응되는 문법을 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; styles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  button&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    padding&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;10px&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;&amp;amp;:hover&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      background&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;blue&apos;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&apos;@media (min-width: 1024px)&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    button&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      padding&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;20px&apos;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 스타일을 도큐먼트에 추가하면 자동으로 생성된 스타일이 제공된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; classes &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; jss&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createStyleSheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;attach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;생성된 선택자들은 자바스크립트 안에서 마크업을 작성할 때 직접 작성된 선택자 대신 사용될 수 있다. 이 패턴은 제대로 된 구조나 innerHTML같은 간단한 방법에 상관없이 잘 작동한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
 &amp;lt;h1 class=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;classes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;heading&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;Hello World!&amp;lt;/h1&gt;
`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;스타일을 이렇게 관리하는 방식은 그 자체로서 컴포넌트 라이브러리들과 잘 결합한다는 약간의 장점이 있다. 그 결과로서 이 방식이 대부분의 인기있는 라이브러리와 결합한 형태를 쉽게 발견할 수 있다. 예를 들면 JSS는 &lt;a href=&quot;https://github.com/cssinjs/react-jss&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;react-jss&lt;/a&gt;의 도움의 받아 전역 스타일을 관리하면서 필요한 부분을 조금씩 컴포넌트로 주입하는 방식으로 React 컴포넌트와 쉽게 결합할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; injectSheet &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-jss&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Button&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; classes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
 &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;classes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;classes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;injectSheet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Button&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;컴포넌트와 관련된 스타일에 집중함으로써 스타일이 코드 레벨에서 더 밀접하게 통합되며 사용자는 효과적으로 BEM을 논리적인 결론으로 받아들인다. 그런데 스타일과 컴포넌트가 너무 강하게 연결되어 있으므로 CSS-in-JS 커뮤니티의 많은 사람은 스타일 추출, 명칭, 컴포넌트의 재사용이 가지는 중요성을 놓치고 있다고 느꼈다.&lt;/p&gt;
&lt;p&gt;이 문제를 완전히 새로운 방식으로 접근한 방법은 Glen Maddern과 Max Stoiber의 &lt;a href=&quot;https://github.com/styled-components/styled-components&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;styled-components&lt;/a&gt;의 등장과 함께 나타났다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6zSsjCCrPGWQocs2quS0ck/1e2ae395991c7d234c61d98fb6ec0639/styled_components.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 64.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAaCAMAAADyku75AAABLFBMVEUAAAAzMzOlpaU4ODhdXV2ysrJKSkpKSkpLS0u7u7umpqbQ0NC8vLxkZGRkZGRmZmbj4+O9vb3////S0tL79PjYlsP38/b+/f7Tp8X7+frr6+u0tLT8+PvftNj05/L15/Lky9y/v7/a2trKysrAwMD+/v7++OnqupnEhG/Ggabcpsft4eq6urre3t6vr6/w8PD/+u/90l/zvDv3xEnNhGDEjLCurq7b29v8/PzFxcX99N7xuDL6xkHssTDss1zu0JrMzMz++e7835bwzYj/56b74KD+/fzX19fBwcHd3d23t7fW1tbHx8e8vLzLy8vGxsbV1dXq6urp6enh4eHs7Ozk5OTt7e3m5ubl5eW5ubmVlZXCwsKSkpKUlJTExMSQkJCrq6unp6f9/f37+/sLp10dAAAAEHRSTlMAAAAAAAAZGhr8A/z8Hh8fI9PLpAAAAAlwSFlzAAAXEQAAFxEByibzPwAAAAd0SU1FB+YMGw8eHIU9Sw0AAADISURBVDjLY2CgHWBkY8cAHExYFDJzCmAAQS5sCrmFMIAw1RSKiIoRp1BcQhK/QilpoKyMrJy8gpCiEh6FyiqqQkJq6hqaWtpARTq4Ferq6QsZGBoZm5gKCembmeOx2sJSyMraxtbOXkjIwYKAZxydnF1c3agejvRX6O7h6eXg7UNYobKvmZ+ZfwBhhYFBusEhUqFBYVLh3hH4FOpERkXHSMfGxZsl6CTiU2hhkaQqYJGsmmSRZJGMWyEPkVmBhZcPA/CzMtAOAAA4xi6rRJUtSAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;styled components&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6zSsjCCrPGWQocs2quS0ck/1e2ae395991c7d234c61d98fb6ec0639/styled_components.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6zSsjCCrPGWQocs2quS0ck/1e2ae395991c7d234c61d98fb6ec0639/styled_components.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/6zSsjCCrPGWQocs2quS0ck/1e2ae395991c7d234c61d98fb6ec0639/styled_components.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/6zSsjCCrPGWQocs2quS0ck/1e2ae395991c7d234c61d98fb6ec0639/styled_components.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;스타일시트를 생성하는 대신 컴포넌트에 스타일이 직접 연결된 형태로 만들도록 강제한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; styled &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;styled-components&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; styled&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;h1&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
  font-family: Comic Sans MS;
  color: blue;
`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 스타일을 적용할 때는 기존의 요소에 클래스를 붙이지 않는다. 생성된 컴포넌트를 단순히 렌더링한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hello World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;styled-components가 tagged template literals를 이용해 전통적인 CSS 문법을 사용하는 대신 다른 라이브러리들은 별도의 데이터 구조를 사용한다. 주목할만한 대안은 PayPal의 Kent C. Dodds가 만든 &lt;a href=&quot;https://github.com/paypal/glamorous&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Glamorous&lt;/a&gt;다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/3z42L3RblKEwUKe2su20Ks/330003b9df6c3fadcd0911963ea51d22/glamorous.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 64.125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAaCAMAAADyku75AAAA/FBMVEUAAAD////+/v7+/v7////Y2Nj////m5ubX19f+/v7n5+fU1NT////l5eX+/v7i5+f//v77oKP3gIb83uHk6en8/f34+fnc4+L70tXvVGLrtbr4+Pjy9fXy9PTr7+/46+zo6OjN0dGxtLXw8/Pt8PDv8vLl5+dUWl0+Q0epq63z9fXs7+/c4+OKjY9iZmr9/f3i6Ofy9fTd4+Ojpafu7u/g5uX7+/v+9PX71tn6zND1kpv1lZ72oqr4srj3qbD4rbT3qK/2oaj4sbf4t734r7b3rrX3p672mKDzgIv2mqP2nKT0h5H0hI72o6v3rLP3qLDzgoz70NT96uzT09PYUHsVAAAACnRSTlMAAC4vAfwD/PwI+IhMWQAAAAlwSFlzAAAXEQAAFxEByibzPwAAAAd0SU1FB+YMGw8eHfI6e5sAAACuSURBVDjLY2CgHWBkYsYELNgUsnJhAG42bArZeTAA71BWyMfPIyAoJAxWJCKKR6GYOA+PhKSUNEghvwwehbJyQBXyCopKQEpZBY9CZVWQWWrqGpo8PFra+KzWASnk09XQ0+cxMMTnGSOwP/iMTXh4TM2oEo7mFsQptLSytrG1s3ewdXRydnHFo9DN3cPTy9vH18HPP8DaAbdCgcCggUkUHLwYIBirQk4sgJGBdgAAUOAe6niu6RYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;glamorous&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/3z42L3RblKEwUKe2su20Ks/330003b9df6c3fadcd0911963ea51d22/glamorous.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/3z42L3RblKEwUKe2su20Ks/330003b9df6c3fadcd0911963ea51d22/glamorous.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/3z42L3RblKEwUKe2su20Ks/330003b9df6c3fadcd0911963ea51d22/glamorous.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/3z42L3RblKEwUKe2su20Ks/330003b9df6c3fadcd0911963ea51d22/glamorous.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;Glamorous는 styled-components처럼 컴포넌트 우선 API를 제공한다. 하지만 &lt;em&gt;문자열&lt;/em&gt;대신 &lt;em&gt;객체&lt;/em&gt;를 사용해서 라이브러리가 CSS 문법 분석기(parser)를 사용할 필요가 없도록 했으며 그 결과 라이브러리의 크기와 성능 지표를 향상했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; glamorous &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;glamorous&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; glamorous&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  fontFamily&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Comic Sans MS&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;blue&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;어떤 문법을 사용하든 이 두 방식으로 만들어진 스타일은 범위가 지정되어 있지 않다. &lt;em&gt;그들은 컴포넌트와 결코 분리될 수 없다&lt;/em&gt;. React 같은 라이브러리를 사용할 때 컴포넌트는 기본적인 구성 요소이며 이제 우리의 스타일은 그것의 일부가 되었다. &lt;em&gt;만약 앱의 모든 것을 컴포넌트로 표현한다면, 스타일이라고 그러지 않을 이유가 있는가?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;–&lt;/p&gt;
&lt;p&gt;BEM에 아주 능숙한 베테랑이라면 이 모든 것들은 우리의 시스템에 도입된 변화의 중요성을 고려했을 때 상대적으로 미약한 변화라고 느낄 수 있다. 사실 CSS Modules는 이를 지금 누리고 있는 CSS 개발 도구 생태계를 유지하면서도 얻을 수 있게 해준다. 이것이 많은 프로젝트가 친숙한 표준 CSS를 버리지 않고도 선택자 충돌 같은 문제를 효과적으로 해결할 수 있고 확장성도 있는 CSS를 작성할 수 있도록 도와주는 CSS Modules를 계속 사용하고 있는 이유이기도  하다.&lt;/p&gt;
&lt;p&gt;하지만, 이제는 지금까지의 기본적인 개념들을 기반으로 더 흥미로운 일을 시도할 때가 되었다.&lt;/p&gt;
&lt;h2 id=&quot;2&quot;&gt;&lt;a href=&quot;#2&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.&lt;/h2&gt;
&lt;h3 id=&quot;필수적인critical-css&quot;&gt;&lt;a href=&quot;#%ED%95%84%EC%88%98%EC%A0%81%EC%9D%B8critical-css&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;필수적인(critical) CSS&lt;/h3&gt;
&lt;p&gt;현재 페이지를 렌더링하는 데 필요한 필수적인 스타일을 도큐먼트 헤드에 추가해서 최초 로딩 시간을 향상하는 방법은 비교적 최근의 모범 사례다. 이는 브라우저가 단 하나의 픽셀도 화면에 렌더링하기 전에 가능한 모든 시각적인 스타일을 불러오도록 강제하는 일반적인 스타일 로딩 방식과는 뚜렷하게 대조적이다.&lt;/p&gt;
&lt;p&gt;필수적인 CSS를 추출하고 인라인에 적용하는 Addy Osmani의 &lt;a href=&quot;https://github.com/addyosmani/critical&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;critical&lt;/a&gt;이라는 도구가 있긴 하다. 하지만 필수적인 CSS를 유지하면서 자동화하기 어렵다는 사실을 근본적으로 바꾸지는 않는다. 이는 까다롭고 순수하게 선택적인 성능 최적화 과정이기 때문에 대부분의 프로젝트가 이 과정을 생략한다.&lt;/p&gt;
&lt;p&gt;CSS-in-JS는 완전히 다르다.&lt;/p&gt;
&lt;p&gt;서버 렌더링 된 앱을 개발할 때 필수적인 CSS를 추출하는 일은 단순히 최적화 과정이 아니다. CSS-in-JS는 기본적으로 필수적인 CSS가 우선해서 작동하도록 &lt;em&gt;요구한다&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;예를 들어 Khan Academy의 &lt;a href=&quot;https://github.com/Khan/aphrodite&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Aphrodite&lt;/a&gt;를 사용하면 클래스를 요소에 적용할 때 인라인에서 사용되는 &lt;code class=&quot;language-text&quot;&gt;CSS&lt;/code&gt; 함수가 각각의 렌더링 과정에서 어떤 스타일이 사용되었는지 계속 추적한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; StyleSheet&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; css &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;aphrodite&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; styles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; StyleSheet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Heading&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;모든 스타일이 자바스크립트에서 선언되어 있지만, 현재 페이지에 필요한 모든 스타일을 정적인 문자열로 구성된 CSS로 쉽게 추출할 수 있고 서버 사이드 렌더링 시 도큐먼트의 head에 삽입할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; StyleSheetServer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;aphrodite&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; html&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; css &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; StyleSheetServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;renderStatic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ReactDOMServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;renderToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 필수적인 CSS 블록을 아래와 같은 방식으로 렌더링할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; criticalCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
 &amp;lt;style data-aphrodite&gt;
  &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;css&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;content&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
 &amp;lt;/style&gt;
`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;React의 서버 렌더링 모델을 살펴보았다면 이는 무척 익숙한 패턴일 것이다. React에서 컴포넌트는 마크업을 자바스크립트 안에서 선언하지만, 서버에서 표준 HTML 문자열로 렌더링 될 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;만약 점진적 향상(progressive enhancement)을 염두에 두고 앱을 개발한다면 완전히 자바스크립트로 작성되었음에도 클라이언트 사이드에서 자바스크립트는 전혀 필요 없을 수도 있다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;어느 방법이든 클라이언트 사이드 자바스크립트는 당신의 싱글 페이지 앱(SPA, single-page app)을 시작하기 위한 코드 묶음(bundle)이 필요하며, 어느 순간 갑자기 불려서 활성화된 후 브라우저 렌더링을 시작한다.&lt;/p&gt;
&lt;p&gt;서버상에서는 HTML과 CSS의 렌더링은 동시에 이루어지기 때문에 Aphrodite 같은 라이브러리는 앞서 살펴보았듯이 한 번의 호출을 통해 필수적인 CSS와 서버 렌더링 된 HTML을 능률적으로 생성할 수 있도록 도와준다. 이는 우리가 비슷한 방법으로 React 컴포넌트를 정적인 HTML로 렌더링할 수 있게 해준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; appHtml &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
  &amp;lt;div id=&quot;root&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;html&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
  &amp;lt;/div&gt;
`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;CSS-in-JS를 서버에서 사용함으로써 싱글 페이지 앱이 자바스크립트 없이 작동하도록 할 뿐만 아니라 &lt;em&gt;더 빠르게 렌더링해줄 수 있다&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;선택자의 범위 지정과 마찬가지로 필수적인 CSS를 렌더링하는 모범 사례는 이제 선택 가능한 옵션에서 시스템에 포함된 요소가 되었다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;3&quot;&gt;&lt;a href=&quot;#3&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.&lt;/h2&gt;
&lt;h3 id=&quot;더-똑똑한-최적화&quot;&gt;&lt;a href=&quot;#%EB%8D%94-%EB%98%91%EB%98%91%ED%95%9C-%EC%B5%9C%EC%A0%81%ED%99%94&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;더 똑똑한 최적화&lt;/h3&gt;
&lt;p&gt;최근 새로운 방식으로 CSS를 구성하는 방법들이 떠오르고 있다. Yahoo의 &lt;a href=&quot;https://acss.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Atomic CSS&lt;/a&gt;나 Adam Morse의 &lt;a href=&quot;http://tachyons.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Tachyons&lt;/a&gt; 같은 라이브러리는 좁은 목적을 가진 작은 “의미론적 클래스”의 사용을 삼가는 방식을 사용한다. 예를 들어 Atomic CSS를 사용할 경우 적절한 스타일 시트를 생성하기 위해 함수 같은 방식의 문법을 사용해서 클래스를 적용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Bgc(#0280ae.5) C(#fff) P(20px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
 Atomic CSS
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;목표는 CSS 번들을 가능한 낭비가 없게(lean) 만들어서 클래스의 재활용성을 극대화하고 클래스를 인라인 스타일처럼 효과적으로 사용하는 것이다. 파일의 크기가 줄어든다는 장점은 쉽게 파악될 수 있지만 당신의 코드베이스와 동료들에게 미치는 영향은 크지 않다. 이 최적화는 CSS와 마크업에 근본적인 영향을 미치며 구조 설계에 더 많은 노력을 기울이도록 한다.&lt;/p&gt;
&lt;p&gt;앞서 다루었듯이 CSS-in-JS나 CSS Modules를 사용하면 마크업에서 클래스 문자열을 직접 입력할 필요가 없다. 대신 라이브러리나 빌드 도구에 의해 자동 생성된 값에 대한 동적인 참조를 사용한다.&lt;/p&gt;
&lt;p&gt;이런 코드 대신:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;aside&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;sidebar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 코드를 작성한다:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;aside&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;{styles.sidebar}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이는 아주 큰 변화처럼 보인다. 하지만 이 변화는 우리가 마크업과 스타일 간의 관계를 관리하는 방식에 대한 기념비적인 변화다. CSS 개발 도구에 스타일을 변경하는 능력뿐만 아니라 요소에 적용하는 클래스까지 관리하는 능력을 줌으로써 스타일 시트의 클래스를 최적화활 수 있는 완전히 새로운 방식을 열었다고 할 수 있다.&lt;/p&gt;
&lt;p&gt;위의 코드를 살펴보면 &lt;code class=&quot;language-text&quot;&gt;styles.sidebar&lt;/code&gt;는 문자열로 변환되지만 하나의 클래스만 사용하라는 제한은 없다.  복수의 클래스를 지정하는 일은 무척 쉽게 가능해질 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;aside&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;{styles.sidebar}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- 아래의 코드로 간단히 변환 가능하다: --&gt;&lt;/span&gt;

&amp;lt;aside className={&apos;class1 class2 class3 class4&apos;} /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;각각의 스타일에 대해 복수의 클래스를 생성하는 방식으로 스타일을 최적화한다면 아주 흥미로운 일들이 가능해진다.&lt;/p&gt;
&lt;p&gt;이와 관련해 내가 가장 좋아하는 사례는 Ryan Tsao의 &lt;a href=&quot;https://github.com/rtsao/styletron&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Styletron&lt;/a&gt;이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/eUxJdcWQ5a2eoWkQacWUc/214c48af6782264d7357c357029eb9ca/styletron.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 27.500000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAALCAMAAAA6GD/HAAAAvVBMVEUAAAAAVf8AAAAAWP8AWP8AV/8AWP8AVv8AAAAAAAAAWP8AVf8FUfoAVf8AWf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD1AwtBQb6PJXDeCyRVOKsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAP/AAH/AAD/AAP/AAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAL/AAP/AAP/AAMAAAAAAAAuFaDfAAAAP3RSTlMAAQUPTFc2ARokajspOjoqJyFFKAEmC0kjNCkwGVNpPwQYJQ8ENyACHC5UNSZWLx8xB0EIIhUsDjMBM00VLQPatUA4AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAIJJREFUGNNjYKAdYEQwmfAoY2ZhZWMHMTiAmBNKYwNc3Dy8fAz8AoJCwiKinIKiwkJi4hLCkvxS0mgKZWTl5BUYFJWUlVQ4pThVOdWU1CWUGDQ0waYjAy1tHV09BiVRfQNDI2MTCSVTM3M1DmELEwYTNIWWVtY2UJ/YEul7QzsG+gAAIiAKKSovwI0AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;styletron&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/eUxJdcWQ5a2eoWkQacWUc/214c48af6782264d7357c357029eb9ca/styletron.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/eUxJdcWQ5a2eoWkQacWUc/214c48af6782264d7357c357029eb9ca/styletron.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/eUxJdcWQ5a2eoWkQacWUc/214c48af6782264d7357c357029eb9ca/styletron.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/eUxJdcWQ5a2eoWkQacWUc/214c48af6782264d7357c357029eb9ca/styletron.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;CSS-in-JS와 CSS Modules가 BEM 스타일의 클래스 접두사를 자동으로 붙여주는 것처럼 Styletron은 Atomic CSS와 같은 방식을 사용한다.&lt;/p&gt;
&lt;p&gt;코어 API는 각각의 속성, 값, 미디어쿼리의 조합에 대한 CSS 규칙을 정의한 후 자동 생성된 클래스를 반환하는 한 가지 작업에 초점을 맞추고 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; styletron &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;styletron&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

styletron&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;injectDeclaration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  prop&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;color&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  val&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;red&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  media&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;(min-width: 800px)&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// → &apos;a&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;물론 Styletron은 더 높은 레벨의 API도 제공한다. &lt;code class=&quot;language-text&quot;&gt;injectStyle&lt;/code&gt; 함수를 사용하면 복수의 규칙을 한 번에 정의할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; injectStyle &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;styletron-utils&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;injectStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;styletron&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;red&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 display&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;inline-block&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &apos;a d&apos;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;injectStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;styletron&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;red&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
 fontSize&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1.6em&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// → &apos;a e&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 코드에서 생성된 두 세트의 클래스 이름들 사이의 공통성을 확인해보라. (역주: &lt;code class=&quot;language-text&quot;&gt;color: &amp;#39;red&amp;#39;&lt;/code&gt; 규칙은 &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; 라는 클래스를 공통으로 사용하도록 최적화되었다)&lt;/p&gt;
&lt;p&gt;클래스 자체의 이름을 관리하는 낮은 레벨의 작업을 포기하고 단지 필요한 스타일을 작성하기만 함으로써 라이브러리는 우리에게 이득이 되는 최적화된 작은 단위의(Atomic) 클래스들을 생성할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4EqDU67K9WgwUoK2kkcc8A/68b41ebd77a24b2768faf55f6ad7bd89/css_in_js_bundle_size.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 72.125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAdCAMAAADvl95BAAABjFBMVEUAAAD////////////////////+/v7+/v7////////////////s7Ovu7u/r6+vw8PDv7+/x8PH09PT1+/rN6+jU7uv5+fn39/f29/fv+Pew4Nu75ODs7Ozh4eLk5OTm5+fn6ff9/f7GyuvFyev6+v3///7V1dXa29vc29zy8vL6+/vy9fb3+Pnd4+avvcXL1Nr6+fnr7Oz19fXe3t79/f33+Pj6+vq43vySy/rR6f2Yz/pgtPi83/z09Pf29vf+/v7w8PLY2Njd3d7i4uPn5+bk5eXc3NvX19f29vzm5u3z9Pn29vX8/Pzm5+j9/v642/fj5OTd3d329vbx8vro6ffv8Pnu+Pjj9PPy+vnv8vPs7/Hf4uTi5ujc3vLb3vL29vvw8fr19fv0+vrP7OnV7uzz+fnr9vb1+vrq7e/O1tvf5ej19vfz9fb7/PzA4fye0frU6vzV6v3m5ebp6eni4eHo6eno6Ojp6Ojj4+Pq6urg4ODi4uLg39/z8/PZ2dnl5OTl5eXv7/Da2dng4OEvzhJmAAAAC3RSTlMAER8Sj5SPlBQkFWq0ZsAAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmDBsPHh3yOnubAAABDElEQVQ4y2NgoD5gZCICMAMVsnDDAQ8vHz83VsCKqlBAUFCIKIXCIqLceBSKiYlLQLiSUtL4FMrIyskTpZBbQUGROIVKyipoClXV1DU0iVHIraWtQ5SJ3Lp6+kNHoYGhkTFRCk1MzfiJs9rcwpI4hVbWNkgKbe2MMBTa22NR6ODoBFXo7OIKVejmLoOp0MPTC6rQ29EHqpDX1w+i0D8gMAimMDgkFKowLDwCqjAySgtqYnQM2EQ2+9i4+IREeyBISk5JTUsHsTIys7JzckGsvPyCwkI/drCJRkXFJWZgl5uVlsEyDX8QlFFeURkEdaNMVXWNOFiwtq4elgwbGqGMpuYGiEIOTiIAFw0KFABAVFVT7b0TNgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;css in js bundle size&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4EqDU67K9WgwUoK2kkcc8A/68b41ebd77a24b2768faf55f6ad7bd89/css_in_js_bundle_size.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4EqDU67K9WgwUoK2kkcc8A/68b41ebd77a24b2768faf55f6ad7bd89/css_in_js_bundle_size.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/4EqDU67K9WgwUoK2kkcc8A/68b41ebd77a24b2768faf55f6ad7bd89/css_in_js_bundle_size.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/4EqDU67K9WgwUoK2kkcc8A/68b41ebd77a24b2768faf55f6ad7bd89/css_in_js_bundle_size.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;CSS 최적화는 보통 작성한 스타일을 가장 효율적으로 재활용이 가능한 클래스들로 분리하는 순수 수작업으로 이루어지곤 한다. 하지만 이제는 라이브러리에 의해 완전히 자동화가 가능해졌다. 당신은 이 트렌드를 주목하기 시작할 필요가 있다. &lt;strong&gt;&lt;em&gt;Atomic CSS는 이제 선택 가능한 옵션에서 시스템에 포함된 요소가 되었다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;4&quot;&gt;&lt;a href=&quot;#4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4.&lt;/h2&gt;
&lt;h3 id=&quot;패키지-관리&quot;&gt;&lt;a href=&quot;#%ED%8C%A8%ED%82%A4%EC%A7%80-%EA%B4%80%EB%A6%AC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;패키지 관리&lt;/h3&gt;
&lt;p&gt;이 주제에 대해 알아보기 전에 먼저 자신에게 간단해 보이는 질문을 해볼 필요가 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;우리는 CSS를 어떻게 서로에게 공유하는가?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;수동으로 내려받은 CSS 파일을 사용하던 시절에서 &lt;a href=&quot;https://bower.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Bower&lt;/a&gt;같은 프론트엔드 중심의 패키지 매니저 사용하던 시기를 지나 이제는 Browserify나 webpack 같은 도구 덕분에 npm을 통해 코드를 공유한다. 몇몇 도구가 외부 패키지에서 CSS를 가져오는 과정을 자동화했지만 프론트엔드 커뮤니티에서는 여전히 외부 CSS를 수동으로 관리하는 단계에 머물러 있다.&lt;/p&gt;
&lt;p&gt;자동화와 수동 어느 쪽이든 공유한 라이브러리별로 서로 &lt;em&gt;다른&lt;/em&gt; CSS 의존성에 기대고 있으므로 좋지 않다.&lt;/p&gt;
&lt;p&gt;많은 이들이 기억하겠지만 Bower와 npm의 &lt;em&gt;자바스크립트 모듈&lt;/em&gt;에서 비슷한 사례가 발생했던 일이 있다.&lt;/p&gt;
&lt;p&gt;npm에 배포된 모듈이 &lt;a href=&quot;http://wiki.commonjs.org/wiki/Modules/1.1&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CommonJS 모듈 형식&lt;/a&gt;을 사용한것에 비해 Bower는 어떠한 모듈 형식과도 결합되지 않았다. 이는 결과적으로 각각의 플랫폼에 배포된 패키지 수에 아주 큰 영향을 미쳤다.&lt;/p&gt;
&lt;p&gt;npm은 작고 중첩된 의존성을 가지지만 Bower는 몇 개의 플러그인만 사용해도 크고 거대한 의존성을 가지게 된다. 당신의 의존성이 모듈 시스템에 의존하지 않는다면 각각의 패키지는 의존성을 제대로 사용할 수 없게 된다. 결국 패키지에게 필요한 의존성을 해결하는 일은 항상 사용자에게 넘겨진다.&lt;/p&gt;
&lt;p&gt;결과적으로 npm의 패키지 수의 그래프는 지수 형태를 그렸지만 bower의 패키지 수는 선형적으로 증가했다. 이 차이에는 여러가지 이유가 있긴 하지만 결정적인 요인은 각각의 플랫폼이 패키지가 런타임에서 서로서로 의존할 수 있도록 해주느냐 아니냐의 차이에서 왔다고 할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4ynBMm8cZyKwo00s4imUMg/84049a1b5a902daad007ea1d5613ae2a/module_counts_comparison.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 608px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 72.36842105263158%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAdCAMAAADvl95BAAABwlBMVEX////k4+Tj4+Tu7u/W1dTy8/X9/Pv29vh8dnNaW11/fn1vcXRzdHhxbm+rrrGdm5qEhIVwcHGLhYKKiohkZWnCxMfx8PDz8/Pv7+/q6uvx8fH09PXu7e3y8vLv7/Ds7Oz19fXp6Oj4+Pnl5eXh4eHm5ubo6Oj29vbj39D16LPk5OTj4+P8/Pz9/v/n6+7e39rl59bp6enq6urr6+ve7Pjl5ebe4uTj7/nn5+fp9P3q8vjZ2dnT09P6+vr2+fze6fPb6vf+/v7j7/jw9fnw8PDt7e309PTv9vzi7PPu7u71+fza5/L5+/zW5vLU5PH7/P3i4uL6+/zT4/D09fb7/f/z+PzT4u/8/P39/f3j7vjg6O/V1dXR4O3q7vL+///e6vTW4uzq8/vu8fPg4ODo8fnO3Ojj6e76+PP79Nbw5LLn2qno6Of5+fnw9vzV4u3S3+vo7fD///799t3u4rDn2qbn2qjl2a3q5dD19fTS3+rS4Ovd5On9+OT16brl2Kbm2qjp48nz8/Lc4ufr9f7e6/bT4OrZ5O7Y5O3Y4eju8PH07NDr5Mnw7+zEy9DQ09Xc3Nze3t7b29vX19fa2trf39/Y2NjrdgzJAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAXhJREFUOMtjYGBkYmAGQhZWBgY2dgY8gIOTi5uHl49fQFBIWEQUj0IxcQlJSSlpGVk5eQVFJXxGKqvIq6oRAOogheJS6hqaWspS2gw6QB4UozJ19aCG6hsYGhkbquNUaGIKVWhmbmhsYWGBS6GllRpIlbWNtpaOjo4tGMEwMtPOXkcNaqIqNvvgTAdHBqIUOjkzQBS6uLrhU+juwQBVqKbsiceNXt4girDVPr6OOjAT8SnU9fNngCsMwB08gUHBECYBE0P8AqFM/ApDw8IZkBU6RkRgVRgZ5ciAolA7WguLG+1iYpGci9PquPiECGRX4FKYGJOE6lywQqlkY1SrU1LTkF2RnpGZlQ1SmCOrjmRibl5+AdzwwqLiktKy8gp0q73MK6uqdaB6amrrSusbGlHd2NTc0prQ1t7RiXBYV3cPpmdce/v61SzUQAiG0ZgSYHU6/RMmKgRMMtbWnuA6oT9AXGWysvyEKVL9E/qltKfKB2hPkGegBQAAIN14gDySqnsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;module counts comparison&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4ynBMm8cZyKwo00s4imUMg/84049a1b5a902daad007ea1d5613ae2a/module_counts_comparison.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4ynBMm8cZyKwo00s4imUMg/84049a1b5a902daad007ea1d5613ae2a/module_counts_comparison.png?w=152 152w,
https://images.ctfassets.net/rpmifyuylbfw/4ynBMm8cZyKwo00s4imUMg/84049a1b5a902daad007ea1d5613ae2a/module_counts_comparison.png?w=304 304w,
https://images.ctfassets.net/rpmifyuylbfw/4ynBMm8cZyKwo00s4imUMg/84049a1b5a902daad007ea1d5613ae2a/module_counts_comparison.png?w=608 608w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;Bower의 패키지 수 증가율은 안타깝게도 CSS 커뮤니티에게는 너무나 친숙한 모습이다. npm의 자바스크립트 패키지 비해 CSS 패키지 수의 증가율은 상대적으로 느렸기 때문이다.&lt;/p&gt;
&lt;p&gt;CSS 패키지도 npm의 지수 그래프처럼 되길 바란다면? 모든 것을 포함한 프레임워크가 아닌, 다양한 사이즈를 가지고 있으며 복잡한 의존성 계층을 가진 패키지들을 사용하고 싶다면 어떻게 해야 할까? 이를 위해서는 단지 패키지 관리자만이 아니라 CSS를 위한 적절한 모듈 포맷이 필요하다.&lt;/p&gt;
&lt;p&gt;이는 CSS를 대상으로 디자인된 패키지 매니저가 필요하다는 의미인가? Sass와 Less를 위한 전처리기(preprocessor)처럼?&lt;/p&gt;
&lt;p&gt;흥미롭게도 우리는 이미 HTML을 통해 유사한 깨달음을 얻은 적이 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; styles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;moreRules&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  fontFamily&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Comic Sans MS&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;blue&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 방식으로 스타일을 작성하기 시작한다면 앱의 다른 코드처럼 같은 패턴, 같은 도구, 같은 기반 구조, 같은 &lt;em&gt;생태계&lt;/em&gt;(ecosystem)를 사용하면서 얼마든지 스타일 코드를 조합하고 공유할 수 있다.&lt;/p&gt;
&lt;p&gt;이 방법이 어떻게 성과를 거두는지 좋은 사례로는 Max Stoiber와 Nik Graf, Brian Hough의 &lt;a href=&quot;https://github.com/styled-components/polished&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Polished&lt;/a&gt;가 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/62isYeumdO2giaSqgYiSYY/4462d8b3cb79f2ac0da53a02f652dcc8/polished.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 64.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAaCAMAAADyku75AAAA/FBMVEUAAAAzMzPPz884ODhdXV3T09NKSkpKSkpMTEy9vb3Pz8/S0tJkZGRkZGRmZmbj4+O9vb3////R0dHs7Ozy8vLv7+///vz//v7//v319fWbm5va2trExMT9/f3+89f/89X+79r+/v7Dw8PIyMisrKz+8tf+8tb+663//PfHx8eurq6+vr7x8fGzs7P/+/n+7tH84FP823L+8OK8vLzp6enh4eGfn5/Kysr+8d3//vv85pj+9ujS0tLY2Njt7e397bv+89n97c//+/j//fzo6OjBwcHm5ubOzs7u7u7Gxsa5ubmVlZW6urqWlpaTk5OwsLCXl5fi4uL8/Pz7+/sW1aYEAAAAD3RSTlMAAAAAAAAZGhr8A/weHx+2u15BAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAALtJREFUOMvd08cOgkAUhWHsvRwVO+ooVuy9YsPe5f3fRV0DYqJs/Fez+DKTe5OhKO3SmS2SrHoZaLB5JHntctABSb6voR8I0AiGEAYifmVIR2OIM0gkkQJIWhlmWIIMkM0hXwCKb54uceUKEK7W6o0mWm1F2Ol60XvO0B8MR3idFG8cAxMemM7mz2F86utZYPmrPf4TFGhhtd5s+Z0a3B+OJ5Y7X65q8EbuokiYu/gefvoVjE6XJLeJ0q4HyUEm/hBQzDMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;polished&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/62isYeumdO2giaSqgYiSYY/4462d8b3cb79f2ac0da53a02f652dcc8/polished.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/62isYeumdO2giaSqgYiSYY/4462d8b3cb79f2ac0da53a02f652dcc8/polished.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/62isYeumdO2giaSqgYiSYY/4462d8b3cb79f2ac0da53a02f652dcc8/polished.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/62isYeumdO2giaSqgYiSYY/4462d8b3cb79f2ac0da53a02f652dcc8/polished.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;Polished는 믹스인, 컬러 함수, 약칭 등의 완벽한 컬렉션을 제공하는 CSS-in-JS의 &lt;a href=&quot;https://lodash.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Lodash&lt;/a&gt;라고 할 수 있다. Sass 같은 언어를 사용하던 사람들에게 자바스크립트 안에서 스타일을 보다 친숙하게 작성할 수 있도록 도와준다. 핵심적인 차이는 Sass보다 조합, 공유, 테스트에 있어 더 뛰어나며 자바스크립트 패키지 생태계를 마음껏 사용할 수 있다는 점이다.&lt;/p&gt;
&lt;p&gt;그래서, CSS의 경우 어떻게 작고 재사용성이 있는 패키지를 조합하여 거대한 스타일 컬렉션을 구성하는, 자바스크립트와 같은 수준의 오픈소스 활동을 기대할 수 있을까? CSS를 다른 언어에 내장하고 자바스크립트 모듈을 최대한 활용하는 방법을 통해 가능할 것이다.&lt;/p&gt;
&lt;h2 id=&quot;5&quot;&gt;&lt;a href=&quot;#5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5.&lt;/h2&gt;
&lt;h3 id=&quot;비-브라우저-스타일&quot;&gt;&lt;a href=&quot;#%EB%B9%84-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%8A%A4%ED%83%80%EC%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;비 브라우저 스타일&lt;/h3&gt;
&lt;p&gt;지금껏 살펴본 내용은 ~CSS를 자바스크립트 안에서 작성하는 편이 확실히 쉬운데도 불구하고~ 표준 CSS가 없다면 불가능하다. 내가 가장 흥미롭고 미래지향적인 이 주제를 맨 뒤로 미뤄둔 이유가 여기에 있다. 이 주제는 오늘날의 CSS-in-JS 커뮤니티에서는 꼭 중요한 위치를 차지하고 있을 필요는 없지만, 미래의 &lt;em&gt;디자인&lt;/em&gt;에서는 기반 기술이 될 가능성이 꽤 있다. 개발자뿐만 아니라 디자이너에게도 영향을 미치는 이 기술은 이 두 분야가 서로 의사소통을 하는 방식을 근본적으로 바꾼다.&lt;/p&gt;
&lt;p&gt;본격적으로 얘기하기에 앞서 우선 React에 대해 잠시 빠르게 훑어보고 지나갈 필요가 있다.&lt;/p&gt;
&lt;p&gt;–&lt;/p&gt;
&lt;p&gt;React 모델은 최종값을 즉시 렌더링하는 컴포넌트에 관한 모든 것이다. 브라우저에서 작업할 때는 DOM 요소를 직접 조작하기보다는 복잡한 트리 구조의 가상 DOM을 구성하게 된다.&lt;/p&gt;
&lt;p&gt;이런 중요도에도 불구하고 DOM을 렌더링하는 기능을 React의 코어 라이브러리가 아닌 &lt;em&gt;react-dom&lt;/em&gt;에서 제공한다는 사실은 무척 흥미롭다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-dom&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;React가 DOM을 위해 만들어졌고 브라우저 환경에서 가장 많이 사용되고는 있긴 하다. 하지만 React 모델은 새로운 렌더러를 도입함으로써 매우 다양한 환경에서 사용될 수 있다.&lt;/p&gt;
&lt;p&gt;JSX는 단지 가상(virtual) DOM에 관한 것만이 아니다 - JSX는 &lt;em&gt;무엇이든&lt;/em&gt; 가상으로 만들 수 있다.&lt;/p&gt;
&lt;p&gt;네이티브 앱을 만들 수 있는 &lt;a href=&quot;https://facebook.github.io/react-native&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React Native&lt;/a&gt;가 그런 방식이다. 자바스크립트로 컴포넌트를 작성하면 iOS이나 Android 네이티브에 대응하는 가상 요소를 렌더링한다. 예를 들면 &lt;em&gt;div&lt;/em&gt;와 &lt;em&gt;span&lt;/em&gt; 대신 &lt;em&gt;View&lt;/em&gt;와 &lt;em&gt;Text&lt;/em&gt;를 렌더링하는 식이다.&lt;/p&gt;
&lt;p&gt;CSS의 관점에서 React Native에서 가장 흥미로운 건 자체적으로 &lt;a href=&quot;https://facebook.github.io/react-native/docs/stylesheet.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;StyleSheet API&lt;/a&gt;를 가지고 있다는 점이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; styles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; StyleSheet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  container&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    borderRadius&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    borderWidth&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    borderColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;#d6d7da&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    fontSize&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;19&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    fontWeight&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bold&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  activeTitle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;red&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;친숙한 스타일들을 확인할 수 있을 것이다. 위의 코드에서는 색상, 폰트, 보더 스타일을 정의하고 있다.&lt;/p&gt;
&lt;p&gt;이 규칙들은 무척 직관적이며 대부분의 UI 환경에 쉽게 대응되지만 네이티브 레이아웃을 사용할 때 가장 재밌어진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; styles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; StyleSheet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  container&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    display&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;flex&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;브라우저가 아님에도 불구하고 &lt;em&gt;React Native는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;flexbox&lt;/a&gt;를 네이티브 환경에서 사용할 수 있는 구현체를 탑재하고 있다&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;최초 배포시 &lt;a href=&quot;https://www.npmjs.com/package/css-layout&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;css-layout&lt;/a&gt;이라고 불렸던 자바스크립트 패키지는 flexbox를 자바스크립트만으로 구현했고(종합적인 테스트 코드가 뒷받침하고 있다) 지금은 더 나은 이식성을 위해 C로 이식되었다.&lt;/p&gt;
&lt;p&gt;그리고 프로젝트의 범위와 중요성을 고려해서 Yoga라는 이름의 더 의미 있는 브랜드를 부여받은 상태다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4GYePnRwMo2OCmAaQcmUkS/97edce225bdcbc83ff298829823d443e/yoga.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 60%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAYCAMAAAC/Wk/yAAAA8FBMVEUAAAAuNkQvN0UvN0UwOEYwOEYwOEYwOEYwOEYvN0UwOEYwOEYwOEYwOEYwOEYwOEYwOEYwOEYwOEYvN0UwOEYwOEYwOEYuN0QvN0UvN0UuN0QwOEYwOEYwOEYwOEYwOEYvN0UwOEYwOEYwOEYwOEYwOEYwOEYvN0UvN0UuN0UwOEZAUlxBU1xBVF0/UFpGW2QyPEkyO0lGXGRCVF44RVE3Q08xOUdHXGQ+T1k+TlgxOkg2QU45R1I9TFc/UVsxOkc/T1k6SFM1QE08S1Y9TVg0Pks7SVUwOUc5RlFCVV47SlU5RlI2Qk9AUVtVdHhWdHgbz3DsAAAAKnRSTlMABh0Lbcb6yG8MSOTmTGf9a03nDXLL/AkcIgbHzXTl6Q5L/lBw6MwJIQmGxY3/AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4chT1LDQAAAOVJREFUGBm9welOwkAYBdDbApVFHdYiKoLgeqsjqEjBhSpuqOj7v40fgRBIpuUf52AdLNu2sFIsnnBIZyOZQqR0hjObW4iwrTinsgiVVlygcggRy3BJvgCzOKe8C04VYZaguNRXrfa1vqEowchySN527rp+r9u/fyDplmGyQ+Hzsf3kDXoMKCowsSmeX4Ytrfuvb+8UFZjsOhTeh/b9YOBRuGUY7VF8DoPRSH99U+zDLMmJn/Hv37jDiSrMUgdcUqsjxKHigkYTobKKc40jRMjlOXPcRKRCseSS7km1jpVOzyrnWIN/URQuD42soZYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;yoga&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4GYePnRwMo2OCmAaQcmUkS/97edce225bdcbc83ff298829823d443e/yoga.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4GYePnRwMo2OCmAaQcmUkS/97edce225bdcbc83ff298829823d443e/yoga.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/4GYePnRwMo2OCmAaQcmUkS/97edce225bdcbc83ff298829823d443e/yoga.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/4GYePnRwMo2OCmAaQcmUkS/97edce225bdcbc83ff298829823d443e/yoga.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;Yoga는 CSS 콘셉트를 비 브라우저 환경으로 이식하는 프로젝트지만 CSS의 일부분만을 대상으로 하고 있기 때문에 잠재적으로 관리 불가능한 영역이 존재한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Yoga는 CSS의 완전한 구현이 아닌 풍부한 레이아웃 라이브러리의 구현에 초점을 맞추고 있다.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이런 상충관계는 한계를 가지고 있는 것처럼 보인다. 하지만 CSS 구조의 역사를 살펴보면 확장성있는 CSS를 작성하는 일은 결국 CSS라는 언어 속에서 적합한 부분집합을 선택하는 일과 같다는 사실을 명백히 알 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;Yoga의 경우 범위가 지정된 스타일의 중첩을 피하고 레이아웃 엔진을 전적으로 flexbox에 집중한다. 이는 많은 기능을 사용할 수 없게 하지만 스타일이 탑재된 컴포넌트를 복수의 플랫폼에서 사용할 수 있다는 놀라운 기회를 열어준다. 그리고 이미 이 사실을 입증하는 주목할만한 오픈소스 프로젝트들이 여럿 있다.&lt;/p&gt;
&lt;p&gt;Nicolas Gallagher의 &lt;a href=&quot;https://github.com/necolas/react-native-web&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React Native for Web&lt;/a&gt;은 React Native의 드랍 인(drop-in) 대체에 목표를 맞추고 있다. webpack 같은 번들러를 사용할 때 서드파티 플러그인에 별칭을 붙이는 방식은 무척 직관적이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  alias&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&apos;react-native&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react-native-web&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;React Native for Web은 React Native 컴포넌트를 브라우저 환경에서 사용할 수 있게 하며, &lt;a href=&quot;https://facebook.github.io/react-native/docs/stylesheet.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React Native StyleSheet API&lt;/a&gt;의 브라우저 이식 버전도 포함하고 있다.&lt;/p&gt;
&lt;p&gt;유사한 사례로 Leland Richardson의 &lt;a href=&quot;https://github.com/lelandrichardson/react-primitives&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;react-primitives&lt;/a&gt;는 타깃 플랫폼의 상세 구현을 추상화함으로써 크로스 플랫폼 기본 컴포넌트의 실행 가능한 베이스라인을 만든다.&lt;/p&gt;
&lt;p&gt;심지어 마이크로소프트도 &lt;a href=&quot;https://microsoft.github.io/reactxp&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ReactXP&lt;/a&gt;를 소개하며 이 흐름에 동참하고 있다. 웹과 네이티브간의 코드 공유를 위한 수고를 덜 수 있도록 디자인되었으며 &lt;a href=&quot;https://microsoft.github.io/reactxp/docs/styles.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;플랫폼 독립적(platform-agnostic)인 스타일 구현&lt;/a&gt;도 포함하고 있다.&lt;/p&gt;
&lt;p&gt;–&lt;/p&gt;
&lt;p&gt;당신이 네이티브 앱을 개발하지 않는다 하더라도 크로스 플랫폼 컴포넌트 추상화는 우리에게 사실상 제한이 없는 환경, 또는 전혀 상상하지 못했던 방식을 선택할 수 있도록 해준다는 점에서 꼭 주목해야 한다.&lt;/p&gt;
&lt;p&gt;이와 관련해서 내가 지금껏 봤던 가장 놀라운 사례는 Airbnb의 Jon Gold가 만든 &lt;a href=&quot;http://airbnb.io/react-sketchapp&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;react-sketchapp&lt;/a&gt;이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5F4tw2CknKO4EWqiImOmGM/2aa934a85c0420aa0b1bd09442279cd4/react_sketchapp.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 718px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 22.701949860724234%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAJCAMAAAB30J7MAAAA/FBMVEUAAAD/rgT/rgT+pQn+pQn9pAj/rgT/rgP/rAT+pQn+pQn+pQn/sAP/sAP+pAn/swH/swH/tAD/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT+pQn+pQn/rgT+lRL/rQX/tAH/tAH/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgP/rgT/rgT/rgT/rgT/rgT/rwP/sgL/rgT/sgL/tAD/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rgT/rAIOe/PjAAAAVHRSTlMAJSwbSQJEHUgtHi9eU19STgNUTyMZdEw5DCFHRpDgEDeElLeLqH6ZL5KgkLGfiI+dFLaYVjRYQURVTgI1XUlFX0FjQ1w+TkBbjZ4fWTAdIigXRwHDBmqHAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4bG1nergAAAGtJREFUGNNjYCASMEJIEPiNVyELmGQlrBBsIjfYRMb3QoxvgTwRIPMlg8gbCcbnaAqlQKq+8jA+UGTEAO84uc7ArX5mChF8wC4Cpj8IfuYDUhdVgTZx8zAie8b+uBsj4yYiPCPgBTSBgToAAK31ErN/Si0BAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;react sketchapp&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5F4tw2CknKO4EWqiImOmGM/2aa934a85c0420aa0b1bd09442279cd4/react_sketchapp.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5F4tw2CknKO4EWqiImOmGM/2aa934a85c0420aa0b1bd09442279cd4/react_sketchapp.png?w=180 180w,
https://images.ctfassets.net/rpmifyuylbfw/5F4tw2CknKO4EWqiImOmGM/2aa934a85c0420aa0b1bd09442279cd4/react_sketchapp.png?w=359 359w,
https://images.ctfassets.net/rpmifyuylbfw/5F4tw2CknKO4EWqiImOmGM/2aa934a85c0420aa0b1bd09442279cd4/react_sketchapp.png?w=718 718w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;우리 중 많은 사람은 시스템에서 가능한 중복이 없게 할 목적으로 디자인 언어를 표준화하는 일에 많은 시간을 할애한다. 불행히도 단 하나의 소스(single source of truth)만 가지고 싶지만, 현실적으로 줄일 수 있는 최선은 2개-개발자를 위한 디자인 가이드와 &lt;em&gt;디자이너를 위한 정적인 스타일 가이드&lt;/em&gt;다. 예전에 비하면 많이 좋아진 편이지만 여전히 Sketch같은 디자인 도구를 수동으로 동기화시켜야 한다. 그런 이유에서 react-sketchapp이 등장했다.&lt;/p&gt;
&lt;p&gt;Sketch의 &lt;a href=&quot;http://developer.sketchapp.com/reference/api&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;자바스크립트 API&lt;/a&gt;와 React의 브라우저가 아닌 다른 플랫폼으로 렌더링할 수 있는 능력 덕분에 react-sketchapp은 크로스 플랫폼 React 컴포넌트를 Sketch 문서로 렌더링해준다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/GSgv5QKa2GWYOu8K0EeE6/d388134cb04427a71b6b3036467dad6e/react_sketchapp_profile_card.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 74.125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;react sketchapp profile card&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/GSgv5QKa2GWYOu8K0EeE6/d388134cb04427a71b6b3036467dad6e/react_sketchapp_profile_card.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/GSgv5QKa2GWYOu8K0EeE6/d388134cb04427a71b6b3036467dad6e/react_sketchapp_profile_card.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/GSgv5QKa2GWYOu8K0EeE6/d388134cb04427a71b6b3036467dad6e/react_sketchapp_profile_card.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/GSgv5QKa2GWYOu8K0EeE6/d388134cb04427a71b6b3036467dad6e/react_sketchapp_profile_card.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;말할 필요도 없이, 이것은 디자이너와 개발자가 협력할 수 있는 방식을 완전히 바꿔버릴 수 있는 잠재력이 있다. 이제 디자인에서 반복적으로 사용되는 컴포넌트를 언급할 때 디자이너와 개발자가 각각 어떤 도구를 사용하든 같은 컴포넌트를 참조할 수 있게 될 것이다.&lt;/p&gt;
&lt;p&gt;우리의 업계는 Sketch의 symbols과 React의 컴포넌트와 함께 기본적으로 같은 추상화에 도달하기 시작했다. 이는 같은 도구를 공유하면서 더 긴밀히 협업할 가능성을 열어줄 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;많은 새로운 실험들이 React 커뮤니티와 그 주변에서 이루어지고 있다는 건 우연이 아니다.&lt;/p&gt;
&lt;p&gt;컴포넌트 기반 구조에서는 특정 컴포넌트와 관련된 요소들을 최대한 같은 위치에 두는 일에 높은 우선순위를 둔다. 물론 여기에는 로컬 범위를 가진 스타일도 포함되며 &lt;a href=&quot;https://facebook.github.io/relay&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Relay&lt;/a&gt;나 &lt;a href=&quot;http://dev.apollodata.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Apollo&lt;/a&gt; 덕분에 데이터 가져오기와 같은 복잡한 영역까지 확장된다. 이는 엄청난 잠재력을 가진 무언가의 봉인을 해제한 것과 비슷하며 우리는 아직 그것의 일부분만 확인했을 뿐이다. 그것은 우리의 앱을 스타일링하는 방법뿐만 아니라 구조적으로도 전반적인 영역에 커다란 영향을 미치겠지만, 긍정적인 영향일 것이다.&lt;/p&gt;
&lt;p&gt;하나의 언어로 작성된 컴포넌트 관련 모델을 통합함으로써 기술이 아닌 &lt;em&gt;기능성&lt;/em&gt;을 바탕으로 관심사를 더 잘 분리할 가능성이 생겼다. 컴포넌트와 관련된 모든 것의 범위를 지정하고, 최적화된 방법으로 유지보수가 가능한 한 큰 규모로 확장하고, 작업 결과물을 쉽게 공유하고, 작은 오픈소스들로 큰 규모의 앱을 조합하는 일은 예전에는 가능하지 않았다. 그리고 이 모든 일은 현재 타협 불가능하다고 여겨지는 기존의 웹 플랫폼 개발 원칙들을 버리는 것처럼 급진적인 변화 없이 가능하다는 사실이 가장 중요하다.&lt;/p&gt;
&lt;p&gt;나는 새로운, &lt;em&gt;통합 스타일링 언어&lt;/em&gt;의 기반을 구성할 단일 언어로 작성된 컴포넌트가 가져올 가능성이 무척 기대된다. 통합 스타일링 언어는 우리가 지금껏 보지 못한 방식으로 프론트엔드 커뮤니티를 통합할 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;SEEK에서는 통합 스타일링 언어의 장점을 활용하기 위해 컴포넌트 모델과 관련된 자체적인 스타일 가이드를 만들고 있다. 그것은 의미론적이고 상호작용성과 시각 스타일링이 모두 하나의 추상화 안에 통합되어 있다. 이것은 개발자와 디자이너가 공유할 수 있는 일반적인 디자인 언어를 구성한다.&lt;/p&gt;
&lt;p&gt;페이지를 만드는 일은 작업물이 특징을 가진 컴포넌트를 조합하는 것처럼 간단하면서도 품질을 유지할 수 있어야 한다. 또 제품이 출시된 후에도 디자인 언어를 업그레이드할 수 있어야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  PageBlock&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Card&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Text
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;seek-style-guide/react&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;PageBlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Card&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Text&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;heading&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Hello World!&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;Card&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;PageBlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;비록 지금은 스타일 가이드가 React, webpack과 CSS Modules로 만들어져 있지만 이 구조는 CSS-in-JS로 만들어진 시스템에서 볼 수 있는 구조를 그대로 반영했다. 선택한 기술은 다를 수 있지만 사고방식은 같다.&lt;/p&gt;
&lt;p&gt;하지만 현재의 선택은 향후 예상하지 못한 방법으로 변경해야 할 가능성이 있기에 우리의 컴포넌트 생태계에서 진행 중인 진화를 주의 깊게 지켜보는 일이 무척 중요하다. 지금은 CSS-in-JS를 사용하지 않을 수 있지만, 앞으로는 그 방법으로 바꿔야 할 설득력 있는 이유가 생각보다 빠르게 등장할 가능성이 무척 크다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;CSS-in-JS는 짧은 시간 만에 놀라울 정도로 큰 변화를 만들어냈지만 큰 틀에서는 단지 시작일 뿐이라는 사실을 주목해야 한다.&lt;/p&gt;
&lt;p&gt;여전히 발전의 여지가 많으며 혁신은 멈출 기미가 보이지 않는다. 주목할만한 이슈를 제시하며 개발자 경험을 향상할 라이브러리는 계속해서 등장하고 있다. 성능 향상, 정적 CSS를 빌드시점에서 추출하기, CSS 변수 사용 등과 함께 프론트엔드 개발에 입문하는 사람들의 진입 장벽을 낮추기 위한 시도는 계속되고 있다.&lt;/p&gt;
&lt;p&gt;여기가 CSS 커뮤니티가 필요한 지점이다. 작업 방식의 이 거대한 변화에도 불구하고 &lt;strong&gt;&lt;em&gt;그 어떤 것도 CSS를 제대로 알아야 할 필요가 있다는 사실은 바꾸지 못한다.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다른 방식으로 문법을 표현할 수도 있고 앱을 다른 방식으로 구성할 수도 있지만 근본적인 구성 요소인 CSS는 사라지지 않는다. 마찬가지로 우리의 업계가 컴포넌트 구조로 나아가는 흐름은 불가피하며 이 렌즈를 통해 프론트엔드를 다시 생각해보고 싶은 욕구는 더 강해지고 있다. 우리의 솔루션이 디자인이나 공학, 또는 양쪽 모두의 배경을 가진 개발자들에게 널리 활용될 수 있도록 CSS와 JS 커뮤니티가 함께해야 할 필요가 있다.&lt;/p&gt;
&lt;p&gt;그것이 때로는 어려울 수도 있겠지만, CSS와 JS 커뮤니티는 모두 프론트엔드의 진보를 바라는 열정을 공유하고 있다. 웹 플랫폼을 진지하게 대하며 다음 세대의 웹 사이트를 위한 프로세스를 개선하길 바란다. 여기에는 너무나 큰 가능성이 있고 지금까지도 많은 것들을 이뤄냈지만 여전히 해야 할 일들이 많이 남아 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;여기까지 읽은 당신은 여전히 확신을 얻지 못했을 수도 있지만, 괜찮다. &lt;em&gt;지금 당장&lt;/em&gt;은 CSS-in-JS가 당신이 지금 진행 중인 작업에 확실히 부적합할 수 있다. 하지만 나의 희망은 그것이 단지 문법에 대한 피상적인 거부감이 아닌 &lt;em&gt;적합한 이유&lt;/em&gt;이길 바랄 뿐이다.&lt;/p&gt;
&lt;p&gt;그런데도 이 스타일을 제작에 대한 이 접근법은 향후 몇 년간 더 많은 인기를 끌고야 말 것이다. 그리고 빠르게 성장하는 이 접근법을 꾸준히 눈여겨봐야 할 가치가 있다. 나는 당신이 모든 프론트엔드 개발자에게 유용할 차세대 CSS 개발 도구를 만드는 과정에 코드로 기여하거나, &lt;em&gt;대화에 활발히 참여하는 방식&lt;/em&gt;을 사용해서라도 참여하기를 진심으로 바란다. 그것이 어렵다면 &lt;em&gt;최소한&lt;/em&gt; 내 글이 사람들이 이 일에 왜 그렇게 열정적으로 참여하는지, 왜 그렇게 터무니없는 생각만은 아닌지를 이해하는 데 도움이 되었길 바란다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;이 글은 독일 베를린에서 개최된 CSSconf EU 2017에서 발표된 같은 제목을 가진 세션과 함께 작성되었다. 영상은 &lt;a href=&quot;https://www.youtube.com/watch?v=X_uTCnaRe94&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Youtube에서 시청할 수 있다&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;iframe-video-wrapper&quot;&gt;
  &lt;iframe class=&quot;iframe-video video&quot; src=&quot;https://www.youtube.com/embed/X_uTCnaRe94&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; allowFullScreen&gt;&lt;/iframe&gt;
&lt;/div&gt;</content:encoded></item><item><title><![CDATA[[번역] Flow와 TypeScript의 채택 - 두 프로세스의 비교]]></title><description><![CDATA[이 글은 James Kyle의  Adopting Flow & TypeScript - A comparison between the two on-boarding processes 를 번역한 글입니다. 우리가 타입 검사기를 도입한다고 가정해보자. 앱 개발 중에 NaN값이 …]]></description><link>https://blog.rhostem.com//posts/2017-06-11-adopting-flow-and-typescript</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-06-11-adopting-flow-and-typescript</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 11 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 James Kyle의 &lt;a href=&quot;http://thejameskyle.com/adopting-flow-and-typescript.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Adopting Flow &amp;#x26; TypeScript - A comparison between the two on-boarding processes&lt;/a&gt;를 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;우리가 타입 검사기를 도입한다고 가정해보자.&lt;/p&gt;
&lt;p&gt;앱 개발 중에 NaN값이 많이 발생해서 원인을 찾아보았고 아래의 코드를 찾을 수 있었다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// math.js&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oops&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;한숨을 쉬면서 이제는 정말로 타입 검사기를 도입해야겠다고 결심하게 된다. 그리고 한발 물러서서 우리에게 어떤 대안이 있는지 알아본다: &lt;a href=&quot;https://flow.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Flow&lt;/a&gt;와 &lt;a href=&quot;http://www.typescriptlang.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;TypeScript&lt;/a&gt;가 있다.&lt;/p&gt;
&lt;p&gt;둘다 간단하게 파일별로 채택이 가능하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flow: &lt;code class=&quot;language-text&quot;&gt;// @flow&lt;/code&gt; 코멘트를 파일 최상단에 추가한다.&lt;/li&gt;
&lt;li&gt;TypeScript: 파일의 확장자 &lt;code class=&quot;language-text&quot;&gt;.js&lt;/code&gt;를 &lt;code class=&quot;language-text&quot;&gt;.ts&lt;/code&gt;로 변경한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;하지만 각각 어떤 일이 일어나는지 비교해보자.&lt;/p&gt;
&lt;h2 id=&quot;typescript-채택&quot;&gt;&lt;a href=&quot;#typescript-%EC%B1%84%ED%83%9D&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TypeScript 채택&lt;/h2&gt;
&lt;p&gt;TypeScript를 채택하기 위해서는 우선 확장자 &lt;code class=&quot;language-text&quot;&gt;.js&lt;/code&gt;를 &lt;code class=&quot;language-text&quot;&gt;.ts&lt;/code&gt;로 변경한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// math.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oops&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그러고는 TypeScript를 실행한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;no errors&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;TypeScript는 아래와 같이 우리가 함수의 인자에 직접 타입 주석(annotation)을 작성하도록 요구하기 때문에 오류가 발생하지 않는다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oops&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그런데 타입 주석이 없으면 TypeScript는 설정에 기반을 둬 아래 두 가지 행동 중에서 하나를 실행한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;확인되지 않은 모든 타입을 &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; 타입으로 묵시적으로 형 변환(cast)한다. &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; 타입은 타입 체크를 하지 않는다.&lt;/li&gt;
&lt;li&gt;만약 &lt;code class=&quot;language-text&quot;&gt;--noImplicitAny&lt;/code&gt; 옵션을 사용하고 있다면 확인되지 않은 모든 타입에 대해 에러를 발생시킨다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이는 TypeScript에 의해 &lt;em&gt;적용&lt;/em&gt;(cover)되는 범위는 당신이 직접 작성한 타입에 의해 결정된다는 말이다. 타입 적용 범위는 타입을 작성함에 따라 &lt;em&gt;선형적&lt;/em&gt;으로 증가한다.&lt;/p&gt;
&lt;h2 id=&quot;타입-적용-범위type-coverage&quot;&gt;&lt;a href=&quot;#%ED%83%80%EC%9E%85-%EC%A0%81%EC%9A%A9-%EB%B2%94%EC%9C%84type-coverage&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;타입 적용 범위(Type coverage)&lt;/h2&gt;
&lt;p&gt;조금 더 깊이 들어가기 전에, 타입 적용 범위에 관한 설명을 해야겠다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5dMplAzjKgkOOacYccOCsk/21bef3a51fce1830498e31127eaa3e0a/type-coverage.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 738px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 64.49864498644986%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAaCAMAAADyku75AAABsFBMVEX////y5/T8+fz+/f717ff9/f3/z8//xcX5+fn19fXHls7iyeblzujVsdraud7XtdzTrdn06/bfw+PkzejeweLv4PG0tP+7u/+qqv/ExP/Q0P+iov/R0f+oqP++vr7FntjKlcq4uLj7+/vq2O3dv+Haud/gxeTr2e3r2u7Ytt3kzefhx+XMzP/Gxv+Pj//Ly//Fxf+2tv/Dw//Ozv/IyP/h4f/RptXQmcjBwcHz8/P/9vb/9/f29v/8/P//+vr/9fX/8/P/8fH//v/27ff/urr/x8fw8PD/2Nj/srL//v7QptbSqtfCjMnTrNjs2+7Ekczaut/jy+fLntLn0eqpmrfBsMXS0tK/wdaslK/Ixsbjyub27vfz6fXiyObo1Ovw4/Lp1uzes77ev8rm0tvWqbfNzMz+/v7Ly8uzs7Pa2tr36OiBgYG9vb2vr6+6urqVlZW7u7vHx8eHh4fW1tbT09P78/Pv0tLjsrLnvb3ltbXfpaXir6/ktLS8vLz29vbY2NiXl5fNzc3IyMjMzMzp6enDw8Pq6ur46+vsysrw1dXtzc3alpbw1NTy2trGxsb8/Px0EVznAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAPdJREFUGBndwT0vQ1EAx+Hf/95zUC/JHaQ3TCIRXWwMSAwmMYhPQdJNiJgtNt/B2sRgtBGSrlYJVV0aunvrzanTe1NNY2hiq+dhAIR4ClpA2DKOHtaYwDFiE8Dg5fQjsY28Ul/DiZVcjSlVAINnJ1WfVtvzzEcjr46XWHWIVQHELyuSbljTNXZVt02wTSCkY332idT80JVZeizYuQdXrTrA4Rkym5K21HYeicUakbRcpsuQifQ2drkh6YwJUYJxaaHMX+3SX4An+gvwxL+UK5IpkqOHAXasdLGtzKFSnxzcF47pEmCPAkmvsaS70olS+5y+j+4xWL4BjHA1itgzi04AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;type-coverage&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5dMplAzjKgkOOacYccOCsk/21bef3a51fce1830498e31127eaa3e0a/type-coverage.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5dMplAzjKgkOOacYccOCsk/21bef3a51fce1830498e31127eaa3e0a/type-coverage.png?w=185 185w,
https://images.ctfassets.net/rpmifyuylbfw/5dMplAzjKgkOOacYccOCsk/21bef3a51fce1830498e31127eaa3e0a/type-coverage.png?w=369 369w,
https://images.ctfassets.net/rpmifyuylbfw/5dMplAzjKgkOOacYccOCsk/21bef3a51fce1830498e31127eaa3e0a/type-coverage.png?w=738 738w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;적용되지 않은 코드는 붉은색으로 강조되었다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;당신이 코드에서 값과 표현 식을 살펴본 후 타입 체커에게 무슨 타입인지 알겠냐고 물어봤다고 가정하자.&lt;/p&gt;
&lt;p&gt;만약 타입 체커가 안다고 대답한다면 그 값과 표현 식은 적용되었다고 할 수 있다. 타입 체커가 모른다면 그렇지 않은 것이다.&lt;/p&gt;
&lt;p&gt;코드에서 명시될 수 있는 모든 타입들 중에서 타입 체커가 파악한 타입의 백분율이 “타입 적용 범위”다.&lt;/p&gt;
&lt;p&gt;당신은 당신의 프로그램이 가능한 높은 적용 범위를 가지기를 바랄 것이다. 그럴수록 타입 체커가 더 많은 실수를 확인해줄 수 있기 때문이다.&lt;/p&gt;
&lt;p&gt;타입을 하나도 파악하지 못한다면 타입 체커는 아무것도 아니다.&lt;/p&gt;
&lt;h2 id=&quot;flow-채택&quot;&gt;&lt;a href=&quot;#flow-%EC%B1%84%ED%83%9D&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Flow 채택&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oops&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Flow를 실행시키면 아래와 같은 결과를 볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt;   &lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt;
 &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oops&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; The operand &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; an arithmetic operation must be a number&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;즉시 &lt;em&gt;무엇&lt;/em&gt;인가가 잘못되었다는 타입 에러가 발생한다.&lt;/p&gt;
&lt;p&gt;Flow는 우리에게 직접 타입을 명시하라고 하지 않았다. 그저 소스 코드 상단에 Flow로 타입을 체크하겠다는 플래그를 추가하고 외부 모듈을 설치하기만을 요구했을 뿐이다. 다른 모든 것은 &lt;em&gt;추론&lt;/em&gt;에 의해 가능했다.&lt;/p&gt;
&lt;p&gt;이는 타입 적용 범위가 훨씬 더 빠르게 증가할 수 있도록 해 준다. 타입을 많이 명시하지 않아도 높은 타입 적용 범위를 가진 코드를 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;내 경험상으로는 70-90%의 타입 적용 범위를 가진 소스 파일을 얻을 수 있었다.&lt;/p&gt;
&lt;p&gt;바로 아래에 차이점을 보여주는 아주 과학적인 그래프가 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/38DDoXeIJa2USE6WgoCU26/e7cb50b33690659504dd2065d1b93694/adopting-flow-and-typescript-graph.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 54.39914163090128%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAWCAMAAACFUC6CAAAB8lBMVEX////1+vL//fu84Kn++vb98+j//v3++PH98OT86df748v73cD62Lb61K750Kf5zaLzn0z007L74MX61K/4ypz4ypv86db//Pr98ub86NT73sL61bH5zaH4yZv4yp350an738P85s/969r+9ez++fPU5vajpKHJ3vH//Pn61bD4yJj4yZr5zKD617T74cb86tj+8+nc7f2l0frO5vzs9f762rn67d/7+/v8/Pz8/P38/f37/Pz7+/zg7vuk0PjG4vz+///i4+Pp9P6o0/rA3/z9/v/t8u75+/n19fXu9v6r1Pu63Pv6/f/f6eHr8ez++vf39/fy+f6v1vu12fv4+//c5971+PX4+fn2+/+z2Puw1/vu9/7Y6/3k8f7k7OXz9/S71JL5/P+42/us1fvm8v6t1fvh6uL4+vi5wW/29vb8/f+93vup0/rr9f7l8v6r1Pq93fvv9/7u8++3s1X+9/D+/v/E4fyn0vrW6v283fvh8P3y9vL9/v23slP///7L5Pzf7/3F4vzC4Py4u2PS6P2k0fq5wnHa7P3Q5/y6yX7g4eGm0frJ4/y6z4jk8Puo0vq705Cq1Pr7/f+v0JWu1vu22vu+4a2636i84Kjq9ePn7ujY5Nrk7ebd6N/d597a5tz9/f33+vj5+/r6/Pvx9fL2+fcXXXYVAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAUhJREFUGBmFwb8rhAEcx/H353m+zt0RkR+J5WSwGIR/wCBGmwF1k7IrGRj8SimDMljMynJ1kyP+AItNDEqRFNIhxT3nefw8D3deL3yiNAefAU7O9ExYRN9uwQhUyXdFo97wRrquky6a5bsFI9BwnIioHgWejvh0D1RJOgAEuDl6HioleXJylnfw9OJ6eSefV5mkfQIC3FxHNrHHb72SMrwz+rRTdnbXzi8DktJ8MspFU2cqSsigpC2+GVFRmaKcH4YUoIAR00gG7iiQlE5bPfeRAkZUeoYEX8bl65BO0hQwqNDoMjd8mJB03nLVuMBPDvXxeBxqeTezG186bIudLfCHWaCfwNz8MEUY4ALVwKLnTlKMAS7QtTlVo/Q+RRlggK1ktzOUYIBL98hl0zQlCViVDjb4h4A1idI0hghZF/IlCTFCIkI+wl4BO4JFu/vLiHAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;adopting-flow-and-typescript-graph&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/38DDoXeIJa2USE6WgoCU26/e7cb50b33690659504dd2065d1b93694/adopting-flow-and-typescript-graph.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/38DDoXeIJa2USE6WgoCU26/e7cb50b33690659504dd2065d1b93694/adopting-flow-and-typescript-graph.png?w=466 466w,
https://images.ctfassets.net/rpmifyuylbfw/38DDoXeIJa2USE6WgoCU26/e7cb50b33690659504dd2065d1b93694/adopting-flow-and-typescript-graph.png?w=932 932w,
https://images.ctfassets.net/rpmifyuylbfw/38DDoXeIJa2USE6WgoCU26/e7cb50b33690659504dd2065d1b93694/adopting-flow-and-typescript-graph.png?w=1864 1864w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;이는 나의 개인적인 의견이 아니다. 직접 실행해서 적은 수의 타입이 만들어내는 타입 적용 범위의 차이를 직접 확인해볼 수 있다.&lt;/p&gt;
&lt;p&gt;Flow에서 파일의 타입 적용 범위를 확인하기 위해서는 아래의 명령어를 실행하면 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;flow coverage path/to/file.js --color&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rpl/flow-coverage-report&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;flow-coverage-report&lt;/a&gt;의 도움을 받을 수도 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;나는 내가 알고 있는 TypeScript의 타입 적용 범위 보고서 도구가 없다(만약 알고 있다면 나에게 링크를 전달해주길!). 하지만 코드를 테스트해서 오류를 제대로 보고하고 있는지 확인할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;어떻게-작동하는-것인가&quot;&gt;&lt;a href=&quot;#%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B8%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;어떻게 작동하는 것인가?&lt;/h2&gt;
&lt;p&gt;두 도구가 이렇게 다른 적용 범위를 나타내는 이유는 구조(architecture)상의 차이점에 있다&lt;/p&gt;
&lt;h3 id=&quot;typescript-구조---ast-지향&quot;&gt;&lt;a href=&quot;#typescript-%EA%B5%AC%EC%A1%B0---ast-%EC%A7%80%ED%96%A5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TypeScript 구조 - AST 지향&lt;/h3&gt;
&lt;p&gt;TypeScript는 코드를 &lt;em&gt;진행&lt;/em&gt;하면서 파악된 타입들로 테이블을 구성한다. 값과 표현 식을 발견하는 즉시 그들에게 타입을 부여한다. 타입스크립트는 모르는 타입을 만나면 &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; 타입을 부여할지 에러를 발생시킬지 즉시 결정한다.&lt;/p&gt;
&lt;h3 id=&quot;flow-구조---그래프-지향&quot;&gt;&lt;a href=&quot;#flow-%EA%B5%AC%EC%A1%B0---%EA%B7%B8%EB%9E%98%ED%94%84-%EC%A7%80%ED%96%A5&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Flow 구조 - 그래프 지향&lt;/h3&gt;
&lt;p&gt;Flow는 우선 모든 값과 표현 식에 대해 서로의 관계를 표현하는 그래프를 구성한다. 그러고 나서 각각의 값과 표현 식에 대해 타입을 부여하기 시작한다. 만약 모르는 타입을 만나면 우선 “open” 타입으로 지정한 후 나중에 다시 확인한다.&lt;/p&gt;
&lt;p&gt;Flow가 프로그램 전체 그래프를 완성하고 나면 모든 점을 연결하기 시작하고 타입은 서로에게 전달(flow)된다. Open 타입은 자신에게 전달된 모든 타입을 검토한 후 최종적으로 하나의 타입을 결정하는데 이를 &lt;em&gt;추론&lt;/em&gt;(inferred)된 타입이라고 부른다.&lt;/p&gt;
&lt;p&gt;이렇게 추론된 타입의 발생은 직접 확인할 수 있다. 앞에서 Flow가 발생시킨 타입 에러를 다시 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt;   &lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oops&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; The operand &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; an arithmetic operation must be a number&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;어떻게 &lt;code class=&quot;language-text&quot;&gt;square(&amp;quot;oops&amp;quot;)&lt;/code&gt;보다는 &lt;code class=&quot;language-text&quot;&gt;n * n&lt;/code&gt;에 에러가 표시되었는지 생각해보자. &lt;code class=&quot;language-text&quot;&gt;square&lt;/code&gt; 함수의 인자 &lt;code class=&quot;language-text&quot;&gt;n&lt;/code&gt;에는 타입이 명시되어 있지 않다. 하지만 함수 실행에 사용된 &lt;code class=&quot;language-text&quot;&gt;&amp;quot;oops&amp;quot;&lt;/code&gt;가 문자열이므로 Flow가 &lt;code class=&quot;language-text&quot;&gt;n&lt;/code&gt;이 string 타입이라고 추론했기 때문이다.&lt;/p&gt;
&lt;p&gt;타입 주석을 추가하면 에러 발생 지점이 바뀌는 것을 확인할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; n &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; n&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oops&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt; Error

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Error&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; This type is incompatible &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; the expected param type &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이는 중요한 사실을 상기시킨다: Flow가 타입을 추론한다고 해서 타입 주석을 달지 말아야 한다는 의미는 아니라는 것이다.&lt;/p&gt;
&lt;h2 id=&quot;결론&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;
&lt;p&gt;TypeScript와 Flow는 둘 다 훌륭한 타입 체크 프로세스다. 특히 파일별로 적용할 수 있다는 건 좋은 장점이다.&lt;/p&gt;
&lt;p&gt;하지만 Flow를 사용한다면 높은 타입 적용 범위를 훨씬 빠르게 얻을 수 있고 결과적으로 당신은 편안한 잠을 잘 수 있게 된다.&lt;/p&gt;
&lt;p&gt;Flow를 사용하면 단지 에러를 제거하기 위해서가 아니라 에러를 더 멋지고 정밀하게 표현하기 위해서 타입을 추가할 수 있다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[[번역] 몇가지 HTML 팁]]></title><description><![CDATA[이글은  hacks.mozilla.org 에 게시된  A few HTML tips 를 번역한 글입니다. 얼마전 나는  CSS 작성에 도움이 될 팁 에 대해 글을 썼다. 이번에는 HTML 작성 능력을 업그레이드할 시간이다. 이 글에서 나는 HTML 코딩에 대해 몇가지 …]]></description><link>https://blog.rhostem.com//posts/2017-05-29-few-html-tips</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-05-29-few-html-tips</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 29 May 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이글은 &lt;a href=&quot;https://hacks.mozilla.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;hacks.mozilla.org&lt;/a&gt;에 게시된 &lt;a href=&quot;https://hacks.mozilla.org/2016/08/a-few-html-tips/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;A few HTML tips&lt;/a&gt;를 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;얼마전 나는 &lt;a href=&quot;https://hacks.mozilla.org/2016/05/css-coding-techniques/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CSS 작성에 도움이 될 팁&lt;/a&gt;에 대해 글을 썼다. 이번에는 HTML 작성 능력을 업그레이드할 시간이다. 이 글에서 나는 HTML 코딩에 대해 몇가지 팁과 조언을 공유하려 한다. 단락, 제목, 폼을 적절히 구성하는 방법 등의 몇가지 가이드는 초심자에게 적합하겠지만 SVG 스프라이트를 아이콘으로 사용하는 방법과 몇가지 고급 주제에 대해서도 얘기할 것이다.&lt;/p&gt;
&lt;h2 id=&quot;텍스트text&quot;&gt;&lt;a href=&quot;#%ED%85%8D%EC%8A%A4%ED%8A%B8text&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;텍스트(Text)&lt;/h2&gt;
&lt;h3 id=&quot;단락paragraphs&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EB%9D%BDparagraphs&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단락(Paragraphs)&lt;/h3&gt;
&lt;p&gt;대부분의 글을 단락으로 구성되어 있고 HTML는 이를 위한 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt; 요소가 있다. 텍스트를 &lt;strong&gt;단락처럼 구분하기 위해 &amp;#x3C;br&gt; 태그를 사용하지 말아야 한다.&lt;/strong&gt; 그 태그의 목적은 거기에 있지 않다.&lt;/p&gt;
&lt;h4 id=&quot;안좋은-방법&quot;&gt;&lt;a href=&quot;#%EC%95%88%EC%A2%8B%EC%9D%80-%EB%B0%A9%EB%B2%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;안좋은 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;Cupcake ipsum dolor sit. Amet chupa chups chupa chups sesame snaps. Ice cream pie jelly
beans muffin donut marzipan oat cake.

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

Gummi bears tart cotton candy icing. Muffin bear claw carrot cake jelly jujubes pudding
chocolate cake cheesecake toffee.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;추천하는-방법&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EC%B2%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추천하는 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Cupcake ipsum dolor sit. Amet chupa chups chupa chups sesame snaps. Ice cream
pie jelly beans muffin donut marzipan oat cake.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Gummi bears tart cotton candy icing. Muffin bear claw carrot cake jelly jujubes
pudding chocolate cake cheesecake toffee.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;줄바꿈 태그의 제대로 된 역할은 노래나 시의 절을 변경하는 데 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;So close, no matter how far&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
Couldn’t be much more from the hearth&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
Forever trusting who we are&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
And nothing else matters&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;제목headings&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%AA%A9headings&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제목(headings)&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;부터 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;까지의 제목 태그는 각각 1(가장 중요함)부터 6(덜 중요함)까지의 암시적인 순위를 가지고 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/html5/sections.html#headings-and-sections&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;의미를 바르게 사용하기&lt;/a&gt;위해서는 &lt;strong&gt;제목 태그의 순위를 1부터 6까지 순서대로 사용&lt;/strong&gt;해야 한다. 단지 브라우저가 렌더링해주는 텍스트의 사이즈를 기준으로 제목 태그를 지정해서는 안된다. 원하는 스타일을 위해서는 CSS를 사용할 수 있으니(사실 반드시 사용해야 한다!) 순서에 맞게 올바른 제목 태그를 사용하길 바란다.&lt;/p&gt;
&lt;h4 id=&quot;안좋은-방법-1&quot;&gt;&lt;a href=&quot;#%EC%95%88%EC%A2%8B%EC%9D%80-%EB%B0%A9%EB%B2%95-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;안좋은 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Monkey Island&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Look behind you! A three-headed monkey!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ... --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;추천하는-방법-1&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EC%B2%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-1&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추천하는 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Monkey Island&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Look behind you! A three-headed monkey!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- ... --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;또 고려해야 할 사항은 &lt;strong&gt;제목에 뒤따르는 부제목을 어떻게 작성&lt;/strong&gt;하느냐다. &lt;a href=&quot;https://www.w3.org/TR/html5/common-idioms.html#common-idioms&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;W3C의 추천&lt;/a&gt;에 의하면 낮은 순위의 제목 태그를 사용하기보다 일반 텍스트를 사용하길 권장하고 있다.&lt;/p&gt;
&lt;h4 id=&quot;안좋은-방법-2&quot;&gt;&lt;a href=&quot;#%EC%95%88%EC%A2%8B%EC%9D%80-%EB%B0%A9%EB%B2%95-2&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;안좋은 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Star Wars VII&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The Force Awakens&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;추천하는-방법-2&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EC%B2%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-2&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추천하는 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Star Wars VII&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The Force Awakens&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;폼forms&quot;&gt;&lt;a href=&quot;#%ED%8F%BCforms&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;폼(Forms)&lt;/h2&gt;
&lt;h3 id=&quot;플레이스홀더placeholders&quot;&gt;&lt;a href=&quot;#%ED%94%8C%EB%A0%88%EC%9D%B4%EC%8A%A4%ED%99%80%EB%8D%94placeholders&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;플레이스홀더(Placeholders)&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;input&amp;gt;&lt;/code&gt;의 플레이스홀더 속성은 사용자가 입력해야 할 예제 값을 표시해주며 입력이 시작되면 자동으로 사라진다. 플레이스홀더는 &lt;strong&gt;유효한 값의 형식&lt;/strong&gt;을 표시하기 위한 양식이다.&lt;/p&gt;
&lt;p&gt;그런데 현실에서는 많은 플레이스홀더들이 입력 필드가 가져야 할 유효한 값을 알려주는 대신 그 필드가 &lt;em&gt;무엇&lt;/em&gt;인지를 알려주는 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;label&amp;gt;&lt;/code&gt;처럼 사용되고 있다. 그런 방식은 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;접근성&lt;/a&gt;이 떨어지기에 지양해야 한다.&lt;/p&gt;
&lt;h4 id=&quot;안좋은-방법-3&quot;&gt;&lt;a href=&quot;#%EC%95%88%EC%A2%8B%EC%9D%80-%EB%B0%A9%EB%B2%95-3&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;안좋은 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Your e-mail&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mail&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;추천하는-방법-3&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EC%B2%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-3&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추천하는 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    Your e-mail:
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;darth.vader@empire.gov&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mail&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;모바일-기기의-키보드&quot;&gt;&lt;a href=&quot;#%EB%AA%A8%EB%B0%94%EC%9D%BC-%EA%B8%B0%EA%B8%B0%EC%9D%98-%ED%82%A4%EB%B3%B4%EB%93%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;모바일 기기의 키보드&lt;/h3&gt;
&lt;p&gt;스마트폰이나 태블릿같은 모바일 기기로 접근하는 사람들을 위해 &lt;strong&gt;입력 힌트를 제공&lt;/strong&gt;하는 일은 매우 중요하다. 이는 우리가 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;input&amp;gt;&lt;/code&gt; 태그에 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;올바른 타입&lt;/a&gt;을 지정함으로써 쉽게 제공할 수 있다.&lt;/p&gt;
&lt;p&gt;예를 들어 &lt;code class=&quot;language-text&quot;&gt;type=&amp;quot;number&amp;quot;&lt;/code&gt;는 스마트폰이 일반적인 문자열 키보드 대신 숫자 키패드를 표시하도록 한다. &lt;code class=&quot;language-text&quot;&gt;type=&amp;quot;email&amp;quot;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;type=&amp;quot;tel&amp;quot;&lt;/code&gt; 역시 각각 메일, 전화번호 입력에 알맞은 인터페이스를 제공할 수 있도록 도와준다.&lt;/p&gt;
&lt;h4 id=&quot;안좋은-방법-4&quot;&gt;&lt;a href=&quot;#%EC%95%88%EC%A2%8B%EC%9D%80-%EB%B0%A9%EB%B2%95-4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;안좋은 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Phone number: &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mobile&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;추천하는-방법-4&quot;&gt;&lt;a href=&quot;#%EC%B6%94%EC%B2%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;추천하는 방법:&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Phone number: &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mobile&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;아래의 이미지를 비교해보자. 왼쪽이 &lt;code class=&quot;language-text&quot;&gt;type=&amp;quot;text&amp;quot;&lt;/code&gt;, 오른쪽이 &lt;code class=&quot;language-text&quot;&gt;type=&amp;quot;number&amp;quot;&lt;/code&gt;인 경우 보여지는 키보드의 모습이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5Gao6Cph60oW4Q8GcoAqM0/96915bf3addc9dd95468287a13f027ce/keyboard_compare-500x443.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 500px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 88.6%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAjCAIAAABpW9/5AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5gwbDx4d8jp7mwAABJtJREFUWMPtV1tv4kYU9q+N1Jc+9RdEah66T22fVn2IGml3W6VqGm1z6ybZkHANYBtsbC7G9xtgbC6GhFzafmYI9RJoV7xVyujT6PNhjj/PmRnmHIrjeVlRTMviKxVBFPE4vrkhKBSLhUJh/gi0Wi2aYaRmc3NzM53JVCoVPNq2XeY4URR5nr95anDMZDL9fn9ugS/Lso1GY2trK5/PU/H3xlEql+FcEQR8zdIBfhCMRuP5e+M/cRw38xWEuD3eqN5gOBiGM4T/oA97GC7B0+B2x1s1ZqXvE4ZhSNWl5lUyRTOsrKj5Ik2zpXQ2K6uaZdudjrcKbqvd6fq8ICYur2iWVTS9yLAFmrm8SgrVmu24mtHurG5wpzp+0PG6DqjXtRwXxAH1A7zZ94NVgAuEva6PwZbttDqeYdmm7dhuCy9p4dVt/9/cu12qNwyHo/EagPBgXV/gRfhF+EX4RfhF+H8g3A16Qa+/BnAJ4S7vD8M1MAhHVK8/wO2G3g96uLBA/hN5OWS1UFAx5wCAD+k/B/4ToYjkvP8c1O0h0DTbcMHFTHy7n/fRx5XRiTAq6yGVZ4WlKJZFSTGAQmn5gNQ1MwxHSLviCJ9ZlhvHY8pueZbTdpb15qrejnpZNR4fH/+aNqSYhNzf3xPieZ4sywtGTdMMwyCcKpW5Is0mk6l0OlukGcO0GbYEY73eQE4jNWXdMBuSJFZrIH7QL3M8fmUYlq8Ic2HIPNd4bnQcB7nwTNiwnKaiOS5SpxZUdRNpUwcciRdSKdLDZlrujFsOXNy2pxvWw8PDn5+2u7s7QkzThMaCUVVVZGKEU/WmXpO0NXpOqGMqD9OmKAohk8mEENd1gyBYMOJrkOITTtm2g+9HbovpoldUrSmjsHBUVdN0gzxOJneYH8Ie5aBuKxyNaZqZhxryh4eHC1FFxYA6Y8GYTCbxibNQW0hLsVs6CJ0JGFOO7Wpatqrp4EiScdDtKAFuWxYWyYERp6haqxPh8XiMGGJacY12O8pvF4RrtVpsc/H19ZCnOSze/bQhgITc3t4SgiIFn7JgxKkiJAo1/m9xGDQz2jtIyjueD+JGabrbjdJ6DwRrgfweHD02WiJN//jut939k/muvr6+XpgcLCjRFowo41BWzWZcrdY2NjYMuyVHS2tjRZuyWm9I0JuusonzI4o1UaxyfCWfLxSKzGkit/Xq++03vxBhbCJJkgaDQVxD13XLsuLCCADKSZyomXBTNVXdFutKU43+pz7tzVkv6/E+U+DOLq9PE1lUOjjZXjfA1nNR8gQI+YBs2qh+HI8Jn/6nRsNQ6wDgAPUxVfyQyCVzpZPzDGI4/dvqOO2u5/cWAPvHZAGDZ7jIsHwdKFUaACGG1SITQhhIDKLZmw4Zky2U80wFpCxIFFbr62++++Mi+8WXX+2828M54XjhdnIHB0Qml8vRNF2tVvGIU7R/lHi9/dOrb1//sLO7d3AOsbNEOpWlD47P3v78a1yYm5bqC8LbO29/PzqdCR+eJvcOz8+v8m923x+dpRQVYXOicpHcYrHbGsE4+JDcP7ogeH98MZ/uHHPheJsLzwHhvwG6HSQRxP9P7wAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;keyboard compare-500x443&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5Gao6Cph60oW4Q8GcoAqM0/96915bf3addc9dd95468287a13f027ce/keyboard_compare-500x443.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5Gao6Cph60oW4Q8GcoAqM0/96915bf3addc9dd95468287a13f027ce/keyboard_compare-500x443.png?w=125 125w,
https://images.ctfassets.net/rpmifyuylbfw/5Gao6Cph60oW4Q8GcoAqM0/96915bf3addc9dd95468287a13f027ce/keyboard_compare-500x443.png?w=250 250w,
https://images.ctfassets.net/rpmifyuylbfw/5Gao6Cph60oW4Q8GcoAqM0/96915bf3addc9dd95468287a13f027ce/keyboard_compare-500x443.png?w=500 500w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;h2 id=&quot;이미지&quot;&gt;&lt;a href=&quot;#%EC%9D%B4%EB%AF%B8%EC%A7%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이미지&lt;/h2&gt;
&lt;p&gt;SVG 파일은 아래처럼 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; 태그에 사용할 수 있을 뿐만 아니라&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;acolyte_cartoon.svg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;acolyte&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;웹 폰트를 사용하는 대신 &lt;strong&gt;SVG 스프라이트로 벡터 아이콘을 구현&lt;/strong&gt;하는 일에도 사용할 수 있다. 웹 폰트는 해킹이며 완벽한 결과를 얻지 못할 수도 있다. 그 이유는 브라우저가 웹 폰트를 이미지가 아닌 텍스트로 처리하기 때문이다. 그리고 컨텐츠/광고 차단 프로그램이 웹 폰트를 다운받지 못하게 하는 등의 잠재적인 문제도 존재한다. 이에 대해 더 알아보고 싶다면 웹 폰트보다 SVG를 아이콘으로 사용하는게 왜 더 좋은지에 대해 말해주는 &lt;a href=&quot;http://wordpress.tv/2016/05/28/sarah-semark-stop-using-icon-fonts-love-svg/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Sarah Semark의 프레젠테이션&lt;/a&gt;를 시청해보길 바란다.&lt;/p&gt;
&lt;p&gt;SVG 스프라이트의 아이디어는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Images/Implementing_image_sprites_in_CSS&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CSS 스프라이트&lt;/a&gt;의 그것과 매우 유사하다. SVG의 경우에 모든 자산(assets)은 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;symbol&amp;gt;&lt;/code&gt; 태그에 래핑되어 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;symbol&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;social-twitter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;viewBox&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- 실제 이미지 데이터는 여기에 들어감 --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;symbol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 아이콘은 HTML 안에서 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; 태그와 symbol ID를 명시하는 방식으로 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;social-icon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;use&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;xlink:&lt;/span&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icons.svg#social-twitter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SVG 스프라이트를 만드는 일이 다소 반복적인 작업으로 보이는가? 그래서 여러 SVG 파일들을 모아서 하나의 SVG 스프라이트로 만드는 작업을 처리해주는 &lt;a href=&quot;https://github.com/w0rm/gulp-svgstore&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;gulp-svgstore&lt;/a&gt;같은 자동화 툴이 존재한다.&lt;/p&gt;
&lt;p&gt;그리고 기억하길 바란다. 우리는 사진을 불러오기 위해 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; 대신 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; 태그를 사용했으므로 CSS를 이용해서 스타일을 적용할 수 있다. 웹 폰트로 가능했던 모든 멋진 일들이 이 SVG 아이콘을 통해서도 가능하다는 얘기다!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.social-icon&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; all 0.2s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.social-icon:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #00f&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;비록 CSS에 제한이 있기는 하다. SVG를 이런 방식으로 사용하면서 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;use&amp;gt;&lt;/code&gt; 태그를 이용해 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;symbol&amp;gt;&lt;/code&gt;에 접근하면 이미지가 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Shadow DOM&lt;/a&gt;에 들어가버려서 적용할 수 있는 CSS에 제한이 생기게 된다. 이 경우 스타일링하고 싶은 요소를 골라낼 수 없고 몇몇 속성(ex. fill)은 해당 속성이 정의되어 있지 않은 요소에만 적용될 것이다. 하지만, 이건 웹 폰트에서도 불가능하니까 그렇게 큰 단점이라고 볼 수는 없지 않을까?&lt;/p&gt;
&lt;p data-height=&quot;265&quot; data-theme-id=&quot;0&quot; data-slug-hash=&quot;OXBQZq&quot; data-default-tab=&quot;html,result&quot; data-user=&quot;ladybenko&quot; data-embed-version=&quot;2&quot; data-pen-title=&quot;SVG acolyte demo&quot; class=&quot;codepen&quot;&gt;See the Pen &lt;a href=&quot;https://codepen.io/ladybenko/pen/OXBQZq/&quot;&gt;SVG acolyte demo&lt;/a&gt; by ladybenko (&lt;a href=&quot;https://codepen.io/ladybenko&quot;&gt;@ladybenko&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;이번 글에서 제시한 몇가지 팁들이 도움이 되었길 바란다.&lt;/p&gt;
&lt;script async src=&quot;https://production-assets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;</content:encoded></item><item><title><![CDATA[Angular 2의 양방향 연결(two-way binding) 구현]]></title><description><![CDATA[
 One way sign ( https://unsplash.com/@karinacarvalho ) one-way binding Angular 2(Angular >= 2.x, 이하 Angular로 표기)는 React.js, Vue.js처럼 데이터의 흐름이 단방향(on…]]></description><link>https://blog.rhostem.com//posts/2017-05-11-angular2-two-way-binding</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-05-11-angular2-two-way-binding</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Thu, 11 May 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5BFKOnqQzmW80momaUSa8s/47a28f09df67fbb3b0975f5258c155a6/karina-carvalho-94650-unsplash.jpg&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 66.650390625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAcFBQYFBAcGBgYIBwcICxILCwoKCxYPEA0SGhYbGhkWGRgcICgiHB4mHhgZIzAkJiorLS4tGyIyNTEsNSgsLSz/2wBDAQcICAsJCxULCxUsHRkdLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCz/wAARCAAbACgDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAABQcAAwYE/8QAMBAAAgEDAQUGBAcAAAAAAAAAAQIDAAQRBQYSIVFhBxQiMUFxEzI00RUzgZHB4fH/xAAZAQADAAMAAAAAAAAAAAAAAAAAAQIDBAX/xAAjEQACAQIEBwAAAAAAAAAAAAAAARMCURESIUEDBDFCYaGx/9oADAMBAAIRAxEAPwBgTdpejoIntG77A67zSpkBePIjNdtv2gabqNu/4PBNf3SoW+ABukEAnBz0BPrWQ0+CBI0UIuOWKNWyxhgyqF9hilHxddfQ26cDqg7R1kCK2nKZpCQI0uVJHIHr7ZojJtfC2iLdLZXK3Tj6Yr4lbqfLFBItP0+KWN4bG3V14qyxqCD74qyZt1SPX/KcdS7iU0LHbOHaLaXUJZGjnj8R+GSF3VUg+EHPlx6VK3F9OEiYnzqVhXL5d/gZU+oGsr21QLvanH0KwuQf3Ao1DqFifPUT+luT/NKKzuZhjEho1BczY+c114Vc058dhlJf2Sn698D17ufvVc2oabjL6hKBzWH+6wXeJiPzDVM88oX5zRArjm8GwvL/AENkIbULzjyhX71KXs8rsDk5qVMCuXO7H//Z&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;karina-carvalho-94650-unsplash&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5BFKOnqQzmW80momaUSa8s/47a28f09df67fbb3b0975f5258c155a6/karina-carvalho-94650-unsplash.jpg&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5BFKOnqQzmW80momaUSa8s/47a28f09df67fbb3b0975f5258c155a6/karina-carvalho-94650-unsplash.jpg?w=512 512w,
https://images.ctfassets.net/rpmifyuylbfw/5BFKOnqQzmW80momaUSa8s/47a28f09df67fbb3b0975f5258c155a6/karina-carvalho-94650-unsplash.jpg?w=1024 1024w,
https://images.ctfassets.net/rpmifyuylbfw/5BFKOnqQzmW80momaUSa8s/47a28f09df67fbb3b0975f5258c155a6/karina-carvalho-94650-unsplash.jpg?w=2048 2048w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;One way sign (&lt;a href=&quot;https://unsplash.com/@karinacarvalho&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;https://unsplash.com/@karinacarvalho&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;one-way-binding&quot;&gt;&lt;a href=&quot;#one-way-binding&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;one-way binding&lt;/h2&gt;
&lt;p&gt;Angular 2(Angular &gt;= 2.x, 이하 Angular로 표기)는 React.js, Vue.js처럼 데이터의 흐름이 단방향(one-way binding)이다. 최근 대부분의 모던 자바스크립트 프레임워크들은 단방향 데이터 연결을 지향하고 있다. 그 이유는 앱 구조를 보다 명확하게 할 수 있고 확실한 명령을 통해서만 앱 상태를 변경하기에 사이드 이펙트 발생을 줄이고 컴포넌트간의 데이터 흐름을 직관적으로 만들어주기 때문이다.&lt;/p&gt;
&lt;h2 id=&quot;angularjs의-two-way-binding&quot;&gt;&lt;a href=&quot;#angularjs%EC%9D%98-two-way-binding&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;AngularJS의 two way binding&lt;/h2&gt;
&lt;p&gt;Angular 1(Angular &gt; 1.x, 이하 AngularJS로 표기)에서 많은 인기를 얻었던 특징 중에는 &lt;code class=&quot;language-text&quot;&gt;ng-model&lt;/code&gt; 지시자(directive)를 이용한 양방향 데이터 연결(two-way binding)이 있다. UI에 연결된 데이터가 변경되면 앱의 상태가 자동으로 업데이트되는 기능이다. 예를 들면 input 태그의 value 속성에 변수를 할당한 후 사용자가 텍스트를 입력하면 onChange에 콜백 함수를 할당하지 않아도 자동으로 변수가 입력된 값으로 업데이트된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;// name 속성이 연결된 input에 텍스트를 입력하면 h1 태그 내부의 텍스트가 동시에 변경된다.
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-app&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;myApp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-controller&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;myCtrl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  Name: &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-model&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;You entered: {{name}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;앞서 Angular는 메이저 업데이트를 통해 데이터는 흐름이 단방향으로 바뀌었다고 했다. 그렇다면 자연스럽게 ng-model을 통한 양방향 데이터 연결은 컨셉에 맞지 않으니 제거했을 거라고 생각할 수도 있지만 그렇지 않다. 새로운 Angular에서도 여전히 ng-model 기능을 지원한다. 하지만 실제 구현을 살펴보면 양방향 데이터 연결이 아니라 단방향에 기반을 두고 있다는 사실을 확인할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;banana-in-box-표기법&quot;&gt;&lt;a href=&quot;#banana-in-box-%ED%91%9C%EA%B8%B0%EB%B2%95&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;banana-in-box 표기법&lt;/h2&gt;
&lt;p&gt;Angular에서는 컴포넌트에 속성(property)과 이벤트(event)를 바인딩하는 문법을 구분해서 사용한다. 속성에는 대괄호, 이벤트에는 중괄호를 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;app-two-way-binded&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[data]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prop&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;(dataChange)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;onDataChange($event)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;app-two-way-binded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그리고 Angular에서는 양방향 연결을 위한 특수한 표기법을 지원한다. banana-in-box라는 표기법으로 중괄호와 대괄호를 동시에 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;app-two-way-binded&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[(data)]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prop&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;app-two-way-binded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;저 표기법을 사용하면 예전처럼 프레임워크가 양방향 연결을 구현해 주는 것일까? 그렇지 않다. 대신 Angular는 컴파일시 banana-in-box 표기법을 &lt;strong&gt;속성 바인딩과 이벤트 바인딩으로 자동으로 분리&lt;/strong&gt;한다. 속성 바인딩은 할당된 이름을 그대로 사용하고 이벤트 바인딩에는 속성에 사용한 이름에 &lt;code class=&quot;language-text&quot;&gt;Change&lt;/code&gt;라는 접미사가 붙은 이름을 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;app-two-way-binded&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;[data]&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prop&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;(dataChange)&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prop = $event&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;app-two-way-binded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;prop = $event&lt;/code&gt;부분은 콜백 함수를 선언하지 않고 함수 본문을 직접 할당한 방식이다. &lt;code class=&quot;language-text&quot;&gt;dataChange&lt;/code&gt; 이벤트가 발생하면 부모 컴포넌트의 &lt;code class=&quot;language-text&quot;&gt;prop&lt;/code&gt; 속성에 &lt;code class=&quot;language-text&quot;&gt;$event&lt;/code&gt; 변수가 할당되어서 부모의 상태가 업데이트된다. 그런데 &lt;code class=&quot;language-text&quot;&gt;$event&lt;/code&gt;라는 변수는 어디서 온 것일까? 다음 코드를 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  selector&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app-two-way-binded&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  template&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
    &amp;lt;div&gt;
      &amp;lt;label&gt;data: &amp;lt;/label&gt;
      &amp;lt;input [value]=&quot;data&quot; /&gt;
      &amp;lt;button (click)=&quot;increase()&quot;&gt;+&amp;lt;/button&gt;
    &amp;lt;/div&gt;
  `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TwoWayBinded&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  @&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  @&lt;span class=&quot;token function&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; dataChange &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EventEmitter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;increase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;TwoWayBinded&lt;/code&gt;의 버튼을 클릭하면 &lt;code class=&quot;language-text&quot;&gt;increase&lt;/code&gt; 메소드가 실행된다. &lt;code class=&quot;language-text&quot;&gt;increase&lt;/code&gt; 메소드는 &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt; 속성의 값을 변경한 후 EventEmiter인 &lt;code class=&quot;language-text&quot;&gt;dataChange&lt;/code&gt;의 &lt;code class=&quot;language-text&quot;&gt;emit&lt;/code&gt; 메소드를 실행해서 이벤트를 발생시킨다.
&lt;code class=&quot;language-text&quot;&gt;emit&lt;/code&gt; 메소드의 인자로 전달된 값이 바로 앞선 예제의 &lt;code class=&quot;language-text&quot;&gt;(dataChange)=&amp;quot;prop = $event&amp;quot;&lt;/code&gt; 부분에 등장한 &lt;code class=&quot;language-text&quot;&gt;$event&lt;/code&gt;에 해당한다.&lt;/p&gt;
&lt;p&gt;컴포넌트에서 버튼을 클릭하면 &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt; 속성이 변경되고, 그 값은 다시 부모 컴포넌트로 전달된다. 이렇게 양방향 연결이 구현되었다.&lt;/p&gt;
&lt;h2 id=&quot;getter와-setter를-이용한-양방향-바인딩&quot;&gt;&lt;a href=&quot;#getter%EC%99%80-setter%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%96%91%EB%B0%A9%ED%96%A5-%EB%B0%94%EC%9D%B8%EB%94%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;getter와 setter를 이용한 양방향 바인딩&lt;/h2&gt;
&lt;p&gt;앞서 살펴본 예제에서는 버튼을 클릭해야만 양방향 연결이 작동한다. AngularJS에서 사용했던 것처럼 따로 버튼을 클릭하지 않고도 input 값만 변경되어도 양방향 연결이 되도록 만들어보자. 이를 위해서는 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/get&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;getter&lt;/a&gt;와 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/set&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;setter&lt;/a&gt;를 사용해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  selector&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app-two-way-binded&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  template&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
    &amp;lt;div&gt;
      &amp;lt;label&gt;data: &amp;lt;/label&gt;
      &amp;lt;input [value]=&quot;data&quot; /&gt;
    &amp;lt;/div&gt;
  `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TwoWayBinded&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  componentData&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 컴포넌트 속성&lt;/span&gt;

  @&lt;span class=&quot;token function&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; data &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentData&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataChange&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;componentData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  @&lt;span class=&quot;token function&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; dataChange &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EventEmitter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;핵심은 컴포넌트 로컬 속성을 따로 관리한다는 점이다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt; 속성은 Input 데코레이터가 적용되었기에 컴포넌트와 그 부모에서 모두 값을 참조하고 할당할 수 있다. 대신 단순 참조와 할당을 하지 않고 getter로 로컬 속성인 &lt;code class=&quot;language-text&quot;&gt;componentData&lt;/code&gt;를 반환하고, setter로는 &lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt;에 입력된 값을 로컬 속성(componentData)에 할당한 후 다시 그 값을 부모 컴포넌트로 올려보낸다.&lt;/p&gt;
&lt;p&gt;setter에서 이벤트를 발생시키지 않으면 컴포넌트는 부모 컴포넌트로 변경된 값을 전달하지 않는다. UI에서 값이 변경되고 있지만 그것은 &lt;code class=&quot;language-text&quot;&gt;TwoWayBinded&lt;/code&gt;의 독립된 공간에서만 일어나는 일일 뿐이며 부모 컴포넌트와는 관계가 없게 된다.&lt;/p&gt;
&lt;p&gt;이 예제를 통해 Angular는 단방향 데이터 흐름을 가지며, 직접 지시를 하지 않으면 데이터가 아래에서 위로 거슬러 올라가는 일은 발생하지 않는다는 사실을 확인할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;ngmodel과-banana-in-box&quot;&gt;&lt;a href=&quot;#ngmodel%EA%B3%BC-banana-in-box&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ngModel과 banana-in-box&lt;/h2&gt;
&lt;p&gt;banana-in-box 표기법에 ngModel이라는 이름을 사용하면 AngularJS에서 사용했던 것처럼 프레임워크가 자동으로 양방향 바인딩을 구현해준다. 하지만 &lt;code class=&quot;language-text&quot;&gt;input&lt;/code&gt;같은 HTML 폼 요소에서 사용가능하고 커스텀 컴포넌트에서는 앞서 제시한 getter, setter를 이용한 방법 등으로 사용자가 직접 구현해야 한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  selector&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app-ngModel-sample&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  template&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`
    &amp;lt;label for=&quot;search&quot;&gt;
      search:
      &amp;lt;input [(ngModel)]=&quot;search&quot; name=&quot;search&quot;/&gt;
    &amp;lt;/label&gt;
  `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NgModelSample&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  search&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;syntactic-sugar&quot;&gt;&lt;a href=&quot;#syntactic-sugar&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Syntactic sugar&lt;/h2&gt;
&lt;p&gt;Angular에서 양방향 바인딩은 &lt;a href=&quot;https://en.wikipedia.org/wiki/Syntactic_sugar&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;syntactic sugar&lt;/a&gt;(문법을 사용하기 쉽게 표현한 형태)일 뿐이며 단방향으로 구현되어 있다. 프레임워크가 어려운 일을 대신 해주면 사용하는 입장에서는 편하지만 표준에서는 멀어진다는 단점이 있다. React.js가 &lt;a href=&quot;https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;컴포넌트 메소드 자동 바인딩을 제거한 사례&lt;/a&gt;도 그런 철학에 기반한 것으로 여겨진다. 좋은 도구를 사용하면서도 항상 원리에 접근하려는 자세를 가져야 하겠다.&lt;/p&gt;
&lt;h2 id=&quot;참고자료&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0%EC%9E%90%EB%A3%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://angular.io/docs/ts/latest/guide/template-syntax.html#!#two-way&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Angular Docs - Two way binding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.thoughtram.io/angular/2016/10/13/two-way-data-binding-in-angular-2.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;TWO-WAY DATA BINDING IN ANGULAR&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[함수형 프로그래밍: partial application과 curry]]></title><description><![CDATA[함수형 프로그래밍에 관심을 두게 된 계기는 React-Redux였다. Redux의 reducer를 통해 단순 연산 정도만 가능할 줄 알았던 reduce 함수의 또 다른 매력을 알게 되고, redux-middleware를 통해 재귀 함수의 멋진 구현 사례를 접하고, R…]]></description><link>https://blog.rhostem.com//posts/2017-04-20-curry-and-partial-application</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-04-20-curry-and-partial-application</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Thu, 20 Apr 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;함수형 프로그래밍에 관심을 두게 된 계기는 React-Redux였다. Redux의 reducer를 통해 단순 연산 정도만 가능할 줄 알았던 reduce 함수의 또 다른 매력을 알게 되고, redux-middleware를 통해 재귀 함수의 멋진 구현 사례를 접하고, React 컴포넌트를 작성할 때 higher-order 함수를 이용하면 객체지향의 상속보다는 다소 유연한 코드 공유가 가능하다는 사실을 알게 되면서 조금씩 함수형 프로그래밍에 대한 관심을 두게 되었다. 그리고 ES7이 decorator 문법을 지원하는 것을 보면서 함수형 프로그래밍을 본격적으로 공부해 보자는 마음을 먹게 되었다.&lt;/p&gt;
&lt;p&gt;공부에 주로 활용한 서적은 &lt;a href=&quot;https://github.com/getify/Functional-Light-JS&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Functional-Light JavaScript&lt;/a&gt;인데 단순 설명이 아닌 근본에 접근하려는 집필 방향과 다양한 예제 코드를 제공한다는 점에서 무척 좋았다. 이 글에서는 책의 3장에서 소개하는 partial application과 curry에 대해 정리해보고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;partial-application&quot;&gt;&lt;a href=&quot;#partial-application&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;partial application&lt;/h2&gt;
&lt;p&gt;partial application은 함수 인자의 일부를 미리 전달해 둔 함수를 생성한다. 다시 말하자면 함수와 복수의 인자를 전달받아서 더 적은 수의 인자로 그 함수를 실행할 수 있는 함수를 만든다. 함수의 인자를 고정한다고도 볼 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;partial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;presetArgs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;partiallyApplied&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;laterArgs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;presetArgs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;laterArgs &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 코드에서 작성한 partial 함수는 특정 함수와 인자들(presetArgs)을 전달받아서 함수를 리턴한다. 리턴된 함수(partiallyApplied)가 호출될 때 미리 전달받은 인자들(presetArgs)을 더한 후 애초에 호출하려고 했던 함수(fn)를 호출한다. 말보다는 조금 직관적인 예제를 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;adder&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; add10 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;partial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;adder&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;add10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 30&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;partial 함수를 이용해서 첫번째 인자 a에 10이 고정된 새로운 함수를 만들었다. 위의 예제에서 함수가 실행된 &lt;code class=&quot;language-text&quot;&gt;add10(20)&lt;/code&gt; 부분을 풀어서 작성하면 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;partiallyApplied&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;laterArgs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;adder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;laterArgs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;partial에 의해 만들어진 partiallyApplied 함수에 고정되지 않은 나머지 인자를 전달해서 adder 함수를 호출하는 과정을 확인할 수 있다. partial application이 어떤 원리로 동작하는지는 알았고 뭔가 그럴듯해 보이긴 한다. 하지만 아직은 쓸만해 보이지 않으니 더욱 실용적인 예제를 들어보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ajax&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;endPoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; search &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; getUser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;partial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ajax&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/user&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;getUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;A1&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 예제에서는 ajax 함수에 요청 경로를 사전에 할당해 둔 후 search 객체만 전달해서 사용할 수 있는 getUser라는 함수를 만들었다. 이렇게 일부 인자를 고정해서 호출할 필요가 있으면 partial application을 사용하면 중복 코드를 줄이고 적절한 함수명을 통해 가독성을 높일 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;curry&quot;&gt;&lt;a href=&quot;#curry&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;curry&lt;/h2&gt;
&lt;p&gt;curry는 partial application의 특수한 형태다. partial application이 미리 전달받아둘 수 있는 인자의 수에 제한이 없다면 curry는 1개만 가능하다. 그리고 원래의 함수가 요구하는 인자의 수(Function.arity, 또는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Function.length&lt;/a&gt;)가 모두 전달될 때까지 재귀적으로 curry 함수를 생성한다. 그리고 함수 호출에 필요한 인자가 모두 전달되면 curry 함수를 만들지 않고 원래의 함수를 호출한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ajax&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/user&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;A1&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 예제에서는 ajax 함수를 curry한 후 함수를 2번 더 호출했다. curried 함수를 호출할 때마다 변수에 할당해서 보다 직관적으로 작성하자면  아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; curriedGet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ajax&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; curriedGetUser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curriedGet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/user&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;curriedGetUser&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;A1&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;curry 함수의 실제 구현을 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;nextCurried&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prevArgs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curried&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nextArg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; args &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; prevArgs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;nextArg&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; arity&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;nextCurried&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; args &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여기에 제시한 curry의 구현에는 재귀, 클로져와 &lt;a href=&quot;https://en.wikipedia.org/wiki/Immediately-invoked_function_expression&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;IIFE&lt;/a&gt;를 사용했다. curry 함수를 호출하면 nextCurried 함수도 즉시 실행된다. 즉 curry 함수를 호출했을 때 리턴되는 값은 2개의 함수로 둘러싸인 curried 함수다.&lt;/p&gt;
&lt;p&gt;curry가 실행되면 prevArgs의 초깃값을 빈 배열로 우선 할당해 둔다. 그리고 curried 함수가 실행될 때 전달받은 인자(nextArg)를 상위 컨텍스트에 위치한 prevArgs 배열에 하나씩 추가하는 방식이다. 그리고 누적된 인자의 수가 curried 함수 생성에 사용된 함수(fn) 인자의 수보다 같거나 많은 때 실제 함수를 실행한다. 그렇지 않으면 nextCurried 함수를 호출해서 다시 curried 함수를 리턴한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; d&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; c &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; curriedSum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sum&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curriedSum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;curry를 사용한 간단한 예제다. partial application과는 달리 curried 함수 생성에 인자 없이 함수만 전달하고 원래의 함수 호출 결과를 얻기 위해서는 인자의 수만큼 curried 함수를 호출해야 한다는 사실을 확인할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;curry와-partial-application&quot;&gt;&lt;a href=&quot;#curry%EC%99%80-partial-application&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;curry와 partial application&lt;/h2&gt;
&lt;p&gt;partial application와 curry의 차이점은 대략 파악이 되었다. 하지만 함수를 재사용하겠다는 목적은 같고 구현과 사용 방식에만 차이가 있는 것 같은데, curry는 어디에 사용하면 좋을까?&lt;/p&gt;
&lt;p&gt;partial application은 함수에서 복수의 인자를 한번에 고정시켜두고 싶을 때 사용하면 유용하다. 하지만 함수 조합(function composition)에 적용하려면 이야기가 달라진다. 왜냐하면 partial application이 반환하는 함수는 요구하는 인자의 수가 서로 다를 수 있기 때문이다. 다음과 같이 3개의 함수를 조합한다고 생각해보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;h&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;함수를 조합하는 경우 모두 1개의 인자를 요구한다. 하지만 만약 조합하려는 함수에 정의된 인자의 수가 1개가 아니라면? curry를 이용해 1개의 인자만 전달받아도 되는 함수로 만든 후 조합하면 될 것이다.&lt;/p&gt;
&lt;p&gt;이렇듯 curry는 일관성을 제공하기 때문에 함수 조합 같은 경우에 유용하게 사용될 수 있는데, 이는 마치 Promise에서 resolve, reject 함수를 1개의 인자로만 호출하는 것과 유사하다. resolve 함수가 1개의 값만 전달한다는 것을 알기에 어떤 함수가 Promise를 리턴한다면 사용자는 then 콜백 함수에서 하나의 인자만 전달받는 일관적인 방식으로 함수를 작성할 수 있는 것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;partial application, curry를 직접 구현해서 사용해도 무방하지만 역시 &lt;a href=&quot;http://ramdajs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Ramda&lt;/a&gt;, &lt;a href=&quot;https://github.com/lodash/lodash/wiki/FP-Guide&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;lodash/fp&lt;/a&gt;같은 라이브러리를 사용하는 편이 함수형 프로그래밍을 더 즐거워지게 할 수 있다. 특히 Ramda는 lodash만큼 양적으로 다양하지는 않지만, 데이터 조작에 필수적인 함수를 대부분 제공하며 특히 자동으로 curry가 이루어지기에 함수 재사용과 조합을 더 쉽게 할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;참고&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/getify/Functional-Light-JS/blob/master/ch3.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Functional-Light JavaScript Chapter 3: Managing Function Inputs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/curry-or-partial-application-8150044c78b8&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Curry or Partial Application?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ramdajs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Ramda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[견고하고 확장성있는 CSS 구조를 위한 8가지 규칙]]></title><description><![CDATA[CSS는 쉬운 듯하면서도 어렵다. 복잡한 로직은 없지만 이름 그대로 중첩(cascading)되는 성질 때문에 기대했던 결과가 나오지 않는 경우가 무척 많다. 때에 따라서는 디버깅에 자바스크립트 버그 수정보다 많은 시간을 투자해야 하는 경우가 생기기도 한다. 까다로운 …]]></description><link>https://blog.rhostem.com//posts/2017-04-11-8-simple-rules-for-a-robust-scalable-css-architecture</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-04-11-8-simple-rules-for-a-robust-scalable-css-architecture</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Tue, 11 Apr 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;CSS는 쉬운 듯하면서도 어렵다. 복잡한 로직은 없지만 이름 그대로 중첩(cascading)되는 성질 때문에 기대했던 결과가 나오지 않는 경우가 무척 많다. 때에 따라서는 디버깅에 자바스크립트 버그 수정보다 많은 시간을 투자해야 하는 경우가 생기기도 한다. 까다로운 CSS를 상대하기 위해 &lt;a href=&quot;https://smacss.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;SMACSS&lt;/a&gt;, &lt;a href=&quot;http://getbem.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;BEM&lt;/a&gt;같은 방법론을 적용하기도 하고 &lt;a href=&quot;https://github.com/css-modules/css-modules&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CSS module&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/Web_Components/Shadow_DOM&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;shadow DOM&lt;/a&gt;처럼 기술적인 대안을 마련하기도 한다. 하지만 모든 것을 만족하게 할 수 있는 방법론은 없기에 CSS의 특성을 잘 이해한 상태에서 앞서 언급한 기술들을 필요한 곳에 적용해야 제대로 된 스타일 시트 작성이 가능할 것이다.&lt;/p&gt;
&lt;p&gt;얼마 전에 읽은 &lt;a href=&quot;https://github.com/jareware/css-architecture/blob/master/README.md#7-respect-component-boundaries&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;‘8 simple rules for a robust, scalable CSS architecture’&lt;/a&gt;라는 글에서는 어떻게 하면 좋은 CSS를 작성할 수 있는지 아주 잘 정리해 두었다. 지금껏 다양한 글에서 읽어 왔던 CSS에 대한 조언을 하나로 합쳐 정리한 글을 읽은 듯한 느낌이었다. 앞으로 프론트엔드 개발을 하면서 계속 참고해야 할 내용인 것 같아서 정리해 보려고 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;목표&quot;&gt;&lt;a href=&quot;#%EB%AA%A9%ED%91%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;목표&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;컴포넌트 지향: 컴포넌트 기반의 UI에 맞춘 CSS&lt;/li&gt;
&lt;li&gt;샌드박스: 로컬 컴포넌트의 스타일을 외부 스타일로부터 보호&lt;/li&gt;
&lt;li&gt;편리함: 사용하는 데 어려움이 없어야 함&lt;/li&gt;
&lt;li&gt;안전한 경우 오류 허용: 로컬 컴포넌트 스타일 기반이지만 전역 스타일은 예외적으로 허용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;항상-클래스-이름-셀렉터를-우선한다&quot;&gt;&lt;a href=&quot;#%ED%95%AD%EC%83%81-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%9D%B4%EB%A6%84-%EC%85%80%EB%A0%89%ED%84%B0%EB%A5%BC-%EC%9A%B0%EC%84%A0%ED%95%9C%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;항상 클래스 이름 셀렉터를 우선한다.&lt;/h2&gt;
&lt;p&gt;CSS에 아이디(ex. #something), 태그(ex. div, a, p, …)를 사용하지 않는 편이 좋다. 아이디는 하나의 요소에만 적용할 수 있으니 당연히 피하는 것이 좋고 태그는 오류 발생 가능성이 커진다. 스타일 시트가 수백 줄 이상으로 길어질 경우 이미 특정 태그에 스타일을 지정해 둔 상태에서 해당 태그에 클래스를 지정해서 스타일을 추가하는 경우가 발생할 수 있다. 그러면 태그에서 선언한 스타일이 병합되어서 의도하지 않은 결과가 나올 수 있다. 그리고 컴포넌트 내부에 자식 컴포넌트가 존재할 경우 태그 스타일은 전파가 되기 때문에 자식 컴포넌트에서 해당 태그를 사용할 경우 외부 스타일이 유입되는 결과를 낳게 된다.&lt;/p&gt;
&lt;h2 id=&quot;컴포넌트-코드들은-같은-폴더에-둔다&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%BD%94%EB%93%9C%EB%93%A4%EC%9D%80-%EA%B0%99%EC%9D%80-%ED%8F%B4%EB%8D%94%EC%97%90-%EB%91%94%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트 코드들은 같은 폴더에 둔다.&lt;/h2&gt;
&lt;p&gt;마크업, 스크립트, 스타일 시트, 테스트코드 파일들은 같은 폴더에 함께 위치시키는 것이 작업하기에 편하다. Angular 2를 사용할 경우 angular-cli를 이용해 컴포넌트를 생성시키면 아래와 같은 형태로 4개의 파일을 동시에 만들어준다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ui/
├── layout/
|   └── Header.html            // 컴포넌트 마크업
|   ├── Header.js              // 컴포넌트 코드
|   ├── Header.scss            // 컴포넌트 스타일
|   └── Header.spec.js         // 컴포넌트 테스트 코드&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;클래스-이름을-일관적으로-작성한다&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%9D%B4%EB%A6%84%EC%9D%84-%EC%9D%BC%EA%B4%80%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%9E%91%EC%84%B1%ED%95%9C%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클래스 이름을 일관적으로 작성한다.&lt;/h2&gt;
&lt;p&gt;마크업과 스타일 시트 작성 시 클래스 이름명을 그때그때 생각나는 대로 붙여버리면 그 순간은 편하지만, 언젠가 디버깅할 때 괴로움이 반드시 찾아오게 된다. 그나마 직접 작성한 코드라면 어렴풋이 기억이라도 나겠지만, 타인의 코드라면? 게다가 정해진 기준 없이 클래스를 여기저기 붙여버렸다면? 하나를 건드리면 다른 한곳에 구멍이 뚫리는 루프를 경험할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;이와 같은 문제를 방지하고 공동 작업의 효율을 위해 제안된 것이 SMACSS, BEM, &lt;a href=&quot;https://suitcss.github.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;SUIT&lt;/a&gt;같은 방법론들이다. 각각 조금씩 다르지만 기본적으로 block(컴포넌트), entity(컴포넌트 하위 요소), modifier(하위 요소 중에서 일부만 변경된 경우), utility(버튼 같은 공유 스타일), state(hover 등의 상태 변경 시 일부 스타일이 변경되는 경우) 등으로 클래스를 구분해서 마크업과 스타일 시트를 작성하자는 내용이다. 개인적으로는 SUIT에서 제안하는 방법을 주로 사용하고 있다.&lt;/p&gt;
&lt;h2 id=&quot;컴포넌트-파일명과-클래스-이름을-확실히-대응시킨다&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%8C%8C%EC%9D%BC%EB%AA%85%EA%B3%BC-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%9D%B4%EB%A6%84%EC%9D%84-%ED%99%95%EC%8B%A4%ED%9E%88-%EB%8C%80%EC%9D%91%EC%8B%9C%ED%82%A8%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트 파일명과 클래스 이름을 확실히 대응시킨다.&lt;/h2&gt;
&lt;p&gt;특히 셀렉터에 컴포넌트 명을 붙여주면 디버거에서 클래스 이름명만 확인해도 어떤 컴포넌트에서 만들어진 요소인지 바로 확인할 수 있다는 장점이 있다. 여기서 한발 더 나아가 클래스 이름 앞에 앱의 이름을 붙이는 방법도 제안할 수 있는데 이 경우 복수의 프로젝트 사이에서 코드를 공유하는 경우 도움이 될 것이다.&lt;/p&gt;
&lt;h2 id=&quot;컴포넌트-외부로-스타일이-전파되지-않도록-한다&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%99%B8%EB%B6%80%EB%A1%9C-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%9D%B4-%EC%A0%84%ED%8C%8C%EB%90%98%EC%A7%80-%EC%95%8A%EB%8F%84%EB%A1%9D-%ED%95%9C%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트 외부로 스타일이 전파되지 않도록 한다.&lt;/h2&gt;
&lt;p&gt;앞서 언급한 규칙 중에서 태그를 사용하지 말라는 것과 관련이 있는 내용이다. CSS module을 사용하지 않을 경우 부모에서 사용한 클래스 이름명과 자식 컴포넌트에서 사용한 클래스 이름명이 중복될 가능성이 얼마든지 있기에 스타일이 유입될 가능성도 함께 존재한다. 이를 방지하기 위해서는 모든 클래스 이름명에 컴포넌트의 이름을 붙여주는 방법을 사용하는 것이 가장 확실하다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.myapp-Header-btn &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.myapp-Header-link &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 이렇게 작성할 경우 중복 텍스트가 너무 많아지기에 어느 순간 본능이 요구하는 대로 클래스 이름명을 짧게 사용하고 싶은 마음이 샘솟게 될 것이다. 이를 방지하기 위해서는 Sass, Less, &lt;a href=&quot;https://github.com/postcss/postcss&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;PostCSS&lt;/a&gt;의 &lt;a href=&quot;https://github.com/jonathantneal/precss&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;precss&lt;/a&gt; 플러그인 등이 제공하는 부모 참조 선택자(&lt;code class=&quot;language-text&quot;&gt;&amp;amp;&lt;/code&gt;)를 사용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.myapp-Header &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;-btn &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; black&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;-link &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;외부-스타일이-컴포넌트로-전파되지-않도록-하라&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%9D%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A1%9C-%EC%A0%84%ED%8C%8C%EB%90%98%EC%A7%80-%EC%95%8A%EB%8F%84%EB%A1%9D-%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부 스타일이 컴포넌트로 전파되지 않도록 하라.&lt;/h2&gt;
&lt;p&gt;태그 또는 일반적인 클래스 이름(ex. btn)을 사용하면 자식 요소에 스타일이 전파될 가능성이 커진다. 굳이 태그를 사용해야겠다면 자식 요소 선택자(&lt;code class=&quot;language-text&quot;&gt;&amp;gt;&lt;/code&gt;)를 사용하는 방법이 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.myapp-header &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt; &gt; a &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;하지만 스타일 시트 작성시 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/CSS/Specificity&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;명시도&lt;/a&gt;는 가능하면 높이지 않는 편이 좋다. 높은 명시도의 단점은 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;의도하지 않은 곳에 스타일이 적용될 수 있다. (과연 &lt;code class=&quot;language-text&quot;&gt;li &amp;gt; a&lt;/code&gt; 에 해당하는 마크업이 처음에 생각한 곳 하나뿐일까?)&lt;/li&gt;
&lt;li&gt;마크업 구조가 변경되면 셀렉터도 함께 변경해야 해서 리팩토링을 어렵게 만든다.&lt;/li&gt;
&lt;li&gt;스타일을 적용하기 위해서는 셀렉터에 대응하는 마크업을 사용해야만 하므로 재사용성이 떨어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;부모 컴포넌트로부터 스타일이 유입되는 일은 어느 정도 방지할 수 있다. 하지만 &lt;a href=&quot;http://cssreset.com/scripts/eric-meyer-reset-css/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;reset.css&lt;/a&gt;, &lt;a href=&quot;https://necolas.github.io/normalize.css/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;normalize.css&lt;/a&gt;같은 전역 스타일이나 사용자가 직접 지정한 전역 스타일이 유입되는 일은 근본적으로 방지할 수는 없다.(특히 Bootstrap을 사용할 경우 수천개의 &lt;em&gt;일반적인&lt;/em&gt; 클래스 이름을 가진 스타일 부대의 유입을 방지해야 한다)&lt;/p&gt;
&lt;p&gt;iframe을 사용하면 부모 컴포넌트와 완벽하게 분리할 수는 있지만 추가 페이지 로드 부담이 생긴다. 그리고 shadow DOM은 브라우저 지원 이슈 때문에 아직은 적극적으로 사용하기는 어려운 상황이다.&lt;/p&gt;
&lt;h2 id=&quot;컴포넌트의-경계를-중시하라&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EA%B2%BD%EA%B3%84%EB%A5%BC-%EC%A4%91%EC%8B%9C%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포넌트의 경계를 중시하라.&lt;/h2&gt;
&lt;p&gt;예를 들어 헤더, 네비게이션 바, 컨텐츠 컴포넌트를 포함한 부모 컴포넌트가 있다면 자식 컴포넌트들의 레이아웃과 관련된 스타일을 작성해야 할 것이다. 하지만 이 경우 결코 컴포넌트 내부의 스타일은 건드리지 말고 CSS box model 바깥의 스타일(position, margin, display, width, float, z-index)만 선언해야 한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6oiMEIKhMsAaqwG8sG2Y0O/8415261129523430d62903b201d32efe/box-model.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 192px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 89.58333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;box-model&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6oiMEIKhMsAaqwG8sG2Y0O/8415261129523430d62903b201d32efe/box-model.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6oiMEIKhMsAaqwG8sG2Y0O/8415261129523430d62903b201d32efe/box-model.png?w=48 48w,
https://images.ctfassets.net/rpmifyuylbfw/6oiMEIKhMsAaqwG8sG2Y0O/8415261129523430d62903b201d32efe/box-model.png?w=96 96w,
https://images.ctfassets.net/rpmifyuylbfw/6oiMEIKhMsAaqwG8sG2Y0O/8415261129523430d62903b201d32efe/box-model.png?w=192 192w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;h2 id=&quot;외부-스타일을-느슨하게-연결하라&quot;&gt;&lt;a href=&quot;#%EC%99%B8%EB%B6%80-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%9D%84-%EB%8A%90%EC%8A%A8%ED%95%98%EA%B2%8C-%EC%97%B0%EA%B2%B0%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;외부 스타일을 느슨하게 연결하라&lt;/h2&gt;
&lt;p&gt;반복적인 스타일은 공유하게 된다. 하지만 전역 스타일을 지정한 후 마크업에 외부 클래스를 직접 연결하는 것보다는 @extend 같은 키워드를 이용해 스타일 시트에서 직접 추가하는 방법이 더 좋다. 마크업에 클래스 이름을 나열하는 것보다는 스타일 시트를 통해 외부에서 상속받은 스타일과 컴포넌트에 직접 추가한 스타일을 한번에 확인하는 편이 더 직관적이다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;myapp-Button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.myapp-Button &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;@extend&lt;/span&gt; .btn&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;외부 스타일을 적용하는 방법으로는 Sass의 extend/mixin, &lt;a href=&quot;http://cssnext.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;cssnext&lt;/a&gt;의 &lt;a href=&quot;http://cssnext.io/features/#custom-properties-set-apply&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;custom properties set &amp;#x26; @apply&lt;/a&gt;를 이용하는 방법 등이 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;--danger-theme: &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.danger &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  @apply --danger-theme&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;p&gt;CSS를 아키텍처를 제대로 구성하는 일은 생각보다 어려운 일이기에 Bootstrap같은 프레임워크가 높은 인기를 가지게 된 것은 자연스러운 현상이다. 하지만 모든 것을 외부 프레임워크에 의존할 수는 없다. React, Angular, Vue같은 컴포넌트 기반의 라이브러리가 널리 사용되는 상황에서 CSS 작성도 그에 맞춰서 컴포넌트 기반으로 견고하게 작성할 수 있도록 해야 할 것이다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[[번역] 프로그래머가 되고 싶은가? 그렇다면 대학 학위를 취득하는 일은 큰 시간 낭비일 수 있다]]></title><description><![CDATA[이 글은  Eric Elliott 의  Want to Code? A University Degree Might be a Huge Waste of Time 를 번역한 글입니다. 많은 사람은 소프트웨어 엔지니어가 되기 위해서는 대학 학위가 필요하다고 생각한다. 얼마 전 …]]></description><link>https://blog.rhostem.com//posts/2017-03-06-a-university-degree-might-be-a-huge-wasteof-time</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-03-06-a-university-degree-might-be-a-huge-wasteof-time</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 06 Mar 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://medium.com/@_ericelliott&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Eric Elliott&lt;/a&gt;의 &lt;a href=&quot;https://medium.com/javascript-scene/want-to-code-a-university-degree-might-be-a-huge-waste-of-time-81e1817a2ef0#.jdu89qa4a&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Want to Code? A University Degree Might be a Huge Waste of Time&lt;/a&gt;를 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;많은 사람은 소프트웨어 엔지니어가 되기 위해서는 대학 학위가 필요하다고 생각한다.&lt;/p&gt;
&lt;p&gt;얼마 전 나는 &lt;a href=&quot;https://medium.com/javascript-scene/learn-to-code-13-tips-that-could-save-you-years-of-effort-92ce799a3e1f&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“Learn to Code: 13 Tips that Could Save You Years of Effort”&lt;/a&gt;라는 포스트를 작성했다. 그 글에서 제시한 첫번째 팁은 “대학 커리큘럼은 잊어라”였다. 전문은 아래와 같다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;대학 수업 과정은 잊어라. 스탠퍼드나 MIT가 아니라면 당신의 학위는 남들에게 보여줄만한 앱을 개발한 이력보다 큰 의미가 없을 수도 있다. 사실 대부분의 대학 수업들은 변화하는 기술을 따라잡기 위해 고군부투하고 있다. 대학 학위가 커리어의 처음 1~3년간은 학위가 없는 사람보다 몇천 달러를 더 벌게 해 줄 수 있을지 모른다. 하지만 그 이후에는 전혀 상관이 없다. 마치 화장실에서 시간과 돈을 흘려보내고 싶지 않다면…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;나는 이런 얘기를 많이 해 왔지만 데이터에 근거한 자세한 글을 쓴 적은 없었다. 이런 민감한 주제에 대해 강하게 주장하라면 그만한 증거를 제시하는 것이 좋을 것이다.&lt;/p&gt;
&lt;p&gt;다음이 현실이다. (&lt;a href=&quot;http://stackoverflow.com/research/developer-survey-2016&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;2016 Stack Overflow servey&lt;/a&gt;에서 56033명의 프로그래머를 대상으로 한 조사에서 얻은 데이터)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;고액 연봉은 대학 학위보다 멘토링 프로그램과 더 강한 연관관계를 가지고 있다&lt;/li&gt;
&lt;li&gt;석사 학위 취득자와 bootcamp 졸업자 사이의 연봉 차이는 1% 이하다.&lt;/li&gt;
&lt;li&gt;69%의 현직 소프트웨어 개발자는 교육기관을 통하지 않고 직접 공부했다.&lt;/li&gt;
&lt;li&gt;43%가 실무 교육을 가장 주요한 학습 수단으로 꼽았다.&lt;/li&gt;
&lt;li&gt;25%가 온라인 교육을 이용했다.&lt;/li&gt;
&lt;li&gt;19%만이 컴퓨터 과학과 관련된 학위를 가지고 있다..&lt;/li&gt;
&lt;li&gt;8.5%만이 컴퓨터 과학 학사 학위를 가지고 있다.&lt;/li&gt;
&lt;li&gt;6.5%는 Bootcamp를 졸업했다.(이 수치는 점점 더 높아지고 있다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;소프트웨어 엔지니어링 직무에서 ‘동등한 경험’이란 96% 정도는 ‘일을 한 시간이 동일하다’라는 의미를 내포하고 있다.(개발자 직무의 4% 정도만이 고급 수학, 과학을 요구한다)&lt;/p&gt;
&lt;p&gt;일을 시작한 지 3년이 지난 시점이 되어 그때까지 만들어낸 멋진 소프트웨어들을 보여준다면 &lt;strong&gt;누구도 당신이 대학을 다녔는지 상관하지 않는다&lt;/strong&gt;. 또 이러한 이유로 경험을 쌓으면 쌓을수록 학위가 있는 사람과 없는 사람의 연봉에는 차이가 줄어들게 된다.&lt;/p&gt;
&lt;h2 id=&quot;학위가-더-빠르고-덜-비싼-선택지보다-더-많은-기회의-문을-열어주지는-않는다&quot;&gt;&lt;a href=&quot;#%ED%95%99%EC%9C%84%EA%B0%80-%EB%8D%94-%EB%B9%A0%EB%A5%B4%EA%B3%A0-%EB%8D%9C-%EB%B9%84%EC%8B%BC-%EC%84%A0%ED%83%9D%EC%A7%80%EB%B3%B4%EB%8B%A4-%EB%8D%94-%EB%A7%8E%EC%9D%80-%EA%B8%B0%ED%9A%8C%EC%9D%98-%EB%AC%B8%EC%9D%84-%EC%97%B4%EC%96%B4%EC%A3%BC%EC%A7%80%EB%8A%94-%EC%95%8A%EB%8A%94%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;학위가 더 빠르고 덜 비싼 선택지보다 더 많은 기회의 문을 열어주지는 않는다.&lt;/h2&gt;
&lt;p&gt;대학의 컴퓨터 과학 커리큘럼이 당신에게 알고리즘, 데이터 구조, 컴퓨터 과학 기초와 관련된 탄탄한 기초 지식을 제공하는 것은 사실이다. 이는 확실한 사실이며 충분히 가치 있는 일이다. 당신은 이론에 있어 훨씬 더 튼튼한 기반을 가지게 될 것이다.&lt;/p&gt;
&lt;p&gt;여기서 핵심은 &lt;strong&gt;이론에서&lt;/strong&gt;이다.&lt;/p&gt;
&lt;p&gt;문제는 대부분 대학은 실제 소프트웨어 엔지니어링에 큰 도움을 주지 못한다는 사실이다. 엔지니어링은 응용 기술에 관한 것이다. 이론이 아니다.&lt;/p&gt;
&lt;p&gt;대부분의 대학은 잘 알려진 다양한 알고리즘들을 가르친다. 그 알고리즘들은 대부분 최근의 프로그래밍 언어에서는 잘 사용되지 않는 것들이다. 왜냐면 더 좋은 대체물들이 이미 언어 자체나 표준 라이브러리에 포함되어 있기 때문이다.&lt;/p&gt;
&lt;p&gt;학생들이 정말로 배워야 하는 것은 소프트웨어가 지금과는 아주 다른 모습을 가진 30년도 전에 첫 번째 판이 집필된 책의 솔루션을 공부하는 것이 아니라 그들 스스로 문제를 어떻게 해결하는지다.&lt;/p&gt;
&lt;p&gt;그리고 무엇보다 데이터 구조와 알고리즘은 성능에 관한 해결책을 찾기 위한 기술이다.&lt;/p&gt;
&lt;h2 id=&quot;고등-교육과-정렬-알고리즘&quot;&gt;&lt;a href=&quot;#%EA%B3%A0%EB%93%B1-%EA%B5%90%EC%9C%A1%EA%B3%BC-%EC%A0%95%EB%A0%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;고등 교육과 정렬 알고리즘&lt;/h2&gt;
&lt;p&gt;컴퓨터 과학 커리큘럼의 초반부는 알고리즘을 가르치는 데 집착한다. 당신은 아마 학기 대부분의 시간을 그 알고리즘들을 공부하는데 보내게 될 것이다. 6개의 서로 다른 알고리즘 중에서 가장 효율적인 알고리즘 1개를 고를 수 있는 능력을 갖춰야 하기 때문인가?&lt;/p&gt;
&lt;p&gt;아니다. 절대로 아니다. 알고리즘을 배우는 이유는 성능 특성이 크게 다른 여러 종류의 정렬 알고리즘이 존재하기에 성능 프로파일링에 대한 흥미로운 연구가 가능하고, 알고리즘의 성능 특성을 이해하는 방법인 big O 표기법을 배울 방법이기 때문이다.&lt;/p&gt;
&lt;p&gt;문제는 big O 표기법이 유용하다 하더라도 당신은 ‘더 많은 작업 = 더 나쁜 퍼포먼스’라는 간단한 직관으로 문제를 가늠할 수 있다는 점이다.&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;요점은 이거다. 아주 큰 사이즈의 컬렉션이나 몹시 나쁜 알고리즘을 상대하고 있는 것이 아니라면 학생들에게 네트워크, 디스크, 렌더링의 비용이 더 많이 든다는 사실을 가르치는 것이 &lt;code class=&quot;language-text&quot;&gt;Array.prototype.sort()&lt;/code&gt; 보다 모두 느린 6개의 서로 다른 알고리즘을 가르치는 것보다 낫다는 말이다.&lt;/p&gt;
&lt;p&gt;기존 알고리즘들을 공부하고 비교하는 일이 유용하다는 사실은 나도 모르는 건 아니지만 정말로… 이건 바보스러운 일이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cs.cmu.edu/~adamchik/15-121/lectures/Sorting%20Algorithms/sorting.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;CMU&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cs.rutgers.edu/~mlittman/courses/cs105-06b/lectures/11sorting.pdf&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Rutgers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-3-insertion-sort-merge-sort/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MIT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-4-heaps-and-heap-sort/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MIT…&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-5-binary-search-trees-bst-sort/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MIT, 하나 더…&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-6-avl-trees-avl-sort/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;또 MIT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-7-counting-sort-radix-sort-lower-bounds-for-sorting/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MIT, 끝이 있기는 한가?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그렇다. 그들은 다양한 데이터 구조 전략을 가진 아주 다양한 알고리즘들을 아주 많은 수업에서 다루고 있다. 하지만 시간이 지나 학생들이 마침내 믿을만한 quick sort를 만났을 때 그들은 이미 관심을 잃어버렸을 것이다. 그들은 요점을 놓쳐버렸고, 의문만이 남을 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;만약 quick 정렬이 merge, heap 정렬보다 그렇게 빠르다면 왜 처음부터 merge, heap 정렬을 배워야 했을까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;저런 의문을 가지게 되는 건 건 전적으로 자연스럽고 정당한 결과다.&lt;/p&gt;
&lt;h2 id=&quot;현실-세계&quot;&gt;&lt;a href=&quot;#%ED%98%84%EC%8B%A4-%EC%84%B8%EA%B3%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;현실 세계&lt;/h2&gt;
&lt;p&gt;현실에서는 저 알고리즘 중에 어떤 것을 선택해도 네트워크 사용량이나 디스크 접근량 줄이기, 또는 계산을 지연시키는 것만큼 큰 효과를 내지 못한다는 것이 판명되었다.&lt;/p&gt;
&lt;p&gt;그리고 웹 개발에서 데이터를 다룸에 있어 알고리즘 효율이 매우 중요한 경우가 생겼을 경우에는 성능 측정에 네트워크, 디스크 접근량, 데이터 스트림이 매우 큰 골칫거리가 된다. 그리고 어떤 알고리즘의 구현 가능성도 공유 메모리의 사용에 가장 큰 의존성을 가진다.&lt;/p&gt;
&lt;p&gt;이론에 치우쳐 시간을 낭비하는 동안 학생들은 성능 측정에 있어 왜곡된 인식을 하게 된다. 그리고 그 이론들은 모던 애플리케이션에서는 고려하지 않는 물리적인 현실성이 없는 것들이다. 학생들은 실제 앱에서는 실질적인 차이가 없는 성능 특성을 고민하도록 배우고 있는 것이다.&lt;/p&gt;
&lt;p&gt;좋은 소식은 당신은 저런 가치가 없는 내용을 거르고 동시성, 스트림, 머신 클러스터와의 연동, 지연(lazy) 처리를 공부할 수 있다는 점이다. 오래된 책에서 가져왔으며 이제는 시대에 뒤처진 1980년도의 하드웨어에 최적화되어 있고 지금껏 천만번 이상은 구현된 비현실적인 공유 메모리 정렬 알고리즘을 공부하는 대신 말이다.&lt;/p&gt;
&lt;h2 id=&quot;그래도-다양한-데이터-구조와-알고리즘을-알아야-할-필요는-없는가&quot;&gt;&lt;a href=&quot;#%EA%B7%B8%EB%9E%98%EB%8F%84-%EB%8B%A4%EC%96%91%ED%95%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%84-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%ED%95%84%EC%9A%94%EB%8A%94-%EC%97%86%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;그래도 다양한 데이터 구조와 알고리즘을 알아야 할 필요는 없는가?&lt;/h2&gt;
&lt;p&gt;대학은 정렬 알고리즘을 정말 좋아한다. 문제는 정말로 알아야 할 알고리즘은 딱 하나만 있다는 사실이다. 그것은 당신이 사용하는 언어나 표준 라이브러리에 구현된 알고리즘이다. 진짜로 알고 있어야 하는 건 그 알고리즘의 API에 대해서 숙지하는 것이다.&lt;/p&gt;
&lt;p&gt;가장 빠른 메모리 기반의 일반적인 용도의 알고리즘은 언어나 표준 라이브러리에 구현되어 있고 다른 조건들이 거의 모든 애플리케이션에서 더 중요하다. (네트워크, 디스크 접근, 레이지 또는 즉시 계산, …).&lt;/p&gt;
&lt;p&gt;난 지난 20년 동안 표준 정렬 알고리즘의 대안을 사용한 일이 딱 한 번 있었다. 어쩌면 당신은 결코 사용할 일이 없을지도 모른다.&lt;/p&gt;
&lt;p&gt;나는 다양한 정렬 알고리즘에 대한 지식을 가지고 의식적으로 어떤 방법을 적용할지 고려하는 한 대부분의 경우 실제 애플리케이션의 성능 문제를 파악하기 어려울 것으로 생각한다. “선택의 역설”이 이와 관련이 있다.&lt;/p&gt;
&lt;div class=&quot;iframe-video-wrapper&quot;&gt;
&lt;iframe class=&quot;iframe-video video&quot; src=&quot;https://embed.ted.com/talks/barry_schwartz_on_the_paradox_of_choice&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; allowFullScreen&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;성능에 관한 결론은 프로파일링을 먼저 하고 테스트를 한 후 애플리케이션에 정말로 병목 현상이 생기는 부분이 어디인지 파악하는 것이다. 문제를 찾았으면 그것을 고치는 일에 집중하면 된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;섣부른 최적화는 만 가지 악의 근원이다 ~ Donald Knuth&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;big O 표기법에 대한 요약서를 읽어보고 기초에 대한 이해는 다져주자.&lt;/p&gt;
&lt;p&gt;하지만 일을 하면서는 머릿속에 적절한 해결책으로 떠오른 첫 번째 알고리즘을 선택해서 사용하면 된다. 그것이 좋은 방법이다. 테스트를 통해 수정할 필요성이 증명되면 그때 수정하면 된다.&lt;/p&gt;
&lt;p&gt;이런 말이 처음에는 이상하게 들리겠지만, 실제 작업을 하다 보면 big O 계산을 하지 않더라도 적절한 알고리즘을 잘 선택하는 자신을 발견하게 될 것이다.&lt;/p&gt;
&lt;p&gt;이것이 현실에서 프로그래밍이 이루어지는 방식이다. 위대한 개발자는 작업할 때 문제에 대해 의식적으로 생각하고 고려하는 경우가 그렇게 많지 않다. 마찬가지로 위대한 음악가는 의식적으로 음표를 생각하며 연주를 하지 않는다. 그저 음악을 느끼고 그들 스스로 연주할 뿐이다.&lt;/p&gt;
&lt;p&gt;일단 프로파일링을 하고 문제가 발생하는 지점을 찾았으면 구글을 검색해서 좋은 방법을 확인하면 된다. 그리고 그 선택을 과거의 유물이 되어버린 컴퓨터 과학 도서의 저자로부터의 조언이 아니라 현재 작업 중인 애플리케이션의 머신 아키텍처를 기반으로 할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;유료-대학-프로그램들은-최악이다&quot;&gt;&lt;a href=&quot;#%EC%9C%A0%EB%A3%8C-%EB%8C%80%ED%95%99-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EB%93%A4%EC%9D%80-%EC%B5%9C%EC%95%85%EC%9D%B4%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;유료 대학 프로그램들은 최악이다&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;대학 커리큘럼은 구글이 나오기 전의 시대에 맞춰져 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;대학 프로그램 수강은 경제적인 면에서나 커리큘럼의 면에서나 정말로 나쁜 선택이다.&lt;/p&gt;
&lt;p&gt;내가 이런 비판을 하게 된 가장 큰 동기는 미국의 평균 대학 등록금은 연당 18,400달러라는 사실이다. 대학을 졸업한 후 학생들은 주니어 레벨의 프로그래머로 경력을 시작하게 되고 학위가 없는 사람보다 1년에 대략 1만 달러를 더 벌 수 있다. 하지만 학위가 없는 사람보다 더 많은 버는 기간은 오직 처음 3년뿐이다. 말하자면 7만 6천 달러를 들여서 얻은 학위로 얻을 수 있는 이익은 3만 달러이며 손해는 약 4만 6천 달러라고 할 수 있다. 그리고 이 수치는 평균값일 뿐이며 사람에 따라 더 큰 손해를 볼 수도 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;많은 미국 대학들은 당신이 일하면서 결코 돌려받을 수 없는 큰돈을 아주 즐겁게 뺏어가고 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;대학 교육이 무료인 나라에서는 상황이 이만큼 나쁘지는 않다.&lt;/p&gt;
&lt;p&gt;나는 많은 대학의 프로그램을 살펴봐 왔다. 나는 그 학교들이 무엇을 가르치고 있는지 알고 있다.&lt;/p&gt;
&lt;p&gt;슬픈 현실은 대부분의 프로그램이 C/C ++ 기반을 둔 현실과는 다소 동떨어진 오래된 이론들에 집중하고 있다는 점이다. 또 LISP는 흥미로 접하기도 하며 몇몇은 Python이나 Javascript 겉핥기를 하기도 한다.&lt;/p&gt;
&lt;p&gt;안타깝게도 함수형 프로그래밍이나 프로토타입 상속 같은 다양한 프로그래밍 패러다임을 소개하지 않는 프로그램은 당신에게 클래스 상속이나 서툰 객체지향 디자인 같은 나쁜 습관을 지니도록 만들 수 있다. 그러한 습관들은 당신을 도와주기보다는 높은 수준의 소프트웨어를 만들기 어렵게 하고 좋은 직업을 가지기 어렵게 만든다.&lt;/p&gt;
&lt;p&gt;이러한 이유로 인해 대학의 컴퓨터 과학 커리큘럼에는 사실상 빨간불이 들어와 있다. 나는 대학을 갓 졸업한 개발자가 훌륭한 코드 샘플을 제출하지 않고 심층 면접에서 중요한 이론을 제대로 이해하고 있다는 것을 증명하지 못한다면 절대 채용하지 않는다.&lt;/p&gt;
&lt;p&gt;하지만 대학의 커리큘럼에 저렇게 문제가 많고, 또 학위를 얻는데 치러야 하는 비용이 저렇게 놓다 하더라도 대학의 좋은 컴퓨터 과학 커리큘럼은 쉽게 접할 수 없는 보물이기도 하다. 좋은 소프트웨어 개발자는 꾸준히 공부한다.&lt;/p&gt;
&lt;h2 id=&quot;인맥의-가치&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%EB%A7%A5%EC%9D%98-%EA%B0%80%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인맥의 가치&lt;/h2&gt;
&lt;p&gt;정말 너무하다 싶을 정도로 비싼 대학 프로그램의 진정한 가치는 당신이 대학에서 만날 수 있는 사람들이다. 하지만 인맥의 수준과 기회는 매우 불균등하게 분포되어 있다. 만약 당신이 스탠포드에 갈 수 있다면 그 인맥들은 정말로 높은 가치를 가질 것이다.&lt;/p&gt;
&lt;p&gt;또 주립 대학이나 아이비리그에 속해있지 않은 대학을 졸업한 후에도 실리콘 밸리의 창업센터나 투자 생태계에 곧바로 들어간다면 SOMA와 샌프란시스코의 어떤 카페에서도 4달러의 커피보다 훨씬 높은 가치의 인맥을 쌓을 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;어디서 사람들을 만날지 잘 모르겠는가? &lt;a href=&quot;Meetup.com&quot;&gt;Meetup.com&lt;/a&gt;에서 시도해보라.&lt;/p&gt;
&lt;h2 id=&quot;대학을-가는-대신-무엇을-해야-할까&quot;&gt;&lt;a href=&quot;#%EB%8C%80%ED%95%99%EC%9D%84-%EA%B0%80%EB%8A%94-%EB%8C%80%EC%8B%A0-%EB%AC%B4%EC%97%87%EC%9D%84-%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;대학을 가는 대신 무엇을 해야 할까?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/learn-javascript-b631a4af11f2&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;그냥 코딩을 시작하기&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;좋은 스승을 만나기&lt;/li&gt;
&lt;li&gt;bootcamp를 찾아보기&lt;/li&gt;
&lt;li&gt;온라인 교육 수강&lt;/li&gt;
&lt;li&gt;다른 개발자와의 만남&lt;/li&gt;
&lt;li&gt;블로그, 책, 기타 등등&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;ko&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&lt;a href=&quot;https://twitter.com/_ericelliott&quot;&gt;@_ericelliott&lt;/a&gt; Thanks! FWIW: My university education was free and I still consider it time well spent (I learned Haskell in 1999, etc.).&lt;/p&gt;&amp;mdash; Axel Rauschmayer (@rauschma) &lt;a href=&quot;https://twitter.com/rauschma/status/715182318223302659&quot;&gt;2016년 3월 30일&lt;/a&gt;
&lt;/blockquote&gt;
&lt;p&gt;(역주: &lt;a href=&quot;http://www.2ality.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;2ality.com&lt;/a&gt;의 Dr. Axel Rauschmayer이 Erric Elliott에게 전달한 트윗)&lt;/p&gt;
&lt;h2 id=&quot;예외&quot;&gt;&lt;a href=&quot;#%EC%98%88%EC%99%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;예외&lt;/h2&gt;
&lt;p&gt;만약 &lt;em&gt;정말로 훌륭한&lt;/em&gt; 대학 프로그램을 찾았다면 너무 비싸거나 돈이 없다 하더라도 어떤 방법이라도 써서 그 프로그램에서 얻을 수 있는 모든 것을 가져와야 한다. 나의 대학에 대한 비판은 &lt;strong&gt;수만 달러를 학비로 손쉽게 가져가고도 졸업 후의 수입을 통해 돌려받지 못하게 하며&lt;/strong&gt; 정말로 배워야 할 내용을 가르치지 않는 대부분의 미국 대학들을 향한 것이다.&lt;/p&gt;
&lt;h2 id=&quot;최첨단의-연구--과학-직무는-학위를-선호한다&quot;&gt;&lt;a href=&quot;#%EC%B5%9C%EC%B2%A8%EB%8B%A8%EC%9D%98-%EC%97%B0%EA%B5%AC--%EA%B3%BC%ED%95%99-%EC%A7%81%EB%AC%B4%EB%8A%94-%ED%95%99%EC%9C%84%EB%A5%BC-%EC%84%A0%ED%98%B8%ED%95%9C%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;최첨단의 연구 &amp;#x26; 과학 직무는 학위를 선호한다.&lt;/h2&gt;
&lt;p&gt;대학 학위가 확실히 차이를 만드는 분야가 몇몇 있으며 보수도 평균보다 높다. 그런 자리는 Stack Overflow에 의하면 개발자 직군 중에서 약 4%에 해당한다.&lt;/p&gt;
&lt;p&gt;머신러닝 전문가, 데이터 과학, 생명과학, 그리고 양자컴퓨터 전문가들은 고학력자일 가능성이 높다. 또 증강현실 플랫폼의 개발자들은 높은 수준의 수학이 필요하다.&lt;/p&gt;
&lt;p&gt;앞서 말한 직무와 관련된 내용을 온라인에서 배울 수는 있다. 하지만 학위는 저 분야에서 일할 사람이 가져야 할 능력에 대한 신뢰도를 높여준다. 만약 당신이 저 분야 중 하나에 대해 열정을 가지고 있으며 또 일하고 싶은 마음이 있다면, 대학에 가서 학위를 취득하라.&lt;/p&gt;
&lt;p&gt;하지만 대학에 갔더라도 공부 중인 분야에 순수한 관심이 없고 수학이나 과학에 대한 적성이 없다면, 당신은 돈과 시간을 낭비하고 있을 수도 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;모든 농구선수가 NBA에서 뛸 수는 없다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;운 좋게도 저들이 지금껏 만들어 온 플랫폼을 기반으로 앱을 만들기 위해서는 그렇게 많은 훈련이 필요하지 않다. 하지만 또 다른 Oculus Rift나 Tesla 자동주행 기술을 만들고 싶다면 관련 스타트업을 배출한 이력이 있는 대학을 찾아가길 바란다.&lt;/p&gt;
&lt;p&gt;그리고 고급 연구 학위 없이도 수익을 배로 늘릴 방법은 많이 있다. 하지만 그러기 위해서는 기업가적 성향이나 좋은 운이 있어야 할 것이다.&lt;/p&gt;
&lt;h2 id=&quot;결론&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;
&lt;p&gt;만약 또 다른 Tesla, Oculus Rift, AI Go 챔피언을 만들고 싶고 남들과는 다른 수학과 과학에 대한 적성을 가지고 있다면 학위를 취득해야 한다.&lt;/p&gt;
&lt;p&gt;만약 최신 기술 스택을 기반으로 훌륭한 앱을 만드는 좋은 직업을 가지고 싶다면 학위를 가질 필요가 없다. 그건 엄청난 돈과 시간 낭비다.&lt;/p&gt;
&lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;</content:encoded></item><item><title><![CDATA[[번역] Redux 아키텍쳐를 위한 10가지 팁]]></title><description><![CDATA[이 글은  Eric Elliott 의  10 Tips for Better Redux Architecture 를 번역한 글입니다. 처음 React를 사용하기 시작했을 때는 Redux가 없었다. Flux 아키텍쳐만 있었고 Flux의 여러 구현체들이 경쟁하고 있었다. 이제…]]></description><link>https://blog.rhostem.com//posts/2017-01-23-10-tips-for-better-redux-architecture</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-01-23-10-tips-for-better-redux-architecture</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 23 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://medium.com/@_ericelliott&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Eric Elliott&lt;/a&gt;의 &lt;a href=&quot;https://medium.com/javascript-scene/10-tips-for-better-redux-architecture-69250425af44#.ffjkpicnd&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;10 Tips for Better Redux Architecture&lt;/a&gt;를 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;처음 React를 사용하기 시작했을 때는 Redux가 없었다. Flux 아키텍쳐만 있었고 Flux의 여러 구현체들이 경쟁하고 있었다.&lt;/p&gt;
&lt;p&gt;이제는 React의 데이터 관리 분야에 확실한 선두주자가 둘 있다. 바로 Redux와 MobX다. 심지어 후자는 Flux 구현체도 아니다. 그간 많은 관심을 받아온 Redux는 이제 React에만 사용되지 않는다. Angular 2를 포함한 많은 프레임워크를 위한 Redux 아키텍쳐 구현체들이 존재한다. 예를 들면 &lt;a href=&quot;https://github.com/ngrx/store&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ngrx:store&lt;/a&gt;가 있다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Side note: MobX는 멋지며 나는 이미 간단한 UI에는 Redux 대신 사용하고 있다. 왜냐하면 덜 복잡하고 더 깔끔하기 때문이다. 바꿔 말하면 Redux는 MobX가 제공하지 않는 몇몇 중요한 기능이 있다는 말이다. 프로젝트에 사용할 라이브러리를 선택하기 전에 그 차이점이 무엇인지 이해하는 것이 중요하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Side note: &lt;a href=&quot;https://facebook.github.io/relay/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Relay&lt;/a&gt;와 &lt;a href=&quot;https://netflix.github.io/falcor/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Falcor&lt;/a&gt;는 흥미로운 상태 관리 솔루션이다. 하지만 Redux와 MobX와는 달리 두 솔루션은 각각 GraphQL과 Falcor Server가 뒷받침해야 사용할 수 있다. 그리고 모든 Relay 상태는 서버상에 유지되고 있는 데이터와 연결되어 있다. 내가 아는 한 둘 다 클라이언트 기반의 일시적인 상태 관리를 위한 좋은 시나리오를 제공하지는 않는다. 서버에 유지되는 상태와 클라이언트의 상태를 구분지으며 Relay와 Falcor를 Redux와 Mobx와 조합해서 양쪽의 장점을 모두 취할 수는 있다. 결론: 현재 시점에서 클라이언트 상태(state) 관리 영역에는 확실한 승자가 없다. 그저 하는 각각의 작업에 맞는 도구를 선택해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Redux를 만든 Dan Abramov는 주제와 관련된 좋은 강의를 만들어 두었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/getting-started-with-redux&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Getting Started with Redux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://egghead.io/courses/building-react-applications-with-idiomatic-redux&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Building Applications with Idiomatic Redux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;둘다 Redux의 기초를 알려주는 무척 훌륭한 단계별 자습서지만 Redux를 최대한 활용하려면 더 깊은 이해가 필요하다.&lt;/p&gt;
&lt;p&gt;다음은 당신이 더 나은 Redux 앱을 만드는데 도움을 줄 팁들이다.&lt;/p&gt;
&lt;h2 id=&quot;1-redux의-장점을-이해하라&quot;&gt;&lt;a href=&quot;#1-redux%EC%9D%98-%EC%9E%A5%EC%A0%90%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. Redux의 장점을 이해하라&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;결정적인(deterministic) 뷰 렌더&lt;/li&gt;
&lt;li&gt;결정적인 상태 재생산&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;결정론(determinism)은 애플리케이션 테스트와 진단, 버그 픽스에 중요하다. 만약 당신의 앱이 비결정적이라면 뷰와 상태가 언제나 유효한 값을 가진다고 장담할 수 없다. 비결정적인 상태 자체가 버그라고도 말할 수 있다.&lt;/p&gt;
&lt;p&gt;하지만 어떤 것들은 본질적으로 비결정적이다. 유저의 입력 타이밍 또는 네트워크 입출력이 그렇다. 그렇다면 코드가 정말로 작동하는지 어떻게 알 수 있을까? 답은 간단하다. 분리시키는 것이다.&lt;/p&gt;
&lt;p&gt;Redux의 주 목적은 뷰의 렌더링 과정이나 네트워크 입출력과 같은 사이드 이펙트로부터 상태 관리를 독립시키는 것이다. 사이드 이펙트가 분리된다면 코드는 훨씬 간결해진다. 또 훨씬 이해하기 쉽고 DOM 업데이트와 네트워크 요청과 분리된 상태의 비즈니스 로직을 테스트하기도 쉬워진다.&lt;/p&gt;
&lt;p&gt;뷰 렌더링이 네트워크 입출력과 상태 업데이트와 분리되면 결정적인 뷰 렌더링이 된다. 이는 같은 상태값이 주어졌을 때 언제나 같은 결과값이 표시된다는 말이다. 그리고 렌더링 과정에서 불규칙적으로 상태값을 없애버리거나 조작해버리는 비동기적인 작업들의 경쟁에서 오는 사이드 이펙트의 발생 가능성도 제거한다.&lt;/p&gt;
&lt;p&gt;경험이 적은 사람은 뷰를 생성하는 일에 대해 보통 이렇게 생각한다. “이건 유저 모델이 필요할 것 같으니 우선 데이터를 가져올 비동기 요청을 하고, 프라미스(promise)가 해결(resolve)되면 모델에서 이름을 가져와서 유저 컴포넌트의 이름을 업데이트해야지. 그 위에는 할일 목록이 필요하니까 데이터를 요청하고(fetch) 프라미스가 해결되면 반복문을 이용해서 뷰에다 그려야겠다.”&lt;/p&gt;
&lt;p&gt;이러한 접근 방식에는 주요한 문제점이 몇 가지 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;어떤 시점에도 완전한 뷰를 렌더링하기 위한 모든 데이터를 가지고 있지 못하다. 컴포넌트가 관련 작업을 하기 전까지는 데이터 요청을 실제로 시작하지 않는다.&lt;/li&gt;
&lt;li&gt;서로 다른 데이터 요청 작업은 서로 다른 시점에 끝나며 뷰 렌더링 과정에서 일어나는 일의 순서를 미묘하게 바꿔버린다. 렌더 순서를 정확히 이해하기 위해서는 예상할 수 없는 것들에 대한 지식을 가지고 있어야 한다. 돌발 질문: 위의 시나리오에서 무엇이 먼저 렌더링될까? 할일 목록? 서로 순서를 경쟁하기 때문에 알 수 없다.&lt;/li&gt;
&lt;li&gt;때로는 이벤트 리스너가 뷰를 변경한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;“비결정론 = 병렬 프로세스 + 상태 공유”
~ Martin Odersky(Scala designer)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;데이터 요청과 데이터 조작, 그리고 뷰 렌더링을 연결하는 것은 시간여행 스파게티 코드를 만드는 레시피다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;저 말이 B급 공상과학 영화처럼 들릴 수 있다는 것을 안다. 하지만 날 믿어보기 바란다. 시간여행 스파게티 코드는 프로그래밍에서 최악의 취미다.&lt;/p&gt;
&lt;p&gt;Flux 아키텍쳐는 다음과 같은 규칙들을 따름으로서 엄격한 분리와 작업 순서를 강제한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;먼저 고정된 상태(state)으로 시작한다.&lt;/li&gt;
&lt;li&gt;뷰를 렌더링한다. 이 렌더링 루프에서는 어떤 것도 상태를 변경할 수 없다.&lt;/li&gt;
&lt;li&gt;동일한 상태가 주어지면 뷰는 언제나 같은 결과를 렌더링한다.&lt;/li&gt;
&lt;li&gt;이벤트 리스너는 유저의 입력과 네트워크 요청을 처리하기 위해 대기한다. 이벤트 리스너가 관련 요청을 감지했을 때 액션(action)이 저장소(store)로 발송(dispatch)된다.&lt;/li&gt;
&lt;li&gt;액션이 발송되고 나면 상태는 새로운 상태로 업데이트되고 렌더링 과정이 반복된다. 오직 발송된 액션만이 상태에 영향을 줄 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이상이 UI를 위한 단방향 아키텍쳐인 Flux에 대한 단순한 설명이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/Ayi62jM04oMMYAMmWMo4q/b9a72afc8696b28d8acc78eb445302bf/flux-architecture.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 25.206611570247933%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAKCAMAAADxROxiAAABKVBMVEX///+xj0nJkia7onBJsY8myZKEwa37+/vGjBnqngXkmgfpnQbsnwXxogPGnEcZxowI4JgG6J0F7J8H5ZsH4pkF6p4D86NfyKX5+fm+xbC+x6vW2dDw8PDq6urs7Oze3t7EixfVkQvSkAvWkgrPjgzclQnCmEMXxIsK15MNy4wOxYgMzo0L0Y8G5pxZwp/y8vK5xMqrvsewvsXS0tKk9QOq/wCXwUS2hiTmmwa7mFEktoYG5ptovqLc3NwMjtAAqv8DpPXR0dGg7gSDvBGHww+P0Qucxkjr6+vg4ODx8fH09PT29vYLj9ERgboOiMUFnuul9gKm+AKk9QKn+gGYwkT8/PwDpPQDovKkw2amyWGruozn5+fz8/Pt7e3p6elyo7xhpslmpMP39/fHATJcAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAI9JREFUGBmVwTEKgmAAgNHvix+UIJOmIIhuEbg1d4aWztbWGZqDxo7gELk2RRCmltFgUO/xK3lTKt6BIA1vtKQV+XSBKPaloBXGCn5Iwdha2TOiHCl4ChMF8zPMpeIeyKThEWYKGhJrByCVilQG0ugBiYq6GFrb8s3SvgU6zQR1Q7eVCu5CnlNbT+l23fKfB/2JFM5bfqhEAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;flux-architecture&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/Ayi62jM04oMMYAMmWMo4q/b9a72afc8696b28d8acc78eb445302bf/flux-architecture.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/Ayi62jM04oMMYAMmWMo4q/b9a72afc8696b28d8acc78eb445302bf/flux-architecture.png?w=242 242w,
https://images.ctfassets.net/rpmifyuylbfw/Ayi62jM04oMMYAMmWMo4q/b9a72afc8696b28d8acc78eb445302bf/flux-architecture.png?w=484 484w,
https://images.ctfassets.net/rpmifyuylbfw/Ayi62jM04oMMYAMmWMo4q/b9a72afc8696b28d8acc78eb445302bf/flux-architecture.png?w=968 968w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Flux 아키텍쳐&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Flux 아키텍쳐와 함께 뷰는 유저 입력을 듣고, 그것들을 액션 객체로 변환하고, 그 객체들은 저장소로 전송된다. 저장소는 애플리케이션의 상태를 업데이트하고 뷰에게는 다시 렌더링하라고 알려준다. 물론 뷰는 입력과 이벤트의 유일한 소스인 경우는 거의 없지만 문제될 것은 없다.  아래와 같이 추가적인 이벤트 리스너가 액션 객체를 발송한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/mm3pTMQOHeEC4cY8EY4aW/264571c83caa87037bb955f6335f617f/flux-architecture-server.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 17.299999999999997%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAHCAMAAABN2v+8AAABIFBMVEX////e6OWU1b+X0r729vbO2+KUv9Xb4+ec08EE7aAE8aID8aIF658E7qAE8KHc3Nx2tNMFnusEoe8CpPWWv9Tfx9fTirvQn8DVyKzTu4rg3tvv7++LwrAG5pwL0pAL05EI3JYK1ZIF657Pz89opsUHmeIKktYGm+WNt8vu7u7a29i60oq704rb4s7UdLTsBZ/uBKDvBKHQHpTw8PDr6+vKlzDtnwTqngXuoAT2pQLUy7jI39dZ06tdzqivy9hZq9PD1N3J0Lul9gKe6gWk9QK404LfCJjdCJbVCpLRG5Tm5ubx8fHRnjbalQnclgjelwjP1sCg7gSW3AiY4QfZsszVY6/LfbHPuo7Vr2PVr2TOy8T9/f3Z29Sv1GSv1WPR3LvwWUOOAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAAAIhJREFUGBldwb0KQWEAgOH3zaeTnPwMcpTJ4CaUchUG12dxGYobsFiM6igDNmXwHR+D8zxSp0T6oin4JAnUZSZ32kZPksBA8AKFgOJXTsso66h4CIyMxpjsmUnk9cRc8HheGB0CDK2UhVo2YMfPlo+u+iAwFdzAUpjY71Fzy5Uc+be6UdNbU3kDJ7oVdPnsDiQAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;flux-architecture-server&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/mm3pTMQOHeEC4cY8EY4aW/264571c83caa87037bb955f6335f617f/flux-architecture-server.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/mm3pTMQOHeEC4cY8EY4aW/264571c83caa87037bb955f6335f617f/flux-architecture-server.png?w=250 250w,
https://images.ctfassets.net/rpmifyuylbfw/mm3pTMQOHeEC4cY8EY4aW/264571c83caa87037bb955f6335f617f/flux-architecture-server.png?w=500 500w,
https://images.ctfassets.net/rpmifyuylbfw/mm3pTMQOHeEC4cY8EY4aW/264571c83caa87037bb955f6335f617f/flux-architecture-server.png?w=1000 1000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
   &lt;/p&gt;
&lt;p&gt;중요한 사실은 Flux의 상태 업데이트는 트랜잭션(transaction)이라는 점이다. 상태가 가지고 있는 업데이트 메소드를 단순 호출하거나 직접 수정하는 대신 액션 객체가 저장소로 발송된다. 액션 객체는 트랜잭션의 기록이다. 적용되어야 할 사항이 기록된 은행 거래 내역에 비유해서 생각해 볼 수 있다. 예를 들어 은행에 입금을 한 경우 입금 전의 잔액이 지워지지는 않는다. 대신 변경된 잔액이 기존의 거래 내역에 추가되는 식이다. 액션 객체는 애플리케이션의 상태에 트랜잭션 내역이 추가된다.&lt;/p&gt;
&lt;p&gt;엑션 객체는 아래와 같은 형태를 가진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Learn Redux&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;액션 객체는 상태 처리에 관한 모든 실행 로그를 유지할 수 있게 한다. 그 로그는 결정적인(deterministic) 방법으로 상태를 재생산할 수 있다. 이는 동일한 초기 상태와 같은 트랜잭션이 같은 순서대로 주어지면 언제나 같은 상태를 결과로 가진다는 의미다.&lt;/p&gt;
&lt;p&gt;이것은 중요한 사실을 내포한다.&lt;/p&gt;
&lt;ol&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;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6bGAThPhvOoweUeSm0mom0/b37e738d3b7a84ce419d2f894f65f65f/redux-dev-tool.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 55.571030640668525%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,R0lGODlhKAAWAPcAMQD/ACYsNycsOCgtOCgtOSguOSkuOSkuOikvOiouOSovOSovOisvOiswOywwOywxOywxPC0xPC0yPC0yPS4yPC4zPS80PzM4QzQ5QzU5RDU6RDU6RTY6RTY7RTc7Rjc8Rjg8Rjg8Rzg9Rzk9Rzk9SDk+SDo+SDo+STo/STtASjxASjxASzxBSz1BSz1BTD1CTD5CTD5CTT5DTT9DTUBETUBETkBFTkBFT0FFT0FGT0FGUEJGUEJHUENHUUNIUUNIUkRIUURIUkRJUkRJU0VJUkVJU0VKU0ZKU0ZKVEZLVEdLVEdLVUdMVUhMVUlOV0pOV05SWk9TXFBUXVBVXlFVXlJWX1NXYFVYYVlcZVteZ11haV1hal5hal9ja2BjbGBkbGdqcWdqcmdrcmhsc2tudmxwd21xeHV4f3V4gHZ5gHZ6gXl8gnl8g3p9hHt+hHt+hXx/hn2AhoCCieLj5OPj5OPk5OPk5eTk5eTl5eTl5uXl5uXm5uXm5+bm5+rq6uvr6+zr6+zs7O3t7e3u7u7t7e7u7u/u7u/v7/Du7vDw8PHx8fHy8vLx8fLy8vLz8vLz8/Py8vPz8/P09PP29vTx8fTy8vTz8vTz8/T09PT29vT39vX09PX19fX29fX39vX39/b19fb29fb29vb39/f39vf39/f49/j4+Pn4+Pn5+Pn5+fr5+fr6+vr8/Pv6+vv7+/v8/Pv9/fv+/fz7+/z7/Pz8/Pz9/fz+/fz+/v38/P39/P39/f3+/f3+/v79/f7+/v/+/v///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQFMAAAACwAAAAAKAAWAIcA/wAmLDcnLDgoLTgoLTkoLjkpLjkpLjopLzoqLjkqLzkqLzorLzorMDssMDssMTssMTwtMTwtMjwtMj0uMjwuMz0vND8zOEM0OUM1OUQ1OkQ1OkU2OkU2O0U3O0Y3PEY4PEY4PEc4PUc5PUc5PUg5Pkg6Pkg6Pkk6P0k7QEo8QEo8QEs8QUs9QUs9QUw9Qkw+Qkw+Qk0+Q00/Q01ARE1ARE5ARU5ARU9BRU9BRk9BRlBCRlBCR1BDR1FDSFFDSFJESFFESFJESVJESVNFSVJFSVNFSlNGSlNGSlRGS1RHS1RHS1VHTFVITFVJTldKTldOUlpPU1xQVF1QVV5RVV5SVl9TV2BVWGFZXGVbXmddYWldYWpeYWpfY2tgY2xgZGxnanFnanJna3JobHNrbnZscHdtcXh1eH91eIB2eYB2eoF5fIJ5fIN6fYR7foR7foV8f4Z9gIaAgoni4+Tj4+Tj5OTj5OXk5OXk5eXk5ebl5ebl5ubl5ufm5ufq6urr6+vs6+vs7Ozt7e3t7u7u7e3u7u7v7u7v7+/w7u7w8PDx8fHx8vLy8fHy8vLy8/Ly8/Pz8vLz8/Pz9PTz9vb08fH08vL08/L08/P09PT09vb09/b19PT19fX19vX19/b19/f29fX29vX29vb29/f39/b39/f3+Pf4+Pj5+Pj5+fj5+fn6+fn6+vr6/Pz7+vr7+/v7/Pz7/f37/v38+/v8+/z8/Pz8/f38/v38/v79/Pz9/fz9/f39/v39/v7+/f3+/v7//v7///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8I4AA5CRxIsCCnTZsOYsKU0KDDhxAFbqpEkdIlShEzQly48JLHSpcqccSkseQpVShTqlRZqmTESLWC/QpGs6ZNmqxIunwoyI/Pn0CBFtoJsdGho0iTJm1E9KGop1CjShXVtKrVq1izat3KtavXr2DDDhxJtqzZs2gxrVzLtq3blXr07Ilr507cu3jz6tXbZ49fv3xGmAiBYkQNGCROmFjMuLHjx4tLeBABQoTlDxhOeGDCoswWGEJ+iB5NurRpH0F2NLAwwUIGDRAWGIAwwMCA2gZy697N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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;redux-dev-tool&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6bGAThPhvOoweUeSm0mom0/b37e738d3b7a84ce419d2f894f65f65f/redux-dev-tool.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6bGAThPhvOoweUeSm0mom0/b37e738d3b7a84ce419d2f894f65f65f/redux-dev-tool.gif?w=359 359w,
https://images.ctfassets.net/rpmifyuylbfw/6bGAThPhvOoweUeSm0mom0/b37e738d3b7a84ce419d2f894f65f65f/redux-dev-tool.gif?w=718 718w,
https://images.ctfassets.net/rpmifyuylbfw/6bGAThPhvOoweUeSm0mom0/b37e738d3b7a84ce419d2f894f65f65f/redux-dev-tool.gif?w=1436 1436w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Redux dev tools history slider view&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-redux가-필요하지-않은-앱도-있다&quot;&gt;&lt;a href=&quot;#2-redux%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%95%B1%EB%8F%84-%EC%9E%88%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. Redux가 필요하지 않은 앱도 있다.&lt;/h2&gt;
&lt;p&gt;UI 워크플로우가 단순하면 Redux를 사용하는 것은 너무 과한 일이 될 수 있다. 만약 당신이 틱택토(tic-tac-toe) 게임을 만든다면 정말로 복구/재현이 필요할까? 게임이 몇 분 이상 유지될 일은 거의 없고 유저가 게임을 망친다면 그냥 초기화하고 새로 시작하게 하면 된다.&lt;/p&gt;
&lt;p&gt;만약:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;유저 워크플로우가 단순하다면&lt;/li&gt;
&lt;li&gt;유저 간의 공동 작업이 없다면&lt;/li&gt;
&lt;li&gt;서버 사이드 이벤트(SSE)나 웹 소켓을 관리할 필요가 없다면&lt;/li&gt;
&lt;li&gt;하나의 페이지가 하나의 소스에서 데이터를 가져온다면&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;앱의 이벤트 흐름은 충분히 단순할 것이며 트랜잭션 기반의 상태 관리를 적용하기 위한 노력 대비 얻는 이점은 없을 것이다.&lt;/p&gt;
&lt;p&gt;만약 앱에 Flux를 적용하고 싶지 않다면 Flux같은 역할을 하는 더 간단한 솔루션이 있다. &lt;a href=&quot;https://github.com/mobxjs/mobx&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;MobX&lt;/a&gt;를 확인해보길 바란다.&lt;/p&gt;
&lt;p&gt;하지만 앱의 복잡도가 증가할수록, 뷰의 상태 관리의 복잡도가 증가할수록, 트랜잭션 기반의 상태는 가치가 상승한다. MobX는 트랙잭션 상태 관리를 제공하지 않는다.&lt;/p&gt;
&lt;p&gt;만약:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;워크플로우가 복잡하다면&lt;/li&gt;
&lt;li&gt;앱이 다양한 유저 행동 흐름을 가진다면(일반 유저와 어드민이 동시에 사용한다고 가정)&lt;/li&gt;
&lt;li&gt;유저 간에 공동 작업이 가능하다면&lt;/li&gt;
&lt;li&gt;웹 소켓이나 SSE를 사용한다면&lt;/li&gt;
&lt;li&gt;하나의 뷰에서 복수의 API를 이용해 데이터를 가져온다면&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;트랜잭션 상태 관리에 시간을 들인 만큼 이득을 볼 수 있을 것이다. Redux가 당신의 앱에 딱 좋을 수 있다.&lt;/p&gt;
&lt;p&gt;웹 소켓과 SSE는 Redux는 무슨 관련이 있는가? 불확정한 상태 관리에서는 비동기 I/O 기반의 소스를 추가할수록 앱에게 대체 무슨 일이 일어나고 있는지 이해하기 힘들어진다. 결정적인 상태와 상태 트랜잭션 기록은 근본적으로 저런 문제를 단순화시킨다.&lt;/p&gt;
&lt;p&gt;개인적인 의견으로는 대부분의 대규모 SaaS 제품에는 적어도 몇 가지 복잡한 UI가 포함되어 있고 트랜잭션 상태 관리를 사용해야 한다고 생각한다. 대부분의 작은 유틸리티 앱이나 프로토타입은 사용해서는 안된다. 작업에 맞는 도구를 사용해야 한다.&lt;/p&gt;
&lt;h2 id=&quot;3-reducer-이해하기&quot;&gt;&lt;a href=&quot;#3-reducer-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. Reducer 이해하기&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Redux = Flux + Functional Programming&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Flux는 액션 객체를 이용해 단반향 데이터 흐름과 트랜잭션 상태를 규정한다. 하지만 액션 객체를 어떻게 처리해야 할지는 언급하지 않았다. 그래서 Redux가 등장했다.&lt;/p&gt;
&lt;p&gt;Redux 상태 관리의 주요 구성 요소는 리듀서(reducer) 함수다. 리듀서 함수란 무엇인가?&lt;/p&gt;
&lt;p&gt;함수형 프로그래밍에서는 일반적으로 &lt;code class=&quot;language-text&quot;&gt;reduce()&lt;/code&gt;나 &lt;code class=&quot;language-text&quot;&gt;fold()&lt;/code&gt; 유틸리티 함수는 리스트에 포함된 각각의 값에 리듀서 함수를 적용해서 하나의 누적된 값을 만들어내는 데 사용한다. 아래는 자바스크립트 배열에 &lt;code class=&quot;language-text&quot;&gt;Array.prototype.reduce()&lt;/code&gt; 메소드를 이용해서 합계를 계산하는 리듀서의 예제다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; initialState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;reducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; initialState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; total &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;total&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 6&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Redux는 리듀서를 배열에 적용하는 대신 액션 객체의 스트림에 적용한다. 액션 객체는 아래와 같은 형태임을 기억하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Learn Redux&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;합계를 계산하는 리듀서를 Redux 스타일의 리듀서로 바꿔보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; defaultState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;reducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; defaultState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ADD&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이제 테스트 액션을 적용할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; actions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ADD&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ADD&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ADD&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; total &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; actions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;4-리듀서는-순수pure-함수여야-한다&quot;&gt;&lt;a href=&quot;#4-%EB%A6%AC%EB%93%80%EC%84%9C%EB%8A%94-%EC%88%9C%EC%88%98pure-%ED%95%A8%EC%88%98%EC%97%AC%EC%95%BC-%ED%95%9C%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 리듀서는 순수(pure) 함수여야 한다&lt;/h2&gt;
&lt;p&gt;결정적인 상태 재생산을 위해서는 리듀서는 반드시 순수 함수여야 한다. 예외는 없다. 순수 함수란:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;동일한 입력이 주어지면 언제나 동일한 출력을 반환한다.&lt;/li&gt;
&lt;li&gt;사이드 이펙트가 없다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;자바스크립트에서 중요한 사실은 원시 값이 아닌(non-primitive) 모든 객체는 함수에 참조로서 전달된다는 사실이다. 달리 말하자면 함수에 객체를 전달하고 그 객체의 속성을 직접 수정하면 함수 외부의 값도 함께 변경된다는 의미다. 이것이 사이드 이펙트다. 함수에 전달하는 객체가 어디에서 왔는지 완전히 알지 못한다면 그 함수의 호출이 무엇을 의미하는지 결코 알 수 없다. 그러면 안된다.&lt;/p&gt;
&lt;p&gt;리듀서는 반드시 새로운 객체를 반환해야 한다. 예를 들면 &lt;code class=&quot;language-text&quot;&gt;Object.assign({}, state, { 변경사항 })&lt;/code&gt;으로 이를 구현할 수 있다.&lt;/p&gt;
&lt;p&gt;배열 인자 또한 참조값이다. 리듀서 내부에서 배열에 &lt;code class=&quot;language-text&quot;&gt;.push()&lt;/code&gt; 메소드를 이용해서 새로운 아이템을 추가해서는 안된다. 마찬가지로 &lt;code class=&quot;language-text&quot;&gt;.pop()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.shift()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.reverse()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.unshift()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.shift()&lt;/code&gt; 를 비롯한 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#Mutator_methods&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;변경(mutator) 메소드&lt;/a&gt;를 사용해서는 안된다.&lt;/p&gt;
&lt;p&gt;배열을 안전하게 처리하고 싶다면 상태를 처리할 때 &lt;code class=&quot;language-text&quot;&gt;.push()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;.concat()&lt;/code&gt; 같은 메소드 대신 안전한 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array#Accessor_methods&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;accessor 메소드&lt;/a&gt;를 사용하도록 제한해야 한다.&lt;/p&gt;
&lt;p&gt;아래의 예제에서 &lt;code class=&quot;language-text&quot;&gt;ADD_CHAT&lt;/code&gt; 케이스를 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_CHAT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;CHAT::ADD_CHAT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; defaultState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  chatLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  currentChat&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    msg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Anonymous&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    timeStamp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1472322852680&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;chatReducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; defaultState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; payload &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_CHAT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        chatLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatLog&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;새로운 객체가 &lt;code class=&quot;language-text&quot;&gt;Object.assign()&lt;/code&gt; 으로 생성되었고 &lt;code class=&quot;language-text&quot;&gt;.push()&lt;/code&gt; 대신 &lt;code class=&quot;language-text&quot;&gt;.concat()&lt;/code&gt;을 이용해서 배열에 아이템을 추가했음을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;개인적으로는 상태값이 우발적으로 변경되길 바라지 않는다. 그래서 나는 &lt;a href=&quot;https://facebook.github.io/immutable-js/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;immutable data API&lt;/a&gt;와 Redux를 함께 사용하는 방법을 연구해왔다. 만약 상태가 불변(immutable) 객체라면 그것이 우발적으로 변경되었는지 확인하기 위해 코드를 살펴볼 필요도 없어진다. 나는 팀원들과 일하면서 의도치 않게 변경된 상태로 인해 발생한 버그를 발견한 후 이런 결론이 이르게 되엇다.&lt;/p&gt;
&lt;p&gt;순수 함수에 대해서는 이보다 더 많은 내용이 있다. 만약 Redux를 배포용 애플리케이션에 사용할 계획이 있다면 순수 함수에 대해 확실히 파악해야 한다. 그리고 그와 함께 그리고 시간 다루기, 로그 기록, 난수 등도 알아둬야 한다. 더 자세한 사항은 &lt;a href=&quot;https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-pure-function-d1c076bec976#.26ljw27tp&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“Master the JavaScript Interview: What is a Pure Function?”&lt;/a&gt;을 살펴보기 바란다.&lt;/p&gt;
&lt;h2 id=&quot;5-기억하라-리듀서는-반드시-신뢰할-수-있는-단일-출처여야-한다&quot;&gt;&lt;a href=&quot;#5-%EA%B8%B0%EC%96%B5%ED%95%98%EB%9D%BC-%EB%A6%AC%EB%93%80%EC%84%9C%EB%8A%94-%EB%B0%98%EB%93%9C%EC%8B%9C-%EC%8B%A0%EB%A2%B0%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%8B%A8%EC%9D%BC-%EC%B6%9C%EC%B2%98%EC%97%AC%EC%95%BC-%ED%95%9C%EB%8B%A4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 기억하라: 리듀서는 반드시 신뢰할 수 있는 단일 출처여야 한다&lt;/h2&gt;
&lt;p&gt;앱의 모든 상태는 신뢰할수 있는 단일 출처(single source of truth)를 가져야 한다. 이 말은 상태는 한곳에만 저장되어 있다는 의미다. 그리고 상태 값이 필요한 모든 곳에서는 이 단일 출처를 참조해서 상태에 접근해야 한다.&lt;/p&gt;
&lt;p&gt;서로 다른 요소에 각각의 출처가 있는 것은 괜찮다. 예를 들어 URL은 유저의 요청 경로와 파라메터에 대해 단일 신뢰 출처가 될 수 있다. 앱은 API URL을 단일 신뢰 출처로 가지는 서비스 설정 기능을 가질 수 있다. 하지만…&lt;/p&gt;
&lt;p&gt;어떤 상태라도 Redux 저장소에 저장할 경우, 어떤 접근이라도 Redux를 통해 이루어져야 한다. 이 원칙에 충실하지 않는다면 Flux와 Redux가 해결하려고 했던 문제점인 Redux 저장소의 상태가 망가져버리는 일이나 의도치 못한 상태 조작으로 인한 버그가 발생할 수 있다.&lt;/p&gt;
&lt;p&gt;달리 말하자면 신뢰할 수 있는 단일 출처가 없다면 Redux를 사용하든 안하든 아래와 같은 것들을 잃어버리게 된다.&lt;/p&gt;
&lt;ul&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;li&gt;테스트 용이성&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;6-액션-타입에-상수를-사용하라&quot;&gt;&lt;a href=&quot;#6-%EC%95%A1%EC%85%98-%ED%83%80%EC%9E%85%EC%97%90-%EC%83%81%EC%88%98%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;6. 액션 타입에 상수를 사용하라&lt;/h2&gt;
&lt;p&gt;나는 리듀서에 전달된 액션의 히스토리를 살펴봤을 때 가능하면 추적하기 쉽게 하려고 한다. 만약 모든 액션이 &lt;code class=&quot;language-text&quot;&gt;CHANGE_MESSAGE&lt;/code&gt;같은 짧고 일반적인 이름을 가진다면 앱에서 어떤 일이 일어나고 있는지 파악하기 어렵다. 하지만 &lt;code class=&quot;language-text&quot;&gt;CHAT::CHANGE_MESSAGE&lt;/code&gt;처럼 보다 구체적인 이름을 사용한다면 앱의 상태를 더 명확하게 파악할 수 있다.&lt;/p&gt;
&lt;p&gt;또 정의되지 않은(&lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt;) 타입의 액션을 발송한다면 앱은 에러를 발생시킬 것이다. 만약 상수를 사용하지 않고 잘못된 문자열을 직접 입력한다면 그 액션은 조용히 잘못된 결과를 발생시킬 것이다.&lt;/p&gt;
&lt;p&gt;리듀서에 사용할 모든 액션의 타입을 한곳에 모아서 유지할 때 얻게되는 장점은 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;일관적인 이름을 사용하게 된다&lt;/li&gt;
&lt;li&gt;리듀서 API를 빠르게 이해할 수 있다&lt;/li&gt;
&lt;li&gt;풀 리퀘스트(pull request)에서 무엇이 변경되었는지 확인하라&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;7-발송을-호출하는-쪽에서-액션의-로직을-분리하기-위해-액션-생성자를-사용하라&quot;&gt;&lt;a href=&quot;#7-%EB%B0%9C%EC%86%A1%EC%9D%84-%ED%98%B8%EC%B6%9C%ED%95%98%EB%8A%94-%EC%AA%BD%EC%97%90%EC%84%9C-%EC%95%A1%EC%85%98%EC%9D%98-%EB%A1%9C%EC%A7%81%EC%9D%84-%EB%B6%84%EB%A6%AC%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4-%EC%95%A1%EC%85%98-%EC%83%9D%EC%84%B1%EC%9E%90%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;7. 발송을 호출하는 쪽에서 액션의 로직을 분리하기 위해 액션 생성자를 사용하라&lt;/h2&gt;
&lt;p&gt;내가 리듀서 내부에서는 ID를 생성하거나 현재 시간을 파악할 수 없다고 말하면 사람들은 나를 이상하게 바라본다. 만약 당신도 화면을 의심스럽게 바라보고 있다면 안심해도 된다. 당신만 그런 것이 아니니까.&lt;/p&gt;
&lt;p&gt;그렇다면 액션이 필요한 모든 곳에서 순수하지 못한(impure) 로직을 중복 작성하는 일을 피하기 위해서는 어떻게 해야 하는가? 답은 액션 생성자(action creator)다.&lt;/p&gt;
&lt;p&gt;액션 생성자는 아래와 같은 여러 이점을 가지고 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;액션을 발송하기 전에 입력값을 연산을 할 수 있다.&lt;/li&gt;
&lt;li&gt;상용구(boilerplate)를 줄인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이제 &lt;code class=&quot;language-text&quot;&gt;ADD_CHAT&lt;/code&gt; 타입의 액션 객체를 액션 생성자를 이용해 만들어보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 액션 생성자는 순수하지 않을 수 있다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; addChat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// cuid는 random uuids/v4 GUIDs보다 안전하다.(usecuid.org 참조)&lt;/span&gt;
  id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  msg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Anonymous&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  timeStamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_CHAT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timeStamp &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 코드에서 볼 수 있듯 &lt;a href=&quot;https://github.com/ericelliott/cuid&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;cuid&lt;/a&gt;를 이용해서 각각의 채팅 액션별로 랜덤 아이디를 생성한다. 그리고 &lt;code class=&quot;language-text&quot;&gt;Date.now()&lt;/code&gt;를 이용해 타임스탬프를 생성한다. 둘다 리듀서 내부서는 실행하기에 안전하지 못한 순수하지 못한 연산들이다. 하지만 액션 생성자 내부에서는 사용해도 무방하다.&lt;/p&gt;
&lt;h3 id=&quot;액션-생성자를-이용해-상용구를-줄이자&quot;&gt;&lt;a href=&quot;#%EC%95%A1%EC%85%98-%EC%83%9D%EC%84%B1%EC%9E%90%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-%EC%83%81%EC%9A%A9%EA%B5%AC%EB%A5%BC-%EC%A4%84%EC%9D%B4%EC%9E%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;액션 생성자를 이용해 상용구를 줄이자&lt;/h3&gt;
&lt;p&gt;어떤 사람들은 액션 생성자를 사용하는 방법이 프로젝트에 상용구를 증가시킨다고 말한다. 그들의 의견과는 달리 내가 액션 생성자를 이용해서 리듀서 내부에서 상용구를 얼마나 줄이는지 확인해보라.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;팁: 만약 상수, 리듀서, 액션 생성자를 모두 같은 파일에 저장하면 별도 위치에서 가져올 상용구가 줄어든다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;채팅 유저에게 사용자 이름과 신분을 조정할 수 있는 기능을 부여한다고 가정하자. 구현을 위해 몇개의 액션을 처리하기 위한 핸들러를 리듀서에 아래와 같이 추가할 수 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;chatReducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; defaultState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; payload &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_CHAT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        chatLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatLog&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CHANGE_STATUS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        statusMessage&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; payload
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CHANGE_USERNAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        userName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; payload
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;리듀서 함수의 규모가 커지면 반복되는 상용구도 함께 늘어난다. 내가 만든 대부분의 리듀서는 위의 예제보다 훨씬 복잡했고 많은 중복 코드가 있었다. 단순한 속성 변경 액션을 하나로 모은다면 어떨까? 바꿔보자. 어렵지 않다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;chatReducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; defaultState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; payload &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ADD_CHAT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        chatLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatLog&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 간단한 속성 변경 액션을 모두 모은다&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CHANGE_STATUS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CHANGE_USERNAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;여분의 공간과 코멘트가 붙어도 수정한 버전이 더 짧고 &lt;code class=&quot;language-text&quot;&gt;case&lt;/code&gt;문도 2개로 줄었다.&lt;/p&gt;
&lt;h3 id=&quot;switchcase-문은-위험하지-않은가&quot;&gt;&lt;a href=&quot;#switchcase-%EB%AC%B8%EC%9D%80-%EC%9C%84%ED%97%98%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;switch…case 문은 위험하지 않은가?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://ericelliottjs.com/product/programming-javascript-applications-paper-ebook-bundle/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;어디선가&lt;/a&gt; &lt;code class=&quot;language-text&quot;&gt;switch&lt;/code&gt; 문은 가능한 사용하지 않아야 한다는 글을 읽었을 수도 있다. 원하지 않게 에러가 발생할 수도 있고 switch문의 덩치가 커져버릴 수도 있다. 그리고 의도적인 fall-through(위의 코드처럼 복수의 케이스를 겹쳐서 사용하는 것)를 사용하지 말라는 얘기도 들었을 것이다. 왜냐하면 버그가 발생하면 찾기 어렵기 때문이다. 모두 좋은 조언이다. 하지만 방금 언급한 위험들에 대해서 좀 더 생각해보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;리듀서는 조합이 가능(composable)하다. 그러므로 덩치가 커지는 것은 문제가 아니다. case문이 커지면 리듀서를 분리하면 된다.&lt;/li&gt;
&lt;li&gt;모든 case의 본체는 &lt;code class=&quot;language-text&quot;&gt;return&lt;/code&gt;한다. 그러므로 의도치 않은 fall-through는 결코 발생하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Redux는 &lt;code class=&quot;language-text&quot;&gt;switch...case&lt;/code&gt;문을 잘 사용한다.공식적으로 이 건에 대해서는 나의 조언을 바꾸겠다. 위의 사례처럼 간단한 룰(switch 문을 목적에 맞게 작게 만들고 모든 case 몸통에서 return문을 사용하는 것)을 적용한다면 &lt;code class=&quot;language-text&quot;&gt;switch&lt;/code&gt;는 괜찮다.&lt;/p&gt;
&lt;p&gt;그리고 이 버전이 다른 형태의 페이로드(payload)를 요구한다는 것을 알아챘을 것이다. 액션 생성자는 아래와 같은 형태를 가진다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; changeStatus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;statusMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Online&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CHANGE_STATUS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; statusMessage &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; changeUserName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Anonymous&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CHANGE_USERNAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; userName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;코드에서 알 수 있듯이 이 액션 생성자들은 인자와 상태의 구조가 변경되었다. 하지만 그것이 전부는 아니다.&lt;/p&gt;
&lt;h2 id=&quot;8-서명-문서에-기본-매개변수default-parameter를-사용하라&quot;&gt;&lt;a href=&quot;#8-%EC%84%9C%EB%AA%85-%EB%AC%B8%EC%84%9C%EC%97%90-%EA%B8%B0%EB%B3%B8-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98default-parameter%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;8. 서명 문서에 기본 매개변수(default parameter)를 사용하라&lt;/h2&gt;
&lt;p&gt;텍스트 에디터 플러그인으로 Tern.js(Sublime Text나 Atom에서도 사용 가능하다)를 사용하고 있다면 ES6 기본 매개변수를 통해 액션 생성자에 필요한 인터페이스를 파악해줄 것이다. 그리고 액션 생성자를 호출할 때 자동 완성이 가능하며 이는 개발자에게 인지 부하를 덜어준다. 왜내하면 액션 생성자에 필요한 페이로드 타입을 외우고 있을 필요가 없기 때문이다.&lt;/p&gt;
&lt;p&gt;만약 Tern, TypeScript, Flow 같은 타입 인터페이스 플러그인을 사용하고 있지 않다면 꼭 사용하길 바란다.&lt;/p&gt;
&lt;p&gt;노트: 나는 타입 주석(type annotation)보다는 함수 선언에 함께 작성된 기본 매개변수를 통해 파악하는 것을 더 선호한다. 그 이유는:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Flow나 TypeScript를 사용할 필요가 없다. 대신 표준 자바스크립트를 사용하면 된다.&lt;/li&gt;
&lt;li&gt;TypeScript나 Flow를 사용하고 있다면 타입 주석은 기본 매개변수와 중복된다. TypeScript와 Flow 모두 기본 매개변수로부터 타입을 추론하기 때문이다.&lt;/li&gt;
&lt;li&gt;코드가 문법 잡음이 적을 때 더 가독성이 높다는 것을 알게 되었다.&lt;/li&gt;
&lt;li&gt;기본 설정을 얻게 된다. 이는 타입 에러에 의한 CI(continuos integration) 빌드 실패를 통해 확인하지 않아도 의도하지 않은 &lt;code class=&quot;language-text&quot;&gt;undefined&lt;/code&gt; 파라메터가 코드 안에 숨어있는 일이 결코 없게 된다는 의미다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;9-연산이-추가된-상태와-분리decoupling을-위해-선택자selector를-사용하라&quot;&gt;&lt;a href=&quot;#9-%EC%97%B0%EC%82%B0%EC%9D%B4-%EC%B6%94%EA%B0%80%EB%90%9C-%EC%83%81%ED%83%9C%EC%99%80-%EB%B6%84%EB%A6%ACdecoupling%EC%9D%84-%EC%9C%84%ED%95%B4-%EC%84%A0%ED%83%9D%EC%9E%90selector%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;9. 연산이 추가된 상태와 분리(decoupling)을 위해 선택자(Selector)를 사용하라&lt;/h2&gt;
&lt;p&gt;역사상 가장 복잡한 채팅 앱을 만든다고 가정해보자. 50만줄 이상의 코드를 작성한 상태에서 기획 부서에서 새로운 기능을 요구했다. 그런데 그 기능은 지금껏 구현한 상태의 구조를 바꿔야만 구현이 가능한 상황이다.&lt;/p&gt;
&lt;p&gt;당황할 필요는 없다. 기존 상태 구조와 앱의 나머지 부분들 사이의 결합도를 낮추기 위한 스마트한 방법인 선택자를 사용하면 된다.&lt;/p&gt;
&lt;p&gt;내가 작성한 거의 대부분의 리듀서에서 나는 뷰를 만들기 위해 필요한 변수를 추출하기 위한 선택자를 추가로 작성했다. 간단한 채팅 리듀서는 어떻게 생겼을지 한번 살펴보자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;export const getViewState = state =&amp;gt; Object.assign({}, state);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;너무 간단해서 gist로 저장할 가치조차 없다. 코드를 보고 어이가 없겠지만 잠깐, 상태에 약간의 연산을 추가하면 어떨까? 세션이 열려있는 동안 채팅에 참여한 모든 유저의 목록을 가져오는 것 같은? 이것을 &lt;code class=&quot;language-text&quot;&gt;recentlyActiveUsers&lt;/code&gt;라고 하자.&lt;/p&gt;
&lt;p&gt;이 정보는 현재 상태에 이미 저장되어 있지만 가져오기는 쉽지 않다. &lt;code class=&quot;language-text&quot;&gt;getViewState()&lt;/code&gt;에서 이 값들을 가져오자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getViewState&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// return a list of users active during this session&lt;/span&gt;
  recentlyActiveUsers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatLog&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;chat &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; chat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;만약 연산된 상태를 모두 선택자를 통해 가져온다면:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;리듀서와 컴포넌트의 복잡도를 줄여준다.&lt;/li&gt;
&lt;li&gt;상태 구조와 앱 사이의 결합도를 낮춰준다.&lt;/li&gt;
&lt;li&gt;리듀서 내부에서조차 단일 출처 원칙을 따른다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;10-tdd-사용-테스트를-먼저-작성하라&quot;&gt;&lt;a href=&quot;#10-tdd-%EC%82%AC%EC%9A%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EB%A8%BC%EC%A0%80-%EC%9E%91%EC%84%B1%ED%95%98%EB%9D%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;10. TDD 사용: 테스트를 먼저 작성하라&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://link.springer.com/article/10.1007%2Fs10664-008-9062-z&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;많은&lt;/a&gt; &lt;a href=&quot;https://www.computer.org/csdl/mags/so/2007/03/s3024.pdf&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;연구&lt;/a&gt;들이 테스트를 먼저 하는 방법론과 테스트를 나중에 하는 방법론, 그리고 테스트를 전혀 하지 않는 방법론을 비교해 보았다. 결과는 명확하고 극명하다. 대부분의 연구가 구현 전에 테스트를 작성했을 경우 애플리케이션 배포시 40~80%의 버그가 감소했다는 결과를 내놓았다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TDD는 배포시 버그 비율은 절반 이상으로 줄일 수 있고 이 주장을 뒷받침할 수 있는 많은 증거들이 존재한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 글에서 작성한 예제 코드를 작성할 때 나는 모두 유닛 테스트부터 시작했다.&lt;/p&gt;
&lt;p&gt;부서지기 쉬운 테스트를 피하기 위해서 나는 기대값을 만들기 위한 팩토리 함수를 작성했다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; createChat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  msg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Anonymous&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  timeStamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1472322852680&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timeStamp
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; createState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  userName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Anonymous&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  chatLog &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  statusMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Online&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  currentChat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createChat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  userName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chatLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; statusMessage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; currentChat
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;둘다 함수 모두 기본값을 제공하므로 테스트에서는 팩토리가 제공하는 기본 구조에다가 내가 필요한 속성을 덮어쓸 수 있다.&lt;/p&gt;
&lt;p&gt;다음과 같이 사용한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;chatReducer()&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;with no arguments&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; same&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; msg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;should return correct default state&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; actual &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;same&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;actual&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; expected&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;노트: 나는 그 단순함 때문에 유닛 테스트에 &lt;a href=&quot;https://medium.com/javascript-scene/why-i-use-tape-instead-of-mocha-so-should-you-6aa105d8eaf4#.711m3iv2m&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;tape를 사용한다&lt;/a&gt;. 나는 물론 Mocha와 Jasmine도 2~3년간 사용해 본 경험이 있고, 기타 다양한 테스트 프레임워크도 사용해 보았다. 어떤 프레임워크를 사용하든 합당한 목적과 원칙을 가지고 선택해야 할 것이다.&lt;/p&gt;
&lt;p&gt;위에서 작성한 테스트 코드의 스타일에 대해 간략히 설명하고자 한다. Jasmine과 Mocha를 사용해 온 경험이 있는 탓에 나는 바깥 블럭에서 테스트하고자 하는 컴포넌트를 설명하는 것으로 시작하는 편이다. 그리고 안쪽 블럭에서는 컴포넌트에 전달하는 것을 서술한다. 내부에서는 간단한 테스트 라이브러리의 &lt;code class=&quot;language-text&quot;&gt;deepEqual()&lt;/code&gt;이나 &lt;code class=&quot;language-text&quot;&gt;toEqual()&lt;/code&gt;등으로 실행할 수 있는 간단한 등가 가정문을 작성한다.&lt;/p&gt;
&lt;p&gt;위에서 볼 수 있듯 나는 &lt;code class=&quot;language-text&quot;&gt;beforeEach()&lt;/code&gt;나 &lt;code class=&quot;language-text&quot;&gt;afterEach()&lt;/code&gt;같은 유틸리티 메소드 대신 독립된 테스트 상태값과 팩토리 함수를 사용했다. 왜냐하면 저 유틸리티 함수들로 인해 경험이 부족한 개발자들이 의도치 않게 상태값을 테스트 케이스마다 공유하게 되는 일을 피하기 위해서이다.&lt;/p&gt;
&lt;p&gt;짐작했겠지만 나는 리듀서마다 서로 다른 3개의 테스트를 작성했다.&lt;/p&gt;
&lt;ol&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;/p&gt;
&lt;h3 id=&quot;액션-생성자-테스트&quot;&gt;&lt;a href=&quot;#%EC%95%A1%EC%85%98-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;액션 생성자 테스트&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;addChat()&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;with no arguments&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; same&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; msg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;should add default chat message&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; actual &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;undefined&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addChat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// id, timestamp 속성은 반드시 존재해야 하지만 실제 값은 무엇이든 상관없다.&lt;/span&gt;
      state &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; chat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chatLog&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        chat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;chat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        chat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timeStamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;chat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timeStamp&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      chatLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        user&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Anonymous&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        msg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        timeStamp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;same&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;actual&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; expected&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;


  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;with all arguments&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; same&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; msg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;should add correct chat message&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; actual &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;undefined&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addChat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      user&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@JS_Cheerleader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      msg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Yay!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      timeStamp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1472322852682&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      chatLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        user&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@JS_Cheerleader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        msg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Yay!&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        timeStamp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1472322852682&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;same&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;actual&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; expected&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 예제는 여러모로 흥미롭다. &lt;code class=&quot;language-text&quot;&gt;addChat()&lt;/code&gt; 액션 생성자는 순수(pure)하지 않다. 그 말은 함수 내부에서 조작될 값을 직접 전달하지 않는다면 그 함수가 만들어낼 기대값을 얻을 수 없다는 의미다. 이에 대응하기 위해, 우리는 &lt;code class=&quot;language-text&quot;&gt;pipe&lt;/code&gt;를 사용했다. 나는 때때로 &lt;code class=&quot;language-text&quot;&gt;pipe&lt;/code&gt;를 필요하지 않은 여분의 변수를 생성하는 데 사용한다. 나는 그것을 생성된 값을 무시할 때 사용한다. 존재한다는 것은 확실히 할 수 있지만 어떤 값을 가지는지는 알 필요가 없다. 내가 타입 체크조차 하지 않았던 것을 확인해보라. 그 값들의 처리는 타입 추론과 기본값의 역할에 맡긴다.&lt;/p&gt;
&lt;p&gt;Pipe는 입력값을 일련의 함수들에 연속적으로 전달해주는 유틸리티 함수다. Pipe 내부에서 값을 전달받은 함수는 그 결과를 다음 함수의 입력으로 전달하고, 이 과정은 pipe 내부의 모든 함수들에 연속적으로 적용된다. 나는 &lt;code class=&quot;language-text&quot;&gt;lodash/fp/pipe&lt;/code&gt;로부터 lodash pipe 함수를 이용한다. 이는 lodash/flow의 별칭이기도 하다. 흥미롭게도 &lt;code class=&quot;language-text&quot;&gt;pipe()&lt;/code&gt; 함수 그 자체도 리듀서 함수로부터 만들어졌다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;pipe&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;fns&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; fns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;fn1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; s &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;fn2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; s &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;fn3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; s &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; s &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;!&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newFunc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fn2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fn3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Time&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// emit!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;나는 &lt;code class=&quot;language-text&quot;&gt;pipe()&lt;/code&gt; 함수를 상태 전환을 단순화시키기 위해 리듀서 파일에서 많이 사용한다. 모든 상태 전환은 궁극적으로 하나의 데이터 표현에서 다음으로 옮겨가는 데이터의 흐름이다. 그것을 구현하는데 &lt;code class=&quot;language-text&quot;&gt;pipe()&lt;/code&gt;가 무척 적합하다.&lt;/p&gt;
&lt;p&gt;액션 생성자 또한 모든 기본값을 덮어쓰게 한다는 점을 기억하라. 그래서 우리는 특정 값의 테스트를 위해 특정한 아이디와 타임스탬프를 전달할 수 있는 것이다.&lt;/p&gt;
&lt;h3 id=&quot;선택자selector-테스트&quot;&gt;&lt;a href=&quot;#%EC%84%A0%ED%83%9D%EC%9E%90selector-%ED%85%8C%EC%8A%A4%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;선택자(selector) 테스트&lt;/h3&gt;
&lt;p&gt;마지막으로 상태 선택자를 테스트하고 계산된 모든 값들이 기대한 값과 일치하는지 확인하자.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;getViewState&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;with chats&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; same&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; msg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;should return the state needed to render&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; chats &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;createChat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        user&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Bender&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        msg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Does Barry Manilow know you raid his wardrobe?&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        timeStamp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;451671300000&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;createChat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        user&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Andrew&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        msg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`Hey let&apos;s watch the mouth, huh?`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        timeStamp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;451671480000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;createChat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        user&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Brian&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        msg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;`We accept the fact that we had to sacrifice a whole Saturday in
              detention for whatever it was that we did wrong.`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        timeStamp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;451692000000&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chats&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addChat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; actual &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getViewState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      chatLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; chats&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      recentlyActiveUsers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Bender&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Andrew&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Brian&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;same&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;actual&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; expected&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 테스트에서 우리는 &lt;code class=&quot;language-text&quot;&gt;Array.prototyope.reduce()&lt;/code&gt; 메소드를 &lt;code class=&quot;language-text&quot;&gt;addChat()&lt;/code&gt; 액션 생성자를 처리하기 위해서 사용했다. Redux 리듀서의 멋진 점은 그들은 그저 보통의 &lt;code class=&quot;language-text&quot;&gt;reducer&lt;/code&gt; 함수라는 사실이다. 이는 다른 리듀서 함수로 함 수 있는 모든 것들을 Redux 리듀서 함수에도 할 수 있다는 의미와 같다.&lt;/p&gt;
&lt;p&gt;우리의 ‘기대된(expected)’ 값은 모든 채팅 객체가 로그에 남아있는지, 최근에 활성화된 유저가 제대로 표시되어 있는지 확인한다.&lt;/p&gt;
&lt;h2 id=&quot;redux-규칙&quot;&gt;&lt;a href=&quot;#redux-%EA%B7%9C%EC%B9%99&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Redux 규칙&lt;/h2&gt;
&lt;p&gt;만약 당신이 Redux를 제대로 사용한다면 다음과 같은 장점을 취할 수 있다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시간차 때문에 발생하는 버그의 제거&lt;/li&gt;
&lt;li&gt;결정적인 뷰 렌더링&lt;/li&gt;
&lt;li&gt;결정적인 상태(state) 재생산&lt;/li&gt;
&lt;li&gt;기능의 간편한 번복/재실행&lt;/li&gt;
&lt;li&gt;디버깅 단순화&lt;/li&gt;
&lt;li&gt;시간 여행자 되기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;하지만 위의 것들이 가능하게 하려면 다음 규칙들을 따라야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;리듀서는 반드시 순수한 함수여야 한다&lt;/li&gt;
&lt;li&gt;리듀서는 반드시 앱 상태의 신뢰할 수 있는 단일 출처여야 한다&lt;/li&gt;
&lt;li&gt;리듀서는 언제나 열거 가능(serializable)해야 한다&lt;/li&gt;
&lt;li&gt;리듀서는 함수를 포함해서는 안된다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;또한 다음을 명심하길 바란다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;어떤 앱은 Redux가 필요하지 않다&lt;/li&gt;
&lt;li&gt;액션 타입에는 상수를 사용하라&lt;/li&gt;
&lt;li&gt;액션 로직와 디스패치 호출자를 분리하기 위해 액션 생성자를 사용하라&lt;/li&gt;
&lt;li&gt;자체 타입 기술을 위해 ES6 기본 파라메터를 활용하라&lt;/li&gt;
&lt;li&gt;계산된 상태와 분리를 위해 셀렉터를 이용하라&lt;/li&gt;
&lt;li&gt;항상 TDD 개발!&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[버전 관리 시스템 Git 기초]]></title><description><![CDATA[Git: 분산형 파일 버전 관리 시스템 Git은 분산형 파일 버전 관리 시스템이다.  파일 버전 ,  관리 시스템 ,  분산형 이라는 세 단어로 Git을 설명할 수 있다. 파일 버전 작업을 하다보면 동일한 파일을 계속 수정하기 때문에 변경 이력에 대한 기록이 필요하다…]]></description><link>https://blog.rhostem.com//posts/2017-01-07-git-basic</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-01-07-git-basic</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Sun, 08 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;git-분산형-파일-버전-관리-시스템&quot;&gt;&lt;a href=&quot;#git-%EB%B6%84%EC%82%B0%ED%98%95-%ED%8C%8C%EC%9D%BC-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git: 분산형 파일 버전 관리 시스템&lt;/h2&gt;
&lt;p&gt;Git은 분산형 파일 버전 관리 시스템이다. &lt;em&gt;파일 버전&lt;/em&gt;, &lt;em&gt;관리 시스템&lt;/em&gt;, &lt;em&gt;분산형&lt;/em&gt;이라는 세 단어로 Git을 설명할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;파일-버전&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%BC-%EB%B2%84%EC%A0%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파일 버전&lt;/h3&gt;
&lt;p&gt;작업을 하다보면 동일한 파일을 계속 수정하기 때문에 변경 이력에 대한 기록이 필요하다. 가령 소스 코드에서 오류를 발견했을 경우 정상적으로 작동했던 시점의 코드와 비교하고 싶어질 것이다. 하지만 사용자가 직접 특정 시점의 파일을 유지하려면 파일의 복제본을 만들 수밖에 없다. 그래서 자동화를 위한 시스템이 등장했다.&lt;/p&gt;
&lt;h3 id=&quot;파일-버전-관리-시스템&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%BC-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파일 버전 관리 시스템&lt;/h3&gt;
&lt;p&gt;버전 관리 시스템(&lt;code class=&quot;language-text&quot;&gt;VCS&lt;/code&gt;, Version Control System)은 파일 버전을 관리하는 소프트웨어다. 사용자가 파일의 복제본을 직접 생성하지 않아도 여러가지 명령어를 통해 파일 버전을 효율적으로 관리할 수 있도록 도와준다. 단순 파일 복제를 넘어 변경 사항만 기록해서 디스크를 효율적으로 사용하고 브랜치를 생성해서 서로 다른 작업을 독립적으로 진행하는 등 다양한 기능을 지원한다.&lt;/p&gt;
&lt;h3 id=&quot;분산형-파일-버전-관리-시스템&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0%ED%98%95-%ED%8C%8C%EC%9D%BC-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산형 파일 버전 관리 시스템&lt;/h3&gt;
&lt;p&gt;분산형은 집중형의 반대말이다. 이는 파일 저장소의 원본이 어디에 있느냐의 차이라고 할 수 있다.&lt;/p&gt;
&lt;h4 id=&quot;중앙-집중식-버전-관리-시스템code-classlanguage-textcvcscode-central-version-control-system&quot;&gt;&lt;a href=&quot;#%EC%A4%91%EC%95%99-%EC%A7%91%EC%A4%91%EC%8B%9D-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9Ccode-classlanguage-textcvcscode-central-version-control-system&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;중앙 집중식 버전 관리 시스템(&lt;code class=&quot;language-text&quot;&gt;CVCS&lt;/code&gt;, Central Version Control System)&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;CVCS&lt;/code&gt;에서 사용자는 &lt;strong&gt;원격 저장소에서 최신 버전의 파일만을 내려받아서 사용한다&lt;/strong&gt;. 사용자가 새로운 버전을 추가하기 위해서는 무조건 원격 저장소에 추가해야 한다. 최신 버전이 항상 원격에 유지되며 사용자는 원격에 접속하지 않고는 작업 이력을 남길 수 없으므로 관리자로서는 관리하기 쉽다는 장점이 있다. 하지만 사용자가 원격 저장소에 연결되어 있지 않을 경우 협업, 백업을 할 수 없다는 단점이 생긴다.&lt;/p&gt;
&lt;p&gt;대표적인 소프트웨어는 SVN이다.&lt;/p&gt;
&lt;h4 id=&quot;분산형-버전-관리-시스템code-classlanguage-textdvcscode-distributed-version-control-system&quot;&gt;&lt;a href=&quot;#%EB%B6%84%EC%82%B0%ED%98%95-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9Ccode-classlanguage-textdvcscode-distributed-version-control-system&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;분산형 버전 관리 시스템(&lt;code class=&quot;language-text&quot;&gt;DVCS&lt;/code&gt;, Distributed Version Control System)&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DVCS&lt;/code&gt;는 원격 저장소에서 최신 버전의 파일만 가져오는 것이 아니라 &lt;strong&gt;과거 이력을 포함한 저장소의 모든 데이터를 복제&lt;/strong&gt;한다. 복제한 후에는 로컬에서 자유롭게 작업을 진행할 수 있고 원격 저장소에 문제가 생겨도 로컬에 복제된 저장소로 복구할 수 있다는 등의 장점이 있다. 복제한 로컬 저장소는 원격에 접속하지 않고도 모든 버전에 접근할 수 있는 진정한 백업이다.&lt;/p&gt;
&lt;p&gt;대표적인 소프트웨어로는 Git, Mercurial, Bazaar 등이 잇다.&lt;/p&gt;
&lt;h2 id=&quot;git-기초&quot;&gt;&lt;a href=&quot;#git-%EA%B8%B0%EC%B4%88&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git 기초&lt;/h2&gt;
&lt;h3 id=&quot;스냅샷&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%EB%83%85%EC%83%B7&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스냅샷&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;특정 시점에서 파일, 폴더 또는 워크스페이스의 상태&lt;/strong&gt;를 의미한다. 스냅샷을 통해 특정 시점에 어떤 파일에 어떤 내용이 기록되어 있었는지, 폴더 구조는 어떠했는지, 어떤 파일이 존재했는지 등 저장소의 모든 정보를 확인할 수 있다. Git에서는 새로운 버전을 기록하기 위한 명령인 커밋(&lt;code class=&quot;language-text&quot;&gt;commit&lt;/code&gt;)을 실행하면 스냅샷이 저장된다.&lt;/p&gt;
&lt;h4 id=&quot;git과-svn의-스냅샷의-차이점&quot;&gt;&lt;a href=&quot;#git%EA%B3%BC-svn%EC%9D%98-%EC%8A%A4%EB%83%85%EC%83%B7%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git과 SVN의 스냅샷의 차이점&lt;/h4&gt;
&lt;p&gt;SVN은 개별 파일별로 변화를 감지해서 스냅샷을 저장한다. 버전 생성은 시간순으로 파일들의 집합을 구성하는 방식을 이용한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/59K2gZwGY88awUAqOCCSA6/f7b77a3accef66966a59230cf34c13b1/snapshot-svn.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 38.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;snapshot-svn&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/59K2gZwGY88awUAqOCCSA6/f7b77a3accef66966a59230cf34c13b1/snapshot-svn.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/59K2gZwGY88awUAqOCCSA6/f7b77a3accef66966a59230cf34c13b1/snapshot-svn.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/59K2gZwGY88awUAqOCCSA6/f7b77a3accef66966a59230cf34c13b1/snapshot-svn.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/59K2gZwGY88awUAqOCCSA6/f7b77a3accef66966a59230cf34c13b1/snapshot-svn.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;SVN 스냅샷&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;반면에 &lt;strong&gt;Git은 저장소의 파일 시스템 전체를 스냅샷으로 취급&lt;/strong&gt;한다. 커밋 시점의 저장소 상태가 하나의 버전이 된다. 파일이 변경되지 않았다면 Git은 파일을 새로 저장하지 않고 링크만 저장한다. Git은 데이터를 스냅샷의 스트림으로 취급한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/24JQ2XvCgQa0Uq8Y02UgoA/ccff88d7135b61eacacdc6cdba077fde/snapshot-git.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 38.125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;snapshot-git&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/24JQ2XvCgQa0Uq8Y02UgoA/ccff88d7135b61eacacdc6cdba077fde/snapshot-git.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/24JQ2XvCgQa0Uq8Y02UgoA/ccff88d7135b61eacacdc6cdba077fde/snapshot-git.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/24JQ2XvCgQa0Uq8Y02UgoA/ccff88d7135b61eacacdc6cdba077fde/snapshot-git.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/24JQ2XvCgQa0Uq8Y02UgoA/ccff88d7135b61eacacdc6cdba077fde/snapshot-git.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Git 스냅샷&lt;/em&gt;&lt;/p&gt;
&lt;h4 id=&quot;git이-작고-빠를-수-있는-이유&quot;&gt;&lt;a href=&quot;#git%EC%9D%B4-%EC%9E%91%EA%B3%A0-%EB%B9%A0%EB%A5%BC-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%9D%B4%EC%9C%A0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git이 작고 빠를 수 있는 이유?&lt;/h4&gt;
&lt;p&gt;Git에서 커밋을 하면 워크스페이스 전체가 스냅샷으로 저장되지만 덩치가 배로 불어나지는 않는다. Git은 마지막 커밋의 스냅샷만 통째로 저장하고 나머지 커밋에 대해서는 스냅샷과 스냅샷이 차이를 기록한 ‘델타’를 저장하기 때문이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/67wMaUyIhyMyOeg8YCCYAw/0d2252ade62bf726c925b53048efb2bb/snapshot-delta.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 455px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 54.285714285714285%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAWCAIAAAA97EnnAAAMHWlDQ1BpY2MAAHjalVd3VBMJ971TklBC6BEQJPQmiiBNpHdBQDqsLSQBAiHEIUHF7rqsgmsXC5YVXRVx1bWAshZEXeui2PuHuqisrIsFGyq/PxJw1/Od8zvfO2dm7nlz33v3vZkzZx6g7yFUKGSkAVAkVzLJ0WGCzKxsAecBCJDQhj4shKISRWhSUjwA9F3/bW+ugwCAK+5ChUKG/80MxZISEUAkAcgRl4iKAGI/QJuJFIwSYF0AYDdZqVACrC4AJkxmVjbApgGY5KmxGQCTHDUeDMCESU0OB9ghgBZXKGTyAF48AEGpKE8J8GQAPORiqRzgrQMQJMoXigHeXQCDi4qKxYC+FgDnnH/kyftXzpz+nEJhXj9W9wIA0IqQlihkwqn/4zj+fyuSqfpq2ALg5jMxyQBMAGJ7YXFcMgAuQByW5yQkAjACiDNSMaDBt/NVMWkafqeoJDwbAB8gIRZGxAGwBEi+qjAtVIM9hQyg5pMJUmVsqgbnMMXJmvxkqVyWEK/JMz9fEtuHN0hKIlP6OLnSqFgABgC5vyw/NUOtkzxZKk1PAMADyEslhSlxmtj7ZfnhCX0cRpWcBsAeIF/nMlHJag5lVlTS1xc1RCSMTAFgBlAhyvzUGHUslSkpyYzv0yCWRESqNVBiiTxNo41SKpRhyZrYcoUsScOnNkhk0cnqOVN7SkpT+mIvK5lUzcyphwXCUUmaWm8UyqRUtTaaRDzCEQEBVBAgB8UogLSls6ETAs2dKAjBIA8SuGs8fREZEIKBHEKkoAx/QQ4JSvrjwiAEAwlKIcenfq/67I5cCMGgFBKUoBCPwaCItqCD6AA6ng6iQ+gg2pP2o/374gT6fVXZkewIdgw7iu3Sr0OEYshQDAbS/+KLgwwSqMBAAnlfD1/ysR6zWlkPWddYbaxbSMcfYCDtY02QzmW+Ui7AaLRBpZmKBDmQo6OPQzvSnrQ3HUYH0kG0PwQ0n7aAOz2c9qND6WA6gPam/f+lUNWv7cssv64ngfxf/Wj8PFeet0ZFTv+TCe9nfZ0l/B8zEqMYcV8zqfnUPuo0dZw6Sx2mGiCgjlEHqQvUEarhH2/CH2CQ118tGRLIUQgZpH0cjzqPDo+PX9UWauozkKBEKZmiBIDwYsVURpqXrxSEKhQyiSBWLhoyWODpMcwXyMzKFqg/Ha/4IAAQ/HNffJOaAP8KgMj74hPaAYceA8ZvvvjsXgLcJcCRSyIVU6r20QDAgg70YQJzDIIdnOEOT/ggACGIxCgkIhVZGA8R8lEEBpMxHXNQjkoswUqsxUZsxnb8jL1owGEcx284j0u4hjtoQzueoQtv0EMQBIfQI4wJc8KacCDcCE/CjwgiIol4IpnIIiYSeYScUBHTiW+JSmIZsZbYRNQSvxCHiOPEWaKVuEU8IDqIl8QHkiK5pAlpRTqSQ0k/MpSMI1PJcWQeOYksI+eRi8jVZA25k6wnj5PnyWtkG/mM7KZA6VJ8yoZyp/yocCqRyqZyKYaaSVVQVVQNtYtqpE5TV6g2qpN6T7NpY1pAu9MBdAydRovoSfRMeiG9lt5O19Mn6Sv0A7qL/szSY1my3FgjWLGsTFYeazKrnFXF2so6wDrFusZqZ71hs9l8thPblx3DzmIXsKexF7LXs3ezm9it7Efsbg6HY85x4wRyEjlCjpJTzlnD2ck5xrnMaee809LVstby1IrSytaSa83VqtLaoXVU67LWE60ebQNtB+0R2onaYu2p2ou1t2g3al/Ubtfu0THUcdIJ1EnVKdCZo7NaZ5fOKZ27Oq90dXVtdf11x+hKdWfrrtbdo3tG94Hue64R15Ubzh3LVXEXcbdxm7i3uK/09PQc9UL0svWUeov0avVO6N3Xe8cz5g3hxfLEvFm8al497zLvub62voN+qP54/TL9Kv19+hf1Ow20DRwNwg2EBjMNqg0OGdww6DY0NhxmmGhYZLjQcIfhWcOnRhwjR6NII7HRPKPNRieMHhlTxnbG4cYi42+NtxifMm43YZs4mcSaFJhUmvxs0mLSZWpkOtw03XSKabXpEdM2PsV35MfyZfzF/L386/wPA6wGhA6QDFgwYNeAywPemg00CzGTmFWY7Ta7ZvbBXGAeaV5ovtS8wfyeBW3hajHGYrLFBotTFp0DTQYGDBQNrBi4d+BtS9LS1TLZcprlZssLlt1Wg6yirRRWa6xOWHUO4g8KGVQwaMWgo4M6rI2tg6yl1iusj1n/KTAVhApkgtWCk4IuG0ubGBuVzSabFpseWyfbNNu5trtt79np2PnZ5dqtsGu267K3th9tP92+zv62g7aDn0O+wyqH0w5vHZ0cMxy/d2xwfOpk5hTrVOZU53TXWc852HmSc43zVRe2i59Loct6l0uupKu3a75rtetFN9LNx03qtt6tdTBrsP9g+eCawTfcue6h7qXude4PhvCHxA+ZO6RhyPOh9kOzhy4denroZw9vD5nHFo87w4yGjRo2d1jjsJeerp4iz2rPq156XlFes7wOer0Y7jZcMnzD8Jvext6jvb/3bvb+5OPrw/js8unwtfed6LvO94afiV+S30K/M/4s/zD/Wf6H/d+P8BmhHLF3xN8B7gGFATsCno50GikZuWXko0DbQGHgpsC2IEHQxKAfg9qCbYKFwTXBD0PsQsQhW0OehLqEFoTuDH0e5hHGhB0Iexs+InxGeFMEFREdURHREmkUmRa5NvJ+lG1UXlRdVFe0d/S06KYYVkxczNKYG7FWsaLY2tiuUb6jZow6GceNS4lbG/cw3jWeiW8cTY4eNXr56LsJDgnyhIZEJMYmLk+8l+SUNCnp1zHsMUljqsc8Th6WPD35dIpxyoSUHSlvUsNSF6feSXNOU6U1p+unj02vTX+bEZGxLKMtc2jmjMzzWRZZ0qyD2Zzs9Oyt2d3fRH6z8pv2sd5jy8deH+c0bsq4s+MtxsvGH5mgP0E4Yd9E1sSMiTsmfhQmCmuE3TmxOetyukTholWiZ+IQ8QpxhyRQskzyJDcwd1nu07zAvOV5HfnB+VX5ndJw6Vrpi4KYgo0FbwsTC7cV9soyZLuLtIomFh2SG8kL5SeLBxVPKW5VuCnKFW2TRkxaOamLiWO2lhAl40oOKk2UCuUFlbPqO9WD0qDS6tJ3k9Mn75tiOEU+5cJU16kLpj4piyr7aRo9TTStebrN9DnTH8wInbFpJjEzZ2bzLLtZ82a1z46evX2OzpzCOb/P9Zi7bO7rbzO+bZxnNW/2vEffRX9XV84rZ8pvfB/w/cb59Hzp/JYFXgvWLPhcIa44V+lRWVX5caFo4bkfhv2w+ofeRbmLWhb7LN6whL1EvuT60uCl25cZLitb9mj56OX1KwQrKla8Xjlh5dmq4VUbV+msUq1qWx2/+uAa+zVL1nxcm7/2WnVY9e51lusWrHu7Xrz+8oaQDbs2Wm2s3PjhR+mPNzdFb6qvcayp2szeXLr58Zb0Lad/8vupdqvF1sqtn7bJt7VtT95+sta3tnaH5Y7FdWSdqq5j59idl36O+PngLvddm3bzd1fuwR7Vnj9/mfjL9b1xe5v3+e3btd9h/7oDxgcq6on6qfVdDfkNbQezDrYeGnWouTGg8cCvQ37ddtjmcPUR0yOLj+ocnXe091jZse4mRVPn8bzjj5onNN85kXni6skxJ1tOxZ0681vUbydOh54+dibwzOGzI84eOud3ruG8z/n6C94XDvzu/fuBFp+W+ou+Fw9e8r/U2Dqy9ejl4MvHr0Rc+e1q7NXz1xKutV5Pu37zxtgbbTfFN5/ekt16cbv0ds+d2XdZdyvuGdyrum95v+Y/Lv/Z3ebTduRBxIMLD1Me3nkkevTsj5I/PrbPe6z3uOqJ9ZPap55PD3dEdVz685s/258pnvV0lv9l+Ne6587P9/8d8veFrsyu9hfMi96XC1+Zv9r2evjr5u6k7vtvit70vK14Z/5u+3u/96c/ZHx40jP5I+fj6k8unxo/x32+21vU26sQMkIAAAWAzM0FXm4D9LIA40uADk+9ewEACPW+CKj/Qf47Vu9nAAAfYFsIkDYbiG8CNjQBDrMBbhOQBCA1BKSXV/+hsZJcL091Li4DsN719r6yAjiNwCemt7dnfW/vpy0AdQtomqTe+QCAbQD86AoAF0cavvt69/o/TwNsUFk549YAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmDBsPHh3yOnubAAAE/klEQVRIx6WUe0xTVxzHm2y6TDLdsizz9cc0M5AYI2MuAzdgy1Qcm6JbMjajDpFHeQx8QAeIQAGr0vLWAlLGMCObSOlDGsejdcVCS1vaQnt7e1voa0UbQKW0BcSKO3haY5uwaP3mk/ac3/mdfnJv7z24J09jt9uH3VG0/tHa1na97xZvUCKDJb1e/8Q7d/P/HgnIB+jDqNogiimKhgWSwRRZW6LbXqX5sAxZW6wLrtRuqwCr6s0XkfcKYf/shA3+Ag5+OZ1OtSf8pxmSiGVyBayYzWYfsZbM6Q7MBsgTaIrkJll8w3Bqc9/nxfyIYslPl+Xx9dyQs+KYioFoiuwIlR9+jh9aAPvtkw+8xC+bBf19J9/gB4uPXF7ihYWFSe+YzSaLZRyObTabj9hEYAtxmQDd3gZtRK3+h9+w8Bowla07i0XWYp9Vi9/KRndWqj4io5+UyzYWKjYXwX6H1fuKp6en+9zhE4uIJNIF+vV2egcTllQqlY/YwpJKM5oBWBUHpbDRCramioOUdshzWlXnOhASXUZoRUgdyuJ2sKosoQ/n/Qn7Z20OL/Hi4uLD5QPuh4/YcVM7kdcNmCzmgc+pc//A6fNMFnEnC3rh6hSRC4uPHPO+4sfLB6z6iEcJdC4Oz8Ml6w63aGNpY4lXtT829QVkijafUR9s0ByoE6wjKKOqFeHlmn3UgQ25Q8ElvKV+vN1630s8MzMjdmewjlpHo9F6e3s6mExYwjDsmfLxQ4vL3ntP2qJvuwgwMcsBRvDJqvi3s8pwvczIoBjpZLC0NO6ggCVQMbSXwf65SQ7Y7lp44BbPz88bPFl6c0dG1AiiwbSwYrVan4nvGchPdLip/B26rXG6rccs+7OMEWl3D58xRqaDylhIonlXpj4Urw9LMUdlmKNOGMPT9J/ijRGpT/vj5qUrwPZxPRP30Dnqsma4rL+8IA5sJ9iJ4j/mrDgKGPqmTLafLP2aJIsh8zak8z7IEH1ZMhR9vuvdJFHY2b5tv0p2l/I2ZfKDTsH+GdGS2KBpw02YO8FoDttkUhEhqv7TmCRXJ81FxTmwMqH6FvQ8j0v52rx0pR88xnBusdV0A4xsqsCBWw2QWkpyLQXfSScx20mwohYc8hFbEiOHcFmA0dBL2u1Vhq/qsSAymI68U6ANrgRj5ftEbUg5GlSmCSxDt1xE1hNh/6x4pVtsMXA13I3qnvXI/6L2cEf0Ntg5RtzC27Gbt2OXIjFRnpAgjz82jE8SRn8v2BMjOXRE9vPRvi/2SWIPiQ7GyuLiBHsODER/B5oBTunrbrHD4UAQBDxPQ8tHLpcrPVENnPK5ej9YEsNnFRyKIneENdU1VGodg8lgstiwhKLos6faqiOBnUpBHJ3F9gOn6k0vMTiexj3R6bS60dGxsTGjyQQrU1NTPmIhN7PuqtwPZkYCvMQvHii+3ZVe2TgIKK3syittzycxcoqv5ZXST+Y1gymgvF5AIF4ruMA+X8tLy26AzYCZ4VVe4rm5Ob0n4sFBcIIo5HIU1cDK8wcIFPM4Kecv3QYQiv46kddyPIUSl0I5nlaRmfN7yumGtOxGIqUrnUBLzaovpPTgT9bDZoDNRwyOTM9/LKLRmq5caWQxGOwbHFjRaDQ+4m5WUiGF6wc2xapXutXCm3updSQ/sI+84afYrK4aF64ZF64eH/AL4eo7ojWjasbLX7HVKni19Pf3g9fkP5EkiuQ7zPCKAAAAKHRFWHRpY2M6Y29weXJpZ2h0AENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDE3v/MY0AAAABd0RVh0aWNjOmRlc2NyaXB0aW9uAERpc3BsYXkXG5W4AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;snapshot-delta&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/67wMaUyIhyMyOeg8YCCYAw/0d2252ade62bf726c925b53048efb2bb/snapshot-delta.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/67wMaUyIhyMyOeg8YCCYAw/0d2252ade62bf726c925b53048efb2bb/snapshot-delta.png?w=114 114w,
https://images.ctfassets.net/rpmifyuylbfw/67wMaUyIhyMyOeg8YCCYAw/0d2252ade62bf726c925b53048efb2bb/snapshot-delta.png?w=228 228w,
https://images.ctfassets.net/rpmifyuylbfw/67wMaUyIhyMyOeg8YCCYAw/0d2252ade62bf726c925b53048efb2bb/snapshot-delta.png?w=455 455w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Git의 스냅샷과 델타 구성&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;이렇게 하면 버전별로 전체 스냅샷을 모두 저장하지 않아도 마지막 스냅샷을 기준으로 특정 시점의 스냅샷을 만들 수 있다. 저장소의 크기도 자연히 줄어든다. (Git을 개발한 측에서는 VCS 중에서 저장소 크기가 가장 작다고 주장한다)&lt;/p&gt;
&lt;h3 id=&quot;로컬-기반의-명령&quot;&gt;&lt;a href=&quot;#%EB%A1%9C%EC%BB%AC-%EA%B8%B0%EB%B0%98%EC%9D%98-%EB%AA%85%EB%A0%B9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;로컬 기반의 명령&lt;/h3&gt;
&lt;p&gt;Git은 분산형 파일 버전 관리 시스템이기 때문에 로컬에 저장소 전체가 저장되어 있어서 자유롭게 커밋을 하거나 되돌릴 수 있고 과거 버전에도 접근할 수 있다. 반면에 SVN같은 중앙 집중식은 원격 저장소에 연결되어 있어야만 커밋을 할 수 있다. Git에서는 원격 저장소에 작업 결과물을 올리거나 새로 가져올 경우에만 네트워크 원결이 필요하다.&lt;/p&gt;
&lt;h3 id=&quot;파일-상태-라이프사이클&quot;&gt;&lt;a href=&quot;#%ED%8C%8C%EC%9D%BC-%EC%83%81%ED%83%9C-%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;파일 상태 라이프사이클&lt;/h3&gt;
&lt;p&gt;Git에서는 파일을 4가지 상태로 구분한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;untracked&lt;/code&gt; - Git 저장소에 추가되지 않은 상태. Git이 변경 이력을 관리하지 않으며 스냅샷에도 추가되지 않는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;tracked&lt;/code&gt; - 파일을 Git 저장소에 추가되어서 시스템에 의해 관리되고 있는 상태. 스냅샷에 추가된다. &lt;code class=&quot;language-text&quot;&gt;tracked&lt;/code&gt; 는 다음 3가지 상태로 구분된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;unmodified&lt;/code&gt; - 수정되지 않은 상태. 마지막 커밋과 비교해서 변경된 부분이 없는 경우.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;modified&lt;/code&gt; - 수정된 상태.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;staged&lt;/code&gt; - 수정된 파일을 커밋하기 위한 준비 상태. 커밋을 하게 되면 새로운 스냅샷이 생성되고 staged 상태의 파일은 다시 unmodified로 돌아간다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1RUxXnvvDOKaQwwgoeSgc8/07578d32c16d04ca8f3c1340c4bc6697/file-status.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 41.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;file-status&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1RUxXnvvDOKaQwwgoeSgc8/07578d32c16d04ca8f3c1340c4bc6697/file-status.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1RUxXnvvDOKaQwwgoeSgc8/07578d32c16d04ca8f3c1340c4bc6697/file-status.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1RUxXnvvDOKaQwwgoeSgc8/07578d32c16d04ca8f3c1340c4bc6697/file-status.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1RUxXnvvDOKaQwwgoeSgc8/07578d32c16d04ca8f3c1340c4bc6697/file-status.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Git 파일 라이프사이클&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;git-브랜치&quot;&gt;&lt;a href=&quot;#git-%EB%B8%8C%EB%9E%9C%EC%B9%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git 브랜치&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;브랜치는 특정 커밋을 가리키는 포인터와 같은 개념이다.&lt;/strong&gt; 특정 커밋을 기준으로 브랜치를 생성할 수 있다.&lt;/p&gt;
&lt;p&gt;Git의 강점은 브랜치를 쉽게 만들고 병합할 수 있다는 점이다. 브랜치를 이용하면 여러 사람이 함께 진행하는 프로젝트에서 다른 브랜치의 커밋에 영향을 받지 않고 작업을 진행할 수 있다.&lt;/p&gt;
&lt;p&gt;예를 들어 새로운 이슈가 생겨서 개발을 진행해야 할 때 브랜치를 새로 생성해서 작업을 진행할 수 있다. 개발이 진행 중인 상황에 배포된 버전에 문제가 생겼을 때는 이슈를 개발중인 브랜치는 유지하고 배포된 커밋에서 수정용 브랜치를 생성해서 문제를 해결할 수 있다. 이처럼 브랜치를 이용하면 다양한 상황에 유연하게 버전을 관리하면서 대응할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;브랜치-생성과-커밋-히스토리의-분기&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%83%9D%EC%84%B1%EA%B3%BC-%EC%BB%A4%EB%B0%8B-%ED%9E%88%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%9D%98-%EB%B6%84%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브랜치 생성과 커밋 히스토리의 분기&lt;/h3&gt;
&lt;p&gt;Git 저장소를 초기화하면 &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;라는 이름의 기본 브랜치가 만들어져 있다. &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;와는 다른 &lt;code class=&quot;language-text&quot;&gt;testing&lt;/code&gt;이라는 브랜치를 만들고 커밋을 진행한 예제 히스토리를 살펴보자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1Jx8IUqQE8OoKMGCKAi2Ic/2b535a089428ddbbbfe514365ac48dbd/branch.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 64%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;branch&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1Jx8IUqQE8OoKMGCKAi2Ic/2b535a089428ddbbbfe514365ac48dbd/branch.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1Jx8IUqQE8OoKMGCKAi2Ic/2b535a089428ddbbbfe514365ac48dbd/branch.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1Jx8IUqQE8OoKMGCKAi2Ic/2b535a089428ddbbbfe514365ac48dbd/branch.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1Jx8IUqQE8OoKMGCKAi2Ic/2b535a089428ddbbbfe514365ac48dbd/branch.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;2개의 브랜치 히스토리(좌측이 과거 커밋&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;기본 브랜치 &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;가 &lt;code class=&quot;language-text&quot;&gt;f30ab&lt;/code&gt;를 가리키는 상태에서 새 브랜치 &lt;code class=&quot;language-text&quot;&gt;testing&lt;/code&gt;을 생성하고 체크아웃(워크스페이스를 특정 스냅샷(커밋)으로 변경하는 명령)한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;HEAD&lt;/code&gt;(현재 작업 중인 로컬 브랜치를 가리키는 포인터)가 &lt;code class=&quot;language-text&quot;&gt;testing&lt;/code&gt;을 가리키는 상태가 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;커밋을 하면 새로운 스냅샷 &lt;code class=&quot;language-text&quot;&gt;87ab2&lt;/code&gt;가 생성된다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;testing&lt;/code&gt; 브랜치는 새로 생성된 스냅샷을 가리킨다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt; 브랜치는 스냅샷 &lt;code class=&quot;language-text&quot;&gt;87ab2&lt;/code&gt; 이전의 &lt;code class=&quot;language-text&quot;&gt;f30ab&lt;/code&gt;를 여전히 가리킨다.&lt;/li&gt;
&lt;li&gt;아직은 포인터만 다르고 브랜치가 갈라진 상태는 아니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt; 브랜치로 체크아웃한 후 커밋을 해서 새로운 스냅샷 &lt;code class=&quot;language-text&quot;&gt;c2b9e&lt;/code&gt;를 생성한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt; 브랜치는 &lt;code class=&quot;language-text&quot;&gt;testing&lt;/code&gt; 브랜치와는 달리 &lt;code class=&quot;language-text&quot;&gt;c2b9e&lt;/code&gt; 스냅샷을 가리킨다.&lt;/li&gt;
&lt;li&gt;이제 &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;testing&lt;/code&gt;은 스냅샷 &lt;code class=&quot;language-text&quot;&gt;f30ab&lt;/code&gt;로부터 서로 갈라져 나온 상태가 되었다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;브랜치의-병합merge&quot;&gt;&lt;a href=&quot;#%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%9D%98-%EB%B3%91%ED%95%A9merge&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;브랜치의 병합(merge)&lt;/h3&gt;
&lt;p&gt;브랜치를 분리한 후에는 병합 과정이 필수적이다. 병합은 2가지 상황이 있을 수 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;현재 브랜치가 가리키는 커밋이 병합할 브랜치가 가리키는 커밋의 조상인 때.&lt;/li&gt;
&lt;li&gt;현재 브랜치가 가리키는 커밋이 병합할 커밋의 조상이 아닌 때&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;1의-경우&quot;&gt;&lt;a href=&quot;#1%EC%9D%98-%EA%B2%BD%EC%9A%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1의 경우&lt;/h4&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4hwsyKGbSgUeSCwAu82auq/390762ed8601d23c13dfd55d53ca9503/merge-same-ancestor.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 47.87499999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;merge-same-ancestor&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4hwsyKGbSgUeSCwAu82auq/390762ed8601d23c13dfd55d53ca9503/merge-same-ancestor.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4hwsyKGbSgUeSCwAu82auq/390762ed8601d23c13dfd55d53ca9503/merge-same-ancestor.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/4hwsyKGbSgUeSCwAu82auq/390762ed8601d23c13dfd55d53ca9503/merge-same-ancestor.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/4hwsyKGbSgUeSCwAu82auq/390762ed8601d23c13dfd55d53ca9503/merge-same-ancestor.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;에서 &lt;code class=&quot;language-text&quot;&gt;hotfix&lt;/code&gt;를 병합하는 경우다. &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;는 &lt;code class=&quot;language-text&quot;&gt;hotfix&lt;/code&gt;의 부모이므로 &lt;code class=&quot;language-text&quot;&gt;c2&lt;/code&gt; 과 &lt;code class=&quot;language-text&quot;&gt;c4&lt;/code&gt; 커밋을 서로 비교해서 병합을 진행한다.&lt;/p&gt;
&lt;h3 id=&quot;2의-경우&quot;&gt;&lt;a href=&quot;#2%EC%9D%98-%EA%B2%BD%EC%9A%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2의 경우&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;에 &lt;code class=&quot;language-text&quot;&gt;hotfix&lt;/code&gt;가 병합되었고 &lt;code class=&quot;language-text&quot;&gt;iss53&lt;/code&gt; 브랜치에서는 별도로 계속 작업이 진행되었다. &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;에서 작업이 완료된 &lt;code class=&quot;language-text&quot;&gt;iss53&lt;/code&gt; 브랜치를 병합하려고 한다.&lt;/p&gt;
&lt;p&gt;이 경우에는 두 브랜치가 갈라져 나온 공통 조상 커밋, 병합할 두 브랜치의 커밋, 총 3개의 커밋을 비교해서 병합을 진행한다. 이를 &lt;code class=&quot;language-text&quot;&gt;3-way merge&lt;/code&gt;라고 한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5bsWsqkSYoSqcs2UgMkewm/27db6e42952d9d9081984a0b0233123f/3-way-merge.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 47.625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;3-way-merge&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5bsWsqkSYoSqcs2UgMkewm/27db6e42952d9d9081984a0b0233123f/3-way-merge.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5bsWsqkSYoSqcs2UgMkewm/27db6e42952d9d9081984a0b0233123f/3-way-merge.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/5bsWsqkSYoSqcs2UgMkewm/27db6e42952d9d9081984a0b0233123f/3-way-merge.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/5bsWsqkSYoSqcs2UgMkewm/27db6e42952d9d9081984a0b0233123f/3-way-merge.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;3-way merge&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;c4&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;c5&lt;/code&gt;는 모두 &lt;code class=&quot;language-text&quot;&gt;c2&lt;/code&gt;를 공통 조상 커밋으로 기지므로 &lt;code class=&quot;language-text&quot;&gt;c2&lt;/code&gt;를 기준으로 두 브랜치의 커밋을 비교해서 병합을 진행한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/20UIZWiszG8ymwueECYAMg/d8c1b78280c369ee5b772d53dbcd5936/3-way-merge-result.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 39.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;3-way-merge-result&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/20UIZWiszG8ymwueECYAMg/d8c1b78280c369ee5b772d53dbcd5936/3-way-merge-result.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/20UIZWiszG8ymwueECYAMg/d8c1b78280c369ee5b772d53dbcd5936/3-way-merge-result.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/20UIZWiszG8ymwueECYAMg/d8c1b78280c369ee5b772d53dbcd5936/3-way-merge-result.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/20UIZWiszG8ymwueECYAMg/d8c1b78280c369ee5b772d53dbcd5936/3-way-merge-result.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;3-way merge 실행 결과&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;병합이 완료되면 &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt; 브랜치는 두 브랜치를 병합한 결과인 &lt;code class=&quot;language-text&quot;&gt;c6&lt;/code&gt; 커밋을 가리키게 된다.&lt;/p&gt;
&lt;h3 id=&quot;병합-과정에서-발생하는-충돌conflict&quot;&gt;&lt;a href=&quot;#%EB%B3%91%ED%95%A9-%EA%B3%BC%EC%A0%95%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EC%B6%A9%EB%8F%8Cconflict&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;병합 과정에서 발생하는 충돌(conflict)&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;3-way merge&lt;/code&gt;는 실패하는 경우가 생길 수 있다. 서로 다른 브랜치에서 같은 부분을 수정한 경우에 발생한다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD:index.html
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;footer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;contact : email.support@github.com&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
=======
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;footer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; please contact us at support@github.com &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&gt;&gt;&gt;&gt;&gt;&gt;&gt; iss53:index.html&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 예제 코드는 index.html 파일의 &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; 태그 내부에서 충돌이 발생했음을 나타낸다. &lt;code class=&quot;language-text&quot;&gt;HEAD&lt;/code&gt;는 병합 시도시 체크아웃되어 있던 브랜치, 즉 앞서 살펴본 병합 예제에서는 &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt; 브랜치에 해당한다.&lt;/p&gt;
&lt;p&gt;그리고 &lt;code class=&quot;language-text&quot;&gt;=======&lt;/code&gt; 아래의 코드는 병합할 브랜치인 &lt;code class=&quot;language-text&quot;&gt;iss53&lt;/code&gt; 브랜치에서 커밋한 index.html 파일에서 충돌한 부분이다.&lt;/p&gt;
&lt;p&gt;충돌은 특정 브랜치의 코드를 사용하도록 선택하거나, 코드를 직접 수정하거나, 외부 병합 툴을 사용해서 해결한다. 브랜치간의 충돌을 최소하기 위해서는 작업중인 브랜치(&lt;code class=&quot;language-text&quot;&gt;feature&lt;/code&gt;)에서 다른 사용자와 공동으로 사용하는 브랜치(&lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;)에 추가되는 커밋들을 가능한 한 자주 병합해 나가야 한다.&lt;/p&gt;
&lt;h3 id=&quot;재배치rebase&quot;&gt;&lt;a href=&quot;#%EC%9E%AC%EB%B0%B0%EC%B9%98rebase&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;재배치(rebase)&lt;/h3&gt;
&lt;p&gt;재배치는 브랜치를 병합하는 다른 방식이다. 재배치를 사용하면 복수의 브랜치를 커밋 히스토리가 분기된 것처럼 보이지 않고 처음부터 1개의 브랜치였던 것처럼 구성할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/16AC51bvkYemUkCSSmaqky/f46be65912f37690fc902040aacda5ce/basic-rebase-1.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 47.87499999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAATCAMAAADVnb8xAAABWVBMVEUAAADt7d/u7uaOh3/v7+aFhXvzTSfzTSfzTCb0TSfzTCbxYUH0TSf0TSftUS3zTCaPiYHu7ubv7+fv7+eyrqbv7+fv7+fv7+fv7+fv7ufv7+fv7+eOiIGOiIHl5Nzv7+fv7+fv7+eOiIGOiIHv7+fv7+fv7+fv7+fv7+fv7+fv7+fv7+e5tq6PiYHv7+fv7+fIxb2PiYGOiIHp6eHk49uPiYGOiYHf3tbt7eWPiYGOiICPiYHv7+fv7+fv7+fv7+eQiYHv7+fv7+ft7eWPiYHv7+fv7+ePiYHu7ufv7+fv7+fv7+fv7+fv7+bv7+axraX0TSf0TSjzTyrsdFvtc1jvaUvseWHubVHxXDrv7+fm5t7s7OTY18/k5NzZ18/f3tbh4dni4trl5d3c3NTb2tLp6eHo59/n59/s7OPj49vr6+PzVDHua0/vZ0nwZkjzUSzxWznyWDXyVzT45oLyAAAAUHRSTlMAAAAAAAAcHgHqCOp8h5AEIAQkJmcKweQO7P0fAho5fn9KChh8Wg1ycQtde0wwM8ppMhWPkzUUUchIFRXrn/EiAV7+WhHzmgcBDxoZEAEEZ/tuRDgAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmDBsPHh3yOnubAAAA9UlEQVQoz62SPUvDYBhFz0nStFJJtOCmS4cOIrpJBzcHwZ/hXxRROyiCdNRBoeAHtYNKUQeX+oFNnLL6dvDOh/PwcK8EkgqASQiMd7Qso6MgGMWqYgicF0uImCELQNi4qJZELyHQpW20OAl+bQyRRWzAuKwKOPwbbPswrdS0Wj9A7b6g4xfQcEDSmQBNvWL9vTJurFLzm4Haoe4nt2qbOScMVVdoFh9nRmSQpDzm+UU2Jk0Z5/nl3StpAVnW799ArcF0mtB8ugY2D+F0twd0D+Ct3gPWsn3K7rFb50Qv/9dM1XVwPdM91VEvvIrnJElGM5yuFv4Lz8o8f+BY0JAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;basic-rebase-1&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/16AC51bvkYemUkCSSmaqky/f46be65912f37690fc902040aacda5ce/basic-rebase-1.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/16AC51bvkYemUkCSSmaqky/f46be65912f37690fc902040aacda5ce/basic-rebase-1.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/16AC51bvkYemUkCSSmaqky/f46be65912f37690fc902040aacda5ce/basic-rebase-1.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/16AC51bvkYemUkCSSmaqky/f46be65912f37690fc902040aacda5ce/basic-rebase-1.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;재배치 시작 전 분기된 브랜치 히스토리&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6r0wo9vOyQsqGGmsyqWWSq/3f91a722d833b609c8269cfcc811e83e/basic-rebase-2.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 37.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAPCAMAAAChiX3RAAABL1BMVEUAAACPiYHAtqv0TSf0TSf0TSf0TSf0TSf0TSf0TSf0TynyTin0TSePiIDv7+fv7+fv7+fV1Mzv7+fv7+fv7+fv7+ePiIDb2dHv7+fv7+fv7+eSjISPiICPiICPiYCPiICPiIDu7ubv7+fv7+fv7+fv7+fv7+fv7+fv7+ff3dWPiYHv7+fu7ufFwrrv7+fv7+fv7+fv7+fm5d2PiYGOiYDv7+fs6+OPiYGPiIHt7eXu7uaPiYGPiIHq6uKsp5/j4trv7+fv7+fv7+fv7+fv7+fv7+fv7+fv7+fv7+fv7+fV08uPiYH0TSf0TSj0TSf0TSfzVjPvakzwYkPub1PvaEr0TSfv7+fp6eHR0MrU08zf39je3tff39fh4Nna2tPR0cvLysTzUi3ubFDvaEvyVTL/xFKZAAAAUHRSTlMAAAAoTRyCXGbIyMxIHQ1cYYQ/hP0XBj+wtZE7DQ8BFQQBi7E3cLRRVYEeMQIkphQP+50sEOO7MxG/3jUQi0pXTDtdLGAcHioCUoQdLsg6EoD7igcAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmDBsPHh3yOnubAAAA00lEQVQYGY3B11bCUBAF0COgckVhQFQOdkWwxIi9YY8lFsCSwd7//xtcKz7rzd74UzwRaodNR/P65vYu6IRN0nSljDHdiKAHUaQzkpUc7Hq1da/5Ptj0DxRIFgfb8K/Y0DBCI6Nj+DU+QXKyFMMUyXIFmC6TnJnFXIFk0cG8S9JdQHVRHx51aXllVZ+edW19Y1NfXnVru7ajrTfN79b2VN9V94EDETmsAkci4h0DJ56InALpjIjkgDNfzsV3EMUForg0JnVlDKzqwcfn13cAq0Yi9AOPAB4ZEXZqJwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;basic-rebase-2&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6r0wo9vOyQsqGGmsyqWWSq/3f91a722d833b609c8269cfcc811e83e/basic-rebase-2.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6r0wo9vOyQsqGGmsyqWWSq/3f91a722d833b609c8269cfcc811e83e/basic-rebase-2.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/6r0wo9vOyQsqGGmsyqWWSq/3f91a722d833b609c8269cfcc811e83e/basic-rebase-2.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/6r0wo9vOyQsqGGmsyqWWSq/3f91a722d833b609c8269cfcc811e83e/basic-rebase-2.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;3-way merge 완료&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1woMSMZY6Ieuw4mk00sgIK/0d82b8302df066405c02d67fde4b1b32/basic-rebase-3.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 28.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAMCAMAAAAnHQ9/AAABiVBMVEUAAADu7ujv7+bu7ubw8Ob0TSf0TSf0TSfv7+fv7+fu7ubo6ODv7+fv7+fv7+f0TSf0TSfs6+Pv7+fr6uLc29Tv7+fv7+f0TSfyWDbxXj3yVjPxXDryVjL0TSePiYGsqKDv7+fv7+fv7+f0TiTzTiScgXXzTSTv7+fv7+bv7+fv7+fv7+bv7+etqaGPiYGOiIHv7+bv7+emoZnv7+fv7+fv7+fm5d2PiYGOiYHv7+fv7+fv7+fr6+OPiYGOiIDu7eXv7+eyrqbr6+Pv7+fv7+fv7+fr6+OPiYHv7+fr6+OPiYGOiIHt7eXu7uaPiYHq6uLv7+eqpp7h4dnv7+fv7+fv7+fv7+fv7+fv7+fv7+fu7ufv7+e0sKjv7+ePiID0TSf0TSf0TijzTyrzTSb0TSf0TSf0TSfzTSbxXTzwYEDxWjjwY0TxXDvv7+fl5N3p6eHp6ODq6eHo59/t7eXk49zt7OTs7OTi4dno5+Dn59/o6ODl5d3h4dns6+Ph4NnzUy/tcVbvaUv0TScdQrpxAAAAaHRSTlMAAAAAAAwzDyd1eHh4ZAQ6S0CAgIB8CTn8/Pz8/EoJByAiEwEDEwEFEQ4CEAEHFQINBkgH3/52Hg7AkZjAFgNj5CAz9zHoehHKoywRpsQjce0wQPs3DR4IIQMDHVgWEXru7u8DgiNEAdlKXFgAAAAJcEhZcwAAFxEAABcRAcom8z8AAAAHdElNRQfmDBsPHh3yOnubAAAA8ElEQVQYGYXBYSsDcQDH8e9v97/Z//BgM46UPKLwQEPS5F1oUXhlHlEeeResyJI82J5QtJI9UCaX27o716w8Oj4f8R9XpGTIUtBA19tT6lRkKUqKHXV8DRgyzEkCPTImlJAzLArUSliKwWnGrCQRuNLd6hdYeJgqecFbR2vakupKrTtJ3Gg/VytG/VtJwY7Ce0VGlSTXkBPNlz3X/7TJ+ISxvcBfsMVRNwxtt7ycHwl79n3SzuQ/+lbs3sDGeUTtGjZ1xn58BaUGplavXhaacHAB2yf85ZBfIsuRpBjnmB+GLO6s9Dr9xJAhi16AtmHoG9LpQxFkjS69AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;basic-rebase-3&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1woMSMZY6Ieuw4mk00sgIK/0d82b8302df066405c02d67fde4b1b32/basic-rebase-3.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1woMSMZY6Ieuw4mk00sgIK/0d82b8302df066405c02d67fde4b1b32/basic-rebase-3.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/1woMSMZY6Ieuw4mk00sgIK/0d82b8302df066405c02d67fde4b1b32/basic-rebase-3.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1woMSMZY6Ieuw4mk00sgIK/0d82b8302df066405c02d67fde4b1b32/basic-rebase-3.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;C4를 C4’으로 재배치해서 experiment 브랜치를 앞으로 이동&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/5etfGf7I5aaA2S4AYyk6CA/59cfaa70cedd8493eb19c3135161d4f1/basic-rebase-4.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 800px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 28.750000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAMCAMAAAAnHQ9/AAAA81BMVEUAAADu7eTt7dj0TSf0TSfzTSf0TSf0TSf0TSfyVjPxXDryVTHxWTbyVDD0TSePiYLv7+fu7ufv7+fv7+fv7+fv7+bu7ubu7ufv7+fv7+fu7uevq6Pv7+fv7+fk5NyPiYGPiYHu7ubv7+ePiYHv7+fu7uaPiYGPiYDs6+Pv7+etqaHm5d3v7+fv7+ft7eWPiYHv7+fr6+OPiYHt7eWuqqL0TSf0TinzUCv0TijxYEDwY0TxXDrvZ0nxXz7v7+fk49vo6ODp6ODp6eHn5t/t7OTi4trs7OTj4tvj4trp6OHo5+Dm5d7i4dr0TSfucFTvZEXvZkc98UJtAAAAOXRSTlMAAAAPQxQ6Szfy8vPy8kcRCRsXBRoCAhoWChBRCOR+IxHGmA6ewBwJaukoOvo0cwzFniiiUfLy8/IbqoC4AAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4chT1LDQAAAJlJREFUGNONkbGKwkAABWc2OcnabEAIEk5QtL3q/v8HrrzOQrA4YiFypUZJtLfZvHrg8eZJLoUAlnnwW/XHLFipmucAElBSC/4/WYxQeKbxDuFCaHqoTtBe5yF2frrR8aiugsOfh11b+jipzcz+7FDa+uwMQIzU9TFBJKV1guqt+uMGTqneHmI3ecwEPV/qsM8LH5fq8OvUC1+zLCn6f8IZsQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;basic-rebase-4&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/5etfGf7I5aaA2S4AYyk6CA/59cfaa70cedd8493eb19c3135161d4f1/basic-rebase-4.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/5etfGf7I5aaA2S4AYyk6CA/59cfaa70cedd8493eb19c3135161d4f1/basic-rebase-4.png?w=200 200w,
https://images.ctfassets.net/rpmifyuylbfw/5etfGf7I5aaA2S4AYyk6CA/59cfaa70cedd8493eb19c3135161d4f1/basic-rebase-4.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/5etfGf7I5aaA2S4AYyk6CA/59cfaa70cedd8493eb19c3135161d4f1/basic-rebase-4.png?w=800 800w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;master 브랜치를 C4’로 이동&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;git-flow&quot;&gt;&lt;a href=&quot;#git-flow&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Git flow&lt;/h2&gt;
&lt;p&gt;Git flow는 Vincent Driessen의 &lt;a href=&quot;http://nvie.com/posts/a-successful-git-branching-model/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;브랜칭 모델&lt;/a&gt;을 사용하기 위한 Git 확장 프로그램이다. 커맨드라인에서 사용하기 위해서는 별도 설치가 필요하고 &lt;a href=&quot;https://www.sourcetreeapp.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;SourceTree&lt;/a&gt;에는 기본 내장되어 있다.&lt;/p&gt;
&lt;p&gt;Git flow는 역할에 따라서 브랜치를 분리해서 사용한다.&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-textmastercode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textmastercode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;기본 브랜치. 배포된 버전의 브랜치로 사용한다.&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-textdevelopcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textdevelopcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;실제 개발이 진행되는 브랜치. 프로젝트를 함께 진행하는 팀원들이 우선적으로 공유하는 브랜치라고 할 수 있다.&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-textfeaturecode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textfeaturecode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;feature&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;이슈별로 &lt;code class=&quot;language-text&quot;&gt;feature&lt;/code&gt; 브랜치를 생성해서 작업을 진행한다. &lt;code class=&quot;language-text&quot;&gt;feature&lt;/code&gt; 브랜치는 &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;에 있는 커밋을 기반으로 생성하며 작업이 완료되면 &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;에 병합된다. 병합 후에는 특별한 경우가 아니라면 삭제한다.&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-textreleasecode&quot;&gt;&lt;a href=&quot;#code-classlanguage-textreleasecode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;release&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;다음 버전 배포를 위한 개발이 완료되었을 때 &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt; 브랜치의 커밋에서 생성하여 배포 준비를 진행한다. 버그 픽스, 간단한 기능 추가, 문서 작업 등을 목적으로 사용한다. 배포 준비가 끝나면 &lt;code class=&quot;language-text&quot;&gt;release&lt;/code&gt; 브랜치는 &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;에 병합된 후 삭제된다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;release&lt;/code&gt; 브랜치가 있으면 배포 준비 중에도 다른 팀원들은 즉시 다음 버전 개발을 시작할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;h3 id=&quot;code-classlanguage-texthotfixcode&quot;&gt;&lt;a href=&quot;#code-classlanguage-texthotfixcode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;code class=&quot;language-text&quot;&gt;hotfix&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;배포된 버전을 수정할 필요가 생기면 &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt; 브랜치의 커밋을 기반으로 생성한다. 작업이 완료되면 &lt;code class=&quot;language-text&quot;&gt;hotfix&lt;/code&gt; 브랜치는 &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;develop&lt;/code&gt;에 병합된 후 삭제된다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1kWu8q7EiMEOymgk06Eka8/8be6f2f6a11ffce5565d20ca124c3b8c/git-flow.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 790px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 71.77215189873417%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAdCAYAAADYSS5zAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDx4d8jp7mwAABUpJREFUWMPtV31MW1UUL4UGnXOyuC3Klk2XGY1/oInIdCaTtqyvry2DbEgHGwMJjm2B8TEBoV98lIJjIN+syPc322hkujG+HCjlD1Y6PyIxJsZlOnT6z2KiZFi9noPvkdfSQqGb/+hLfrn33XPueb977jnnvssr7hq4+/741zYW1QMWW3Bw8A6hUOh15WTkb19lHLGxmEyNQpm/SCTylja13JNdHrCxkHb1oOxxgO+ZNNMf9WqzjcXZ0/02sPcoyNbTrXk2+Yeli6CbdTaw54tzZR0F9rIGjY1XPzbza9vULdJu+YG0Xf+enJ+5S0D5KSQIpObuqOPIzeyjZFYVS+6oYwlLUDYwaJMNjhDZyDUiGxolsrFPCUuwUTv1F4C05X1OGrXXSbNumrAEFUOVRDHMYLSKKK6WE5ag4uMaGIcx1BmtJvKPSgmvoMk0XHPVYmZRZho3g/ITYJBnSoy4NpUWbWYxkqRE2SYkSNUZP6EvmswswKMo2wAQ6JO7JqqyRswsDCm9ZrC3DmQP0+BS2YWiRdDnVGawJ8C5dKPWTiatyTbz/n/+Mw9mLmAXYKOb+n6M/tMefHMbY2Pzisrll8zftU/fJjpjj8od4zmVLakd1llS3j/x81oJQkJOYPUwdFyuWVFZ39L/Te3QjfmMknPvuGM8rbAiuWbQOq9v+eD2WgnmNVwcqx2yzqur2ytXVMayAq5+DFt3ntXqe2wDlB4C5fBVxpAC8IgHBPkwP3JZpcyzxpg0Q6V8LcZzqlozj2VoX1wrwUPxidt1xl6tUCRyvUiIu1kIVius5FVAHCDQBSSACM471Xnjpz+1dV2dy8xZFsm5Je+avp0jh+KP752envYG+DDAvvcCwf0Ryp2y/eFPMluGKb/JxXbudhwLeyMqAM9YD7ZYIJWHHoajzu0JeJZKsR8rl2wtjAo9EBISguQwVl55QPU3cEUlcKcAXcoQpAAvd791sOsXTRz+oYRjICNBwLMWi8Wb1WcgcGiXjLFznM2lKCrIYWsX57jy4DPYj1NIthmiQg9ySwDIAjzJ2jV70BlBF8Y245HoKSmhQuEvDAvDo1UgpOlosVjs3hYrlcqNNE0/52rrYmJi/OVy+U7HbbJarXYts5VLbACh7Rg+8Pf9JX2h79a+bFWxYvozIoqIiEdP4k8yx473v+pB5uciAPv78vLDJXpDtDg2bgdVUaULhjqIGb1iIoKSL2uEqfJ+rHFcHWA99J/nfBSPKb4re1A+vDg2XlpOl9EJcuCyjrvFmEVoMAi3ITOhvK5ePTkXIqaOwr1i/lSsvgjGRYDjgD0AsVFl/j0roaLeSRFGG8Hpb555D+4m90RC0QF4PeZG8ZYDlFhz4ZowWXr60ozL1aTEGkJ0Jxv7YCVbilPPt544rNmNlx52G3CFcPdoSI0rkjjxxOsg90mKyd9blNLbAv1VZ74qsS5Je6JBs+oSgN5l+xLJP9wIITyoZwt9zEiuzn15mO3lQ/b5APhshkIMBbIybFEGWbwVCLyGMSXrLrxJFSTnSsszm+FGZsH4xDCIjIzcwCnQfK4Nbst8y8exZXT4XIJekNqY4naArQl0JsOEASJb4BI+DgTjpWUZuXB97MMijoWd1YMPLrHpbMyVzv2t8g/yYZhzWx6lP6WkG7STeInCio+QVmaVwGW6lnn3o1tyhyl98hFp2ds5dJ2qOyEhgcfOZzxvZ5PxjOMYz2FsUeZ4kvDZFmOBKklPl/UafmTi7QWEtDbnCm1UjzPve2Q9hbOS/CQDxGAn3aT7QqPRcGNI4NA6G1v4QYCF2MmYWFyIwb8BoAoHGLkIfoAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;git-flow&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1kWu8q7EiMEOymgk06Eka8/8be6f2f6a11ffce5565d20ca124c3b8c/git-flow.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1kWu8q7EiMEOymgk06Eka8/8be6f2f6a11ffce5565d20ca124c3b8c/git-flow.png?w=198 198w,
https://images.ctfassets.net/rpmifyuylbfw/1kWu8q7EiMEOymgk06Eka8/8be6f2f6a11ffce5565d20ca124c3b8c/git-flow.png?w=395 395w,
https://images.ctfassets.net/rpmifyuylbfw/1kWu8q7EiMEOymgk06Eka8/8be6f2f6a11ffce5565d20ca124c3b8c/git-flow.png?w=790 790w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  
&lt;em&gt;Git flow 브랜치 히스토리 샘플&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;참고-자료-이미지-출처&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%B6%9C%EC%B2%98&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료, 이미지 출처&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/book/ko/v2&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;book “Pro Git”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rogerdudler.github.io/git-guide/index.ko.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Git 간편 안내서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://backlogtool.com/git-guide/kr/intro/intro1_1.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;누구나 이해할수 있는 git 입문&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://dogfeet.github.io/articles/2012/git-delta.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Git: 델타와 스냅샷&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Git flow workflow tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://danielkummer.github.io/git-flow-cheatsheet/index.ko_KR.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Git flow cheatsheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[데코레이터를 이용한 React 컴포넌트 메소드 자동 바인딩]]></title><description><![CDATA[React 컴포넌트 메소드에 자동으로 바인딩되는  React 컴포넌트도 하나의 객체이므로 자바스크립트 문법을 따른다. 하지만 다른 점이 있는데, React는  를 이용해 컴포넌트를 생성하면 모든 메소드에 컴포넌트 객체를 자동으로 바인딩한다는 점이다. 자동 바인딩을 통…]]></description><link>https://blog.rhostem.com//posts/2017-01-03-react-class-component-autobinding</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2017-01-03-react-class-component-autobinding</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Tue, 03 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;react-컴포넌트-메소드에-자동으로-바인딩되는-code-classlanguage-textthiscode&quot;&gt;&lt;a href=&quot;#react-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A9%94%EC%86%8C%EB%93%9C%EC%97%90-%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C-%EB%B0%94%EC%9D%B8%EB%94%A9%EB%90%98%EB%8A%94-code-classlanguage-textthiscode&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;React 컴포넌트 메소드에 자동으로 바인딩되는 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;React 컴포넌트도 하나의 객체이므로 자바스크립트 문법을 따른다. 하지만 다른 점이 있는데, React는 &lt;code class=&quot;language-text&quot;&gt;React.createClass&lt;/code&gt;를 이용해 컴포넌트를 생성하면 모든 메소드에 컴포넌트 객체를 자동으로 바인딩한다는 점이다.&lt;/p&gt;
&lt;p&gt;자동 바인딩을 통해 React 컴포넌트의 메소드는 어떤 위치에서 호출되어도 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;를 통해 컴포넌트에 접근할 수 있게 된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; Button &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;_onClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  render&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위의 예제에서 &lt;code class=&quot;language-text&quot;&gt;div&lt;/code&gt; 컴포넌트를 클릭하면 &lt;code class=&quot;language-text&quot;&gt;Button&lt;/code&gt; 컴포넌트의 메소드로서 &lt;code class=&quot;language-text&quot;&gt;_onClick&lt;/code&gt;이 실행되는 것이 아니라 &lt;strong&gt;onClick 속성에 할당된 함수로서 호출&lt;/strong&gt;된다. 코드로 풀어내면 아래와 같다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_onClick&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// div 클릭시 호출&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;this._onClick&lt;/code&gt;을 할당받은 &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; 함수는 &lt;strong&gt;Button 컴포넌트의 메소드가 아니다&lt;/strong&gt;. 따라서 실행된 함수 내부에서의 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;는 자연히 컴포넌트를 가리키지 않아야 정상이다.(이 경우 &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;에 접근하면 &lt;code class=&quot;language-text&quot;&gt;null&lt;/code&gt;이 반환된다)&lt;/p&gt;
&lt;p&gt;하지만 &lt;code class=&quot;language-text&quot;&gt;React.createClass&lt;/code&gt;는 자동 바인딩을 통해 저 경우에도 this를 통해 컴포넌트에 접근할 수 있게 해 준다. 이는 작업시 편의성을 제공하기 위한 일종의 트릭이라고 할 수 있다.&lt;/p&gt;
&lt;h2 id=&quot;es6-클래스-형식의-컴포넌트-선언에서는-사라진-자동-바인딩&quot;&gt;&lt;a href=&quot;#es6-%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%98%95%EC%8B%9D%EC%9D%98-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%84%A0%EC%96%B8%EC%97%90%EC%84%9C%EB%8A%94-%EC%82%AC%EB%9D%BC%EC%A7%84-%EC%9E%90%EB%8F%99-%EB%B0%94%EC%9D%B8%EB%94%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;ES6 클래스 형식의 컴포넌트 선언에서는 사라진 자동 바인딩&lt;/h2&gt;
&lt;p&gt;그런데 React 버전 0.13부터 지원하는 ES6 클래스 형식의 컴포넌트 선언에서는 자동 바인딩을 지원하지 않는다. 공식 홈페이지의 &lt;a href=&quot;https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;포스트&lt;/a&gt;에 의하면 자바스크립트 표준과 다르게 동작하는 것이 혼란을 줄 수 있기 때문에 제거하기로 했다고 한다. 그리고 &lt;code class=&quot;language-text&quot;&gt;React.createClass&lt;/code&gt;를 이용하면 여전히 자동 바인딩이 지원된다.&lt;/p&gt;
&lt;p&gt;클래스 형식의 컴포넌트를 선언할 경우 메소드에 컴포넌트 인스턴스를 전달하기 위한 방법은 여러가지가 있다. &lt;code class=&quot;language-text&quot;&gt;constructor&lt;/code&gt; 내부에서 직접 바인딩하는 방법, 호출되는 장소에서 인라인으로 직접 바인딩해주는 방법, &lt;code class=&quot;language-text&quot;&gt;arrow function&lt;/code&gt;을 사용하는 방법 등이 있다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SelfBinding&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;say &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;say&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;React component&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;say&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;hello&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;hi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;hi&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;say&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;press&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;  &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;press&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hi&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;press&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;데코레이터code-classlanguage-textdecoratorcode를-이용한-자동-바인딩&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0code-classlanguage-textdecoratorcode%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9E%90%EB%8F%99-%EB%B0%94%EC%9D%B8%EB%94%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데코레이터(&lt;code class=&quot;language-text&quot;&gt;decorator&lt;/code&gt;)를 이용한 자동 바인딩&lt;/h2&gt;
&lt;p&gt;데코레이터는 차기 자바스크립트 표준(ES2016/ES7)에 추가될 문법으로 &lt;a href=&quot;https://en.wikipedia.org/wiki/Higher-order_function&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;higher-order&lt;/a&gt; 함수 호출을 위한 간편한 문법을 제공한다. higher-order 함수는 다른 함수를 래핑해서 새로운 함수를 생산한다. 함수를 변경하지 않고 기능을 확장할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p&gt;데코레이터 라이브러리인 &lt;a href=&quot;https://github.com/jayphelps/core-decorators.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;core-decorators.js&lt;/code&gt;&lt;/a&gt;의 &lt;code class=&quot;language-text&quot;&gt;autobind&lt;/code&gt; 메소드를 이용하면 자동 바인딩을 간편하게 적용할 수 있다.&lt;/p&gt;
&lt;p&gt;특정 메소드에만 적용할 수도 있고 컴포넌트에 적용하면 모든 메소드가 바인딩된다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; autobind &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;core-decorators&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

@autobind
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DecoratedComponent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;데코레이터를-사용하기-위한-필요조건&quot;&gt;&lt;a href=&quot;#%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%ED%95%84%EC%9A%94%EC%A1%B0%EA%B1%B4&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;데코레이터를 사용하기 위한 필요조건&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;babel&lt;/code&gt; 버전이 6일 경우 &lt;a href=&quot;https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;babel-plugin-transform-decorators-legacy&lt;/code&gt;&lt;/a&gt; 플러그인을 설치해야 한다.&lt;/li&gt;
&lt;li&gt;react-hot-loader를 사용하고 있을 경우 &lt;a href=&quot;https://github.com/gaearon/react-hot-loader/pull/182&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;2.0.0-alpha&lt;/code&gt;&lt;/a&gt;이상의 버전이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참조&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EC%A1%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참조&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes#Prototype_methods&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Classes - Prototype methods (MDN references)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React v0.13.0 Beta 1 # autobinding&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React and ES6 - Part 3, Binding to methods of React class (ES7 included)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://facebook.github.io/react/docs/react-api.html#createclass&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React.createClass&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.4s8bu1oeo&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Exploring EcmaScript Decorators&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[[번역] 자바스크립트 피로감을 줄여주기 위한 학습 계획]]></title><description><![CDATA[이 글은  “A Study Plan To Cure JavaScript Fatigue” 를 번역한 글입니다. 많은 이들처럼 나도 최근 Jose Aguinaga의 글  “How it feels to learn JavaScript in 2016” 을 읽었다. 그의 글은 정…]]></description><link>https://blog.rhostem.com//posts/2016-12-19-a-study-plan-to-cure-javascript-fatigue</link><guid isPermaLink="false">https://blog.rhostem.com//posts/2016-12-19-a-study-plan-to-cure-javascript-fatigue</guid><dc:creator><![CDATA[rhostem]]></dc:creator><pubDate>Mon, 19 Dec 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;이 글은 &lt;a href=&quot;https://medium.freecodecamp.com/a-study-plan-to-cure-javascript-fatigue-8ad3a54f2eb1#.boi9p3ig6&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“A Study Plan To Cure JavaScript Fatigue”&lt;/a&gt;를 번역한 글입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/24frjpOsOQ2cAUIwQiKeAm/84b752bfb1d55ef0e28b739dcc0c1b0e/2016-12-19-main.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 40.050000000000004%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAQCAMAAABTCc2fAAACAVBMVEX////+8fz6wvD82PXJ0fG/ye/+/v/d8/ja8ffc8vj+8fv6wfDmr+XWodz4wO/81/X7+v749f38+/6/yu+stOWipdyoruG0wOz9/v+75vGy4O6gv92y3+255fH+/v746/birOLPmtf4v+/m3PfNue/GserDrujt5fmpseOZmNSjp92kxeCQos6iwt75+Pn5+fn5+fr6+vr87/r0u+zYo93fqeHstOiym9u6o+GhpduyvOqpz+Whwd645fH88fr7yfL83PfKtu2xmdrAq+bAyu+t1uiiw9+s1Oj6+fri2PO8puKzm9u4od+/qeT4+Pnl9fnk9fnl9vr49/j9/f38/Pz4+Pjr5PfbzPPy7fvy+/Hc9Nnd9dre9tzr9uv39/j+7d7+5M7/+vXq+ejL8cewxrurvrjn+eX597T49rH497n39vf+z6b9t3rmpHzyrXv669+ks7WdqbL18ozp5Ink3onq5Yn18pTyrnuocYLEiH//8OT/yd7/n8P/4e3K78azy7yzzLzJ7sbh2Ynd1Yn6y6Pgn33vrHv/xtzqi7rLd7HVfrT5lb7/3+vK8MfD5cTF58TK78fi24nUyonl34n285X1sHvfnn3pp3zsjLrtjbvliLj6/vrz/PL6/vny7ons54n+59T+273/+PL/mcDmiLn9/Oj9/Of9/en/yt//ocX/4u0/I/hoAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAATFJREFUGBkFwc9rzgEAwOHnY9/I3nfed7MDiXIgP7KmLCk5OzlIahdXB3+GP0VqB6acXJTyB1gpB/lR00RtMjsoZTwPAAAAAIAAAACF/gIwYAj94VD0Gxyuatc09IMBk6q+sVD1BU61vVg7HK32Z34wwGLtgPn6uRTVYrUDC23DAPMfnPkE0/5NX4Mb4fIrTtY2DLj89oCPwWy9A8uzL4Cz1fn3CAe5ubHJ0unoGW73FNyp0BqB1Uq/nwDuFXp55fMbt54DAzhc7c884n7U92PV/szyOrfXAQNg3C8YVa1tPhjV3ujSEi4+BAbgRE23YNJuC/PHvx1vz7gqwABMqi2YNG3D9Qv1lWntAhwA1yZH5sZXYW5uhJXxaHblnNFo/xhAAMDjaBWwFroLAAAAAAD4DyenPaz8I9wmAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-main&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/24frjpOsOQ2cAUIwQiKeAm/84b752bfb1d55ef0e28b739dcc0c1b0e/2016-12-19-main.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/24frjpOsOQ2cAUIwQiKeAm/84b752bfb1d55ef0e28b739dcc0c1b0e/2016-12-19-main.png?w=500 500w,
https://images.ctfassets.net/rpmifyuylbfw/24frjpOsOQ2cAUIwQiKeAm/84b752bfb1d55ef0e28b739dcc0c1b0e/2016-12-19-main.png?w=1000 1000w,
https://images.ctfassets.net/rpmifyuylbfw/24frjpOsOQ2cAUIwQiKeAm/84b752bfb1d55ef0e28b739dcc0c1b0e/2016-12-19-main.png?w=2000 2000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;많은 이들처럼 나도 최근 Jose Aguinaga의 글 &lt;a href=&quot;https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f#.5wjpn7svo&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;“How it feels to learn JavaScript in 2016”&lt;/a&gt;을 읽었다.&lt;/p&gt;
&lt;p&gt;그의 글은 정곡을 찔렀던 것이 분명한 모양이다.(나는 그 포스트가 Hacker News에 한번도 아니고 두번이나 탑 스팟에 오르는 것을 보았다.) 그건 &lt;a href=&quot;https://www.reddit.com/r/javascript/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;/r/javascript&lt;/a&gt;에서도 가장 인기있는 글이었고 Medium에서는 1만개의 ‘like’를 기록했다.&lt;/p&gt;
&lt;p&gt;하지만 이건 그리 놀라운 일이 아니다. 난 많은 사람들에게 자바스크립트 생태계(ecosystem)가 혼란스럽게 느껴질 것이라고 이미 생각하고 있었다. 사실 내가 &lt;a href=&quot;http://stateofjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;State Of JavaScript&lt;/a&gt; 설문조사를 진행했던 가장 큰 이유는 어떤 라이브러리가 실제로 인기가 있는지를 찾아내고, 최종적으로는 사람들의 의견을 정리하기 위함이었다.&lt;/p&gt;
&lt;p&gt;하지만 오늘, 나는 한 단계 더 들어가고 싶다. 나는 현 상황을 불평하는 대신 자바스크립트 생태계를 정복하기 위한 확실한, 단계적인 학습 계획을 제공하고자 한다.&lt;/p&gt;
&lt;h2 id=&quot;이-글이-필요한-사람&quot;&gt;&lt;a href=&quot;#%EC%9D%B4-%EA%B8%80%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%82%AC%EB%9E%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이 글이 필요한 사람&lt;/h2&gt;
&lt;p&gt;당신이 아래의 조건을 만족한다면 이 학습 계획이 필요할 것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;당신은 이미 변수, 함수와 같은 기본적인 프로그래밍 개념과 친숙하다&lt;/li&gt;
&lt;li&gt;당신은 PHP, 파이선같은 언어로 백엔드 작업을 충분히 해본 경험이 있고, 프론트엔드에서는 jQuery같은 라이브러리로 간단한 작업 정도는 해본 경험이 있다.&lt;/li&gt;
&lt;li&gt;당신은 프론트엔트 개발을 더 깊이 공부하고 싶지만 시작하기도 전에 프레임워크와 라이브러리의 홍수에 그만 익사하고 있는 중이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;이-글이-다루게-될-내용&quot;&gt;&lt;a href=&quot;#%EC%9D%B4-%EA%B8%80%EC%9D%B4-%EB%8B%A4%EB%A3%A8%EA%B2%8C-%EB%90%A0-%EB%82%B4%EC%9A%A9&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이 글이 다루게 될 내용&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;모던 자바스크립트 웹앱은 어떤 형태인가&lt;/li&gt;
&lt;li&gt;왜 단순히 jQuery를 사용하면 안되는가&lt;/li&gt;
&lt;li&gt;왜 React가 안전한 선택인가&lt;/li&gt;
&lt;li&gt;왜 먼저 자바스크립트를 제대로 공부할 필요가 없을수도 있는가&lt;/li&gt;
&lt;li&gt;어떻게 ES6 문법을 공부하는가&lt;/li&gt;
&lt;li&gt;왜, 그리고 어떻게 Redux를 배워야 하는가&lt;/li&gt;
&lt;li&gt;GraphQL이 무엇이고 왜 그것이 중요한가&lt;/li&gt;
&lt;li&gt;그 다음 행선지는 어디인가&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;이-글에서-인용한-자료들에-대해서&quot;&gt;&lt;a href=&quot;#%EC%9D%B4-%EA%B8%80%EC%97%90%EC%84%9C-%EC%9D%B8%EC%9A%A9%ED%95%9C-%EC%9E%90%EB%A3%8C%EB%93%A4%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;이 글에서 인용한 자료들에 대해서&lt;/h2&gt;
&lt;p&gt;이 포스트가 포함하고 있는 링크 중에는 &lt;a href=&quot;http://wesbos.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Wes Bos&lt;/a&gt;의 학습 강의가 몇 있다. 저 강의들이 정말로 좋다고 생각해서 추천한 것이지 나랑 어떤 특별한 관계가 있어서 그런 것은 아니다. 만약 다른 자료들을 찾아보고 싶다면 Marik Erikson이 &lt;a href=&quot;https://github.com/markerikson/react-redux-links&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React, ES6, Redux 관련 자료 링크&lt;/a&gt;들을 멋지게 정리해 두었으니 참조하면 된다.&lt;/p&gt;
&lt;h2 id=&quot;javascript-vs-javascript&quot;&gt;&lt;a href=&quot;#javascript-vs-javascript&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Javascript VS Javascript&lt;/h2&gt;
&lt;p&gt;시작하기 전에, 우리가 생각하고 있는 자바스크립트가 같은 것인지 확실히 해야 한다. 만약 당신이 “자바스크립트 공부” 또는 “자바스크립트 학습 계획”을 검색한다면 당신은 자바스크립트 &lt;em&gt;언어&lt;/em&gt;에 대해서 가르쳐주는 엄청난 양의 검색 결과와 마주하게 될 것이다.&lt;/p&gt;
&lt;p&gt;하지만 사실 그건 &lt;em&gt;쉬운&lt;/em&gt; 영역이다. 당신이 언어에 깊이 파고들면서 공부할수록 대부분의 웹앱들은 상대적으로 간단한 코드를 사용하고 있다는 사실을 알게 될 것이다. 다른 말로 하자면 당신이 웹앱을 개발하기 위해 필요한 코드의 80%는 대체로 일반적인 자바스크립트 언어 서적의 초반 몇 챕터가 다루고 있는 내용들이다.&lt;/p&gt;
&lt;p&gt;정말로 어려운 문제는 수없이 많은 프레임워크와 라이브러리가 서로 경쟁하고 있는 이 자바스크립트 &lt;em&gt;생태계&lt;/em&gt;를 정복하는 일이다. 좋은 소식은, 이 학습 플랜이 초점을 맞추고 있는 문제는 바로 그 생태계라는 점이다.&lt;/p&gt;
&lt;h2 id=&quot;자바스크립트-앱의-구성-요소&quot;&gt;&lt;a href=&quot;#%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%95%B1%EC%9D%98-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;자바스크립트 앱의 구성 요소&lt;/h2&gt;
&lt;p&gt;왜 모던 자바스크립트 앱에 그렇게 복잡해 보이는지 이해하기 위해서는 먼저 그것들이 어떻게 동작하는지를 이해해야 한다.&lt;/p&gt;
&lt;p&gt;먼저 약 2008년경의 “전통적인” 웹앱의 형태를 보자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2FtdzrzhTiU4MQKkSE4oeq/67c1753c4ef549d113267c7c729a4a7a/2016-12-19-1.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 18.06640625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAHCAMAAABN2v+8AAAA81BMVEUAAAD////q1PTg2ezf2Ovg2ezj3O/g2ez30Pn3z/j30Pnf2Ovf2Ov49fXz8PDy7+/y7+/i2+7f2Ovy7+/z8PD2z/j3z/j18vL49vbq5O7k3u349/f18/by7+/28/P08fHz8PD08PD08fHf2Ove1+rf2Ovi2+7f2Oz3z/n2zvj3z/nj3O/i2+7k3fD60/v50vr50/vm3/Ll3vHl3vL71vz71vz71fzUzeTGvNvOxeDe1+q3rdO0p9DYsuXSrOHeuOnVzuW7sdXz8fXp5vC6stW0qdHl4e7iveznwe/jve3Lw9/HvtzUy+TLwt7SyuP2zvjVruNtWeQpAAAAN3RSTlMAAABytZEkK4a1fqHMASoqFjM9GSi9sjCcZ1Gt9kgBHQ8SHFyTdh0jbZNmKCwXLSwrLikSIiQgoaHOngAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+YMGw8QItrfeygAAACQSURBVBgZBcHBSgJhFAbQ8znT33WojbRsF4Sh0Kb3f4CgVWSJtWvbrnRCGTwnAF3IEW1C948K2QN6gFonr5UkD8n0cXW6+LleJu97QA9gntGwylvM81dZbW7HIQEwAzAcFi6/Pmtc/NbN2LbtW1UDEIC7kB3uJ3RbLEM2gB6ggjVAoIUABADMHpMXAJ6SZ4AzPG0eZ+JmHcgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-1&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2FtdzrzhTiU4MQKkSE4oeq/67c1753c4ef549d113267c7c729a4a7a/2016-12-19-1.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2FtdzrzhTiU4MQKkSE4oeq/67c1753c4ef549d113267c7c729a4a7a/2016-12-19-1.png?w=256 256w,
https://images.ctfassets.net/rpmifyuylbfw/2FtdzrzhTiU4MQKkSE4oeq/67c1753c4ef549d113267c7c729a4a7a/2016-12-19-1.png?w=512 512w,
https://images.ctfassets.net/rpmifyuylbfw/2FtdzrzhTiU4MQKkSE4oeq/67c1753c4ef549d113267c7c729a4a7a/2016-12-19-1.png?w=1024 1024w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;데이터베이스가 백엔드로 데이터를 보낸다.(당신의 PHP 또는 rails 앱)&lt;/li&gt;
&lt;li&gt;백엔드가 데이터를 읽어서 HTML을 내보낸다.&lt;/li&gt;
&lt;li&gt;HTML이 브라우저로 전달되고, 브라우저는 그것을 DOM으로 표시한다.(기본적으로는 웹 페이지)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이제 많은 앱들이 탭 메뉴나 모달 윈도우같은 상호작용을 구현하기 위해 클라이언트에서 자바스크립트 코드를 첨가한다. 하지만 근본적으로 브라우저는 여전히 HTML을 전달받은 후 시작한다.&lt;/p&gt;
&lt;p&gt;이제 이것을 SPA(Single Page App)이라고도 알려져 있는 2016년의 “모던” 웹 앱과 비교해 보자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/26XGRapm5W0sU2WkyIYawE/4405cb2dd10ce56a2447dbc448162fca/2016-12-19-2.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 18.06640625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAHCAMAAABN2v+8AAABZVBMVEUAAACsrP//////rf+vr///qv+qqv+urvn/sP/h2u3f2Ovj3PDg2ezg2ez3z/n3z/j30Pn3zvj3z/n40Png2ezj3O/y7+/y7+/y7+/e1+vg2uv18/Ps6fDt6vHy7+/20ff2zvjy8PDy7+/3zvj2zvj30Pnt6PD18/b08vXx7u7f2Ov18vL49vby7+/2zvj08PHz8PT49vfx7O708PD08fH08PDb1Oj29PTu6vLr5/Dy8PDqw/H08fH08fHg2eze1+rk3PDf2Ovf2Ov3zvj2zvj3z/n3z/n3z/n30Pnk3fDi2+7j3O/50vr50vr60/vm3/Ll3vHm3/H61Pz61Pz71fze1+rDuNnIv93Atti1qdHUy+T2zvjqwvHsxPLKpt3WsOTlvu68stbEu9vp5vDBuNm3rtPUzOS/mNbas+fn4+/duOnnwe/mv+/QyOLHvdzXz+bHvtzJv93Mw9/dt+nctujfuOnet+lUo9ciAAAAVXRSTlMAAAAAAAAAAABBtQ2oWlW1rgK0TFwTICoh7Yls6elxhPYlHQP+bC/P1UGAQsU1eCDmvT8WHRftaeLiafYaFDSTC4lKRZONApM+HCwiISwfJSIhIykbu3rMRgAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+YMGw8QI63YS74AAACxSURBVBgZBcGxSoJRAAbQ8+UPEffeyE2KeoAeQaSIKKIl6DmD1qYQRAiaWtp6g0RN/mhROic4CNKTsmPQo4ZstgyzpVvQoVwlu2lJvttlMj1Mkkky689+SzJO5gs6ULIZZjIfrmpylEiUOM/dW35qAnswav3xsn60r9Na66rW1taj1k4+63td25QlBBdBpuyPdwxecR0y++M2W7oXOpTAPSBwENwABAHwkDwDAB6TJ4B/bFcqgqaTkOsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-2&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/26XGRapm5W0sU2WkyIYawE/4405cb2dd10ce56a2447dbc448162fca/2016-12-19-2.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/26XGRapm5W0sU2WkyIYawE/4405cb2dd10ce56a2447dbc448162fca/2016-12-19-2.png?w=256 256w,
https://images.ctfassets.net/rpmifyuylbfw/26XGRapm5W0sU2WkyIYawE/4405cb2dd10ce56a2447dbc448162fca/2016-12-19-2.png?w=512 512w,
https://images.ctfassets.net/rpmifyuylbfw/26XGRapm5W0sU2WkyIYawE/4405cb2dd10ce56a2447dbc448162fca/2016-12-19-2.png?w=1024 1024w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;차이점을 알겠는가? 서버은 이제 HTML을 보내는대신 &lt;em&gt;데이터&lt;/em&gt;를 보낸다. 그리고 “데이터에서 HTML로의 전환” 단계는 서버 대신 클라이언트에서 일어난다.(그래서 당신은 클라이언트에게 데이터에서 HTML로의 전환이 어떻게 일어나는지를 설명해주는 코드를 전달해줘야만 한다)&lt;/p&gt;
&lt;p&gt;이는 많은 사실을 내포하고 있다. 먼저 장점으로는:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;주어진 컨텐츠에 대해 HTML 전체를 전달하는 것보다는 데이터만을 전달하는 것이 더 빠르다&lt;/li&gt;
&lt;li&gt;클라이언트는 브라우저를 새로고침하지 않고도 컨텐츠를 빠르게 변경할 수 있다(그래서 “Single Page App”).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;단점으로는:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“데이터를 HTML로 변경”하는 코드의 덩치가 꽤 커질 수 있기 때문에 초기 로딩이 더 길어진다.&lt;/li&gt;
&lt;li&gt;이제 당신은 클라이언트에서도 데이터를 저장하고 관리하는 공간이 필요하다. 데이터를 캐싱하거나 검사할 경우가 필요하기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그리고 최악은:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;축하합니다! 이제 당신은 서버 사이드 스택만큼이나 복잡한 클라이언트 사이드 스택을 상대해야만 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;클라이언트-서버-스펙트럼&quot;&gt;&lt;a href=&quot;#%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%84%9C%EB%B2%84-%EC%8A%A4%ED%8E%99%ED%8A%B8%EB%9F%BC&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;클라이언트-서버 스펙트럼&lt;/h2&gt;
&lt;p&gt;단점들이 많다면 왜 굳이 이런 어려움을 겪으면서까지 개발을 해야 할까? 그냥 전통적인 PHP 앱을 그냥 계속 쓰면 안될까?&lt;/p&gt;
&lt;p&gt;좋다, 만약 당신이 계산기를 만든다고 생각해보자. 사용자에게 2+2의 답이 무엇인지 알려주기 위해서는 브라우저만으로도 그 작업을 충분히 할 능력이 있는데도 굳이 서버까지 갈 필요는 없다.&lt;/p&gt;
&lt;p&gt;반면에 당신이 블로그와 같은 순수하게 정적인 사이트를 개발한다면 서버에서 HTML을 생성해서 전달하는 것이 확실히 좋으니 그렇게 하면 된다.&lt;/p&gt;
&lt;p&gt;진실은 거의 대부분의 웹앱이 앞선 두 경우, 계산기와 블로그의 중간쯤 어딘가에 위치한다는 점이다. 문제는 앱이 클라이언트-서버 스펙트럼의 어느 지점에 위치하는지를 파악하는 것이다.&lt;/p&gt;
&lt;p&gt;하지만 핵심은 &lt;em&gt;그 스펙트럼은 연속적이지 않다&lt;/em&gt;는 점이다. 순수한 서버 사이드 앱에서 순수한 클라이언트 사이드 앱으로 조금씩 이동해 나갈 수는 없다. 어떤 지점(the Divide)이 되면 작업을 멈춰야 하고 모든 것을 서버와 클라이언트로 분리하는 리팩토링 과정을 강요받게 된다. 그렇게 하지 않으면 유지보수가 불가능한 스파게티 코드만이 남을 것이다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1PPq9FDJlCisseCwiOUEKi/e123c6b403655342980b4df2d4aa6482/2016-12-19-3.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 18.06640625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAHCAMAAABN2v+8AAAA4VBMVEUAAADphvBAAIAAAID/qv9JJYZEAJ1KI45JIo1JIo3rh/Drh/BJIo1KI49JIo1JIo1LJJJLJJFLJJFLJJFLJJLxi/Xwi/Txi/VLJJFLI5FLJJFLJJFLJJFLJJJMJJKCZbNNJZNNJZPl3fHyjPbu0fXzjPdPJpNiP6B3Wa1MJJJNJJPh2u3j3O/j3O/l3/Ds6PTj3O/j3O/g2e1KI49LI5BKI49KI49KI45KI45KI5BLI5BKI49LI5BKI49LI5FLI5FKI49KI5BKI5BLI5BLI5FKI49LI5FLI5FLJJJLJJFLI5G2r3P9AAAAS3RSTlMAAAAAAAAABwoFAwsCCAkEGUg/LRMeRxMGHkw4JwwaIBgUChVWEwYeGRkMLyw7SWA8MCAHIyQhIiQoJRkpGhcUJiMhGBsiKywvFQ8muW9jAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5gwbDxAjrdhLvgAAAIRJREFUGBmVwT0LQVEAgOH3vV0UOiYy3T9gVIwWf9tAGfEfmBTFcpWPOId8rJ5HqOmJX3XLO19N9YhZMNrxo61bPromm9yAeiAp1iRB90TFmiicayoCPXnxTpJ5I8mvPFVcgvTV1QUYGs15qw50Boy0bDjJWYwPrQtRMOLjnJUdoin/eQB9dxwEZ3nH4QAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-3&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1PPq9FDJlCisseCwiOUEKi/e123c6b403655342980b4df2d4aa6482/2016-12-19-3.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1PPq9FDJlCisseCwiOUEKi/e123c6b403655342980b4df2d4aa6482/2016-12-19-3.png?w=256 256w,
https://images.ctfassets.net/rpmifyuylbfw/1PPq9FDJlCisseCwiOUEKi/e123c6b403655342980b4df2d4aa6482/2016-12-19-3.png?w=512 512w,
https://images.ctfassets.net/rpmifyuylbfw/1PPq9FDJlCisseCwiOUEKi/e123c6b403655342980b4df2d4aa6482/2016-12-19-3.png?w=1024 1024w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;이것이 당신이 ‘그냥 jQuery만 쓰자’라고 생각하면 안되는 이유다. jQuery는 접착테이프 같은 존재라고 생각하면 된다. 집안에 있는 조그만 것들을 고치기에는 정말 편리하다. 하지만 자꾸 쓰게 되면 점점 엉망이 되어가는 집을 발견하게 될 것이다.&lt;/p&gt;
&lt;p&gt;반면에 모던 자바스크립트 프레임워크는 부품을 만들기 위한 3D 프린팅 기법과 유사하다. 더 많은 시간이 걸리지만 결과물은 훨씬 깔끔하고 튼튼하다. 그리고 대부분의 웹앱들은 빠르든 늦든 스펙트럼의 오른쪽으로 향할 것이다. 모던 자바스크립트를 공부하는 것은 많은 노력이 필요하지만 그래도 더 좋은 방향임은 분명하다.&lt;/p&gt;
&lt;h2 id=&quot;0주차-자바스크립트-기본&quot;&gt;&lt;a href=&quot;#0%EC%A3%BC%EC%B0%A8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EB%B3%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;0주차: 자바스크립트 기본&lt;/h2&gt;
&lt;p&gt;당신이 백엔드만 했던 개발자가 아니라면 아마 자바스크립트를 조금은 알고 있을 것이다. 만약 모른다 하더라도 당신이 PHP나 자바 개발자라면 자바스크립트의 C언어와 비슷한 문법은 제법 친숙하게 느껴질 것이다.&lt;/p&gt;
&lt;p&gt;자바스크립트에 당신에게 완전히 미스테리한 대상이라 하더라도 낙담할 필요는 없다. 실력을 빠르게 높여줄 수 있는 무료 자료들이 아주 많이 있으니까 말이다. 예를 들면 &lt;a href=&quot;https://www.codecademy.com/learn/javascript&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Codecademy’s JavaScript lessons&lt;/a&gt;은 공부를 시작하기 좋은 곳이다.&lt;/p&gt;
&lt;h2 id=&quot;1주차-react-시작하기&quot;&gt;&lt;a href=&quot;#1%EC%A3%BC%EC%B0%A8-react-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1주차: React 시작하기&lt;/h2&gt;
&lt;p&gt;이제 자바스크립트 기본 문법도 알았고 자바스크립트 앱이 왜 그렇게 복잡해 보일 수 있는지도 알았으니 이제 본격적으로 뭘 하면 될까? 어디에서부터 시작하면 될까?&lt;/p&gt;
&lt;p&gt;나는 그 답이 &lt;a href=&quot;https://facebook.github.io/react/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;리액트(React)&lt;/a&gt;라고 믿는다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tYfZfAyFGw84o6oqoUkEu/425d6088c8409babbd816cc59864aa4c/2016-12-19-4.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 66.69921875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAbCAMAAAA5zj1cAAABrVBMVEUiIiIcRU0dQEYcQ0odPkQhJycxMTE3NzcqKio9PT02NjYvLy86Ojo0NDQ4ODgyMjJAQEA7Ozs+Pj4zMzMjIyMrKysfNzsgMjUiKy0hLzEjJiYlJSUnJycoKCgmJiYwMDAtLS0sLCwkJCQ3T1U0RUouLi8uLy8tLS4xOjw+ZW9FfIs/aHNAbHhBbXo+ZnEwNTcwNjgxOz41Rks0REkzQEQ0REgwNzk/Pz9JSUlHR0dLS0tCQkJMTExGRkZISEhKSkpFRUVJOzmqcGiqb2erc2urcWmGWVNVQD6qcWqrcGmqbWWrcGiEV1FSPz3OhnzOi4LOioDOjYTOjoWgZV5hRkPNg3nPkIfNh37OiYDOiH/OjIOdY1wuLS0zLi4wLi1gYGD5+fnS0tLV1dXPz8/a2trt7e3X19fT09PFxcXh4eHe3t7g4ODR0dH09PTw8PDx8fHy8vL4+Pj19fXv7+/z8/PY2Njb29vW1tbo6Oju7u7i4uLj4+Pm5ubf39/c3Nzd3d3l5eXn5+f39/f29vbU1NTk5OTp6enZ2dnq6urs7Ozr6+vv3tvx5eLv39z17+4oNm/EAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5gwbDxAjrdhLvgAAAZtJREFUOMu1kUtzElEQhb8zc2cGyIRAIDw0qSIp9W/pL3ZhuXIRLbDUCtQMCcK8mHtdEBXLimFhetfdX3Wf0y3QXiAJISlvLgBOM9dQHs9RH2/H5E3VvsW76fuS09cYgAZh6VlXoAmHhXcgh/mr4kuSXPEoGO4kPw5G9/7/BQ7vj1QR6sKjfDv+3Xti15eSZH2pCmX9Mnr/EBjvfzM/0YMTdy1ZAcT8Aer1oRr1BK6FpO0uDSSojfMyoJW1tDla/QKP5TznPJyXcrxF8lUR6Jayv261ZNc/Vzczu1kVQZSXI6Li+12UJo0wb/SIXViVRUC32wUD/lnVkZSNJTaDxWDb6C/G7dTRLlrh9dUsWttBigdpK/AXNaYsC6hHlRvU598Km1J3iruX04sJ5OBBtYyz7vImDJMM3Vp/Wfqfe2YE4bS9ngZf7LNtAgZeqD76CJ8gw8TzjtPWzfuzq2tzvukE8+EmaDZSDCTJZDZ0XWZnyWpre/bU90YFzz+gtalKXNIdmBQDtcwrhziJcpbL/SO7WOq/IwPQm//9wh+J85/NNMxREQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-4&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tYfZfAyFGw84o6oqoUkEu/425d6088c8409babbd816cc59864aa4c/2016-12-19-4.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1tYfZfAyFGw84o6oqoUkEu/425d6088c8409babbd816cc59864aa4c/2016-12-19-4.png?w=256 256w,
https://images.ctfassets.net/rpmifyuylbfw/1tYfZfAyFGw84o6oqoUkEu/425d6088c8409babbd816cc59864aa4c/2016-12-19-4.png?w=512 512w,
https://images.ctfassets.net/rpmifyuylbfw/1tYfZfAyFGw84o6oqoUkEu/425d6088c8409babbd816cc59864aa4c/2016-12-19-4.png?w=1024 1024w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;리액트는 페이스북이 만든 오프소스 UI 라이브러리다. UI 라이브러리라는 것은 즉 ‘데이터에서 HTML로 전환’하는 단계(뷰 레이어)에 사용된다는 말이다.&lt;/p&gt;
&lt;p&gt;내 주관적인 생각으로 리액트가 &lt;em&gt;가장 좋은&lt;/em&gt; 라이브러리라서 추천하는 것은 아니니 오해하지 않았으면 좋겠다. 리액트를 추천하는 건 그게 &lt;em&gt;꽤 좋기&lt;/em&gt; 때문이다.&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;바꿔 말하면 리액트가 모든 면에서 최고는 아니지만 난 리액트가 &lt;em&gt;가장 안전한 선택&lt;/em&gt;이라고 믿는다. 나를 믿어 달라. “이제 막 시작하는 시점”에 기술적인 선택으로 위험을 부담하는 것은 적절하지 않다.&lt;/p&gt;
&lt;p&gt;리액트는 또 어떤 프레임워크나 라이브러리를 사용하든 유용할 수 있는 컴포넌트, 애플리케이션 상태(state), stateless 함수같은 개념들을 소개해줄 것이다.&lt;/p&gt;
&lt;p&gt;또 리액트는 다른 패키지와 라이브러리와 잘 작동하는 거대한 생태계를 갖추고 있다. 그리고 리액트의 대단한 인기와 많은 사용자는 당신이 스택오버플로우와 같은 사이트에서 많은 도움을 찾을 수 있다는 것을 의미한다.&lt;/p&gt;
&lt;p&gt;나는 개인적으로 Wes Bos의 &lt;a href=&quot;https://reactforbeginners.com/friend/STATEOFJS&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;React for Beginners&lt;/a&gt; 코스를 추천한다. 최신의 모범 사례를 철저히 반영하고 있는 저 코스를 통해 나는 리액트를 공부할 수 있었다.&lt;/p&gt;
&lt;h2 id=&quot;우선-자바스크립트를-제대로-공부해야-하는가&quot;&gt;&lt;a href=&quot;#%EC%9A%B0%EC%84%A0-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A5%BC-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;우선 자바스크립트를 제대로 공부해야 하는가?&lt;/h2&gt;
&lt;p&gt;당신이 꼼꼼한 학습자라면 아마도 다른 것들을 배우기 전에 자바스크립트의 기초를 우선 다지고 싶어할 것이다.&lt;/p&gt;
&lt;p&gt;하지만 어떤 이들에게는 그런 방식이 수영을 인체 해부학과 유체역학을 통해 배우는 것 같다는 느낌을 준다. 물론 그것들이 수영에 아주 큰 역할을 하는 건 사실이지만 그냥 풀장에 뛰어드는 편이 훨씬 더 재미있는 법이다.&lt;/p&gt;
&lt;p&gt;여기에 정답은 없으며 모두 당신의 학습 스타일에 달려 있다. 하지만 대부분의 리액트 튜토리얼들은 자바스크립트의 아주 적은 부분만을 사용하는 것이 사실이니 지금 당장 필요한 것만 공부하고 나머지는 뒤로 미뤄두는 것이 좋을 것이다.&lt;/p&gt;
&lt;p&gt;이는 거대한 자바스크립트 생태계에도 적용될 수 있다. 당장은 웹팩이나 바벨이 어떻게 동작하는지 이해하려고 너무 크게 신경쓸 필요는 없다. 또 리액트는 최근 설정없이 앱을 생성할 수 있는 간단한 &lt;a href=&quot;https://github.com/facebookincubator/create-react-app&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;커맨드라인 유틸리티&lt;/a&gt;를 선보였다.&lt;/p&gt;
&lt;h2 id=&quot;2주차-첫번째-리액트-프로젝트&quot;&gt;&lt;a href=&quot;#2%EC%A3%BC%EC%B0%A8-%EC%B2%AB%EB%B2%88%EC%A7%B8-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2주차: 첫번째 리액트 프로젝트&lt;/h2&gt;
&lt;p&gt;당신이 막 리액트 코스를 마쳤다고 가정하자. 내 경험으로 봐선 아마 아래와 같은 상태일 것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이미 공부한 것의 절반 이상을 잊어먹었다.&lt;/li&gt;
&lt;li&gt;기억하고 있는 절반을 실전에서 사용하고 싶어 기다릴 수가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;나는 라이브러리나 프레임워크를 배우는 가장 좋은 방법은 그냥 사용해 보는 것이라 믿는다. 그리고 개인 프로젝트는 새로운 기술을 시도해보기에 좋은 선택이다. 개인 프로젝트는 싱글 페이지 웹앱부터 복잡도가 높은 앱까지 어떤 것도 될 수 있지만 당신의 개인 사이트를 재구성하는 것도 적절한 방법이다.&lt;/p&gt;
&lt;p&gt;싱글 페이지 앱으로 스태틱 페이지를 만드는 것은 맞지 않는 일이지만 리액트는 비밀 무기: &lt;a href=&quot;https://github.com/gatsbyjs/gatsby&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;개츠비(Gatsby)&lt;/a&gt;를 가지고 있다. 개츠비는 리액트 기반의 스태틱 사이트 제네레이터(static site generator)로서 아무런 어려움 없이 리액트의 모든 장점을 가져올 수 있는 치트키라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/33gAyAVEYgCUYC2WicC6MY/15945e9ef77f7a1e43004c51f44c8fd2/2016-12-19-5.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 831px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 62.69554753309266%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-5&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/33gAyAVEYgCUYC2WicC6MY/15945e9ef77f7a1e43004c51f44c8fd2/2016-12-19-5.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/33gAyAVEYgCUYC2WicC6MY/15945e9ef77f7a1e43004c51f44c8fd2/2016-12-19-5.gif?w=208 208w,
https://images.ctfassets.net/rpmifyuylbfw/33gAyAVEYgCUYC2WicC6MY/15945e9ef77f7a1e43004c51f44c8fd2/2016-12-19-5.gif?w=416 416w,
https://images.ctfassets.net/rpmifyuylbfw/33gAyAVEYgCUYC2WicC6MY/15945e9ef77f7a1e43004c51f44c8fd2/2016-12-19-5.gif?w=831 831w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;개츠비가 리액트를 시작하기 좋은 도구인 이유는 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;미리 설정되어 있는 웹팩 개발 환경을 제공한다. 즉 복잡한 설정을 하느라 머리아플 필요가 없다.&lt;/li&gt;
&lt;li&gt;디렉토리 기반의 자동 라우팅이 가능하다.&lt;/li&gt;
&lt;li&gt;모든 HTML 컨텐츠는 서버 사이드에서 생성되므로 양쪽의 이점을 모두 취할 수 있다.&lt;/li&gt;
&lt;li&gt;생성된 스태틱 컨텐츠는 서버가 필요없으므로 Github Page에 호스팅하기 매우 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;나는 &lt;a href=&quot;http://stateofjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;State Of JavaScript&lt;/a&gt; 사이트를 만드는데 개츠비를 사용했다. 라우팅을 신경쓸 필요도 없고 환경설정도 없으며 서버 사이드 렌더링은 나에게 많은 시간을 절약해 주었다.&lt;/p&gt;
&lt;h2 id=&quot;3주차-es6-마스터&quot;&gt;&lt;a href=&quot;#3%EC%A3%BC%EC%B0%A8-es6-%EB%A7%88%EC%8A%A4%ED%84%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3주차: ES6 마스터&lt;/h2&gt;
&lt;p&gt;리액트를 공부하기 위한 내 개인적은 여정에서 코드 샘플을 복사해서 붙여넣을 수는 있는 수준에는 금방 도달했다. 하지만 내가 여전히 이해하지 못하고 있었던 것이 있었다. 특히 나는 &lt;a href=&quot;http://es6-features.org/#Constants&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ES6&lt;/a&gt;에서 도입된 새로운 문법들과 친숙하지 않았다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;화살표 함수(arrow functions)&lt;/li&gt;
&lt;li&gt;객체 비구조화 할당(Object destructuring)&lt;/li&gt;
&lt;li&gt;클래스&lt;/li&gt;
&lt;li&gt;전개 연선자(spread operator)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ES6를 제대로 공부하기 위해선 아마도 며칠이 걸릴 것이다. 앞서 소개한 리액트 입문자 코스를 즐겁게 공부했다면 당신은 아마도 Wes의 멋진 &lt;a href=&quot;https://es6.io/friend/stateofjs&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;ES6 for everybody&lt;/a&gt; 비디오도 보고 싶어질 것이다. 만약 무료를 선호한다면 &lt;a href=&quot;https://ponyfoo.com/books/practical-es6/chapters&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Nicolas Bevacqua’s book, Practical ES6&lt;/a&gt;를 살펴보기 바란다.&lt;/p&gt;
&lt;p&gt;ES6를 마스터하기에 좋은 실습은 ES6 이전의 문법들을 더 짧고 명료한 ES6 문법으로 가능한 바꿔보는 것이다.&lt;/p&gt;
&lt;h2 id=&quot;4주차-상태-관리state-management-시작하기&quot;&gt;&lt;a href=&quot;#4%EC%A3%BC%EC%B0%A8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%ACstate-management-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4주차: 상태 관리(state management) 시작하기&lt;/h2&gt;
&lt;p&gt;이제 당신은 정적인 컨텐츠를 만들어내는 간단한 리액트 앱을 만들 수 있게 되었다. 하지만 실제 웹앱은 정적(static)이지 않다. 데이터베이스같은 곳에서 데이터를 가져와야 한다.&lt;/p&gt;
&lt;p&gt;지금은 단순히 데이터를 컴포넌트에 전달하기만 할 수 있다. 하지만 그런 방식은 금세 복잡하고 지저분해진다. 예를 들어 두개의 독립적인 컴포넌트가 동일한 데이터를 보여줘야 한다면? 또 컴포넌트간에 서로 데이터를 주고받아야 할 필요가 생긴다면?&lt;/p&gt;
&lt;p&gt;여기가 바로 &lt;strong&gt;상태 관리&lt;/strong&gt;가 필요한 지점이다. 상태를 각각의 컴포넌트 대신 하나의 전역 저장소에 저장한 후 리액트 컴포넌트에 전달(dispatch)하는 방식을 사용하자.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/4EkdFNgowgYEK4uUCuAyao/c81fe2589f762bfaebe6af021c6ab8a1/2016-12-19-6.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 16.799999999999997%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAHCAMAAABN2v+8AAABdFBMVEUAAADf2Ove1+rf2Ove1+rf2Ov40Pr2zvj3z/j2zvj2z/j2zvj2zvje1+rf2Ovu6+vu6+vu6+ve1+rj3evz8PPk3+zz8PPx5O/2zvju6+vu6+v2zvj2zvju6+vv7Oz2zvj2zvjg2ev39fXy8PXv7Ozf2Ov19PT39vb18/P12Pby5vDz8PX39vbz2PPv7Ozy7+/10Pfu6+vu6+vu6+va0ufk3ez08vTk3+zy8PPx4/Du6+vu6+vu6+vu6+vf2Ove1+rf2Ove1+rf2Ov40Pr2zvj3z/j2zvj2zvj2zvj2zvjf2Ove1+rg2ez2zvj2zvj2zvjg2ezf2Ov3z/n40Pr40PrSyOLHu9vVzOTBs9ezo8/d1ur2zvjTq+LXsOX0zPfBmdfasubdtejQqeDfuOrSyeK9r9XWzeXq5vHDttm3qNHEnNnp5PDCm9fas+fw7fXu6vTlve3nv+7kvO3Jvt3PxeDGutvKv97ft+nguOr1zfjpwfDXr+RQVTwYAAAAVXRSTlMAc7VWnisOtQc1lV9ro3oTKyjgU6zosEsKJhVL0gULh5eFifJBPoXFih0n6Zl1Q32QDiAe4FKp5K1FHA8ECF+VR4IkDJUFLHpOWCgtFjItJhQmEiUpYErFNAAAAAlwSFlzAAAXEQAAFxEByibzPwAAAAd0SU1FB+YMGw8QJDO83h0AAAC5SURBVBgZBcHfKoMBAAfQ87ONTN8f26U7DyKMtZTCcyo3SsumiXLhFbyAZGspZHw5Jwn5Q7uhtWI9yDebDa1PbP3SSTFKbpIsesNknCRHSTPNVzeDZJIPZQ6TaVuV9HM86c/rLHtJorTsZf8+itg+zyxVYk21LN+r5/pltyhWdV3XtaI0r56q+U4K5eNDuXjrvspeyAwHDa07BkFuGTa0xhj90slJAJBrnAYAAAk4+9m4AgDgIrkE8A+7TS8jl8dopAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-6&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/4EkdFNgowgYEK4uUCuAyao/c81fe2589f762bfaebe6af021c6ab8a1/2016-12-19-6.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/4EkdFNgowgYEK4uUCuAyao/c81fe2589f762bfaebe6af021c6ab8a1/2016-12-19-6.png?w=500 500w,
https://images.ctfassets.net/rpmifyuylbfw/4EkdFNgowgYEK4uUCuAyao/c81fe2589f762bfaebe6af021c6ab8a1/2016-12-19-6.png?w=1000 1000w,
https://images.ctfassets.net/rpmifyuylbfw/4EkdFNgowgYEK4uUCuAyao/c81fe2589f762bfaebe6af021c6ab8a1/2016-12-19-6.png?w=2000 2000w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;리액트 생태계에서 가장 인기있는 상태 관리 라이브러리는 리덕스(Redux)다. 리덕스는 데이터를 집중화해줄 뿐만 아니라 데이터를 조작할 때 엄격한 프로토콜을 강제한다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/xqrOW5qOB2eQWQiqYyueo/58c338b871b9c0e8b5f7563b9da7d305/2016-12-19-7.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 78.0625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAfCAMAAACiX39KAAABoVBMVEX////y7fn8+/7Txerg1vDExMTi4uK6urrm5ubl5eXo4PTg1fDXyev18vq4uLiJiYmSkpKbm5uioqKenp739Pvs5vbr5fXr6+vh4eHo6Ojs7Ozg4ODd3d3n5+fe3t7p6enq6urk5OTf39/+/v7j4+Pg6/Xj7fbr8vnk7fbl7vfo7/b5+fn7+/v8/Pz9/f36+vr09PT19fXu7u7w8PDv7+/t7e3k5ujo8Pjq8vnv9Prh6/bm7/fn7/fi7Pbk7vfd6PTt8/n5+/39/v7z8/P4+Pj29vb09/Pz9fL19fT19fbx8/Xy8/X09vP09/T8/vv8/v/8/f39/f68vLx/2F2C3WCRqYh6ho1NpNdps92MjIyHi4V821l921qQ4HKIiIiM2/CU5vyP5fyV5vyas7qGt8SQ5fybvcaNjY2QkJChoaH39/fy8vLx8fH3+vzz9/v2+fz0+Pz7/P7w9frs8vny9/vx9vvx9vrPz8/Nzc3V1dXX19fHx8f4+/34+v33+v3y9vvs8/n1+Pzf6vXq8fjp8fjk7ffm7vfh6/Xe6fXp8Pj7/f5mJAKtAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAeJJREFUGBmtwfdX00AcAPCvg5i46k4ubcxdElJDcndNSo01FCixbgVxb6xbOrAVnNVq3X+18l71B5HQ9/TzgWXr1sNgNmyEoSFhkyhBss1btm7bntqxc9duSLZn7z74v2QFqUo6o+3XhTTCikIMFQZjwt9hLGqKpVuKIaYJwYqOhu3sAWfEhT94LmWMU+4yl3NGOaeU5ahHKfVcjzHmQZ+vaUTGhkhkCRFdIyTQ8pIgY40gXcKSKkPfqIAMTbJwpoBEi8gkLRFFUAzNEESMDobhIegrHo7GSuNRGEaObZcmJmE1HpjgMmZS8MpTQBlQMLkJK8VYErGAMhaWx0cCUcQEC7p1BFbIBQWkqnqAEFJjSMArR48dP3HyVN6PT585S+n0spmZ6Z/OUcoY/DKbOn/h4qXLV1JXr12/cTN16/bcnWr17r25+9Xqg4ePHs8+gT4TBsQrhXk5LtTyagyJeL3RXGiUG42nFddzXQaroa3iZLv9bDG7lPP9eR/+nfL8xctXr0VYE1dHazU5B2tib+qdernefNt81+xAAt5pL7a6rWK3uJR9Dwl4nC/4QSEXuJCMAwMTBsAXPkThWGT3nFLvozPmhJ8+lyLH7oZhGNnDU/Ab733pfnWikp399h2ScBjAD5TfXI3P+OwmAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-7&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/xqrOW5qOB2eQWQiqYyueo/58c338b871b9c0e8b5f7563b9da7d305/2016-12-19-7.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/xqrOW5qOB2eQWQiqYyueo/58c338b871b9c0e8b5f7563b9da7d305/2016-12-19-7.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/xqrOW5qOB2eQWQiqYyueo/58c338b871b9c0e8b5f7563b9da7d305/2016-12-19-7.png?w=800 800w,
https://images.ctfassets.net/rpmifyuylbfw/xqrOW5qOB2eQWQiqYyueo/58c338b871b9c0e8b5f7563b9da7d305/2016-12-19-7.png?w=1600 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;리덕스는 일종의 ‘은행’으로 생각할 수 있다. 은행을 이용할 때 당신은 지역 지점으로 가서 계좌 잔액을 임의로 변경할 수는 없다. 대신 입출금 양식을 작성해서 인증받은 은행원에게 전달해서 관련 작업을 할 수 있도록 요청해야 한다.&lt;/p&gt;
&lt;p&gt;비슷하게 리덕스는 전역 상태를 직접 변경할 수 없게 해 두었다. 대신 리듀서(reducer)에 액션(action)을 전달해야 한다. 리듀서는 특수한 함수로서 액션을 전달받아서 작업을 수행한 후 업데이트된 새로운 상태를 반환한다.&lt;/p&gt;
&lt;p&gt;이러한 추가적인 작업은 앱 전반적으로 매우 유지보수성이 좋고 표준화된 데이터 흐름을 제공한다. 그리고 &lt;a href=&quot;https://github.com/gaearon/redux-devtools&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Redux devtool&lt;/a&gt;같은 툴은 리덕스와 관련된 일련의 작업들을 시각화해준다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/6eQy3v53pKyes0g8G0gqGS/b9f9daab977bef67792adf46ee2b4a11/2016-12-19-8.gif&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 776px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 88.65979381443299%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/gif;base64,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&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-8&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/6eQy3v53pKyes0g8G0gqGS/b9f9daab977bef67792adf46ee2b4a11/2016-12-19-8.gif&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/6eQy3v53pKyes0g8G0gqGS/b9f9daab977bef67792adf46ee2b4a11/2016-12-19-8.gif?w=194 194w,
https://images.ctfassets.net/rpmifyuylbfw/6eQy3v53pKyes0g8G0gqGS/b9f9daab977bef67792adf46ee2b4a11/2016-12-19-8.gif?w=388 388w,
https://images.ctfassets.net/rpmifyuylbfw/6eQy3v53pKyes0g8G0gqGS/b9f9daab977bef67792adf46ee2b4a11/2016-12-19-8.gif?w=776 776w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;리덕스 관련 강의로 다시 한번 우리의 친구 Wes의 &lt;a href=&quot;https://learnredux.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;리덕스 강의&lt;/a&gt;를 들어도 되고(이건 무료다!) egghead.io에 올라와 있는 리덕스를 개발한 Dan Abramov의 &lt;a href=&quot;https://egghead.io/courses/getting-started-with-redux&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;비디오&lt;/a&gt;를 참고해도 좋다. 이 비디오도 역시 무료다.&lt;/p&gt;
&lt;h2 id=&quot;5주차보너스-graphql로-api를-구성하기&quot;&gt;&lt;a href=&quot;#5%EC%A3%BC%EC%B0%A8%EB%B3%B4%EB%84%88%EC%8A%A4-graphql%EB%A1%9C-api%EB%A5%BC-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5주차(보너스): GraphQL로 API를 구성하기&lt;/h2&gt;
&lt;p&gt;지금껏 많은 내용들을 이야기했지만 모두 클라이언트에 관한 것들만이었다. 클라이언트는 등식의 단지 절반일 뿐이다. 노드(Node) 생태계를 깊이 파고들지 않을 것이라 하더라도 이 핵심 요소는 언급할 필요가 있다. 데이터가 서버에서 클라언트로 어떻게 전달되는지에 관한 내용이다.&lt;/p&gt;
&lt;p&gt;기술은 너무나 빠르게 변하고 있기 때문에 &lt;a href=&quot;http://graphql.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;GraphQL&lt;/a&gt;(Facebook의 또 다른 오픈소스 프로젝트)이 전통적인 REST API의 진지한 대안으로 여겨지고 있다는 사실은 또 그렇게 놀랍지 않다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/1NA4WELsmsieiaqYgoMK0o/afee036cb3a95242c07bb390dd149c37/2016-12-19-9.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 58.8125%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAYCAMAAAC/Wk/yAAABnlBMVEUWHiUYICchKC8XHiYaISgdJCwgJy4jKTAYHyciKC8ZICgjKjIzOT83PUM1O0EwNjwoLzYeJSwXHiUdIyscIysiHCxTFUhHF0EdHSkeJS00OkEvNT0yOD8xNz4qMTg4PkUfJS0fJi00O0IvNTwuNTwsMjotMzsyOEAgJi42PUMsMjk3PUQ2PEMrMTkpLzdDFz87GTpOFkUxGjQaISkZICdCGD5bFExfE04wGjQaHyYnIisrNT9GRjwnKSUzOUAeKDMYIy4nIS8yIzUcICkYIiwXJTMYIi0kHC0yGjUsGzIhHSsYJTQYKTsdKTMpLSctLSIfJCUtNDsXJTIYICkmIS4sIjImIS8wIjQpITAsIjEYHiYYHic3GTgrGzEYHygXHygXICoaICYZICYbIikeJCwbIioqMDcrMTgvNj0cIiowNj0cIypobXP19vjq6+309ffz9Pby8/Xz9PXv8PLw8fPx8vTh4uTn5+nd3t/U1dfGx8i/wMHb293DxMa4ubvKy8y3uLm4ubrW19nHyMnX2NnExcfQ0dLMzc/e3uD19ffxqr2YAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAARBJREFUKM+1k7EvBEEUxr/f7M7iBLndUIlOhFxBoVRc+CMkCpT+KYVEoaWSiEJCopSQK5R0uCscEbN2FLsXi5At7r5mJi+/+d6bN/MQ4A2k2RDwqh8aBYCOQnkvZZKUTrT1S34YSV2JWNVEUBE0FTmFUgw8TOVFczcDvI0At/VJwJtOfFOkbgBX1i1y7SRZ1/P42hWOEaAlYPlCklmoA3Du1IQzV7rMGnBsVwE4kv2Ymz3MHbUylteTWg5CrbeTp0SNBLxBMq7V6rmcDro9ZW3tbaL9DUna/dfRbAfByXz4eGmjwT/hTlWQfqfuP1hqePEjcQTvUUoWfZu1Elgr5rFWHHge/wN8yZfp+16gW079CcLYPQ0HUengAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-9&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/1NA4WELsmsieiaqYgoMK0o/afee036cb3a95242c07bb390dd149c37/2016-12-19-9.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/1NA4WELsmsieiaqYgoMK0o/afee036cb3a95242c07bb390dd149c37/2016-12-19-9.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/1NA4WELsmsieiaqYgoMK0o/afee036cb3a95242c07bb390dd149c37/2016-12-19-9.png?w=800 800w,
https://images.ctfassets.net/rpmifyuylbfw/1NA4WELsmsieiaqYgoMK0o/afee036cb3a95242c07bb390dd149c37/2016-12-19-9.png?w=1600 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;REST API가 사전 정의된 데이터 집합에 접근하기 위한 여러개의 REST 경로(ex. /api/posts, /api/comments, …)를 제공하는 것에 반해서 GraphQL은 단 1개의 경로만 제공하고 클라이언트가 필요한 데이터에 접근하기 위한 쿼리를 던지게 한다.&lt;/p&gt;
&lt;p&gt;예를 들어 정육점, 빵집, 식료품점 등 여러 가게들 돌아다니는 것과 쇼핑 목록을 작성해서 세 가게에 모두 전달하는 것을 비교해서 상상해보라. 이 새로운 전략은 복수의 소스(또는 테이블)에서 데이터를 가져오려고 할 때 특히 중요해진다. 앞서 예를 든 쇼핑 목록처럼 이제는 클라이언트에서 필요한 데이터를 조합하기 위해서는 단 한번의 요청만으로도 가능해지기 때문이다.&lt;/p&gt;
&lt;p&gt;GraphQL은 1년간 뜨거운 관심을 받아 왔고 많은 프로젝트(2주차에서 언급한 개츠비 등)들이 적용할 계획을 가지고 있다. GraphQL 자체는 단지 프로토콜일 뿐이고 구현체로는 &lt;a href=&quot;http://apollostack.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;아폴로(Apollo)&lt;/a&gt;(리덕스와도 잘 작동한다) 라이브러리가 있다. 아직은 GraphQL와 아폴로에 대한 교육 자료가 많지 않지만 &lt;a href=&quot;http://dev.apollodata.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;아폴로 공식 문서&lt;/a&gt;가 입문에 도움이 될 것이다.&lt;/p&gt;
&lt;h2 id=&quot;리액트를-넘어서&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%84%98%EC%96%B4%EC%84%9C&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리액트를 넘어서&lt;/h2&gt;
&lt;p&gt;나는 리액트 생태계가 안전한 선택이기에 추천을 했다. 하지만 리액트만이 좋은 프론트 엔드 스택은 아니다. 좀 더 알아보고 싶다면, 다음의 두 라이브러리를 추천한다.&lt;/p&gt;
&lt;h3 id=&quot;vue&quot;&gt;&lt;a href=&quot;#vue&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Vue&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://vuejs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Vue&lt;/a&gt;는 상대적으로 새로운 라이브러리지만 기록적인 속도로 성장하고 있고 이미 중국의 바이두와 알리바바(중국의 구글과 아마존)같은 메이저급 회사들이 도입한 상태다. 또 PHP 프레임워크 &lt;a href=&quot;https://laravel.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Laravel&lt;/a&gt;의 공식 프론트엔드 레이어이기도 하다.&lt;/p&gt;
&lt;p&gt;
&lt;a
  class=&quot;gatsby-resp-image-link&quot;
  href=&quot;https://images.ctfassets.net/rpmifyuylbfw/2sQN0KJ1XyiAg88iy6skik/e45b6f7335f7b9f4a8c65a308b9e4295/2016-12-19-10.png&quot;
  style=&quot;display: block&quot;
  target=&quot;_blank&quot;
  rel=&quot;noopener&quot;
&gt;

  &lt;span
    class=&quot;gatsby-resp-image-wrapper&quot;
    style=&quot;position: relative; display: block; ; max-width: 960px; margin-left: auto; margin-right: auto;&quot;
  &gt;
    &lt;span
      class=&quot;gatsby-resp-image-background-image&quot;
      style=&quot;padding-bottom: 56.56250000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAXCAMAAABODP0nAAABgFBMVEX////09fbw8fL6+/v9/f3+/v709ff29/jy9PXu7/H3+Pnw8vPv8PL7+/zt7vD6+vv+///X8OXG29bY3OD7/PzFy9HI59rs+PLz9PX29vf4+Pn4+fn5+vq55dE/qH5AVWeWoaw4Z2hQvo3z+/f39/jh5Obs7u/p6+3d4OLg4uXl5+nk5un+//5px506e281Sl4/pHyx4szu8PHt7/Dw8fPr7e7x8vPT7+JBsYE7f3BixJj8/v3x8vTv8PHo6uzs7vD29/fv8fLr7e+C0K5BuIPL7N3g9OvH69vM7d79/v75/fv6/fv9/v3o9vCA0Kx90KtfxpdexZdhxplXw5Lk9e32/Pnt+PP7/vz4/Pr6/fz7/v3v+fXm9u7n9u/s+PPp9/H3/Pr7+/vz9fX19vf19vb09vb4+Pj5+fr09fXy8/Tp6+vq7Ozr7e3u8PD3+Pjv8fHq7O3u7+/8/Pzq7e3p6+z29vb19fXs7Ozt7e3r6+v09PTw8PDu7u7q6ury8vLx8fGjT43tAAAACXBIWXMAABcRAAAXEQHKJvM/AAAAB3RJTUUH5gwbDxAjrdhLvgAAAStJREFUGBm1wfk7wnAAB+DPrBlJ5c59pHLkKrkKObMO99GQxHKscn+bIv51qx+39jw9PN4XoKpoqOl0jKyapWpqWX2doR4AbTRBjSmBzAwGYPALDY1NzS2tbbBQbHtHZ5eehobunt6+/gErBm12x9DwyKgNGpxj4xOTUwDrctunHW6jy2MwzKCc2bn5BS/gcy+6qCXTsn9l1WdBWWvrGwA2A4HAFhcMhcLQEtmGbGd3b//g8OjYGoSmE8jC0WiUxynP46+ccHIoMps5lHAcyjk7j8RCkHkjMR5FF/H4JVQSV9eCPwngxp/0C7cAaOFOEKByL6bSnswDdI9i6in9/AJkXt9EMYH/k6gQiFJWepdyeaIEUiEQtY9PogailM0VCrk8UQJR+ZKkb6LyA7a9lCesLxMRAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
    &gt;
      &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        style=&quot;width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;&quot;
        alt=&quot;2016-12-19-10&quot;
        title=&quot;&quot;
        src=&quot;https://images.ctfassets.net/rpmifyuylbfw/2sQN0KJ1XyiAg88iy6skik/e45b6f7335f7b9f4a8c65a308b9e4295/2016-12-19-10.png&quot;
        srcset=&quot;https://images.ctfassets.net/rpmifyuylbfw/2sQN0KJ1XyiAg88iy6skik/e45b6f7335f7b9f4a8c65a308b9e4295/2016-12-19-10.png?w=400 400w,
https://images.ctfassets.net/rpmifyuylbfw/2sQN0KJ1XyiAg88iy6skik/e45b6f7335f7b9f4a8c65a308b9e4295/2016-12-19-10.png?w=800 800w,
https://images.ctfassets.net/rpmifyuylbfw/2sQN0KJ1XyiAg88iy6skik/e45b6f7335f7b9f4a8c65a308b9e4295/2016-12-19-10.png?w=1600 1600w&quot;
        sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
      /&gt;
    &lt;/span&gt;
  &lt;/span&gt;
  
&lt;/a&gt;
  &lt;/p&gt;
&lt;p&gt;리액트와 비교해서 주목할점은 특징은 아래와 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;라우팅과 상태 관리 라이브러리를 공식적으로 지원한다.&lt;/li&gt;
&lt;li&gt;성능에 초점을 맞추고 있다.&lt;/li&gt;
&lt;li&gt;HTML 기반의 템플릿이기 때문에 진입 장벽이 낮다.&lt;/li&gt;
&lt;li&gt;보일러플레이트 코드가 적다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;더 거대한 생태계과 React Native의 존재 때문에 리액트가 지금으로서는 우위에 있다고 말할 수 있다. 하지만 만약 Vue.js가 리액트를 곧 따라잡아도 놀랄 일은 아닐 것이다.&lt;/p&gt;
&lt;h3 id=&quot;elm&quot;&gt;&lt;a href=&quot;#elm&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Elm&lt;/h3&gt;
&lt;p&gt;Vue가 고려해볼만한 선택지라면 Elm은 나온지 얼마 안된 신기술이다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://elm-lang.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Elm&lt;/a&gt;은 단지 프레임워크가 아니라 자바스크립트로 컴파일되는 완전히 새로운 언어다. 이는 향상된 성능, 강제화된 시맨틱 버저닝, 런타임 오류가 없다는 점 등 다양한 장점을 제공한다.&lt;/p&gt;
&lt;p&gt;나는 개인적으로 아직 Elm을 직접 만져보지는 않았지만 사용해본 사람들은 높은 만족도와 함께 추천을 하고 있는 중이다.&lt;/p&gt;
&lt;h2 id=&quot;다음-단계&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EC%9D%8C-%EB%8B%A8%EA%B3%84&quot; aria-hidden class=&quot;anchor&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다음 단계&lt;/h2&gt;
&lt;p&gt;이제 당신은 리액트 프론트엔드 스택에 대해서 전반적으로 파악할 수 있었고 아마 많은 성과가 있었을 것이다.&lt;/p&gt;
&lt;p&gt;하지만 이정도로는 결코 충분하지 않다! 이건 자바스크립트 생태계를 향한 여행의 단지 시작 지점일 뿐이다. 당신은 아래의 주제들 중에 몇몇은 결국 마주하게 될 것이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버 사이드 자바스크립트(Node, Express, …)&lt;/li&gt;
&lt;li&gt;자바스크립트 테스팅(Jest, Enzyme, …)&lt;/li&gt;
&lt;li&gt;빌드 도구(Webpack, …)&lt;/li&gt;
&lt;li&gt;타입 시스템(TypeScript, &lt;a href=&quot;https://flowtype.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Flow&lt;/a&gt;, …)&lt;/li&gt;
&lt;li&gt;자바스크립트 앱에서의 CSS 관리(CSS Module, &lt;a href=&quot;https://github.com/styled-components/styled-components&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Styled Compoents&lt;/a&gt;, …)&lt;/li&gt;
&lt;li&gt;모바일 앱을 위한 자바스크립트(React Native, …)&lt;/li&gt;
&lt;li&gt;데스크탑 앱을 위한 자바스크립트(&lt;a href=&quot;http://electron.atom.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow&quot;&gt;Electron&lt;/a&gt;, …)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;여기에 있는 모든 것을들 다룰 수 없지만 너무 절망하지는 마시길! 무엇이든 처음 시작이 가장 어려운 법이다. 게다가 당신은 이 글을 읽음으로서 그 첫걸음을 이미 뗀 상황이다.&lt;/p&gt;
&lt;p&gt;그리고 자바스크립트 생태계의 다양한 조각들이 어떻게 맞물려 있는지도 이해하게 되었다. 이제는 무엇을 배워야 할지 정리한 후 새로운 기술을 향해 문을 두드리고 들어가기만 하면 된다.&lt;/p&gt;</content:encoded></item></channel></rss>