<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>그럼에도 불구하고</title>
    <link>https://despiter.tistory.com/</link>
    <description>keep going</description>
    <language>ko</language>
    <pubDate>Sun, 5 Apr 2026 19:20:50 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>highcastlee</managingEditor>
    <image>
      <title>그럼에도 불구하고</title>
      <url>https://tistory1.daumcdn.net/tistory/4424635/attach/a1f317124ca64481887f1b4fc36f37b1</url>
      <link>https://despiter.tistory.com</link>
    </image>
    <item>
      <title>jsCodeShift를 활용한 코드 자동 변환</title>
      <link>https://despiter.tistory.com/46</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;문제 상황&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;사내 디자인 시스템에서 제공하는 Modal의 property 명칭을 onCancel에서 onClose로 고친 동료가 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;하지만, 이미 해당 모달과 프로퍼티를 사용하고 있는 코드가 수백 개인 상황.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;단순 onCancel을 검색하니 Modal이 아닌 곳에서의 onCancel까지 검색되어 하나씩 확인해보는 수 밖에 없었는데,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;그 많은 코드를 직접 수정하는 모습을 보며 드는 생각 하나! &quot;아 저거 딱 원하는 코드만 수정되게 자동화할 수 있으면 좋겠다&quot;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;문제 발견&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccZMx9/btsEb8xDl4I/9d7Ig9mKcAXShFS0Y4TWF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccZMx9/btsEb8xDl4I/9d7Ig9mKcAXShFS0Y4TWF1/img.png&quot; style=&quot;width: 48.1011%; margin-right: 10px;&quot; data-widthpercent=&quot;48.67&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;544&quot; data-origin-width=&quot;932&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccZMx9/btsEb8xDl4I/9d7Ig9mKcAXShFS0Y4TWF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccZMx9%2FbtsEb8xDl4I%2F9d7Ig9mKcAXShFS0Y4TWF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;932&quot; height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEEjgd/btsD4tcvlVv/NdTEVKzve0TwsXiETHAmUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEEjgd/btsD4tcvlVv/NdTEVKzve0TwsXiETHAmUk/img.png&quot; data-widthpercent=&quot;51.33&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;508&quot; data-origin-width=&quot;918&quot; style=&quot;width: 50.7361%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEEjgd/btsD4tcvlVv/NdTEVKzve0TwsXiETHAmUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEEjgd%2FbtsD4tcvlVv%2FNdTEVKzve0TwsXiETHAmUk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;918&quot; height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Modal의 onCancel을 onClose로 수정해보자.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;vscode 내에서 제공하는 바꾸기&lt;span style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;(cmd + shift + H)&lt;/span&gt; 기능은 텍스트 검색만 가능합니다. 위에서 언급했던 예시처럼, onCancel로 검색하면 의도했던 Modal의 onCancel이 아닌 프로젝트의 모든 onCancel이 검색에 걸리게 되는 것이죠.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;수정.png&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S6Wmf/btsD3r7hc7R/c8YilIjIEPFkXHX5hcZMP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S6Wmf/btsD3r7hc7R/c8YilIjIEPFkXHX5hcZMP0/img.png&quot; data-alt=&quot;onCancel이 어떤 컴포넌트의 prop인지 하나씩 확인해야하는 번거로움&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S6Wmf/btsD3r7hc7R/c8YilIjIEPFkXHX5hcZMP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS6Wmf%2FbtsD3r7hc7R%2Fc8YilIjIEPFkXHX5hcZMP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;365&quot; height=&quot;409&quot; data-filename=&quot;수정.png&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;onCancel이 어떤 컴포넌트의 prop인지 하나씩 확인해야하는 번거로움&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;// 1번
&amp;lt;ConfirmationModal isOpen onCancel={onClose} /&amp;gt; 

// 2번
&amp;lt;OtherComponent onCancel={onClose}/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;1번 코드가 수십개 있고, 2번 코드도 수십개 있다고 칩시다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;code&gt;&amp;lt;ConfirmationModal /&amp;gt;&lt;/code&gt; 에서 쓰이는 &lt;code&gt;onCancel&lt;/code&gt;을 &lt;code&gt;onClose&lt;/code&gt;로 바꾸려면,&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;방법 1) onCancel 검색 -&amp;gt; 수많은 onCancel 중 &amp;lt;ConfirmationModal /&amp;gt;의 인터페이스인지 확인하고 &amp;rarr; onClose로 바꾸기&lt;br /&gt;방법 2)&amp;nbsp;&amp;lt;ConfirmationModal&amp;nbsp;검색 &amp;rarr; onCancel 마우스로 선택 &amp;rarr; onClose로 타이핑 or 복사하기로 바꾸기&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;즉, &lt;/span&gt;한 줄의 텍스트가 아닌 여러 조건을 가진 코드 변환이 필요할 경우, 일괄 바꾸기 작업에 시간과 노동이 선형적으로 증가하게 됩니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;해결 방안&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;code&gt;JSCodeShift&lt;/code&gt;라는 도구를 활용해서 &lt;b&gt;정확히 원하는 조건&lt;/b&gt;만을 충족하는 코드를 찾아 바꾸는 작업을 자동화하기&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;1. JSCodeShift가 무엇인가요?&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;유사한 상황에서 쓰이는 단어로 codemod가 있는데요. 두 용어에 대한 개념은 아래와 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;b&gt;codemod :&lt;/b&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&quot;code modification&quot;의 줄임말로, 소프트웨어 코드를 자동으로 수정하거나 변환하는 도구나 프로세스를 가리킵니다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/facebook/jscodeshift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;jscodeshift&lt;/a&gt; :&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt; &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;facebook(현 Meta)에서 만든 javascript/typescript 코드 변환 도구입니다. &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;주로 codemod 패턴을 구현하기 위해 jscodeshift를 사용합니다. &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;Q. jscodeshift는 어떻게 텍스트보다 더 세밀한 조건의 코드를 찾아낼 수 있을까요?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 단순 문자열이 아닌, 특정 코드의 AST 구조를 기준으로 탐색하기 때문입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;+ AST(Abstract Syntax Tree, 추상 구문 트리)란?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;AST는 컴파일러가 사용할 프로그램의 소스 코드 구조를 나타내는 데 사용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1255&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vZb7q/btsD5zct1Og/37DQBekfyv5DvizU2JitJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vZb7q/btsD5zct1Og/37DQBekfyv5DvizU2JitJ1/img.png&quot; data-alt=&quot;기본적으로 javascript &amp;amp;amp;rarr; AST &amp;amp;amp;rarr; bytecode 순으로 변환되어 실행됩니다.&amp;amp;amp;nbsp; (JIT compiler 내용 생략)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vZb7q/btsD5zct1Og/37DQBekfyv5DvizU2JitJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvZb7q%2FbtsD5zct1Og%2F37DQBekfyv5DvizU2JitJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;332&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1255&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;기본적으로 javascript &amp;amp;rarr; AST &amp;amp;rarr; bytecode 순으로 변환되어 실행됩니다.&amp;amp;nbsp; (JIT compiler 내용 생략)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;249&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTYrCi/btsEbzbbKYq/qeEiB3o1G0U0nVoKSLJxq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTYrCi/btsEbzbbKYq/qeEiB3o1G0U0nVoKSLJxq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTYrCi/btsEbzbbKYq/qeEiB3o1G0U0nVoKSLJxq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTYrCi%2FbtsEbzbbKYq%2FqeEiB3o1G0U0nVoKSLJxq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;249&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;249&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;코드의 문자들을 읽어서 정해진 룰에 따라 토큰으로 만들어 합치는 렉시컬 분석 후,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;토큰을 트리 구조로 만드는 신택스 분석 단계를 거치면 AST가 만들어 집니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (2).png&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NIIGX/btsEbAVsVwT/aoVYiVhuudcfuOJEKae5pK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NIIGX/btsEbAVsVwT/aoVYiVhuudcfuOJEKae5pK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NIIGX/btsEbAVsVwT/aoVYiVhuudcfuOJEKae5pK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNIIGX%2FbtsEbAVsVwT%2FaoVYiVhuudcfuOJEKae5pK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;522&quot; height=&quot;294&quot; data-filename=&quot;Untitled (2).png&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;(참고) 특정 코드의 AST 구조를 쉽게 보는 방법 : &lt;a href=&quot;https://astexplorer.net/&quot;&gt;AST explorer&lt;/a&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;주의) AST 분석을 위해 사용하는 parser는 라이브러리마다 다른 parser를 사용할 수 있습니다. (크게 다르진 않음)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;JSCodeShift가 사용하는 기본 parser는 &lt;code&gt;@babel/parser&lt;/code&gt;입니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;eslint는 &lt;code&gt;@babel/eslint-parser&lt;/code&gt; &lt;code&gt;@typescript-eslint/parser&lt;/code&gt; 등을 씁니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;미리 알고 있으면 좋을 내용&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;It&amp;rsquo;s important to know that root.find() returns a collection of node-paths.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(jscodeshift는 node의 path를 활용하기 때문에 default export된 모듈을 다른 이름으로 import해도 찾을 수 있다.)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;즉, import OtherName from &amp;lsquo;@my-lib'; 와 같이 호출된 모듈 import도 AST로 다 찾을 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #333333; text-align: start;&quot;&gt;- 단, jscodeshift 내부에서 사용하는 replaceWith() 메서드는 nodePath가 아닌 node를 반환해야한다.&lt;/span&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;3. 연습 예제&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;jscodeshift를 설치해봅니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm i -g jscodeshift&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #006dd7;&quot;&gt;&lt;b&gt;요구사항 : &quot;&lt;/b&gt;&lt;b&gt;프로젝트 내에 존재하는 모든 console.log()를 지워라&quot;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;AST explorer를 통해 console의 AST 구조 보기&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (4).png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;1480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qSyzP/btsEaZuCqbn/8IC8AD9lHT5qpVriZO6Wi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qSyzP/btsEaZuCqbn/8IC8AD9lHT5qpVriZO6Wi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qSyzP/btsEaZuCqbn/8IC8AD9lHT5qpVriZO6Wi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqSyzP%2FbtsEaZuCqbn%2F8IC8AD9lHT5qpVriZO6Wi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;414&quot; height=&quot;506&quot; data-filename=&quot;Untitled (4).png&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;1480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;callExpression 중 MemberExpression인 console을 찾아서 제거하는 코드&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;// remove-console.js
export default (fileInfo, api) =&amp;gt; {
  const j = api.jscodeshift;

  return j(fileInfo.source)
    .find(j.CallExpression, {
      callee: {
        type: 'MemberExpression',
        object: { type: 'Identifier', name: 'console' },
      },
    })
    .remove()
    .toSource();
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;4. 실전 예제&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #006dd7;&quot;&gt;&lt;b&gt;요구사항 : &quot;ConfirmationModal의 onCancel prop을 onClose로 변환하라.&quot;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&amp;nbsp; 1) ConfirmationModal onCancel의 AST 구조 파악하기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&amp;nbsp; 2) &lt;/b&gt;&lt;b&gt;codemod 만들기&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;jscodeshift의 findJSXElements() 메서드는 탐색하는 파일 내 해당 이름을 가진 JSXElements를 찾아줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;우리가 찾는 요구사항의 조건은 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;{name : &amp;lsquo;ConfirmationModal&amp;rsquo;} 인 JSXElement 찾기&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(위 조건 충족 대상 중) JSXAttribute 중에 name.type이 &lt;code&gt;JSXIdentifier&lt;/code&gt;이고, name.name이 &lt;code&gt;onCancel&lt;/code&gt; 인 노드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;(위 조건 충족 대상) 노드들의 &lt;code&gt;JSXIdentifier&lt;/code&gt; name을 &lt;code&gt;onClose&lt;/code&gt;로 대체하기&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;/* eslint-disable @typescript-eslint/no-unused-vars */
export default (fileInfo, { jscodeshift }) =&amp;gt; {
  const root = jscodeshift(fileInfo.source);

  const ConfirmationModalComponents = root.findJSXElements('ConfirmationModal');

  return ConfirmationModalComponents.find(jscodeshift.JSXAttribute, {
      name: {
        type: 'JSXIdentifier',
        name: 'onCancel',
      },
      value: {
        type: 'JSXExpressionContainer',
      },
    })
    .forEach((path) =&amp;gt; {
      jscodeshift(path).replaceWith(
        jscodeshift.jsxAttribute(jscodeshift.jsxIdentifier('onClose'), path.node.value)
      );
    })
    .toSource();
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;&amp;nbsp; 3) &lt;b&gt;jscodeshift 실행하기&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; text-align: start;&quot;&gt;이 때, jscodeshift 실행하면 코드 스타일이 살짝 다르게 적용될 수 있는데,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; text-align: start;&quot;&gt;추가로 prettier를 실행해주면 프로젝트 설정에 맞게 수정됩니다.&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;// jscodeshift --parser={파싱할 대상 파일 파서} -t {codemod 파일} {대상 파일} -p

jscodeshift --parser=tsx -t replace-oncancel.js ../app/src/**/*.tsx -p&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;(팁1) ChatGPT 혹은 copilot에 대략적인 요구사항 구조를 만들어서 jscodeshift 용도로 쓸 AST 구문 달라고하면 잘 줍니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;i&gt;&lt;b&gt;&amp;nbsp; - 물론, &lt;span style=&quot;text-align: center;&quot;&gt;답변이 항상 정확하지는 않으니, 실제 AST 구문과 비교하며 정확도를 높여줍니다.&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;1592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mAtEb/btsD3LRZ4Hb/6ZQ2YiWJqE2CnQTPxxKc5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mAtEb/btsD3LRZ4Hb/6ZQ2YiWJqE2CnQTPxxKc5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mAtEb/btsD3LRZ4Hb/6ZQ2YiWJqE2CnQTPxxKc5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmAtEb%2FbtsD3LRZ4Hb%2F6ZQ2YiWJqE2CnQTPxxKc5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;350&quot; height=&quot;527&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;1592&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;(팁2) VSCode에서 Extension으로 AST 바로 보기 - &lt;/b&gt;&lt;a style=&quot;letter-spacing: 0px;&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=arichy.vscode-ast-explorer&quot;&gt;vscode-ast-explorer&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_edited_Untitled (5).png&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p09aq/btsD4Ut7nB6/GD8rtan6SgwRKwlZE2GsEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p09aq/btsD4Ut7nB6/GD8rtan6SgwRKwlZE2GsEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p09aq/btsD4Ut7nB6/GD8rtan6SgwRKwlZE2GsEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp09aq%2FbtsD4Ut7nB6%2FGD8rtan6SgwRKwlZE2GsEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;726&quot; height=&quot;424&quot; data-filename=&quot;edited_edited_Untitled (5).png&quot; data-origin-width=&quot;1322&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;[추가 내용]&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;ESLint에서도 AST를 통해 문법 등을 검사합니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- eslint-plugin 만들 때도 AST 구조를 기반으로 코드를 찾아 규칙을 설정할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/jsx-eslint/jsx-ast-utils&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;jsx-ast-utils&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- JSX 문법에 맞게 AST를 평가해주는 유틸리티 모듈&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 주어진 유틸리티 함수로 간단하게 찾자&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1706546749894&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { hasProp } from 'jsx-ast-utils';
// OR: var hasProp = require('jsx-ast-utils').hasProp;
// OR: const hasProp = require('jsx-ast-utils/hasProp');
// OR: import hasProp from 'jsx-ast-utils/hasProp';

module.exports = context =&amp;gt; ({
  JSXOpeningElement: node =&amp;gt; {
    const onChange = hasProp(node.attributes, 'onChange');

    if (onChange) {
      context.report({
        node,
        message: `No onChange!`
      });
    }
  }
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/pages/building-your-application/upgrading/codemods&quot;&gt;NextJS Codemod&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- nextjs에서 제공하는 codemod&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- jscodeshift(v0.13.1) 사용하고 있음&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;a href=&quot;https://github.com/coderaiser/putout#-built-in-transformations&quot;&gt;Putout&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 특정 코드 패턴을 원하는 방식으로 자동 변환해주는 도구&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- eslint-plugin-putout을 활용하면, 특정 패턴을 강제로 바꿀 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 강한 규칙이 필요한 경우 활용 가능할 듯&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;- 인터페이스 변경 등 일시적으로 바꾸는 상황에서는 적합하지 않아 보임&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;[참고 문서]&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1706546843236&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - facebook/jscodeshift: A JavaScript codemod toolkit.&quot; data-og-description=&quot;A JavaScript codemod toolkit. Contribute to facebook/jscodeshift development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/facebook/jscodeshift&quot; data-og-url=&quot;https://github.com/facebook/jscodeshift&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cEdoL2/hyVb2H56b6/K6UkDLjtkg86HZmiFyyEG0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/facebook/jscodeshift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/facebook/jscodeshift&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cEdoL2/hyVb2H56b6/K6UkDLjtkg86HZmiFyyEG0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - facebook/jscodeshift: A JavaScript codemod toolkit.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A JavaScript codemod toolkit. Contribute to facebook/jscodeshift development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1706546845203&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - jsx-eslint/jsx-ast-utils: AST utility module for statically analyzing JSX&quot; data-og-description=&quot;AST utility module for statically analyzing JSX. Contribute to jsx-eslint/jsx-ast-utils development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jsx-eslint/jsx-ast-utils&quot; data-og-url=&quot;https://github.com/jsx-eslint/jsx-ast-utils&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mtOGO/hyVcePlDR4/wDcGFfp6u4UvPwAU6VKQKk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/jsx-eslint/jsx-ast-utils&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jsx-eslint/jsx-ast-utils&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mtOGO/hyVcePlDR4/wDcGFfp6u4UvPwAU6VKQKk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - jsx-eslint/jsx-ast-utils: AST utility module for statically analyzing JSX&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;AST utility module for statically analyzing JSX. Contribute to jsx-eslint/jsx-ast-utils development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1706546850011&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Write Code to Rewrite Your Code: jscodeshift | Toptal&amp;reg;&quot; data-og-description=&quot;How many times have you used the find-and-replace functionality (or RegEx) across a directory to make changes to JavaScript source files? Up your refactoring game by using codemods, scripts used to rewrite other scripts. In this article, Toptal Freelance D&quot; data-og-host=&quot;www.toptal.com&quot; data-og-source-url=&quot;https://www.toptal.com/javascript/write-code-to-rewrite-your-code&quot; data-og-url=&quot;https://www.toptal.com/javascript/write-code-to-rewrite-your-code&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FSJZX/hyVb9UNWgT/oKkAtrQvLNnbC6rYkCJkIk/img.png?width=3584&amp;amp;height=1876&amp;amp;face=0_0_3584_1876&quot;&gt;&lt;a href=&quot;https://www.toptal.com/javascript/write-code-to-rewrite-your-code&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.toptal.com/javascript/write-code-to-rewrite-your-code&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FSJZX/hyVb9UNWgT/oKkAtrQvLNnbC6rYkCJkIk/img.png?width=3584&amp;amp;height=1876&amp;amp;face=0_0_3584_1876');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Write Code to Rewrite Your Code: jscodeshift | Toptal&amp;reg;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;How many times have you used the find-and-replace functionality (or RegEx) across a directory to make changes to JavaScript source files? Up your refactoring game by using codemods, scripts used to rewrite other scripts. In this article, Toptal Freelance D&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.toptal.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1706546851912&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;JSCodeShift로 기술 부채 청산하기&quot; data-og-description=&quot;기술 부채는 개발할수록 쌓여만 갑니다. 프론트엔드 챕터가 JSCodeShift를 이용하여 순식간에 100개 서비스의 기술 부채를 해결한 경험을 소개합니다.&quot; data-og-host=&quot;toss.tech&quot; data-og-source-url=&quot;https://toss.tech/article/jscodeshift&quot; data-og-url=&quot;https://toss.tech/article/jscodeshift&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cL2Xec/hyVcePlDUu/YKH1HIqJ0jCy7Fezjarpck/img.png?width=4802&amp;amp;height=2514&amp;amp;face=0_0_4802_2514,https://scrap.kakaocdn.net/dn/oNOLD/hyVccqrEVJ/r4aHReqiKC1tRjJDY0wvu1/img.png?width=4802&amp;amp;height=2514&amp;amp;face=0_0_4802_2514,https://scrap.kakaocdn.net/dn/bEmNkH/hyVccYgvwi/jCvZAvxG8xk9KZ8NWj3q60/img.png?width=2014&amp;amp;height=1394&amp;amp;face=0_0_2014_1394&quot;&gt;&lt;a href=&quot;https://toss.tech/article/jscodeshift&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://toss.tech/article/jscodeshift&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cL2Xec/hyVcePlDUu/YKH1HIqJ0jCy7Fezjarpck/img.png?width=4802&amp;amp;height=2514&amp;amp;face=0_0_4802_2514,https://scrap.kakaocdn.net/dn/oNOLD/hyVccqrEVJ/r4aHReqiKC1tRjJDY0wvu1/img.png?width=4802&amp;amp;height=2514&amp;amp;face=0_0_4802_2514,https://scrap.kakaocdn.net/dn/bEmNkH/hyVccYgvwi/jCvZAvxG8xk9KZ8NWj3q60/img.png?width=2014&amp;amp;height=1394&amp;amp;face=0_0_2014_1394');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;JSCodeShift로 기술 부채 청산하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;기술 부채는 개발할수록 쌓여만 갑니다. 프론트엔드 챕터가 JSCodeShift를 이용하여 순식간에 100개 서비스의 기술 부채를 해결한 경험을 소개합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;toss.tech&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1706546848790&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;1) AST를 이용한 코드 변환&quot; data-og-description=&quot;## 1. 자동화된 코드 변환이 등장한 이유 프런트엔드 개발 스택에서는 코드를 변환해주는 툴이 많이 있다. 가령, babel이 리액트의 JSX 문법을 아래와 같이 변환한다. 또&amp;hellip;&quot; data-og-host=&quot;wikidocs.net&quot; data-og-source-url=&quot;https://wikidocs.net/156284&quot; data-og-url=&quot;https://wikidocs.net/156284&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cyu5Qb/hyVcaTFsaO/EghEHkQWlCnyDDht1amuNK/img.png?width=81&amp;amp;height=129&amp;amp;face=0_0_81_129&quot;&gt;&lt;a href=&quot;https://wikidocs.net/156284&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://wikidocs.net/156284&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cyu5Qb/hyVcaTFsaO/EghEHkQWlCnyDDht1amuNK/img.png?width=81&amp;amp;height=129&amp;amp;face=0_0_81_129');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;1) AST를 이용한 코드 변환&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;## 1. 자동화된 코드 변환이 등장한 이유 프런트엔드 개발 스택에서는 코드를 변환해주는 툴이 많이 있다. 가령, babel이 리액트의 JSX 문법을 아래와 같이 변환한다. 또&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;wikidocs.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1706546851738&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - Bogdan-Lyashenko/js-code-to-svg-flowchart: js2flowchart - a visualization library to convert any JavaScript code into b&quot; data-og-description=&quot;js2flowchart - a visualization library to convert any JavaScript code into beautiful SVG flowchart. Learn other&amp;rsquo;s code. Design your code. Refactor code. Document code. Explain code. - GitHub - Bogd...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart&quot; data-og-url=&quot;https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/m6Mx8/hyVb8n0AAb/bcz9SjKLnqDL5Bq7aC5KH0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/m6Mx8/hyVb8n0AAb/bcz9SjKLnqDL5Bq7aC5KH0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - Bogdan-Lyashenko/js-code-to-svg-flowchart: js2flowchart - a visualization library to convert any JavaScript code into b&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;js2flowchart - a visualization library to convert any JavaScript code into beautiful SVG flowchart. Learn other&amp;rsquo;s code. Design your code. Refactor code. Document code. Explain code. - GitHub - Bogd...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1706546855981&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Upgrading: Codemods | Next.js&quot; data-og-description=&quot;Use codemods to upgrade your Next.js codebase when new features are released.&quot; data-og-host=&quot;nextjs.org&quot; data-og-source-url=&quot;https://nextjs.org/docs/pages/building-your-application/upgrading/codemods&quot; data-og-url=&quot;https://nextjs.org/docs/pages/building-your-application/upgrading/codemods&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/0NUKU/hyVb3NNbNY/3WWeOzNWxO8Xh7yjEiEonk/img.png?width=843&amp;amp;height=441&amp;amp;face=0_0_843_441,https://scrap.kakaocdn.net/dn/bk1VRK/hyVcdbN3aP/LW59H5vZXG20IYdkv1Vsn1/img.png?width=843&amp;amp;height=441&amp;amp;face=0_0_843_441&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/pages/building-your-application/upgrading/codemods&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://nextjs.org/docs/pages/building-your-application/upgrading/codemods&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/0NUKU/hyVb3NNbNY/3WWeOzNWxO8Xh7yjEiEonk/img.png?width=843&amp;amp;height=441&amp;amp;face=0_0_843_441,https://scrap.kakaocdn.net/dn/bk1VRK/hyVcdbN3aP/LW59H5vZXG20IYdkv1Vsn1/img.png?width=843&amp;amp;height=441&amp;amp;face=0_0_843_441');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Upgrading: Codemods | Next.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Use codemods to upgrade your Next.js codebase when new features are released.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;nextjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;mttContainer&quot; style=&quot;transform: translate(601px, 616px);&quot; aria-expanded=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>Development/TIL</category>
      <category>codemod</category>
      <category>jscodeshift</category>
      <category>코드 자동 변환</category>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/46</guid>
      <comments>https://despiter.tistory.com/46#entry46comment</comments>
      <pubDate>Tue, 30 Jan 2024 01:49:52 +0900</pubDate>
    </item>
    <item>
      <title>[회고] 2022년 프론트엔드 개발자 회고</title>
      <link>https://despiter.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;바트 기도.jpeg&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EW31u/btrU2K9hQ6A/3kpxzkY9eHXlMkfmrwXyQ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EW31u/btrU2K9hQ6A/3kpxzkY9eHXlMkfmrwXyQ1/img.jpg&quot; data-alt=&quot;개인 프로젝트 그만하고 실제 프로덕트 개발하게 해주세요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EW31u/btrU2K9hQ6A/3kpxzkY9eHXlMkfmrwXyQ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEW31u%2FbtrU2K9hQ6A%2F3kpxzkY9eHXlMkfmrwXyQ1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;320&quot; data-filename=&quot;바트 기도.jpeg&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개인 프로젝트 그만하고 실제 프로덕트 개발하게 해주세요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;*참고 : 바트(Bart)는 회사에서 사용 중인 저의 닉네임이고, 바트 심슨 짤을 자주 사용합니다.&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;BM을 가진 프로덕트의 개발자가 되는 것&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 많은 이들에게 취업 준비는 끝이 보이지 않는 광야의 길이다. 나 또한 마찬가지였지만, 감사하게도 취준 기간 동안 마음이 크게 불안하지 않았다. 개발자 호황기였던 2021년이 지나면서 조금씩 채용 공고가 줄어드는게 체감이 되고 있었지만, 부족한 점을 채우며 계속 공부를 하고 있었으니 언젠가는 입사해서 현업 개발자가 될 것이라는 믿음이 있었던 것 같다. 하반기에 입사한 모두싸인이라는 회사는 연 초에 계획했던 회사는 아니지만, 전자계약 시장 상황이나 대표님의 비전과 관련해서 충분히 성장 가능성 있는 회사라고 생각했고, 조직문화적으로도 좋은 평가를 받고 있는 것 같아 합류를 하게 되었다. 정말 다행스럽게도 내 예상보다 더 좋은 분위기와 사람들이 있었고, 별 문제 없이 일에만 집중할 수 있는 환경이라는 생각으로 잘 지내고 있다. 특히, 학생 때나 취업 준비 기간에는 실력있는 개발자들과 함께할 기회가 별로 없는데, 회사에는 나보다 잘하는 분들이 많으니 나의 부족한 점을 깨닫고 개선할 수 있는 기회가 종종 있어서 주니어 개발자로서는 만족스러운 환경이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-12-31 오후 9.43.50.png&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BAOin/btrUWbf24BX/Tya2nsHeVLyddjlGJPHkVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BAOin/btrUWbf24BX/Tya2nsHeVLyddjlGJPHkVK/img.png&quot; data-alt=&quot;이거 레거시인데 냅다 바꾸면 안 되나요?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BAOin/btrUWbf24BX/Tya2nsHeVLyddjlGJPHkVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBAOin%2FbtrUWbf24BX%2FTya2nsHeVLyddjlGJPHkVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;380&quot; height=&quot;288&quot; data-filename=&quot;스크린샷 2022-12-31 오후 9.43.50.png&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이거 레거시인데 냅다 바꾸면 안 되나요?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;현업 개발자가 되고 나서 깨달은 것들&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 학생 개발자에서 현업 개발자로 넘어가며 개인적으로 깨달은 것이 있다면, 개발자라고 기술적 퍼포먼스만 추구하지는 않는다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입사한 후 지금까지 내가 주로 보고 흡수하는 것들은 기술적 개선보다는 일하는 방식에 초점이 맞춰져 있다. 챕터 리드는 어떻게 일 하는지, A 개발자와 B 개발자는 어떻게 일 하는지 관찰하며 배움이 될 만한 부분을 선택적으로 흡수하고 있다. 예를 들면, 기존에 존재하는 기능을 다른 곳에 추가하는 상황에서 기존 맥락 파악을 어떤 식으로 하면 효율적일지, 각자 개발 일정은 어떻게 판단하고, 특정 상황에서의 판단과 그 판단의 기준이 무엇인지 등 물어보고 배울 것들이 천지였다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;다행히 동료분들이 거리낌 없이 자신들의 생각과 근거를 잘 이야기해주는 편이다.&lt;/i&gt;&lt;/span&gt; 개인적으로 수습 3개월 동안은 이 조직이 일하는 방식을 흡수하는 과정이라고 생각한다. 경력직이라면 온보딩이 빠르겠지만, 인턴 경험도 없는 신입은 회사 일이 처음이라 &quot;일을 잘 하려면&quot; 배울 게 정말 많다. 그리고 개발자마다의 개성도 있어서 자기 자신이 어떤 개발자가 되고 싶은지 명확하게 하고 그것에 맞는 학습을 진행하는 것이 중요한 것 같다. 내년에는 여러 개발자들과 교류하면서 나는 어떤 개발자가 되고 싶은지 잘 고민해보고 방향을 구체적으로 잡아보고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-12-09 오후 6.37.54.png&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CtAiB/btrUVjyGDV3/90kK1jEeoNcYgVq8ZY0DBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CtAiB/btrUVjyGDV3/90kK1jEeoNcYgVq8ZY0DBk/img.png&quot; data-alt=&quot;긍정왕 김바트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CtAiB/btrUVjyGDV3/90kK1jEeoNcYgVq8ZY0DBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCtAiB%2FbtrUVjyGDV3%2F90kK1jEeoNcYgVq8ZY0DBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;252&quot; height=&quot;251&quot; data-filename=&quot;스크린샷 2022-12-09 오후 6.37.54.png&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;긍정왕 김바트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2023년 계획&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개발자 계획&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 오프라인 컨퍼런스 참석하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 오프라인으로 열리는 컨퍼런스에 참석한 적이 없는데, 한 번쯤은 참석해보고 싶다. 요즘은 워낙 수요가 많아서 티케팅을 해야하는 수준인 것 같지만.. 확실히 혼자 고여있는 것보다는 개발 커뮤니티 활동을 해야 자기객관화를 하고 이 분야에 대한 시야를 넓힐 수 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 회사에 기술적, 환경적으로 기여하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 회사의 코드가 곧 교재인 것 같다. 개인적으로 공부할 때는 특정 기술에 대한 코드 레퍼런스를 찾기가 생각보다 어려운데&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;(맥락 파악이 어려워서인 듯) &lt;/i&gt;&lt;/span&gt;&amp;nbsp;회사 코드에서는 내가 새롭게 배운 기술도 레퍼런스가 있고, 맥락 파악도 PR을 보거나 물어물어 알 수 있으니 학습에 좋은 리소스라고 생각한다. 그리고 무엇보다 과거에서 현재까지 어떻게 판단했고, 왜 넘어왔는지 알 수 있어서 좋다. &lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;미래는 나도 함께 만들어가야지&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;28세 인생 계획&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 나이만큼 책 읽고 블로그에 짧게라도 기록 남기기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 그동안 책을 읽을 때는 주로 인문학, 철학을 선호했는데, 이제는 문학과 더불어 기술 및 소프트웨어 관련 서적을 포함해서 책 읽는 주기를 좁혀보려고 한다. 출퇴근 시간에 짬짬히 읽는게 가장 베스트 습관이라고 생각되는데, 그러려면 책을 사서 들고 다니는 것이 습관을 형성하는데 좋다. 그동안은 돈이 없어서 책 구매를 잘 못 했는데, 이제는 회사 지원이나 복지포인트로 사면 되니까 왕창 사야지. &lt;s&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;회사 책 구매 1등 해야지&lt;/i&gt;&lt;/span&gt;&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) 주 3회 운동 출석하기&lt;/b&gt;&lt;br /&gt;&amp;nbsp; 회사 다니면서 운동의 중요성을 느끼고 있다. 코어 근육이 없어서 몸이 흐물렁흐물렁 거린다. 매일 하는 것은 시간적으로 어려울 수 있으니 주 2~3회 정도로 퇴근하고 집 근처 피트니스 센터에서 운동하고 씻고 잘 계획을 가지고 있다. 계획을 가지고 있다..&lt;s&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt; 누구나 계획은 있다..&lt;/span&gt;&lt;/i&gt;&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) 음악 만들어서 싸클에 올려보기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 취미가 딱히 없는 편이라 요즘 좀 재밌는 것 위주로 생각해보고 있다. 비트메이킹에 관심이 있어서 언젠가 한 번 해봐야지 했었는데, 나의 베스트프렌드가 작곡 전공이라 올 해 함께 노래 하나 만들어보기로 했다ㅎㅎ 만들어보고 괜찮으면 매년 하나씩 만들어야지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;1496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmWko8/btrU1Oc2Mec/yVBCw9WsJ9K88edGXzW3j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmWko8/btrU1Oc2Mec/yVBCw9WsJ9K88edGXzW3j1/img.png&quot; data-alt=&quot;송년회에서 저 작은 앵글에 들어가려고 꾸역꾸역 모여 찍은 팀 사진 :)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmWko8/btrU1Oc2Mec/yVBCw9WsJ9K88edGXzW3j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmWko8%2FbtrU1Oc2Mec%2FyVBCw9WsJ9K88edGXzW3j1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;386&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;1496&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;송년회에서 저 작은 앵글에 들어가려고 꾸역꾸역 모여 찍은 팀 사진 :)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2022년을 보내며...&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대학교를 졸업하고, 개발자 커리어를 시작한 해다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 과정에 좋은 경험만 있던 것은 아니지만, 그럼에도 마음의 큰 요동 없이 평안한 삶을 살 수 있었던 것에 감사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 산책을 참 좋아하는데, 산책하다 보면 어린 아이들과 함께 나온 가족들도 보이고, 젊은 커플이나 노부부도 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 인생 하나만 보고 살면 시야가 좁아져 작은 것도 커 보이기 마련이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;누군가의 어린 시절과 노년을 바라보며 나의 인생도 크게 바라보는 것이 지금의 어려움을 작아보이게 만드는 나만의 비법이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계획대로 되지 않았던 한 해였지만, 예상치 못한 방향으로 감사한 한 해였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023년도 나름의 계획을 가지고 시작하지만, 계획대로 되지 않아도 그 끝에선 감사한 한 해가 될 것이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제나 주변의 사람들과 좋은 영향력을 주고 받으며, 어디서든 필요한 존재가 되기를 소망하며 한 해를 마무리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Life/log</category>
      <category>2022년 회고</category>
      <category>모두싸인</category>
      <category>프론트엔드 개발자</category>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/43</guid>
      <comments>https://despiter.tistory.com/43#entry43comment</comments>
      <pubDate>Sat, 31 Dec 2022 22:14:09 +0900</pubDate>
    </item>
    <item>
      <title>[Typescript] type annotation | type inference | type assertion</title>
      <link>https://despiter.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. type annotation&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type annotation은 개발자가 타입을 타입스크립트에게 직접 말해주는 것을 의미한다.&lt;/p&gt;
&lt;pre id=&quot;code_1662018328602&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const name: string = '홍길동'; // 개발자가 string으로 타입을 지정&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. type inference&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type inference는 타입스크립트가 알아서 타입을 추론하는 것을 의미한다.&lt;/p&gt;
&lt;pre id=&quot;code_1662018387470&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const name = '홍길동' // 변수 선언과 동시에 초기화할 경우에는 typescript가 초기화 값으로 타입 추론&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;+ typescript가 타입 추론을 할 수 없는 경우에는 type annotation을 꼭 해줘야하는 경우이다.&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) any 타입 리턴&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typescript가 추론할 수 없는 초기화 값은 any로 정의되기 때문에 직접 type annotation을 해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1662018717802&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const response = '{&quot;id&quot;:1, &quot;result&quot;:10}';
const data = JSON.parse(response);	// 이 때, data는 any 타입으로 정의됨
console.log(data); // data = {id: 1, result: 10} (실제로는 object type)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) 변수 선언 후 나중에 초기화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언과 동시에 초기화하면 타입을 추론하지만, 나중에 값을 초기화하면 추론을 못한다.&lt;/p&gt;
&lt;pre id=&quot;code_1662018833526&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let name;	// let name: any
name = '홍길동'; // let name: any&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3) 변수에 대입될 값이 일정하지 않은 경우&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 여러 타입이 지정되어야할 경우 직접 annotation을 해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1662018981146&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let 변수명 : boolean | number = false;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. type assertion&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;TypeScript에서는 시스템이 추론 및 분석한 타입 내용을 우리가 원하는 대로 얼마든지 바꿀 수 있습니다. 이때 &quot;타입 표명(type assertion)&quot;이라 불리는 메커니즘이 사용됩니다. TypeScript의 타입 표명은 프로그래머가 컴파일러에게 내가 너보다 타입에 더 잘 알고 있고, 나의 주장에 대해 의심하지 말라고 하는 것과 같습니다.&lt;br /&gt;- &quot;typescript-deep-dive&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type assertion을 사용하면 개발자가 값의 type을 설정하고 컴파일러에 이를 유추하지 않도록 지시할 수 있다. typescript의 추론보다 변수의 유형에 대해 더 잘 이해하고 있을 때 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1662019573743&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Foo {
  bar: number;
  bas: string;
}

// 아래의 경우, 약속한 속성을 추가하지 않아도 type 에러 없음
const foo = {} as Foo;
foo.bar = 123;
foo.bas = 'hi';

// 아래의 경우, JSX 문법과 겹쳐서 React에서는 사용하지 않는게 좋다.
// as Foo와 비슷한 기능에 자동완성을 지원한다.
const foo = &amp;lt;Foo&amp;gt;{
	...
}

// 아래의 경우, 초기화 값에 bar와 bas라는 프로퍼티를 입력하지 않으면 type 에러 발생
// 미리 정해진 타입을 지킬 수 있다는 장점 있다.
const foo:Foo = {
	bar : 123,
    bas : 'hi',
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;참고자료&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://radlohead.gitbook.io/typescript-deep-dive/type-system/type-assertion&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://radlohead.gitbook.io/typescript-deep-dive/type-system/type-assertion&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1662019475130&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;타입 표명(Type Assertion) - TypeScript Deep Dive&quot; data-og-description=&quot;앞서 살펴봤듯이 타입 표명에는 다소 위험성이 있지만 *완전히 해로운&amp;quot; 것만은 아닙니다. 예를 들어, 다음은 아주 정당한 사용 사례이며 (즉, 사용자가 전달된 이벤트에 대해 좀더 명확한 정보를&quot; data-og-host=&quot;radlohead.gitbook.io&quot; data-og-source-url=&quot;https://radlohead.gitbook.io/typescript-deep-dive/type-system/type-assertion&quot; data-og-url=&quot;https://radlohead.gitbook.io/typescript-deep-dive/type-system/type-assertion&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://radlohead.gitbook.io/typescript-deep-dive/type-system/type-assertion&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://radlohead.gitbook.io/typescript-deep-dive/type-system/type-assertion&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;타입 표명(Type Assertion) - TypeScript Deep Dive&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;앞서 살펴봤듯이 타입 표명에는 다소 위험성이 있지만 *완전히 해로운&quot; 것만은 아닙니다. 예를 들어, 다음은 아주 정당한 사용 사례이며 (즉, 사용자가 전달된 이벤트에 대해 좀더 명확한 정보를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;radlohead.gitbook.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/42</guid>
      <comments>https://despiter.tistory.com/42#entry42comment</comments>
      <pubDate>Thu, 1 Sep 2022 17:14:27 +0900</pubDate>
    </item>
    <item>
      <title>[UI/UX] 다수 데이터 등록 '한 번에 저장' vs '입력마다 저장'</title>
      <link>https://despiter.tistory.com/41</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;본 포스트는 더 나은 UX를 위한 개인적 경험과 고민이 담긴 글입니다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 요구사항&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 선거 조직은 [대분류]-[중분류]-[소분류]로 구분된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- [대분류]는 기본 값으로 &quot;중앙자치기구&quot;, &quot;단과대&quot;, &quot;학과&quot;를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- [대분류]인 &quot;중앙자치기구&quot;와 &quot;단과대&quot;는 [중분류]만 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- [대분류]인 &quot;학과&quot;는 [중분류]와 [소분류]를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &quot;학과&quot;의 [중분류]는 &quot;단과대&quot;의 [중분류]를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &quot;저장&quot; 버튼을 누르면, 입력된 input 태그의 데이터를 서버에 전송한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 데이터 전송 후, &lt;span&gt;서버에 저장된&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;새로운 데이터를 호출하여 UI를 최신화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 고민&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 고민했던 내용은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: center;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;선택 1) 모든 정보를 입력한 다음, [저장하기] 버튼을 통해 최종 데이터를 전송한다.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;482&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXKaGc/btrJre1nBLy/fIIFmHLGZ9QW3BmBlAy4U0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXKaGc/btrJre1nBLy/fIIFmHLGZ9QW3BmBlAy4U0/img.png&quot; data-alt=&quot;각 분류별 데이터를 모두 입력한 다음 저장하기를 누르면, 전체 데이터가 업데이트 되는 방식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXKaGc/btrJre1nBLy/fIIFmHLGZ9QW3BmBlAy4U0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXKaGc%2FbtrJre1nBLy%2FfIIFmHLGZ9QW3BmBlAy4U0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;360&quot; data-origin-width=&quot;860&quot; data-origin-height=&quot;482&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;각 분류별 데이터를 모두 입력한 다음 저장하기를 누르면, 전체 데이터가 업데이트 되는 방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: center;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;선택 2) 각 입력마다 [저장]하여 매번 데이터를 전송한다.&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s5W6T/btrJmsz3Z8R/VuK3nGkSjmPRIRWKabYOZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s5W6T/btrJmsz3Z8R/VuK3nGkSjmPRIRWKabYOZK/img.png&quot; data-alt=&quot;세부적인 데이터를 입력할 때마다 전송하고 다시 불러오는 방식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s5W6T/btrJmsz3Z8R/VuK3nGkSjmPRIRWKabYOZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs5W6T%2FbtrJmsz3Z8R%2FVuK3nGkSjmPRIRWKabYOZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;320&quot; data-origin-width=&quot;855&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세부적인 데이터를 입력할 때마다 전송하고 다시 불러오는 방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;*주요 쟁점&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 99.8834%; height: 130px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 100.964%; text-align: center; height: 21px;&quot; colspan=&quot;2&quot;&gt;&lt;b&gt;선택 1 : 한 번에 저장하기&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 47px;&quot;&gt;
&lt;td style=&quot;width: 18.0157%; text-align: center; height: 47px;&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.9482%; height: 47px;&quot;&gt;● 저장하기 버튼을 누를 때만 네트워크 요청을 하기 때문에 네트워크 통신 횟수를 줄일 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 62px;&quot;&gt;
&lt;td style=&quot;width: 18.0157%; text-align: center; height: 62px;&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.9482%; height: 62px;&quot;&gt;&lt;span&gt;●&amp;nbsp;여러 데이터 입력 과정에서 의도치 않게 화면을 벗어나면, 입력 중이던 데이터를 손실할 수 있다.&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;br /&gt;● 저장하기 버튼을 따로 눌러줘야한다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 99.8834%; height: 144px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 100.964%; height: 21px; text-align: center;&quot; colspan=&quot;2&quot;&gt;&lt;b&gt;선택 2 : 입력마다 저장하기&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 61px;&quot;&gt;
&lt;td style=&quot;width: 18.1321%; height: 61px; text-align: center;&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.8318%; height: 61px; text-align: left;&quot;&gt;&lt;span&gt;●&amp;nbsp;입력마다 저장하기 때문에 의도치 않게 화면을 벗어나도 데이터 손실 가능성을 줄일 수 있다.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;● 버튼 클릭 없이, enter key로 빠르게 저장이 가능하다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 62px;&quot;&gt;
&lt;td style=&quot;width: 18.1321%; height: 62px; text-align: center;&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 82.8318%; height: 62px; text-align: left;&quot;&gt;●&amp;nbsp;입력마다 네트워크 요청이 발생한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 최종 선택&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 기능을 사용하는 관리자 입장에서는 네트워크 요청을 고려할 필요가 없다는 생각이 들었다. 그리고 관리자 계정을 통해서만 관리자 페이지에 접근 가능하고, 해당 기능은 소수의 관리자만 사용하므로 네트워크 요청이 그렇게 많이 발생하지 않을 것이라 판단했다. 사용자 경험적 관점에서도 입력마다 저장되는 것이 훨씬 편리하게 느껴졌기 때문에 최종적으로는 &lt;b&gt;선택 2번인 &quot;입력마다 저장하기&quot;&lt;/b&gt;를 택하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도치 않은 페이지 이동으로 입력 중이던 데이터가 손실되는 경우를 막기 위해 브라우저에 데이터를 주기적으로 저장하는 방식은 어떨까 생각해봤다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;(물론, 사용자 입력이 많은 페이지에 한해서) &lt;/i&gt;&lt;/span&gt;localStorage와 같은 web Storage를 사용할 수 있겠지만, localStorage와 sessionStorage는 메인 스레드를 블로킹하여 동기식으로 동작하기 때문에 성능적으로 좋지 않을 것이라는 생각이 들면서, 비동기 방식인 indexDB를 사용하는 것은 어떨까라는 생각이 스쳐 지나간다. 물론, 동기식 storage에 비해 사용 방식도 복잡하고, 직접 사용해본적도 없지만, 충분히 합리적인 생각이 아닐까.&lt;/p&gt;</description>
      <category>Development/Front-End</category>
      <category>UX</category>
      <category>사용자 경험</category>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/41</guid>
      <comments>https://despiter.tistory.com/41#entry41comment</comments>
      <pubDate>Thu, 11 Aug 2022 00:20:57 +0900</pubDate>
    </item>
    <item>
      <title>[Issue] msw + webpack-dev-server</title>
      <link>https://despiter.tistory.com/39</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;WEVOTE 프로젝트 리팩토링 중 mocking 시스템을 도입하기 위해 msw를 적용하는 과정에서 정상적으로 동작하지 않는 상황으로 시간을 소모했다.&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmCk0M/btrHoGFcDFG/Qt5UpWV5jrrYMFk8dT9iK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmCk0M/btrHoGFcDFG/Qt5UpWV5jrrYMFk8dT9iK0/img.png&quot; data-alt=&quot;MSW가 정상적으로 동작하면, 콘솔에 알림이 뜬다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmCk0M/btrHoGFcDFG/Qt5UpWV5jrrYMFk8dT9iK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmCk0M%2FbtrHoGFcDFG%2FQt5UpWV5jrrYMFk8dT9iK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;736&quot; height=&quot;281&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;281&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MSW가 정상적으로 동작하면, 콘솔에 알림이 뜬다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스워커 미실행 이슈 (with webpack-dev-server)&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qt86V/btrIxCpI9It/0btVSwFOefUvdtBzSwKIaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qt86V/btrIxCpI9It/0btVSwFOefUvdtBzSwKIaK/img.png&quot; data-alt=&quot;물론, root 디렉토리는 프로젝트에 따라 public이 아닐수도 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qt86V/btrIxCpI9It/0btVSwFOefUvdtBzSwKIaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqt86V%2FbtrIxCpI9It%2F0btVSwFOefUvdtBzSwKIaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;754&quot; height=&quot;604&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;754&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;물론, root 디렉토리는 프로젝트에 따라 public이 아닐수도 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;msw는 Web API의 Service Worker API를 사용한다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;공식문서에서 설명하듯&amp;nbsp;&lt;/span&gt;브라우저에서 사용할 때는&lt;span style=&quot;color: #171c26;&quot;&gt;&amp;nbsp;Mock Service Worker(mockServiceWorker.js)를 public 디렉토리에 설치하여 실행해야 한다. 나는 &lt;span style=&quot;color: #171c26;&quot;&gt;WEVOTE에 적용하기 전에 msw_test CRA 프로젝트를 만들어서 동작 방식을 이해하고 도입을 시도했다. &lt;/span&gt;public에 mockServiceWorker를 설치하여 실행했고, 정상적으로 동작하는 것을 확인했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #171c26; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문제의 발단&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #171c26; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;동작 방식을 가볍게 이해하고 WEVOTE에 설치하여 사용했는데, 분명 같은 순서로 동일하게 적용했지만 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;[MSW] Mocking enabled&amp;nbsp; &lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;메시지가 출력되지 않았다. 일단 index.js의 worker.start()는 정상적으로 실행됐기 때문에 &lt;span style=&quot;color: #000000;&quot;&gt;public에 설치한 &lt;/span&gt;mockServieWorker.js가 제대로 돌아가지 않는다는 것을 [개발자도구]-[애플리케이션]-[service Worker]를 통해 확인할 수 있었다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xNAro/btrIA2tUbg7/1kfWlhJ12JaqumQXeaKGF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xNAro/btrIA2tUbg7/1kfWlhJ12JaqumQXeaKGF0/img.png&quot; data-origin-width=&quot;799&quot; data-origin-height=&quot;316&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.6867%; margin-right: 10px;&quot; data-widthpercent=&quot;52.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xNAro/btrIA2tUbg7/1kfWlhJ12JaqumQXeaKGF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxNAro%2FbtrIA2tUbg7%2F1kfWlhJ12JaqumQXeaKGF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;799&quot; height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDloMS/btrIBUoRGDY/ujFS0x9W0qbjurExNlqzf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDloMS/btrIBUoRGDY/ujFS0x9W0qbjurExNlqzf1/img.png&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;274&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.1505%;&quot; data-widthpercent=&quot;47.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDloMS/btrIBUoRGDY/ujFS0x9W0qbjurExNlqzf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDloMS%2FbtrIBUoRGDY%2FujFS0x9W0qbjurExNlqzf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;MockserviceWorker.js가 동작하지 않음 -&amp;gt; 정상 동작&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해결의 실마리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;msw 공식문서에서 제공하는 기본 예제는 CRA 환경을 기반으로 소개되고 있다. 그래서 CRA 환경의 webpack configuration을 확인하기 위해 msw_test로 만들었던 CRA를 eject해서 설정을 뜯어보았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clgW3j/btrIvSGx5xd/RUGjy01pvrbizOjZUbTRkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clgW3j/btrIvSGx5xd/RUGjy01pvrbizOjZUbTRkk/img.png&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;628&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;30.92&quot; style=&quot;width: 30.1976%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clgW3j/btrIvSGx5xd/RUGjy01pvrbizOjZUbTRkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclgW3j%2FbtrIvSGx5xd%2FRUGjy01pvrbizOjZUbTRkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;653&quot; height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAn6QY/btrIt89YRuG/rGZKeMNjt0bcbKuFsKrIjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAn6QY/btrIt89YRuG/rGZKeMNjt0bcbKuFsKrIjK/img.png&quot; data-origin-width=&quot;703&quot; data-origin-height=&quot;506&quot; data-is-animation=&quot;false&quot; style=&quot;width: 40.3481%; margin-right: 10px;&quot; data-widthpercent=&quot;41.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAn6QY/btrIt89YRuG/rGZKeMNjt0bcbKuFsKrIjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAn6QY%2FbtrIt89YRuG%2FrGZKeMNjt0bcbKuFsKrIjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;703&quot; height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zKqIs/btrICZKeStb/5G78ivLHyrDIsOduCLOemk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zKqIs/btrICZKeStb/5G78ivLHyrDIsOduCLOemk/img.png&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;744&quot; data-is-animation=&quot;false&quot; style=&quot;width: 27.1288%;&quot; data-widthpercent=&quot;27.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zKqIs/btrICZKeStb/5G78ivLHyrDIsOduCLOemk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzKqIs%2FbtrICZKeStb%2F5G78ivLHyrDIsOduCLOemk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;744&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;CRA의 webpackDevServer.config.js에서 publicPath 찾는 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 나는 &lt;u&gt;CRA를 사용하지 않고, 개발 환경을 구축했기 때문&lt;/u&gt;에 CRA 처럼 별도의 잘 짜여진 상황에 따른 path 변환 없이 webpack-dev-server를 사용하고 있었다.&amp;nbsp; 즉, 나는 번들링한 파일들을 /dist를 통해 제공하고, 접근하는 환경인데, mockServiceWorker.js는 번들 파일이 아닌 서비스워커 실행 파일이기 때문에 /public으로 접근할 수 있도록 해야하는 것이다. 따라서, 서비스워커가 접근할 수 있게 path를 따로 설정해줘야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;583&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l668t/btrIwIqaLuw/ZKtlp8KyZZQ4Zmhnkmjir1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l668t/btrIwIqaLuw/ZKtlp8KyZZQ4Zmhnkmjir1/img.png&quot; data-alt=&quot;현 프로젝트의 webpack output path는 /dist&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l668t/btrIwIqaLuw/ZKtlp8KyZZQ4Zmhnkmjir1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl668t%2FbtrIwIqaLuw%2FZKtlp8KyZZQ4Zmhnkmjir1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;447&quot; height=&quot;583&quot; data-origin-width=&quot;447&quot; data-origin-height=&quot;583&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;현 프로젝트의 webpack output path는 /dist&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;해결&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;우리는 웹팩을 통해 제공되지 않는 정적 리소스의 경로를 설정하는 devServer.contentBase를 통해 서비스워커가 포함된 public 디렉토리를 설정할 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;175&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwzROd/btrIvSmjO4N/DQHthe6ZwMTUUFLu2q19m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwzROd/btrIvSmjO4N/DQHthe6ZwMTUUFLu2q19m1/img.png&quot; data-alt=&quot;derServer.contentBase는 정적 리소스 경로&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwzROd/btrIvSmjO4N/DQHthe6ZwMTUUFLu2q19m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwzROd%2FbtrIvSmjO4N%2FDQHthe6ZwMTUUFLu2q19m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;280&quot; height=&quot;175&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;derServer.contentBase는 정적 리소스 경로&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그럼 이제 webpack-dev-server는 웹팩 번들 파일은 /dist를 통해, 웹팩이 제공하지 않는 static 리소스를 /public을 통해 접근하여 사용하게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;243&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rtFwB/btrIyxnZD7q/5Vi3nPtbjxYK4bh85clVDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rtFwB/btrIyxnZD7q/5Vi3nPtbjxYK4bh85clVDK/img.png&quot; data-alt=&quot;드디어 WEVOTE에도 MSW가 정상 동작한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rtFwB/btrIyxnZD7q/5Vi3nPtbjxYK4bh85clVDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrtFwB%2FbtrIyxnZD7q%2F5Vi3nPtbjxYK4bh85clVDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;378&quot; height=&quot;243&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;243&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;드디어 WEVOTE에도 MSW가 정상 동작한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;+추가&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;webpack-dev-server ^3.x.x 에서는 configuration의 derServer 옵션 속성이 아래와 같았는데,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;145&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDxnMQ/btrJgwOnyrg/KHGzHMLIriJdTZd3Tfi0K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDxnMQ/btrJgwOnyrg/KHGzHMLIriJdTZd3Tfi0K1/img.png&quot; data-alt=&quot;webpack-dev-server v3 의 derServer 옵션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDxnMQ/btrJgwOnyrg/KHGzHMLIriJdTZd3Tfi0K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDxnMQ%2FbtrJgwOnyrg%2FKHGzHMLIriJdTZd3Tfi0K1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;247&quot; height=&quot;145&quot; data-origin-width=&quot;247&quot; data-origin-height=&quot;145&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;webpack-dev-server v3 의 derServer 옵션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;v4 이상부터는 devserver configuration 옵션의 속성이 다음과 같이 바뀌었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSSek2/btrJgxfs9h8/nEIyTG0kgJjtV5KJd2ynI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSSek2/btrJgxfs9h8/nEIyTG0kgJjtV5KJd2ynI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSSek2/btrJgxfs9h8/nEIyTG0kgJjtV5KJd2ynI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSSek2%2FbtrJgxfs9h8%2FnEIyTG0kgJjtV5KJd2ynI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;262&quot; data-origin-width=&quot;456&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://webpack.kr/configuration/dev-server#root&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[참고. devServer 옵션]&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1659948876729&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;DevServer | 웹팩&quot; data-og-description=&quot;웹팩은 모듈 번들러입니다. 주요 목적은 브라우저에서 사용할 수 있도록 JavaScript 파일을 번들로 묶는 것이지만, 리소스나 애셋을 변환하고 번들링 또는 패키징할 수도 있습니다.&quot; data-og-host=&quot;webpack.kr&quot; data-og-source-url=&quot;https://webpack.kr/configuration/dev-server#root&quot; data-og-url=&quot;https://webpack.kr/configuration/dev-server#root&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/P10ah/hyPmip4ZEj/DIDMUvme9HKjAJQc2mtMVK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://webpack.kr/configuration/dev-server#root&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://webpack.kr/configuration/dev-server#root&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/P10ah/hyPmip4ZEj/DIDMUvme9HKjAJQc2mtMVK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;DevServer | 웹팩&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;웹팩은 모듈 번들러입니다. 주요 목적은 브라우저에서 사용할 수 있도록 JavaScript 파일을 번들로 묶는 것이지만, 리소스나 애셋을 변환하고 번들링 또는 패키징할 수도 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;webpack.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;요약&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. msw를 사용하려면, msw github의 버전별 issue나 현재 프로젝트에 포함된 webpack 관련 의존성도 고려한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 공식문서에서 제공하는 msw 실행 환경과 자신의 프로젝트 개발 환경의 차이점을 명확히 인지하고 적용한다.(특히 webpack)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Issue</category>
      <category>MSW</category>
      <category>React</category>
      <category>Webpack</category>
      <category>webpack-dev-server</category>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/39</guid>
      <comments>https://despiter.tistory.com/39#entry39comment</comments>
      <pubDate>Fri, 15 Jul 2022 16:19:14 +0900</pubDate>
    </item>
    <item>
      <title>[프로젝트 회고] WEVOTE - 온라인 선거 관리 서비스</title>
      <link>https://despiter.tistory.com/37</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;본 포스팅은 2021년 상반기에 진행한 협업 프로젝트 [WEVOTE]에 대한 회고 기록입니다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[이전 프로젝트]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://despiter.tistory.com/27&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.11.14 - [Development/Front-End] - [온라인 선거 홍보 서비스] 프로젝트 회고 - UNIVVOTE편&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1656916616678&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[온라인 선거 홍보 서비스] 프로젝트 회고 - UNIVVOTE편&quot; data-og-description=&quot;본 포스팅은 2020년 하반기에 진행한 개인 프로젝트 [UNIVVOTE]에 대한 회고 기록입니다. 프로젝트 명 : UNIVVOTE 프로젝트 기간 : 2020.10 ~ 2020.11 (2개월) 프로젝트 설명 : OO대학교 온라인 선거 홍보 서비&quot; data-og-host=&quot;despiter.tistory.com&quot; data-og-source-url=&quot;https://despiter.tistory.com/27&quot; data-og-url=&quot;https://despiter.tistory.com/27&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/beoNI5/hyOZNCyZk4/uMyT51vkkrnoNBYWMmeAl1/img.jpg?width=800&amp;amp;height=1197&amp;amp;face=237_338_564_393,https://scrap.kakaocdn.net/dn/VHtlY/hyOYoLaQZi/Pz3HyWYqIOKKhOMgKrqbF0/img.jpg?width=800&amp;amp;height=1197&amp;amp;face=237_338_564_393,https://scrap.kakaocdn.net/dn/hopdv/hyOZH3pYOD/ba6gv5An3yrmz4mR9yFfoK/img.jpg?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080&quot;&gt;&lt;a href=&quot;https://despiter.tistory.com/27&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://despiter.tistory.com/27&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/beoNI5/hyOZNCyZk4/uMyT51vkkrnoNBYWMmeAl1/img.jpg?width=800&amp;amp;height=1197&amp;amp;face=237_338_564_393,https://scrap.kakaocdn.net/dn/VHtlY/hyOYoLaQZi/Pz3HyWYqIOKKhOMgKrqbF0/img.jpg?width=800&amp;amp;height=1197&amp;amp;face=237_338_564_393,https://scrap.kakaocdn.net/dn/hopdv/hyOZH3pYOD/ba6gv5An3yrmz4mR9yFfoK/img.jpg?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[온라인 선거 홍보 서비스] 프로젝트 회고 - UNIVVOTE편&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;본 포스팅은 2020년 하반기에 진행한 개인 프로젝트 [UNIVVOTE]에 대한 회고 기록입니다. 프로젝트 명 : UNIVVOTE 프로젝트 기간 : 2020.10 ~ 2020.11 (2개월) 프로젝트 설명 : OO대학교 온라인 선거 홍보 서비&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;despiter.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;[프로젝트 명] WEVOTE&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;[프로젝트 기간] 2021.02 ~ 2021.08 (6개월) 후 리팩토링 진행 중&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;[프로젝트 설명] 대학 온라인 선거 관리 서비스&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 선거 프로젝트 v2&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; UNIVVOTE 프로젝트의 피드백을 반영하여 더 발전된 제품을 만들기 위한 &lt;span&gt;WEVOTE&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;프로젝트는 사실상 '대학 선거'라는 주제만 같고 모든 것을 새롭게 기획 및 디자인했다. 누군가는 그냥 새로운 프로젝트로 볼 수 있지만, 나는 v2로 대규모 업데이트 했다고 표현하고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 백엔드 개발자나 웹 디자이너와 접할 수 있는 배경이 없어서 PurpleCode라는 개발 동아리에 1기로 들어갔다. 사실&amp;nbsp; 나는 WEVOTE 프로젝트의 팀원을 모집할 수 있으면 좋겠다는 생각이었는데, React를 처음 시도하고 배우는 과정에서 당시 유행했던 성격 테스트 프로젝트를 먼저 진행하게 됐고, 이후 새로운 프로젝트를 시작하면서 팀원들에게 WEVOTE 프로젝트를 설득해서 본격적으로 시작하게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QS49q/btrGpCTdX1C/zgNZVRwsMWtt6eoK5qSMs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QS49q/btrGpCTdX1C/zgNZVRwsMWtt6eoK5qSMs0/img.png&quot; style=&quot;width: 49.9369%; margin-right: 10px;&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;672&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;50.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QS49q/btrGpCTdX1C/zgNZVRwsMWtt6eoK5qSMs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQS49q%2FbtrGpCTdX1C%2FzgNZVRwsMWtt6eoK5qSMs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;806&quot; height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnTGP4/btrGpDxLUSg/60KgcyJx22pBkmW0a0vfV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnTGP4/btrGpDxLUSg/60KgcyJx22pBkmW0a0vfV0/img.png&quot; style=&quot;width: 48.9003%;&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;659&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;49.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnTGP4/btrGpDxLUSg/60KgcyJx22pBkmW0a0vfV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnTGP4%2FbtrGpDxLUSg%2F60KgcyJx22pBkmW0a0vfV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;774&quot; height=&quot;659&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;UNIVVOTE -&amp;gt; WEVOTE 아키텍처 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. UNIVVOTE 프로젝트 사용자 피드백 반영&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 배포된 UNIVVOTE MVP를 유권자인 학생들과 선거관리위원장들에게 제공하고 받은 피드백은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) 기획&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- (선관위) 선관위에서는 &lt;b&gt;'선거 예상 득표율' 및 '공약별 좋아요'&lt;/b&gt;는 투표 결과에 영향을 줄 수 있다고 판단하여 &lt;b&gt;비선호&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- (선관위) 개발자의 직접적 정보 수정보다 관리자 권한을 선관위에게 주어 &lt;b&gt;선관위가 정보를 관리&lt;/b&gt;할 수 있기를 희망함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- (유저) 선거 종류가 '중앙자치기구', '단과대학' 을 넘어서 &lt;b&gt;전체 학과도 추가&lt;/b&gt;되면 서비스가 더 완성도 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- (유저) 선거 날짜, 공지사항 등 &lt;b&gt;관련 정보가 더 많이&lt;/b&gt; 포함되면 좋겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diQGIL/btrGt3ItDNh/bEUoUKnpcYJmaMCk9zizz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diQGIL/btrGt3ItDNh/bEUoUKnpcYJmaMCk9zizz1/img.png&quot; data-alt=&quot;투표 정보에 집중된 UNIVVOTE&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diQGIL/btrGt3ItDNh/bEUoUKnpcYJmaMCk9zizz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiQGIL%2FbtrGt3ItDNh%2FbEUoUKnpcYJmaMCk9zizz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;664&quot; height=&quot;292&quot; data-origin-width=&quot;1205&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;투표 정보에 집중된 UNIVVOTE&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HoZy0/btrGqJEv7Vp/8z9FaKzfegygrVzxX7PR61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HoZy0/btrGqJEv7Vp/8z9FaKzfegygrVzxX7PR61/img.png&quot; width=&quot;427&quot; height=&quot;507&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;790&quot; data-filename=&quot;메인페이지.png&quot; data-is-animation=&quot;false&quot; style=&quot;width: 36.846%; margin-right: 10px;&quot; data-widthpercent=&quot;37.28&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HoZy0/btrGqJEv7Vp/8z9FaKzfegygrVzxX7PR61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHoZy0%2FbtrGqJEv7Vp%2F8z9FaKzfegygrVzxX7PR61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doUvep/btrJpMRG5c3/TQ89lKnKxjx36gvF2G9yIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doUvep/btrJpMRG5c3/TQ89lKnKxjx36gvF2G9yIk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;541&quot; data-origin-height=&quot;382&quot; data-filename=&quot;그림4.png&quot; style=&quot;width: 61.9912%;&quot; data-widthpercent=&quot;62.72&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doUvep/btrJpMRG5c3/TQ89lKnKxjx36gvF2G9yIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoUvep%2FbtrJpMRG5c3%2FTQ89lKnKxjx36gvF2G9yIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;541&quot; height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;선거와 관련된 다양한 정보 및 관리자 기능을 제공하는 WEVOTE&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) 기능&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- (유저) 선거 단위별 공약 UI 내 선거 단위 변경 시, 기존 좌우 버튼 클릭 방식에서 터치 스와이프 기능 추가 요청&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byKgiX/btrGupEwFAZ/U5qyF4nmbe4e2s0JjswjHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byKgiX/btrGupEwFAZ/U5qyF4nmbe4e2s0JjswjHK/img.png&quot; data-alt=&quot;좌우 버튼 이동 방식 -&amp;amp;gt; 터치 스와이프 방식 (모바일)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byKgiX/btrGupEwFAZ/U5qyF4nmbe4e2s0JjswjHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyKgiX%2FbtrGupEwFAZ%2FU5qyF4nmbe4e2s0JjswjHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;485&quot; height=&quot;420&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;579&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;좌우 버튼 이동 방식 -&amp;gt; 터치 스와이프 방식 (모바일)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 프로젝트 회고&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 프로젝트가 어떤 기능을 포함하고 있고, 어떻게 구현했는지는 README와 [Refactor] 블로깅을 통해 작성하면 되기 때문에 회고에는 어울리지 않은 것 같아 &lt;a title=&quot;개발자의 회고 문화 바로가기&quot; href=&quot;https://www.inflearn.com/pages/weekly-inflearn-41-20220215&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;KPT&lt;/a&gt;를 중심의 회고를 진행해보려한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;KPT(Keep, Problem, Try)&lt;br /&gt;- Keep : 프로젝트에 만족했고, 앞으로의 업무에서 지속하고 싶은 부분&lt;br /&gt;- Problem : 프로젝트에서 부정적인 요소로 작용했거나 아쉬웠던 점&lt;br /&gt;- Try : Problem에 대한 해결 방식으로 다음 프로젝트에서 시도해볼 점&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt; &lt;b&gt; Keep!&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) 기획&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;#사용자_피드백_반영&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 제품을 출시하고 사용자가 발생하면, 긍정적일 것으로 예상했던 반응보다는 꽤 까다로운 사용자 반응이 나왔다. 물론, 일부분 니즈를 충족시켜주어 '좋은 프로젝트다!' 라는 반응도 있었지만, 떨떠름한 반응도 있었기에 제품을 빠르게 출시하고 피드백을 통해 빠르게 보완하는 개발 프로세스가 결국 더 빠르게 더 나은 제품을 나오게 할 것이라는 깨달음을 직접 경험할 수 있었다. 만약 내가 진짜 비즈니스를 위한 제품을 개발하여 출시한다면, 최대한 빠르게 MVP를 만들어 제공하고 보완하는 식으로 개발을 진행할 것이다.&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;&lt;i&gt; 물론, 서비스가 커질수록 그런 방식이 힘들겠지만&lt;/i&gt;&lt;/s&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) 협업&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#Github_기반_Issue_및_PR_관리&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 개발 팀에서 Jira와 같은 도구를 사용하며 스프린트를 관리한다. 개발 업무와 상태를 가시적으로 나타내고 배포를 관리하기 위한 협업 도구인 것이다. Jira에서 가장 기본이 되는 개념이 Issue인데, 이는 팀이 식별하고 있는 작업 항목의 단위이고, 하나의 업무 당 하나의 이슈가 배정된다고 생각하면 된다. 유료라 Jira를 사용하지는 않았지만, Github의 issues를 통해 개발해야할 기능이나 버그 발생 등 모든 업무를 이슈로 생성하므로써 작업을 잘 분리하여 진행할 수 있었다. 또한 다른 팀원들에게도 누가 어떤 업무를 하고 있는지 명확하게 보이기 때문에 협업 도구로써 여전히 훌륭하다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#Notion_기반_회의록_및_정보_공유&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매주 줌으로 온라인 회의를 진행하며 주간 개발 진행 상황이나 발생한 이슈들, 개발 일정에 대한 이야기를 Notion에 기록하여 모든 팀원이 편하게 열람하고 재확인할 수 있도록 했다. 또한 개발 과정에서 알게된 지식들을 공유하며 팀원과의 공동 성장을 유도했다. 어디서 어떤 일을 하든 메모하는 습관은 필수라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#Figma를_통한_디자인_공유&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자이너와 협업하며 Figma라는 디자인 툴을 처음 경험했다. 디자이너가 디자인한 UI의 css 코드를 바로 확인할 수 있어서 UI 개발에 빠르게 적용할 수 있었고, 단순히 이미지만 받는 것보다 개발자가 상세하게 확인할 수 있는 정보가 많아서 유용하다고 생각한다. 디자인에 대한 정보 전달도 가능해서 디자이너와 소통하기 편리했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qFg6d/btrGEwRKCe5/X8VHHaABqkKrsqO1KaH9XK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qFg6d/btrGEwRKCe5/X8VHHaABqkKrsqO1KaH9XK/img.png&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;883&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.6462%; margin-right: 10px;&quot; data-widthpercent=&quot;45.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qFg6d/btrGEwRKCe5/X8VHHaABqkKrsqO1KaH9XK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqFg6d%2FbtrGEwRKCe5%2FX8VHHaABqkKrsqO1KaH9XK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1232&quot; height=&quot;883&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d8advz/btrGE2P6t7T/BkPHuGaKapm5Ervz2WHaVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d8advz/btrGE2P6t7T/BkPHuGaKapm5Ervz2WHaVk/img.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;757&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;54.83&quot; style=&quot;width: 54.191%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d8advz/btrGE2P6t7T/BkPHuGaKapm5Ervz2WHaVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd8advz%2FbtrGE2P6t7T%2FBkPHuGaKapm5Ervz2WHaVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1282&quot; height=&quot;757&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;Notion과 Figma를 통한 정보 공유&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3) Code&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#Typescript_도입&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;javascript로 개발하고 typescript로 마이그레이션했다. 프로젝트가 커지면서 코드 자동완성 기능의 필요성을 느꼈고, type checking을 하지 않으니 소프트웨어가 커질수록 불안정해진다는 것을 체감했다. 아직 모든 곳에 적절한 type을 사용할 정도는 아니지만, 섬세한 타입 정의를 하도록 최대한 노력하는 중이다. 이전에는 '굳이 써야하나'였다면, 지금은 '굳이 안 쓸 이유가 없다'라는 입장이다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;&lt;i&gt;개인적으로는 많은 다양한 프로그래밍 언어들이 뭔가 비슷한 방향으로 발전하는 것 같다는 생각이다. 언젠간 모든 언어를 통합시킬 최적의 언어가 등장할지도..&lt;/i&gt;&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#CRA를_사용하지_않고_React_&lt;span&gt;프로젝트_&lt;span&gt;개발_환경_&lt;/span&gt;&lt;/span&gt;구성&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 개발 환경을 구성할 때, CRA를 사용하지 않았다. 해당 프로젝트에 필요한 의존성만 추가하며 관리하고자 했고, 개발자는 프로젝트의 개발 환경을 직접 컨트롤할 수 있어야한다고 생각했기 때문이다. CRA는 숨겨져있는 의존성들이 많고, 실제로 사용하지 않는 패키지가 꽤 있을 수 있다. 프로젝트가 커지면 쓸데없는 모듈도 줄일 필요가 있다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;&lt;i&gt;비효율적 의존성 검색, 의존성 설치 등 &lt;/i&gt;&lt;/s&gt;&lt;/span&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;(추가. 최근 webpack 설정을 수정하며 CRA가 상당히 잘 짜여진 구성이라는 것을 느끼고 있다.)&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Problem&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) 기획&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#부족했던_사용자_Needs_파악&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;- 프로젝트를 발전시키면서 사용자를 &quot;일반 학생&quot;과 &quot;선관위&quot; 두 분류로 나누었기 때문에 각각의 니즈를 파악하는 것 중요한데, 프로젝트 초기에만 사용자와의 교류가 있었고 과정에서는 개발에만 집중했다. UNIVVOTE와 대비하여 어떤 부분들이 보완이 되고 왜 그렇게 했는지 다시 선관위에게 PR하는 과정에서 다시금 생각의 차이를 느낄 수 있었다. 그리고 하필 기존의 투표 업체가 운영을 중단하여 투표 기능이 탑재된 서비스를 새로 계약하게 될 것이라는 건 생각도 못 했었다..&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) 협업&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#No_코드_리뷰&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 협업 프로젝트를 통해 코드 리뷰 등을 경험하며 상호 발전하는 경험을 얻기 원했지만, FE의 거의 모든 PR과 이슈를 홀로 담당하며 프론트엔드 개발자 간의 기술적 교류를 하지 못한 점이 참 아쉽다. 격렬한 코드 리뷰를 통해 성장하고 싶은 이 욕망을 얼른 해소하고 싶다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3) Code&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#어설픈_mocking&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 백엔드와 필요한 데이터 및 API에 대해 회의를 하고, API가 개발되는 시간 동안 어느 정도 윤곽이 드러난 API 명세에 맞춰서 mock data를 json으로 만들어 UI를 작성했다. 당시에는 불편하더라도 이렇게 쓰다가 API가 나오면 되겠거니 했지만, 프론트엔드 개발의 생산성 측면에서 꽤 불편한 경험이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#성급한_MUI_Library_도입&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 프로젝트 일정이 예상보다 길어져 관리자 페이지는 &lt;a title=&quot;MUI&quot; href=&quot;https://mui.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Material-ui&lt;/a&gt;를 사용하여 빠르고 간단하게 작업하고자 했다. [관리자 페이지] - [유저 페이지]로 크게 구분되기 때문에 관리자 페이지에서만 material-ui를 사용하면 구분이 잘 될 것이라고 판단했지만, 실제로 도입해보니 프로젝트 내에 스타일 작성법이 나뉘게 되면서 코드가 복잡해지는 문제가 두드러졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#프론트엔드_상태_관리&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Context API, Redux, MobX, Recoil 등 프론트엔드 상태 관리 방법과 관련한 여러 라이브러리나 방법들이 존재한다. 부족한 개발 실력으로 방향을 못 잡고 있는 FE 팀원에게 로그인/회원가입 페이지를 맡기며, React 책의 예제를 보면서 학습이라는 생각으로 개발해보시라 권했고, Redux를 적용한 예제로 작성을 해주셨다. 당시 프론트엔드는 상태 관리가 필요한 부분이 단순한 편이라 Context API로도 충분히 작업할 수 있을 것으로 판단했고, 전역 상태관리가 필요한 상태에 대해서만 Context API를 적용해놓은 상태였다. 현재는 redux를 사용 중이지만, 중간에 상태 관리가 혼합된 적이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#Test의_부재&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 테스트는 기본이 되어버린 현 시점에서 테스트코드가 없는 프로젝트는 개발자의 부족한 실력을 의미하는 것 같아 부끄럽다. 하루 빨리 테스트 작성 범위에 방법에 대한 고찰을 통해 프로젝트마다 프론트엔드에서도 TDD를 진행하는 것이 마땅할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4) 일정 관리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#알_수_없는_자신의_개발_일정&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 개발 일정 관리가 가장 어려웠다. 대부분 학생이거나 인턴과 병행하고 있던 팀원들이라 토요일마다 주간 스크럼을 진행하고 Notion을 통해 일정 관리를 했는데, 다들 개발해야 할 기능들이 처음인 경우가 많아 계획된 일정이 지나도록 구현을 못 하는 상황이 자주 발생했다. 심지어 회사가 아니다보니 개개인마다 프로젝트를 대하는 마음가짐도 달랐고, 능력도 달랐다. FE 개발을 담당하면서 전체 프로젝트를 리드하는 입장에서는 그저 팀원들이 선언한 예상 일정을 기다리는 것 밖에 할 수 없었고, 각자의 예상보다 프로젝트가 길어졌다는 점에서 팀원들의 열정과 진행력이 크게 줄어들었다고 생각한다. 물론, 현재 개발 진행 상황과 해결 중인 이슈에 대해 실시간으로 공유할 수 있게 칸반보드를 생성해 놨지만...실시간으로 공유되진 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt; &lt;b&gt;Try&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1) 기획&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#빠른_업데이트_및_피드백_체크&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 결과주의적 관점에서, 사용자가 만족한 기획이 좋은 기획이다. 만족도를 체크하려면 빠르게 제공하고 피드백을 반영해서 수정하여 업데이트 하는 것이 효율적이다. WEVOTE 프로젝트에서는 MVP 개발에 시간이 꽤 걸렸지만, 중간중간 개발된 부분이라도 선관위 및 학생들과 소통하며 개발하면 더 나은 기획이 될 수 있을 것이라 예상한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) 협업&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#코드_리뷰&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 나와 상대방 코드에 대해 이야기하려면, 일단 자신의 코드에 대한 논리가 있어야하고, 무엇이 더 나은 코드인지 판단하는 기준이 있어야한다. 종종 초보 개발자들은 단순히 나와 다른 코드라서 &quot;아 저 사람은 저렇게 짰구나. 알겠습니다!&quot; 하는 식으로 리뷰가 진행되는 경우도 있는데, 스스로 더 나은 코드에 대한 기준을 가지고 상대방이 왜 저렇게 짰는지, 내가 생각하는 더 나은 방식이 있으면 제안을 할 수도 있어야한다. 그 때, 진정으로 코드의 퀄리티를 논하는 코드 리뷰가 된다고 생각한다. 프로젝트가 끝난 뒤 컨퍼런스나 다른 코드들을 많이 보고 성장하면서, 지난 코드의 문제점들과 어떻게 수정해야할 지 방향이 보이기 시작했다. 다음 프로젝트에서는 나의 기준으로 코드를 판단해서 소통하며 리뷰를 충분히 진행할 수 있다고 생각한다.&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3) Code&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#msw를_이용한_mocking&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; msw library를 사용하여 &lt;a href=&quot;https://despiter.tistory.com/35&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;mocking 방식을 개선&lt;/a&gt;할 수 있다. API 개발과 동시에 진행되는 상황이라면, 작성된 명세를 기반으로 msw를 활용해보는 것이 직접 json을 불러오는 것보다 훨씬 효율적이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#CSS는_직접_작성하자&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; material-ui와 같은 UI Framework는 제한된 상황에서 빠르게 UI를 개발할 수 있다는 장점이 있지만, 커스텀이 불편하다는 단점이 있다. 무엇보다도 새로운 개발자가 팀원으로 들어온다면, 해당 framework 사용법을 익혀야한다는 단점이 있다. 프론트엔드 개발자라면 css 작성법에 익숙하기 때문에 특별한 이유가 없다면 직접 스타일을 작성하는 것이 오히려 개발 속도가 빠를 수 있다. React에서는 emotion이나 styled-components 같은 css-in-js library를 사용하거나, css 전처리기를 통해 작성하는 것이 통상적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#프론트엔드_상태_관리&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 오버엔지니어링을 하지말자. 복잡한 상태 관리가 필요한 상황이 아니었기에 Context API를 사용하면서 충분히 상태의 흐름을 파악할 수 있었다. 다만, 많은 스타트업이나 기업들이 Redux, Mobx와 같은 상태 관리 라이브러리를 사용하고, 때로 그런 경험들을 우대 사항으로 요구하기 때문에 작은 프로젝트에서도 라이브러리를 사용하는 케이스가 많은 것 같다. 물론, 프로젝트가 끝나고 학습을 위해 리팩토링을 하는 시점에서 나 또한 Redux를 적용해 볼 것이지만, 여전히 오버엔지니어링이 아닌가 싶다. 물론, 상태 관리 라이브러리들도 갈수록 단순해지고 있고, saga와 같은 미들웨어를 사용할 수 있다는 것은 장점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;#TDD_도입&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 테스트는 기본이다. 프로젝트를 진행할 당시에는 프론트엔드에서의 TDD에 의구심이 많았다. 하지만, 매번 런타임에서 직접 UI를 테스트하고 있는 스스로를 보면서 테스트 도입의 중요성을 느끼게 되었고, 리팩토링 하는 과정에서도 기능의 변화가 없는지 체크하기 위해서는 테스트 코드가 기반으로 존재해야 했다. 처음이 어렵지 이후엔 습관이 되기 때문에 빠르게 학습해서 단위 테스트로 시작해 범위를 넓혀가며 적용할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4) 일정 관리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;# 스크럼_주기_조절&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 스크럼의 주기는 팀과 팀원의 성향에 따라 다르게 적용해야한다고 생각한다. 때로 너무 짧은 주기의 스크럼은 개발할 시간을 빼앗길 수 있지만, 팀원 개인의 일정 관리가 잘 안 되는 상황에서는 주기를 짧게 설정하여 자주 체크를 하는 것이 문제 해결에 도움이 될 것 같다. 그리고 무엇보다 일정에 문제가 발생하면 팀 리더에게 빠르게 상황을 공유하는 것이 중요하다. 혼자 끙끙대다가 마감일에 닥쳐서 얘기하면 수습하기만 더 힘들어질 뿐이다. 개발자는 많은 일들을 스스로 판단해야겠지만, 주변 동료들에게 의견을 묻는 것도 조금 더 나은 의사결정을 위한 방법이 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 여러 프로젝트를 진행하는 것도 좋지만, 하나의 프로젝트를 더 나은 코드로 발전시키는 경험이 더 현실적으로 성장할 수 있는 것 같다. 어제 작성한 코드를 오늘 본다고 크게 개선점이 보이지 않는다. 많이 보고 배우면서, 다시 돌아왔을 때 비로소 문제가 보이기 마련이기에 프로젝트가 종료된 지 꽤 오랜 시간이 지났지만, 그간 성장한 새로운 관점으로 리팩토링을 진행하는 중이다. 나는 왜 이따위로 개발했을까 싶지만, 그만큼 지금은 성장했다는 의미라고.. 긍정 회로를 돌려본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;참고&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/pages/weekly-inflearn-41-20220215&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[주간 인프런, 개발자의 회고 문화]&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1656923058358&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;[주간 인프런 #41] 개발자의 공유 문화 이모저모 (2) 회고 문화 - 인프런 | 스토리&quot; data-og-description=&quot;기록도 점검도 셀프!&amp;nbsp;개발자는 왜 회고를 할까요? #오픈소스&amp;nbsp;#기술블로그&amp;nbsp;#회고문화 바쁘게 일하고 공부하다 보면 시간이 훌쩍 지나있기 마련이죠.&amp;nbsp;그렇지만 모든 일을 다 기억할 수는 없는 &quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/pages/weekly-inflearn-41-20220215&quot; data-og-url=&quot;https://www.inflearn.com/pages/weekly-inflearn-41-20220215&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1inP5/hyOZApKv9M/jLXxJYB8NH8KvznSQolDeK/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/eLhhG/hyOZElnXIK/kEW0s3MYXqJpruwdshwBK0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/4yVVS/hyOYpDmgWA/OsQamGZ67DVxq0HpzQbYl1/img.png?width=1144&amp;amp;height=936&amp;amp;face=0_0_1144_936&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/pages/weekly-inflearn-41-20220215&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/pages/weekly-inflearn-41-20220215&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1inP5/hyOZApKv9M/jLXxJYB8NH8KvznSQolDeK/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/eLhhG/hyOZElnXIK/kEW0s3MYXqJpruwdshwBK0/img.png?width=1200&amp;amp;height=628&amp;amp;face=0_0_1200_628,https://scrap.kakaocdn.net/dn/4yVVS/hyOYpDmgWA/OsQamGZ67DVxq0HpzQbYl1/img.png?width=1144&amp;amp;height=936&amp;amp;face=0_0_1144_936');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[주간 인프런 #41] 개발자의 공유 문화 이모저모 (2) 회고 문화 - 인프런 | 스토리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;기록도 점검도 셀프!&amp;nbsp;개발자는 왜 회고를 할까요? #오픈소스&amp;nbsp;#기술블로그&amp;nbsp;#회고문화 바쁘게 일하고 공부하다 보면 시간이 훌쩍 지나있기 마련이죠.&amp;nbsp;그렇지만 모든 일을 다 기억할 수는 없는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Front-End</category>
      <category>개발자</category>
      <category>회고</category>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/37</guid>
      <comments>https://despiter.tistory.com/37#entry37comment</comments>
      <pubDate>Mon, 27 Jun 2022 16:57:32 +0900</pubDate>
    </item>
    <item>
      <title>[Refactor] webpack resolve.alias 절대경로 설정</title>
      <link>https://despiter.tistory.com/36</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;본 포스팅은 webpack(v5.6.0)을 사용합니다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1. 상대경로의 불편함&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;WEVOTE 프로젝트는 상대경로를 통해 모듈을 import하여 사용하는 방식이다. 현재 컴포넌트를 기준으로 사용할 모듈의 위치를 '../../components/header'와 같은 방식으로 찾아 입력해야 했는데, 이러한 상대경로는 해당 모듈의 위치가 바뀌었을 때, 그 모듈을 상대경로로 import한 모든 컴포넌트에서 경로를 수정하여 저장해줘야하는 불상사가 발생한다. 따라서, 유지보수성에 좋지 않다는 단점이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1660048498558&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Calendar.tsx 컴포넌트

// 상대경로
import initialCalendar from '../../../../assets/img/calendar.svg';
    
function Calendar(){
  // 내용
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. webpack alias 설정으로 절대경로 사용하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;webpack configuration을 통해 사용하고자하는 특정 경로에 별명(alias)을 설정하면, 해당 별명으로 어디서든 접근할 수 있게 한다. webpack.config.ts 내 resolve.alias 설정으로 절대경로를 정의한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;320&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcRIpp/btrJlpJhJ0D/PFGvtNRvnvrfum0ZuTqFq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcRIpp/btrJlpJhJ0D/PFGvtNRvnvrfum0ZuTqFq1/img.png&quot; data-alt=&quot;webpack.config.ts&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcRIpp/btrJlpJhJ0D/PFGvtNRvnvrfum0ZuTqFq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcRIpp%2FbtrJlpJhJ0D%2FPFGvtNRvnvrfum0ZuTqFq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;320&quot; data-origin-width=&quot;547&quot; data-origin-height=&quot;320&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;webpack.config.ts&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 typescript를 사용하는 프로젝트라면, typescript의 compilerOptions에서도 path 설정을 해주어야 정상적으로 동작한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZX7k5/btrJikPsIcM/vGwv4m7OvmcZHfkXhBkga0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZX7k5/btrJikPsIcM/vGwv4m7OvmcZHfkXhBkga0/img.png&quot; data-alt=&quot;tsconfig.json의 compilerOptions&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZX7k5/btrJikPsIcM/vGwv4m7OvmcZHfkXhBkga0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZX7k5%2FbtrJikPsIcM%2FvGwv4m7OvmcZHfkXhBkga0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;306&quot; data-origin-width=&quot;535&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tsconfig.json의 compilerOptions&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://it-eldorado.tistory.com/124&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[함께 읽으면 좋을 내용]&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660049438112&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[TypeScript] 모듈 해석 (Module Resolution)&quot; data-og-description=&quot;Documentation - Module Resolution How TypeScript resolves modules in JavaScript www.typescriptlang.org 모듈 해석(Module Resolution)이란 컴파일러가 각 import가 어떤 모듈을 가리키는지 해석하는 과정을..&quot; data-og-host=&quot;it-eldorado.tistory.com&quot; data-og-source-url=&quot;https://it-eldorado.tistory.com/124&quot; data-og-url=&quot;https://it-eldorado.tistory.com/124&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/djPkF2/hyPokN78oj/zwRkPkjXP1dwdGYEk9rT6k/img.jpg?width=677&amp;amp;height=452&amp;amp;face=0_0_677_452,https://scrap.kakaocdn.net/dn/ecryo0/hyPotYDmhq/eN98XHLmJmSgZuMPIkeiQ1/img.jpg?width=677&amp;amp;height=452&amp;amp;face=0_0_677_452,https://scrap.kakaocdn.net/dn/Q0Gsu/hyPoq8EGMw/7vKFmerXyEOwWBjvqykgNK/img.jpg?width=1921&amp;amp;height=1721&amp;amp;face=0_0_1921_1721&quot;&gt;&lt;a href=&quot;https://it-eldorado.tistory.com/124&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://it-eldorado.tistory.com/124&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/djPkF2/hyPokN78oj/zwRkPkjXP1dwdGYEk9rT6k/img.jpg?width=677&amp;amp;height=452&amp;amp;face=0_0_677_452,https://scrap.kakaocdn.net/dn/ecryo0/hyPotYDmhq/eN98XHLmJmSgZuMPIkeiQ1/img.jpg?width=677&amp;amp;height=452&amp;amp;face=0_0_677_452,https://scrap.kakaocdn.net/dn/Q0Gsu/hyPoq8EGMw/7vKFmerXyEOwWBjvqykgNK/img.jpg?width=1921&amp;amp;height=1721&amp;amp;face=0_0_1921_1721');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[TypeScript] 모듈 해석 (Module Resolution)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Documentation - Module Resolution How TypeScript resolves modules in JavaScript www.typescriptlang.org 모듈 해석(Module Resolution)이란 컴파일러가 각 import가 어떤 모듈을 가리키는지 해석하는 과정을..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;it-eldorado.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 절대 경로 사용&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;절대 경로를 사용하면 다음과 같이 디렉토리 계층에 상관없이 간략하게 경로를 설정할 수 있다. 흔하지 않겠지만, 절대경로 자체가 자주 바뀌게 된다면 해당 경로를 사용하는 모든 파일의 경로를 다시 수정해야 하는 상황도 있을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660050066712&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Calendar.tsx 컴포넌트

// 상대경로
import initialCalendar from '../../../../assets/img/calendar.svg';

// 절대경로
import initialCalendar from '@img/calendar.svg';
    
function Calendar(){
  // 내용
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고자료&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://jonghyun-park.medium.com/webpack-typescript%EC%97%90%EC%84%9C-path-alias-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-ec32428622d2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[webpack alias 설정하기]&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660050374064&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Webpack, Typescript에서 Path Alias 설정하기&quot; data-og-description=&quot;입사 이후 첫 프로젝트에서 신규 홈페이지를 만드는 업무를 하게 되었다.&quot; data-og-host=&quot;jonghyun-park.medium.com&quot; data-og-source-url=&quot;https://jonghyun-park.medium.com/webpack-typescript%EC%97%90%EC%84%9C-path-alias-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-ec32428622d2&quot; data-og-url=&quot;https://jonghyun-park.medium.com/webpack-typescript%EC%97%90%EC%84%9C-path-alias-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-ec32428622d2&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://jonghyun-park.medium.com/webpack-typescript%EC%97%90%EC%84%9C-path-alias-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-ec32428622d2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jonghyun-park.medium.com/webpack-typescript%EC%97%90%EC%84%9C-path-alias-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0-ec32428622d2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Webpack, Typescript에서 Path Alias 설정하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;입사 이후 첫 프로젝트에서 신규 홈페이지를 만드는 업무를 하게 되었다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jonghyun-park.medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Refactoring</category>
      <category>Alias</category>
      <category>절대경로</category>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/36</guid>
      <comments>https://despiter.tistory.com/36#entry36comment</comments>
      <pubDate>Fri, 24 Jun 2022 21:47:50 +0900</pubDate>
    </item>
    <item>
      <title>[Refactor] WEVOTE - 데이터 Mocking (with MSW)</title>
      <link>https://despiter.tistory.com/35</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;본 포스팅은 msw(v0.42.3)을 사용합니다&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. 구동중인 서버가 없는데, API 없이 프론트엔드 리팩토링을 어떻게 하지?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이전 프로젝트를 혼자 리팩토링 하기로 결정하고 코드를 다시 확인하는데, 서버가 닫혀있으니 당연하게도 API 통신이 되지 않았다. 프로젝트 진행 당시에는 백엔드 개발자와 첫 협업이라 전체적인 업무 프로세스에 대한 이해가 부족했기 때문에 처음 나온 계획은 다음과 같았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1) 전체 팀원들과 회의를 통해 기능 기획 및 API 명세를 정함&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2) 디자이너 : 기능을 디자인해서 FE에 전달&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; &amp;nbsp; 백엔드 : API 명세로 API 개발하여 FE에 전달&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; &amp;nbsp; 프론트엔드 : 디자인과 API를 통해 개발&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3) 완성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문제는 위의 경우 업무의 흐름이 &lt;u&gt;동기적&lt;/u&gt;이기 때문에 상당히 비효율적이라는 것이다. 실제 업무에서는 디자이너, 백엔드 개발자가 일할 동안 프론트엔드는 놀고 있을 수 없지 않은가. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(물론, API 없이 개발할 수 있는 영역에 한해서는 프론트엔드도 작업할 수 있겠지만)&lt;/span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 당시 나는 API 명세를 보고 json 파일을 만들어 dummy data를 만들고 원하는 곳에서 import하여 데이터를 사용할 수 있도록 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS5AY4/btrJnb5AJxh/klOF5Mzbgo2xXZpqtK6Jhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS5AY4/btrJnb5AJxh/klOF5Mzbgo2xXZpqtK6Jhk/img.png&quot; data-alt=&quot;그렇게 모아진 dummy data&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS5AY4/btrJnb5AJxh/klOF5Mzbgo2xXZpqtK6Jhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS5AY4%2FbtrJnb5AJxh%2FklOF5Mzbgo2xXZpqtK6Jhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;223&quot; height=&quot;251&quot; data-origin-width=&quot;223&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그렇게 모아진 dummy data&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;당시에는 '어떻게 일할 것인지' 고민하는 것보다 '어떻게 구현을 해야할 것'인지에만 신경이 집중됐을 때라 불편함이 있음에도 그냥 진행했지만, 이 방식은 dummy data를 사용할 때와 실제 API를 사용할 때 코드를 직접 수정해야한다는 큰 비효율의 문제를 가지고 있다. 그리고 데이터만 사용할 수 있지 HTTP 메서드와 응답 상태에 따른 loading, error 처리 등 실제 개발에서 신경써야할 다른 부분에는 대응하기가 어렵다는 단점도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660121169235&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Notice 컴포넌트 예시


// dummy data 사용을 위해 json 파일 import
import data from '@dummyData/NoticeData.json';


function Notice(props){
	// 실제 API를 통해 불러오는 data
	const {loading, data, error} = useFetch('/api/v1/main/notice');
    
    
    // 위의 data 변수 중 하나는 주석처리하고 사용해야한다
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;별도의 dummy data 파일이 아닌, API처럼 사용하는 mocking system은 없을까?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;결국 백엔드에서 API가 완성되지 않아도 프론트엔드에서 API가 있는 것처럼 원활하게 개발할 수 있는 환경이 주어진다면 더 좋은 효율을 이끌어낼 수 있을 것이다. 그리고, dummy data와 같이 별도의 파일을 불러오는 방식이 아닌 API 호출과 동일하게 data를 사용할 수 있는 방식이면 더 좋을 것이다. 이를 가능하게 하는 것이 &lt;a href=&quot;https://mswjs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;msw&lt;/a&gt;를 활용한 mocking system이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1660120538749&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Mock Service Worker&quot; data-og-description=&quot;Seamless API mocking library for browser and Node.&quot; data-og-host=&quot;mswjs.io&quot; data-og-source-url=&quot;https://mswjs.io/&quot; data-og-url=&quot;https://mswjs.io/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vwlXV/hyPpxFUdVL/5xS88kg6HyKWkU5vqSIqlk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/7PNhR/hyPpIm7So5/38Lhb1JboReUsGAn57wOoK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/PcDx1/hyPoqn4KOp/aB1fKuljAYkjrJoO1YcqEK/img.png?width=460&amp;amp;height=460&amp;amp;face=118_126_292_316&quot;&gt;&lt;a href=&quot;https://mswjs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mswjs.io/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vwlXV/hyPpxFUdVL/5xS88kg6HyKWkU5vqSIqlk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/7PNhR/hyPpIm7So5/38Lhb1JboReUsGAn57wOoK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/PcDx1/hyPoqn4KOp/aB1fKuljAYkjrJoO1YcqEK/img.png?width=460&amp;amp;height=460&amp;amp;face=118_126_292_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Mock Service Worker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Seamless API mocking library for browser and Node.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mswjs.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;MSW(Mock Service Worker)는 무엇인가?&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;간단하게 말하자면, &lt;u&gt;&lt;b&gt;HTTP 요청을 가로채서 mock data를 대신 전송해주는 mocking library&lt;/b&gt;&lt;/u&gt;다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;프론트엔드 개발 단계에서 필요로하는 것은 http request, http response, http 메서드, http 상태코드 등 네트워크 통신과 결과물이다. msw가 서비스 워커를 통해 네트워크 요청을 가로채서 미리 정의한 메서드나 상태코드에 따라 reponse하는 방식으로 동작하기 때문에 개발자가 작성할 코드는 실제 API 요청과 동일하게 작성할 수 있는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;Service Worker란 무엇인가?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GNucz/btrJrflg0j5/sJu8G8qkfEX3wdqD3E2LXk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GNucz/btrJrflg0j5/sJu8G8qkfEX3wdqD3E2LXk/img.jpg&quot; data-alt=&quot;msw 공식문서 발췌&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GNucz/btrJrflg0j5/sJu8G8qkfEX3wdqD3E2LXk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGNucz%2FbtrJrflg0j5%2FsJu8G8qkfEX3wdqD3E2LXk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;441&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;msw 공식문서 발췌&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;서비스 워커는 웹 애플리케이션의 메인 스레드와 분리된 별도의 백그라운드 스레드에서 실행시킬 수 있는 기술로, 스레드가 다르기 때문에 서비스 워커가 실행되더라도 애플리케이션의 블록킹 발생 없이 동작할 수 있다. 웹 서비스와 브라우저 및 네트워크 사이에서 프록시 서버의 역할을 하며, 오프라인에서도 서비스를 사용할 수 있도록 한다. 서비스워커는 프론트엔드 개발자들이 수년간 어려움을 겪었던 문제들을 해결했는데, 웹 사이트 자원 캐싱이나 알림 처리 등 많을 것을 할 수 있게 되었다. 서비스 워커는 매우 강력하기 때문에 안전한 컨텍스트(HTTPS)에서만 실행된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;msw는 어떻게 사용하는가?&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a title=&quot;msw&quot; href=&quot;https://mswjs.io/docs/getting-started/integrate/browser&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서 example&lt;/a&gt;이 친절한 편이라 사용 방법은 가이드만 따라도 이해할 수 있는 수준이다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1) mockServiceWorker.js를 public에 설치한다. (주의. index.html을 통해 실행되는 파일이 아니다. 서비스워커다.)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2) 개발환경에서만 사용할 것이기 때문에 development 환경에서만 실행되도록 entry파일에서 작성한다.&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;(msw가 정상 동작한다면, &lt;span style=&quot;color: #ef5369;&quot;&gt;[MSW] Mocking enabled&lt;/span&gt;라는 메시지가 브라우저 콘솔에 뜬다)&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3) mocks/browser를 통해 작성한 mock handler의 worker를 실행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;4) 각 컴포넌트에서 명세에 맞는 API 요청을 사용하며 테스트한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biSa91/btrJrx0qjs7/lqpDHI8LvXMtCAzySUDka0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biSa91/btrJrx0qjs7/lqpDHI8LvXMtCAzySUDka0/img.png&quot; data-alt=&quot;msw가 development 환경에서만, 먼저 실행되도록 index.js에서 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biSa91/btrJrx0qjs7/lqpDHI8LvXMtCAzySUDka0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiSa91%2FbtrJrx0qjs7%2FlqpDHI8LvXMtCAzySUDka0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;539&quot; height=&quot;247&quot; data-origin-width=&quot;539&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;msw가 development 환경에서만, 먼저 실행되도록 index.js에서 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;나는 express로 API 개발을 해본 경험이 있어서 크게 낯설지는 않았지만, 자세한 내용은 msw 공식문서의 가이드를 따라 직접 사용해보면 쉽게 이해할 수 있을 것이다. 아래의 코드는 공식 문서에서 소개하는 예시인데, HTTP 메서드를 구분하여 작성할 수 있고, HTTP status 코드에 따른 대응이나 스토리지를 사용할 수도 있다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;(프론트엔드도 분명 서버 개발 경험이 있으면 좋다.)&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660122921260&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/mocks/handlers.js
import { rest } from 'msw'
export const handlers = [
  rest.post('/login', (req, res, ctx) =&amp;gt; {
    // Persist user's authentication in the session
    sessionStorage.setItem('is-authenticated', 'true')
    return res(
      // Respond with a 200 status code
      ctx.status(200),
    )
  }),
  rest.get('/user', (req, res, ctx) =&amp;gt; {
    // Check if the user is authenticated in this session
    const isAuthenticated = sessionStorage.getItem('is-authenticated')
    if (!isAuthenticated) {
      // If not authenticated, respond with a 403 error
      return res(
        ctx.status(403),
        ctx.json({
          errorMessage: 'Not authorized',
        }),
      )
    }
    // If authenticated, return a mocked user details
    return res(
      ctx.status(200),
      ctx.json({
        username: 'admin',
      }),
    )
  }),
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;+추가&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;msw 공식문서에서는 React 프로젝트 예시에서 CRA를 사용한 코드를 제공했는데, 나처럼 CRA를 사용하지 않고 직접 webpack을 설정하며 프로젝트를 구성한 사람들을 위해 내가 경험했던 이슈 경험을 공유한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://despiter.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.07.15 - [Development/Issue] - [Issue] msw + webpack-dev-server 동작 이슈&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660123709279&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Issue] msw + webpack-dev-server 동작 이슈&quot; data-og-description=&quot;msw 관련 에러로 고생하고 있는 누군가를 위한 빠른 결론 1. 안정된 msw 버전 사용하기 2. webpack.devserver 정적 리소스 path 체크 WEVOTE 프로젝트 리팩토링 중 mocking 시스템을 도입하기 위해 msw를 적용하&quot; data-og-host=&quot;despiter.tistory.com&quot; data-og-source-url=&quot;https://despiter.tistory.com/39&quot; data-og-url=&quot;https://despiter.tistory.com/39&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5UJwx/hyPpykw0jh/7M2yQGvzkUme1C1xktMUwk/img.png?width=736&amp;amp;height=281&amp;amp;face=0_0_736_281,https://scrap.kakaocdn.net/dn/sBlwe/hyPod3oYzg/613DRCOVYLAb0S3bRaNKjK/img.png?width=736&amp;amp;height=281&amp;amp;face=0_0_736_281,https://scrap.kakaocdn.net/dn/cAYVTV/hyPooX8iDX/XHwwd8Z74D38glmFqBnCn0/img.png?width=695&amp;amp;height=744&amp;amp;face=0_0_695_744&quot;&gt;&lt;a href=&quot;https://despiter.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://despiter.tistory.com/39&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5UJwx/hyPpykw0jh/7M2yQGvzkUme1C1xktMUwk/img.png?width=736&amp;amp;height=281&amp;amp;face=0_0_736_281,https://scrap.kakaocdn.net/dn/sBlwe/hyPod3oYzg/613DRCOVYLAb0S3bRaNKjK/img.png?width=736&amp;amp;height=281&amp;amp;face=0_0_736_281,https://scrap.kakaocdn.net/dn/cAYVTV/hyPooX8iDX/XHwwd8Z74D38glmFqBnCn0/img.png?width=695&amp;amp;height=744&amp;amp;face=0_0_695_744');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Issue] msw + webpack-dev-server 동작 이슈&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;msw 관련 에러로 고생하고 있는 누군가를 위한 빠른 결론 1. 안정된 msw 버전 사용하기 2. webpack.devserver 정적 리소스 path 체크 WEVOTE 프로젝트 리팩토링 중 mocking 시스템을 도입하기 위해 msw를 적용하&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;despiter.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; 리팩토링을 진행하기에 앞서 mocking 시스템을 바꿨는데, msw를 통해 한결 수월해졌다. 유지보수성에 한 몫을 하는 것은 수정이 덜 발생하게 혹은 수정이 쉽도록 코드를 작성하는 것이 아닐까 생각이 든다. 그리고 msw를 통해 service worker을 알게 되었는데, 꽤 활용 가능성이 많은 기술인 것 같아 앞으로 유심히 살펴봐야겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; 몇 년 전까지 프론트엔드 개발자의 영역은 단순 퍼블리싱에 가까웠다고 한다. 하지만, 기술이 발전하며 서비스가 고도화되고, 더 많은 사용자와 더 좋은 UX를 위해 프론트엔드 개발자의 역할도 중요해지고 더 많아진 것으로 보인다. 더 나은 퍼포먼스를 위해 SPA가 등장하고, 다시 더 나은 퍼포먼스를 위해 SSR 기반의 Next.js 같은 기술들이 등장하며 프론트엔드에서 사용하는 기술도 갈수록 전문적이 되어가고 있다. msw나 graphql처럼 API를 다루는 영역에도 조금씩 관여하고 있는 프론트엔드는 앞으로 더 다양해질지 모른다는 생각이 든다. 그렇기 때문에 프론트엔드 개발자는 지금의 나의 역할을 프레임화하여 생각하기 보다는 소프트웨어 전체적으로 어떤 문제들을 어떻게 해결할 것인가에 집중하는 자세가 필요하지 않을까. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;(업무 흐름의 문제를 해결하기 위한 msw 처럼..)&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참고자료&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://tech.kakao.com/2021/09/29/mocking-fe/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://tech.kakao.com/2021/09/29/mocking-fe/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660051705763&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Mocking으로 생산성까지 챙기는 FE 개발&quot; data-og-description=&quot;안녕하세요, 카카오엔터프라이즈 검색플랫폼프론트파트의 Lawrence.net입니다. 프론트엔드 개발 업무의 효율성을 높이기 위한 방법의 하나로 고민해 본 Mocking에 대해 설명하고 이를 적용했던 사례&quot; data-og-host=&quot;tech.kakao.com&quot; data-og-source-url=&quot;https://tech.kakao.com/2021/09/29/mocking-fe/&quot; data-og-url=&quot;https://tech.kakao.com/2021/09/29/mocking-fe/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zsuHJ/hyPogdWcmQ/pYCGSr2jrTbZhNXh7JZ2aK/img.png?width=2486&amp;amp;height=1460&amp;amp;face=0_0_2486_1460,https://scrap.kakaocdn.net/dn/cfxM5p/hyPorzKMSu/HQtIIwTmbRpcdZljxYyWQk/img.png?width=2486&amp;amp;height=1460&amp;amp;face=0_0_2486_1460,https://scrap.kakaocdn.net/dn/pIaWk/hyPopaR5iH/C00h4ZWDVaJgSRewtutjU0/img.png?width=2486&amp;amp;height=1460&amp;amp;face=0_0_2486_1460&quot;&gt;&lt;a href=&quot;https://tech.kakao.com/2021/09/29/mocking-fe/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tech.kakao.com/2021/09/29/mocking-fe/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zsuHJ/hyPogdWcmQ/pYCGSr2jrTbZhNXh7JZ2aK/img.png?width=2486&amp;amp;height=1460&amp;amp;face=0_0_2486_1460,https://scrap.kakaocdn.net/dn/cfxM5p/hyPorzKMSu/HQtIIwTmbRpcdZljxYyWQk/img.png?width=2486&amp;amp;height=1460&amp;amp;face=0_0_2486_1460,https://scrap.kakaocdn.net/dn/pIaWk/hyPopaR5iH/C00h4ZWDVaJgSRewtutjU0/img.png?width=2486&amp;amp;height=1460&amp;amp;face=0_0_2486_1460');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Mocking으로 생산성까지 챙기는 FE 개발&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요, 카카오엔터프라이즈 검색플랫폼프론트파트의 Lawrence.net입니다. 프론트엔드 개발 업무의 효율성을 높이기 위한 방법의 하나로 고민해 본 Mocking에 대해 설명하고 이를 적용했던 사례&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tech.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://so-so.dev/web/service-worker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://so-so.dev/web/service-worker/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1660122578934&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;ServiceWorker 이모저모 이야기&quot; data-og-description=&quot;ServiceWorker는 웹 서비스에서도 백그라운드 동기화, 푸시 알림 등이 가능하도록 지원해주는 도구입니다. 이 글에서는 서비스워커에 대한 소개와 간단하게 CRA기반에서 어떻게 cache&amp;hellip;&quot; data-og-host=&quot;so-so.dev&quot; data-og-source-url=&quot;https://so-so.dev/web/service-worker/&quot; data-og-url=&quot;https://so-so.dev/web/service-worker/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/csUuOV/hyPpMC4HEU/pwk7k0HiLv7C9bqqmaDkfk/img.png?width=800&amp;amp;height=471&amp;amp;face=0_0_800_471,https://scrap.kakaocdn.net/dn/XoxQ0/hyPoglu0rv/7tk7YwL9eVN92LTzKlyQG1/img.png?width=800&amp;amp;height=471&amp;amp;face=0_0_800_471,https://scrap.kakaocdn.net/dn/X93JX/hyPpBIj284/wEn3lJMYHGG8T5L7ngomRK/img.png?width=1200&amp;amp;height=746&amp;amp;face=0_0_1200_746&quot;&gt;&lt;a href=&quot;https://so-so.dev/web/service-worker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://so-so.dev/web/service-worker/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/csUuOV/hyPpMC4HEU/pwk7k0HiLv7C9bqqmaDkfk/img.png?width=800&amp;amp;height=471&amp;amp;face=0_0_800_471,https://scrap.kakaocdn.net/dn/XoxQ0/hyPoglu0rv/7tk7YwL9eVN92LTzKlyQG1/img.png?width=800&amp;amp;height=471&amp;amp;face=0_0_800_471,https://scrap.kakaocdn.net/dn/X93JX/hyPpBIj284/wEn3lJMYHGG8T5L7ngomRK/img.png?width=1200&amp;amp;height=746&amp;amp;face=0_0_1200_746');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;ServiceWorker 이모저모 이야기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ServiceWorker는 웹 서비스에서도 백그라운드 동기화, 푸시 알림 등이 가능하도록 지원해주는 도구입니다. 이 글에서는 서비스워커에 대한 소개와 간단하게 CRA기반에서 어떻게 cache&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;so-so.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Refactoring</category>
      <category>mock</category>
      <category>MSW</category>
      <category>service worker</category>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/35</guid>
      <comments>https://despiter.tistory.com/35#entry35comment</comments>
      <pubDate>Fri, 24 Jun 2022 21:47:33 +0900</pubDate>
    </item>
    <item>
      <title>[Javascript] 재귀 함수의 스택 오버플로우 문제</title>
      <link>https://despiter.tistory.com/34</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;n!을 구하기 위한 factorial(n) 함수를 재귀 함수로 구현한 상황에서 n 값이 매우 클 때 발생할 수 있는 문제는?&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Factorial 함수&lt;/span&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1655211256220&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function factorial(n) { 
    if (n === 1) { 
        return 1; 
    } 
    return n * factorial(n-1);
}


factorial(4);  // 4*3*2*1 =&amp;gt; 24&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;[문제 1] 숫자 값이 너무 크면 표현할 수 있는 int의 범위를 넘어설 수 있다.&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;20! = 20&amp;times;19&amp;times;18&amp;times;...&amp;times;2&amp;times;1 = 2,432,902,008,176,640,000&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;factorial(n)의 n이 20이 되어도 엄청 큰 수가 나온다.&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;Number 타입은&amp;nbsp;&lt;/span&gt;배정밀도 64비트 이진 형식 IEEE 754 값&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;(-(2^53 &amp;minus; 1)부터 2^53 &amp;minus; 1까지의 수)이다. Number 타입은 부동소수점 숫자 외에도&amp;nbsp;&lt;/span&gt;+Infinity&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;,&amp;nbsp;&lt;/span&gt;-Infinity&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;,&amp;nbsp;&lt;/span&gt;NaN&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;(&quot;&lt;/span&gt;&lt;b&gt;N&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;ot a&amp;nbsp;&lt;/span&gt;&lt;b&gt;N&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1b1b1b;&quot;&gt;umber&quot;) 세 개의 상징적인 값을 가진다. &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Data_structures#number_%ED%83%80%EC%9E%85&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;(참고)&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;ECMAScript 2015부터는 Number.isSafeInteger()와 Number.MAX_SAFE_INTEGER 및 Number.MIN_SAFE_INTEGER를 사용하여 숫자가 배정밀도 부동 소수점 숫자 범위 안에 있는지 확인할 수도 있다. 이 범위를 넘어서면 JavaScript의 정수는 더 이상 안전하지 않으며, 그 값의 배정밀도 부동 소수점 근삿값이 된다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;i&gt;즉, Number 타입의 범위를 넘어서면 정확한 값을 구할 수 없다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;[해결 1] BigInt 타입을 사용하여 Number 범위 문제를 해결한다.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;BigInt는 정수 끝에 n을 추가하거나 생성자를 호출해 생성할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Number 타입의 안전 한계는 Number.MAX_SAFE_INTEGER로 알아볼 수 있는데, BigInt의 도입 이후로는 이 한계를 넘는 숫자에 대해 계산이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655211012344&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;gt; const x = 2n ** 53n;
9007199254740992n
&amp;gt; const y = x + 1n;
9007199254740993n&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;[문제 2] 재귀 함수의 반복 호출로 &lt;span style=&quot;color: #ee2323;&quot;&gt;스택 오버플로우&lt;/span&gt;가 발생할 수 있다.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;함수가 실행되면, 개별적인 함수 실행 컨텍스트를 콜 스택에 쌓아 차례대로 실행되는데,&amp;nbsp; 함수가 호출되면 함수의 지역변수, 매개변수, 리턴 후 돌아갈 위치, 리턴 값 등을 스택에 저장하기 때문에 너무 많은 재귀 함수의 호출은 스택 오버플로우를 발생시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;call stack.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I3msU/btrEObn4nCI/J6s4RLVsq08qDPfH0RkF10/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I3msU/btrEObn4nCI/J6s4RLVsq08qDPfH0RkF10/img.jpg&quot; data-alt=&quot;팩토리얼 재귀 함수의 콜스택 쌓이는 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I3msU/btrEObn4nCI/J6s4RLVsq08qDPfH0RkF10/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI3msU%2FbtrEObn4nCI%2FJ6s4RLVsq08qDPfH0RkF10%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;2250&quot; data-filename=&quot;call stack.jpg&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;팩토리얼 재귀 함수의 콜스택 쌓이는 과정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;[해결 2] 꼬리 재귀(tail call recursion) 기법 활용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;재귀 함수의 문제는 자기 자신인 함수를 호출한 뒤 그 결과를 기다리면서 생기는 콜스택 메모리 낭비였는데, 꼬리 재귀라는 개념을 이용하면 재귀 호출이 끝나는 시점에 바로 결과를 반환하도록 하여 오버 플로우를 방지할 수 있다. 다만, 꼬리 재귀 기법은 언어 수준에서 꼬리 재귀 최적화를 지원해야한다는 특징이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;꼬리 재귀 최적화 (TCO : Tail Call Optimization)&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;꼬리 재귀 최적화는 컴파일러 or 인터프리터 수준에서 지원해야한다. 즉, 기존의 방식과는 다르게 꼬리 재귀인 상황에서는 최적화하여 동작하도록 만들어 놓은 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VIoI7/btrEU1LJFMi/9ThytNcCb3TukZPnrlrCL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VIoI7/btrEU1LJFMi/9ThytNcCb3TukZPnrlrCL1/img.png&quot; data-alt=&quot;call stack에 쌓이는 일반 재귀 함수의 메모리 사용&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VIoI7/btrEU1LJFMi/9ThytNcCb3TukZPnrlrCL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVIoI7%2FbtrEU1LJFMi%2F9ThytNcCb3TukZPnrlrCL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;344&quot; height=&quot;286&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;call stack에 쌓이는 일반 재귀 함수의 메모리 사용&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;일반 재귀는 함수가 호출될 때마다 return address, arguments, local variables 등의 메모리를 할당하여 stack에 쌓인다. 꼬리 재귀 최적화는 return 부분에서 연산 없이 재귀로 호출된다면 위의 메모리를 추가로 생성하는 것이 아닌, 기존의 메모리를 사용하여 반환함으로써 메모리 낭비를 줄이는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;factorial 예제에서 꼬리 재귀의 핵심은 반환부에 n * factorial(n-1) 같은 연산이 없어야 한다는 것이다. factorial 예제를 꼬리 재귀로 표현하면 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655299571353&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 일반 재귀
function factorial(n){
  return n &amp;lt;= 1 ? 1 : n*factorial(n-1);
}

// 꼬리 재귀
// JS 스펙 상, 삼항 연산자는 스택의 메모리를 잡아 먹지 않는 연산자다.
function factorial(n){
  const cal = (n, result) =&amp;gt; {
    return n &amp;lt;= 1 ? result : cal(n-1, n*result);
  }
  return cal(n, 1);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;result가 1부터 시작하여 재귀를 통해 첫 번째 인수인 n을 곱하여 cal이 호출될수록 점점 커지게 된다.&amp;nbsp;마지막 재귀 함수에서 cal함수의 n이 1이 되었을 때, 누적 계산된 result를 반환하고, 그 반환된 result를 다시 반환하여 최종 factorial 함수가 원하는 값을 반환하는 방식이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;javascript는 ES6부터 지원을 했지만, 아직 이를 브라우저에 적용하고 있는 js 엔진은 Safari의 Webkit 뿐인 것으로 알려져 있다.&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp; 본문에서는 재귀 함수에 대한 문제를 다루었다. 같은 요구사항도 여러 방식으로 구현될 수 있는데, 굳이 재귀를 사용해야하는 조건의 개발 환경은 주어지지 않을 것이다. 유지보수성보다 우선되어야할 것이 에러 가능성을 줄이는 것이므로, &lt;/span&gt;콜스택 오버플로우가 발생할 가능성이 있을 정도의 요구사항이라면, 반복문을 통해 구현하는 것이 조금 더 안전한 개발이라고 생각한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Development/Javascript</category>
      <category>javascript</category>
      <category>꼬리재귀</category>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/34</guid>
      <comments>https://despiter.tistory.com/34#entry34comment</comments>
      <pubDate>Tue, 14 Jun 2022 22:35:27 +0900</pubDate>
    </item>
    <item>
      <title>[Algorithm] Job Scheduling - Javascript</title>
      <link>https://despiter.tistory.com/33</link>
      <description>&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Leet Code&quot; href=&quot;https://leetcode.com/problems/maximum-profit-in-job-scheduling/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[LeetCode 1235번]&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1655121201186&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Maximum Profit in Job Scheduling - LeetCode&quot; data-og-description=&quot;Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.&quot; data-og-host=&quot;leetcode.com&quot; data-og-source-url=&quot;https://leetcode.com/problems/maximum-profit-in-job-scheduling/&quot; data-og-url=&quot;https://leetcode.com/problems/maximum-profit-in-job-scheduling/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cF0cxr/hyOLf0EJ3N/vofH3zKPD2prq8arkJlfKK/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-profit-in-job-scheduling/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leetcode.com/problems/maximum-profit-in-job-scheduling/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cF0cxr/hyOLf0EJ3N/vofH3zKPD2prq8arkJlfKK/img.png?width=500&amp;amp;height=260&amp;amp;face=0_0_500_260');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Maximum Profit in Job Scheduling - LeetCode&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leetcode.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 문제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;startTime과 endTime이 정해져 있는 작업 데이터로, 최대 profit을 만들 수 있는 작업의 조합을 찾는 문제이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2xqk1/btrEGKdZ8zN/0KjMAHE9gCAWRqnUeqBckK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2xqk1/btrEGKdZ8zN/0KjMAHE9gCAWRqnUeqBckK/img.png&quot; data-alt=&quot;LeetCode 1235번 문제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2xqk1/btrEGKdZ8zN/0KjMAHE9gCAWRqnUeqBckK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2xqk1%2FbtrEGKdZ8zN%2F0KjMAHE9gCAWRqnUeqBckK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;279&quot; data-origin-width=&quot;707&quot; data-origin-height=&quot;279&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LeetCode 1235번 문제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3AKTt/btrEF9rz6iM/4fAQ7KlR8YL7a6vs6XOQbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3AKTt/btrEF9rz6iM/4fAQ7KlR8YL7a6vs6XOQbK/img.png&quot; data-alt=&quot;LeetCode 1235번 Example 2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3AKTt/btrEF9rz6iM/4fAQ7KlR8YL7a6vs6XOQbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3AKTt%2FbtrEF9rz6iM%2F4fAQ7KlR8YL7a6vs6XOQbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;702&quot; height=&quot;345&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LeetCode 1235번 Example 2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Job Scheduling은 DFS를 통해 완전 탐색으로 풀 수는 있겠지만, 문제의 조건에 따라 오버플로우가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는&lt;b&gt; DP&lt;/b&gt;와 &lt;b&gt;이진 탐색&lt;/b&gt;을 활용하여 O(N log N)으로 해결할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[문제 해결의 핵심 포인트]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; DP는 더 큰 값을 계속해서 누적해가기 위함이다. 이 때, jobs 배열은 다음 작업이 현재 작업보다 이후에 시작되는 것을 보장해야하기 때문에 startTime 순으로 정렬해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp는 index가 큰 값부터 작은 값으로 내려가면서 누적한다. (반대로도 가능은 함)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;dp[i] = Math.max(dp[i+1], jobs[i].profit + dp[next])&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next는 현재 작업 다음에 연결되어 작업 가능한 job의 index를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 현재 작업의 endTime보다 next 작업의 startTIme이 작거나 같아야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 그림에서 index가 2 인 작업을 선택한다면, index가 3, 4인 작업들은 수행할 수 없으므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dp[2] = Math.max(dp[3], 100+dp[5])가 되고 (dp[5]는 마지막 0 값임)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 index 2 작업과 index 3 + index 4 작업 중 더 큰 profit 값을 dp[2]에 기록하는 것을 의미한다. (문제 해결의 본질)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 최대를 누적하며 index 0의 값을 반환하면 원하는 최대 profit을 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드7.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ndBbb/btrEE09rH3h/8j1eOLVdQKYkbYRScPtEv1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ndBbb/btrEE09rH3h/8j1eOLVdQKYkbYRScPtEv1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ndBbb/btrEE09rH3h/8j1eOLVdQKYkbYRScPtEv1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FndBbb%2FbtrEE09rH3h%2F8j1eOLVdQKYkbYRScPtEv1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;2250&quot; data-filename=&quot;슬라이드7.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드8.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IHGwe/btrEGKd14vw/kUYVYbAk50cPTkXhSP5gwk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IHGwe/btrEGKd14vw/kUYVYbAk50cPTkXhSP5gwk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IHGwe/btrEGKd14vw/kUYVYbAk50cPTkXhSP5gwk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIHGwe%2FbtrEGKd14vw%2FkUYVYbAk50cPTkXhSP5gwk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;2250&quot; data-filename=&quot;슬라이드8.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드9.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nm4W3/btrEIr5Av2x/KvHhLKza4HQRF2OdgLTTw0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nm4W3/btrEIr5Av2x/KvHhLKza4HQRF2OdgLTTw0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nm4W3/btrEIr5Av2x/KvHhLKza4HQRF2OdgLTTw0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnm4W3%2FbtrEIr5Av2x%2FKvHhLKza4HQRF2OdgLTTw0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;2250&quot; data-filename=&quot;슬라이드9.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드10.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgqGTC/btrEHBVru3m/WWIXkSFNzvItKziOBMd410/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgqGTC/btrEHBVru3m/WWIXkSFNzvItKziOBMd410/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgqGTC/btrEHBVru3m/WWIXkSFNzvItKziOBMd410/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgqGTC%2FbtrEHBVru3m%2FWWIXkSFNzvItKziOBMd410%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;2250&quot; data-filename=&quot;슬라이드10.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드11.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtxaJh/btrEHDlo4iW/d1Dj8JQo2Z30wGDKNt5xOK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtxaJh/btrEHDlo4iW/d1Dj8JQo2Z30wGDKNt5xOK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtxaJh/btrEHDlo4iW/d1Dj8JQo2Z30wGDKNt5xOK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtxaJh%2FbtrEHDlo4iW%2Fd1Dj8JQo2Z30wGDKNt5xOK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;2250&quot; data-filename=&quot;슬라이드11.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;슬라이드12.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjn0Dw/btrEE1gdnbM/l1AdPFNP9v0kdy6xCqSbn1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjn0Dw/btrEE1gdnbM/l1AdPFNP9v0kdy6xCqSbn1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjn0Dw/btrEE1gdnbM/l1AdPFNP9v0kdy6xCqSbn1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcjn0Dw%2FbtrEE1gdnbM%2Fl1AdPFNP9v0kdy6xCqSbn1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;4000&quot; height=&quot;2250&quot; data-filename=&quot;슬라이드12.JPG&quot; data-origin-width=&quot;4000&quot; data-origin-height=&quot;2250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Javascript 코드&lt;/h2&gt;
&lt;pre id=&quot;code_1655118795345&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} startTime
 * @param {number[]} endTime
 * @param {number[]} profit
 * @return {number}
 */
var jobScheduling = function(startTime, endTime, profit) {
    const jobs = profit.map((profitValue,i)=&amp;gt;({start:startTime[i], end:endTime[i], profit:profitValue}));
    const dp = new Array(jobs.length+1).fill(0); 
    
    const searchNextJob = (idx) =&amp;gt; {
        let low = idx+1, high = jobs.length, mid;
        while(low &amp;lt; high) {
            mid = Math.floor((low + high)/2);
            if(jobs[mid].start &amp;gt;= jobs[idx].end) high = mid;
            else low = mid + 1;
        }
        return low;
    }
    
    jobs.sort((a,b)=&amp;gt;a.start-b.start);
    
    for(let i=jobs.length-1; i&amp;gt;=0; i--){
        const next = searchNextJob(i);      
        dp[i] = Math.max(dp[i+1], jobs[i].profit+dp[next]);
    }
    
    return dp[0];
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/알고리즘</category>
      <category>1235번</category>
      <category>javascript</category>
      <category>Leet Code</category>
      <author>highcastlee</author>
      <guid isPermaLink="true">https://despiter.tistory.com/33</guid>
      <comments>https://despiter.tistory.com/33#entry33comment</comments>
      <pubDate>Mon, 13 Jun 2022 20:55:17 +0900</pubDate>
    </item>
  </channel>
</rss>