<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>coding- w00se</title>
    <link>https://coding-w00se.tistory.com/</link>
    <description>공부한 내용을 정리하는 블로그입니다.


</description>
    <language>ko</language>
    <pubDate>Sat, 27 Jun 2026 14:25:21 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>w00se</managingEditor>
    <item>
      <title>[우아한테크코스] 페이먼츠 2단계 회고</title>
      <link>https://coding-w00se.tistory.com/119</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;제어 컴포넌트와 비제어 컴포넌트&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Keep&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 제어 컴포넌트와 비제어 컴포넌트의 차이를 알게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 페이먼츠 1단계에서는 제어 컴포넌트만 사용했지만 2단계에서는 용도에 맞게 제어/비제어 컴포넌트를 적용했다.&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;Problem - Try&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. DOM을 적절하게 활용하지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 비제어 컴포넌트로 Form을 구현할 때 input 태그에 있는 value에 접근하기 위해서 ref를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ref를 이용하는 방법이 잘못된 방법이라 생각하진 않는다. 하지만 ref 이외의 DOM에 접근하는 방법을 몰랐기 때문에 아쉬운 점으로 적었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Form 태그의 onSubmit 이벤트 발생 시 전달받은 event.target 객체와 태그의 id를 같이 활용하면 ref를 사용하지 않고 input 태그의 value에 접근할 수 있다.&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;예시 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1652421995865&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const handleSubmitForm = event =&amp;gt; {
    event.preventDefault();

    const inputValue = event.target['input-test'].value;
    
    console.log(inputValue);
  };

  return (
    &amp;lt;&amp;gt;
    	...
      &amp;lt;Form onSubmit={handleSubmitForm}&amp;gt;
        &amp;lt;Input id=&quot;input-test&quot; /&amp;gt;
        ...
      &amp;lt;/Form&amp;gt;
    &amp;lt;/&amp;gt;
  );&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이뿐만 아니라 pattern 속성처럼 HTML 태그에 유용한 기능들이 있다. 따라서 MDN 공식 문서의 input 태그 속성들과 다른 예시 코드를 보면서 HTML 속성을 잘 활용하는 법에 대해서 공부하려 한다.&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;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Keep&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 컴포넌트의 수가 증가되고 컴포넌트 디렉터리가 복잡해졌다. 불편함을 느끼고 컴포넌트 디렉터리 관리 방법에 대해서 찾아봤고 그중 atomic 디자인을 공부하고 비슷하게 적용했다.&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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Problem - Try&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Atomic 디자인을 적용하려 했지만 현실과 타협을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Atomic 디자인 패턴은 최소 단위인 Atom 컴포넌트를 활용해서 Molecules, Organism 계층의 컴포넌트를 만들어가는 패턴이다. 하지만 미션에서는 Atom부터 다시 구현해서 적용하지 않고, 계층 별로 Atom -&amp;gt; Molecules -&amp;gt; Organism 순으로 &lt;span&gt;유연하게&lt;span&gt; &lt;/span&gt;&lt;/span&gt;분리만 진행했다. 따라서 'Atomic 디자인을 적용시켰다!'의 느낌보다는 '컴포넌트를 Atom, Molecules, Organism이라는 디렉터리에 분리해서 관리했다.'의 느낌에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Atomic 디자인도 좋은 패턴이지만, 현재는 '컴포넌트 디렉터리를 분리하는 기준'이 필요하다. 따라서 Atomic 디자인을 더 연습하기보다는 컴포넌트 디렉터리를 분리하는 기준에 대해 찾아보고 스스로에 맞는 방법을 선택하려 한다. 현재로써는 '이 컴포넌트가 공유되는가?'를 기준을 사용하고 있다.&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>Retrospect</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/119</guid>
      <comments>https://coding-w00se.tistory.com/119#entry119comment</comments>
      <pubDate>Fri, 13 May 2022 15:55:05 +0900</pubDate>
    </item>
    <item>
      <title>[React] 리액트 파일 확장자를 jsx로 하는 이유</title>
      <link>https://coding-w00se.tistory.com/117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 파일은 통상적으로 .js 대신 .jsx 확장자를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 컨벤션은 JSX가 표준 Javascript가 아니기 때문에 발생했다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 파일 내부에 표준 Javascript 문법만 사용됐다면 파일 확장자로 .js가 적절합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 jsx나 Typescript처럼 파일 내부에 표준 Javascript 이외에 다른 코드가 있다면 해당 파일의 확장자는 .js가 아닌 다른 확장자로 지정한다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 jsx가 사용된 파일 확장자는 jsx로, typescript가 작성된 파일 확장자는 .ts로 지정합니다.&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;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;a href=&quot;https://stackoverflow.com/a/46169521&quot;&gt;https://stackoverflow.com/a/46169521&lt;/a&gt;&lt;/p&gt;</description>
      <category>Frontend/React</category>
      <category>jsx</category>
      <category>react</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/117</guid>
      <comments>https://coding-w00se.tistory.com/117#entry117comment</comments>
      <pubDate>Wed, 4 May 2022 10:32:14 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크코스] 페이먼츠 1단계 회고</title>
      <link>https://coding-w00se.tistory.com/116</link>
      <description>&lt;h2&gt;컴포넌트 작성 및 분리&lt;/h2&gt;
&lt;h3&gt;Kepp&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이번 미션의 목표는 CDD(Component-Driven Developmenet)에 따라 UI를 구성하고 재사용하는 것이다.&lt;/li&gt;
&lt;li&gt;미션의 목표에 맞게 작은 컴포넌트부터 개발했다. 그리고 컴포넌트의 재사용을 고민하면서 개발했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Problem - Try&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;컴포넌트를 작은 단위로 나누어서 개발했지만 조립해서 잘하지 못했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;아래는 이번 미션의 입력 폼 부분에 대한 예시 이미지이다.&lt;br&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qb9cb/btrAQiYCoYh/CJaSvwBPmMkQtyClbKScYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qb9cb/btrAQiYCoYh/CJaSvwBPmMkQtyClbKScYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qb9cb/btrAQiYCoYh/CJaSvwBPmMkQtyClbKScYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqb9cb%2FbtrAQiYCoYh%2FCJaSvwBPmMkQtyClbKScYK%2Fimg.png&quot; width=&quot;100%&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;입력 타입과 너비 등이 다르지만 모두 Label과 Input 컴포넌트를 조립해서 만든 LabeledInput 컴포넌트를 사용했다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이런 방식으로 진행하니 컴포넌트를 담고 있는 페이지에서 가독성이 떨어지는 현상이 발견됐다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;따라서 작은 컴포넌트를 조립해서 입력 종류에 따라 컴포넌트를 만들었다면 가독성이 떨어지는 문제가 해결될 거 같다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;예시 코드&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;기존 예시 코드&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// CardAddPage.jsx
// 카드 번호에 대한 input
&amp;lt;LabeledInput
value={convertedCardNumbers.join(&amp;#39;-&amp;#39;)}
handleInputChange={handleChangeCardNumbersInput}
invalidMessage={MESSAGE.INVALID_CARD_NUMBER}
inputProps={{
  type: &amp;#39;text&amp;#39;,
  width: &amp;#39;318px&amp;#39;,
  maxLength: 19,
  placeholder: &amp;#39;ex. 0000-0000-0000-0000&amp;#39;,
  isValid: cardAddCondition.cardNumbers,
}}
inputLabelProps={{
  label: &amp;#39;카드 번호&amp;#39;,
}}
/&amp;gt;
// 만료일에 대한 input
&amp;lt;LabeledInput
value={convertedExpiredDate}
handleInputChange={handleChangeExpiredDateInput}
invalidMessage={MESSAGE.INVALID_EXPIRED_DATE}
inputProps={{
  type: &amp;#39;text&amp;#39;,
  width: &amp;#39;137px&amp;#39;,
  maxLength: 5,
  placeholder: &amp;#39;MM / YY&amp;#39;,
  isValid: cardAddCondition.expiredDate,
}}
inputLabelProps={{
  label: &amp;#39;만료일&amp;#39;,
}}
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;개선 예정 예시 코드&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// CardAddPage.jsx
// 카드 번호에 대한 input
// CardNumberInput과 ExpiredDateInput은 각각 하위 컴포넌트인 Label과 Input 컴포넌트를 조립해서 만든 컴포넌트로 가정
&amp;lt;CardNumberInput /&amp;gt;
// 만료일에 대한 input
&amp;lt;ExpiredDateInput /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;stateless하게 작성한 Input 컴포넌트를 테스트하는 방법&lt;ul&gt;
&lt;li&gt;이번 미션에서 작성한 Input 컴포넌트는 외부에서 상태 값과 상태 값 변경 함수를 주입받는다.&lt;/li&gt;
&lt;li&gt;즉, input 태그의 value와 onChange를 상위에서 주입받는 형태이다.&lt;/li&gt;
&lt;li&gt;해당 컴포넌트는 상태 값과 함수를 자유롭게 전달하는 개발 환경에서는 문제없었다. 하지만 컴포넌트를 테스트하는 storybook을 이용할 때 문제가 생겼다.&lt;/li&gt;
&lt;li&gt;예를 들어 상태 값 변경 함수에 구현한 입력 값 검증 기능을 storybook에서 테스트를 할 수 없었다.&lt;/li&gt;
&lt;li&gt;실제 컴포넌트의 동작과 테스트의 동작이 달라서 storybook을 이용하여 개발하는 의미가 퇴색됐다.&lt;/li&gt;
&lt;li&gt;이를 해결하기 위해 storybook에서 상태 값을 주입할 수 있는 방법에 대해 공부하려 한다. 또한 잘 작성된 storybook 예시 코드를 확인하여 적절한 방법을 찾으려 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;상태 값 관리&lt;/h2&gt;
&lt;h3&gt;Keep&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;프로젝트를 시작할 때 pages라는 디렉터리에 각 페이지를 구현하도록 설계했다.&lt;/li&gt;
&lt;li&gt;각 페이지는 해당 페이지에서 필요한 상태 값을 갖도록 구현했다. 페이지와 관련된 상태 값이 한 곳에 있어서 쉽게 상태 값 변경 흐름을 파악할 수 있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Problem - Try&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;비즈니스 로직과 UI 코드가 잘 분리되지 않았다.(확신은 없지만 그렇게 보인다.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;페이지는 UI 코드이다. 반면 상태 값과 상태 값을 처리하는 함수들은 비즈니스 로직(도메인 영역)이라 생각한다.&lt;/li&gt;
&lt;li&gt;때문에 페이지 내부에 상태 값과 관련된 함수들이 존재한다는 것은 비즈니스 로직과 UI 코드가 분리되지 않은 것이라 생각한다. 이는 유지 보수를 어렵게 만든다.&lt;/li&gt;
&lt;li&gt;따라서 비즈니스 로직과 UI 코드를 분리하는 방법에 대해 찾아보고 리팩터링해야 한다. 지금 떠오르는 방법은 비즈니스 로직에 관련된 코드를 CustomHook으로 작성하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;페이지 별로 상태 값을 공유하는 방법에 대한 고민이 필요하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;현재 미션에는 하나의 페이지만 존재하기 때문에 페이지 내부에서 상태 값을 관리하는 것이 충분하다.&lt;/li&gt;
&lt;li&gt;하지만 추후에 요구사항 추가로인해 새로운 페이지가 추가되면 페이지 간의 상태 값 공유가 필요할 수 있다.&lt;/li&gt;
&lt;li&gt;따라서 상태 값을 공유하는 방법에 대해 고민할 필요가 있다. 현재는 Context API의 도입을 생각하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>Retrospect</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/116</guid>
      <comments>https://coding-w00se.tistory.com/116#entry116comment</comments>
      <pubDate>Fri, 29 Apr 2022 16:59:32 +0900</pubDate>
    </item>
    <item>
      <title>[Javascript] Eslint 설정 파일 옵션 env, parserOptions</title>
      <link>https://coding-w00se.tistory.com/115</link>
      <description>&lt;h3&gt;&lt;a href=&quot;https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments&quot;&gt;env&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;.eslintrc.json의 env&lt;ul&gt;
&lt;li&gt;스크립트의 실행 환경을 명시하는 곳으로, 실행 환경에 맞는 전역 변수들을 사전에 정의할 수 있습니다.&lt;/li&gt;
&lt;li&gt;예를 들어 아래의 예시 코드처럼 jest: true를 설정하면 jest 파일에서 describe, test, expect 등의 함수들이 no-undef 규칙에 걸리지 않는 것을 확인했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;env: {
  jest: true
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;env에 설정할 수 있는 옵션은 &lt;a href=&quot;https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments&quot;&gt;여기서&lt;/a&gt; 확인 가능합니다.&lt;/li&gt;
&lt;li&gt;es2021 옵션&lt;ul&gt;
&lt;li&gt;공식 문서에 의하면 es2021: true 옵션을 사용하면 ECMAScript 2021의 전역 변수들을 사전에 정의하고 ecmaVersion parser option도 12로 설정한다고 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href=&quot;https://eslint.org/docs/user-guide/configuring/language-options#specifying-parser-options&quot;&gt;parserOption&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;eslint에서 사용하는 parser의 option을 설정할 수 있다.&lt;/li&gt;
&lt;li&gt;ecmaVersion은 프로젝트에 사용할 ECMAScript 버전을 명시하는 옵션이다.&lt;/li&gt;
&lt;li&gt;ecmaVersion: latest는 ecmaVersion: 2022와 같다.&lt;/li&gt;
&lt;li&gt;프로젝트에서 사용하는 ecmaVersion에 맞게 parser의 버전을 설정하면 된다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programming/JS</category>
      <category>eslint</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/115</guid>
      <comments>https://coding-w00se.tistory.com/115#entry115comment</comments>
      <pubDate>Thu, 21 Apr 2022 16:32:45 +0900</pubDate>
    </item>
    <item>
      <title>[우아한테크코스] 유튜브 미션 회고</title>
      <link>https://coding-w00se.tistory.com/111</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;retrospective_post_image.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JmzpV/btrwXzv4HWo/4ay0fdCA4vkq2nNQiqAR0K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JmzpV/btrwXzv4HWo/4ay0fdCA4vkq2nNQiqAR0K/img.jpg&quot; data-alt=&quot;https://pixabay.com/ko/photos/글쓰기-작가-메모-펜-공책-923882&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JmzpV/btrwXzv4HWo/4ay0fdCA4vkq2nNQiqAR0K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJmzpV%2FbtrwXzv4HWo%2F4ay0fdCA4vkq2nNQiqAR0K%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;1920&quot; height=&quot;1280&quot; data-filename=&quot;retrospective_post_image.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://pixabay.com/ko/photos/글쓰기-작가-메모-펜-공책-923882&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;bottomup 좋다. 하지만, 최소한의 설계는 하고 진행하도록!&lt;/h3&gt;
&lt;h3&gt;전역 state관리 및 옵저버 패턴 활용&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;전역 store에서 상태값을 관리했다.&lt;/li&gt;
&lt;li&gt;상태 값에 변화가 있을 때, 변화가 있는 상태 값을 구독하고 있는 컴포넌트를 렌더링한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const state = {
  testNum: 100,
  testString: &amp;#39;hello&amp;#39;,
}

// 상태 값 별로 구독 리스트를 따로 관리하면, 상태 값이 변경됐을 때 구독된 컴포넌트들만 렌더링할 수 있다.  
const components = {  
  testNum: new Set(),  
  testString: new Set(),  
}&lt;/code&gt;&lt;/pre&gt;
&lt;br&gt;

&lt;h3&gt;api를 직접 테스트 VS stub을 통한 테스트&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;api 요청 부분을 stub하는 방식은 &lt;a href=&quot;https://coding-w00se.tistory.com/108&quot;&gt;여기&lt;/a&gt;에 정리했다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.cypress.io/guides/guides/network-requests&quot;&gt;공식문서&lt;/a&gt;와 코치님의 말에 의하면 api를 직접 테스트하는 방식과 stub하는  방식은 적용하는 기준이 다르다고 한다.&lt;/li&gt;
&lt;li&gt;api를 직접 테스트하는 방식&lt;ul&gt;
&lt;li&gt;api 요청량에 제한이 없는 경우&lt;/li&gt;
&lt;li&gt;팀내에서 api 서버를 구축한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;api 요청 부분을 stub하는  방식&lt;ul&gt;
&lt;li&gt;api 요청량에 제한이 있는 경우&lt;/li&gt;
&lt;li&gt;api 요청 후 돌아온 응답에 대한 후처리에 관한 테스트에 집중할 때&lt;/li&gt;
&lt;li&gt;데이터를 mocking 해두면 api에 영향받지 않고, 비즈니스 로직만 중점적으로 테스트 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;상수를 Object.freeze() 이용해서 동결화 해야하는가?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;상수를 직접 사용하는 사람들은 개발자이며, 대문자로 이름이 지어진 상수는 변경하지 않을 것이다.&lt;/li&gt;
&lt;li&gt;따라서 객체 또는 배열의 상수의 값을 변경하는 거의 없을 것이고, 상수를 동결화할 필요도 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;고민과 질문 그리고 성장&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이번 미션에서 리뷰어님께 드린 질문의 개수가 적었다. 이유에 대해서 생각을 해봤는데, 고민을 덜 해서 그런 거 같다. 잘 안되는 게 있으면 일단 차선책을 찾아서 해결했다. 그리고 리팩터링할 때 최선의 방법으로 고쳐보자고 생각했지만, 실행에 옮기지 않았다. 차선책이라는 쉬운 길로만 가다 보니 최선책으로 가기 위한 고민이 적었다. 고민이 적으니 그에 따라 질문도 적었다.&lt;/li&gt;
&lt;li&gt;최근 코치님께 상담받으며 기억에 남는 말은 &lt;code&gt;고민을 하는 게 당장 성과로 들어나진 않을 수 있지만, 고민을 많이 해본 크루는 나중에 더 많이 성장해있다.&lt;/code&gt; 이다. 이 말을 듣고, 다른 크루들의 pr을 보니 고민이 무엇이고 리뷰어님과 티키타카하면서 고민을 해결하는 과정이 눈에 보였다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;고민한 만큼 성장한다!&lt;/code&gt;라는 마음에 새기며, 다음 미션에서는 최선책을 찾기 위해 고민하는 시간을 더 갖도록 해야겠다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Retrospect</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/111</guid>
      <comments>https://coding-w00se.tistory.com/111#entry111comment</comments>
      <pubDate>Tue, 22 Mar 2022 23:45:04 +0900</pubDate>
    </item>
    <item>
      <title>[Cypress] Api test stub 하기 with intercept</title>
      <link>https://coding-w00se.tistory.com/108</link>
      <description>&lt;h2&gt;&lt;a href=&quot;https://docs.cypress.io/guides/guides/network-requests&quot;&gt;cypress의 Network Requests&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://docs.cypress.io/api/commands/intercept&quot;&gt;intercept 메서드&lt;/a&gt;를 활용하면 Api 요청 부분을 stub할 수 있다.&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;사용 예시: api 요청부분을 stub으로 처리하고 응답 결과로 dummy 데이터  반환하기&lt;ul&gt;
&lt;li&gt;&amp;#39;&lt;a href=&quot;https://abcd.efg.com/*&amp;#39;%EC%9D%84&quot;&gt;https://abcd.efg.com/*&amp;#39;을&lt;/a&gt; 사용하면 url host가 abcd.efg.com으로 시작하는 모든 요청에 대한 응답을 stub한다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.cypress.io/api/commands/fixture#Shortcuts&quot;&gt;공식문서&lt;/a&gt;에 의하면 아래 예시처럼 fixture를 단축해서 사용할 수 있다.&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;cy.intercept(&amp;#39;https://abcd.efg.com/*&amp;#39;, { fixture: &amp;#39;dummyData.json&amp;#39; }).as(&amp;#39;requestData&amp;#39;);
cy.wait(&amp;#39;@requestData&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Test</category>
      <category>CYPRESS</category>
      <category>Intercept</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/108</guid>
      <comments>https://coding-w00se.tistory.com/108#entry108comment</comments>
      <pubDate>Thu, 17 Mar 2022 01:29:43 +0900</pubDate>
    </item>
    <item>
      <title>[Cypress] viewport 크기 조절하기</title>
      <link>https://coding-w00se.tistory.com/107</link>
      <description>&lt;h2&gt;cypress viewport 크기 조절하기&lt;/h2&gt;
&lt;h3&gt;왜 필요한가 ?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;cypress의 기본 viewport 크기는 1000px X 660px이다.&lt;/li&gt;
&lt;li&gt;만약 테스트하려는 서비스의 엘리먼트 스타일이 해당 viewport의 크기보다 크다면, 예상치 못하게 테스트 실패할 수 있다.&lt;/li&gt;
&lt;li&gt;예시: dimmed 영역 클릭 시 모달이 닫히는지 확인하는 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;1051&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bt6rOC/btrwb62FT0c/8XSOEDEOnodVQuzH4eD5Jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bt6rOC/btrwb62FT0c/8XSOEDEOnodVQuzH4eD5Jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bt6rOC/btrwb62FT0c/8XSOEDEOnodVQuzH4eD5Jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbt6rOC%2Fbtrwb62FT0c%2F8XSOEDEOnodVQuzH4eD5Jk%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;1706&quot; height=&quot;1051&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;1051&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;center&gt;[viewport 크기 조절 전]&lt;/center&gt;

&lt;ul&gt;
&lt;li&gt;viewport의 크기를 조절하지 않으면 모달의 dimmed 영역이 보이지 않아, 테스트가 원활하게 진행되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;1051&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kqgqx/btrv6ehjZM3/ZKvqv1XNRCjD3XHKXNvgMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kqgqx/btrv6ehjZM3/ZKvqv1XNRCjD3XHKXNvgMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kqgqx/btrv6ehjZM3/ZKvqv1XNRCjD3XHKXNvgMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKqgqx%2Fbtrv6ehjZM3%2FZKvqv1XNRCjD3XHKXNvgMK%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;1706&quot; height=&quot;1051&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;1051&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;center&gt;[viewport 크기 조절 후]&lt;/center&gt;

&lt;ul&gt;
&lt;li&gt;성공!&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;h3&gt;방법&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.cypress.io/api/commands/viewport&quot;&gt;cy.viewport()&lt;/a&gt; 메서드를 이용하는 방법&lt;/li&gt;
&lt;li&gt;cypress.json 파일에 viewport의 크기를 설정하는 방법&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;예시&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;// cypress.json
{ 
  &amp;quot;viewportWidth&amp;quot;: 1920,
  &amp;quot;viewportHeight&amp;quot;: 975
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;읽어 주셔서 감사합니다 :)&lt;br&gt;잘못된 부분이 있다면 댓글로 편히 알려주세요&lt;/p&gt;</description>
      <category>Test</category>
      <category>CYPRESS</category>
      <category>viewport</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/107</guid>
      <comments>https://coding-w00se.tistory.com/107#entry107comment</comments>
      <pubDate>Wed, 16 Mar 2022 22:00:13 +0900</pubDate>
    </item>
    <item>
      <title>[우아한 테크코스] 행운의 로또 미션 회고</title>
      <link>https://coding-w00se.tistory.com/105</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;retrospective_post_image.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1280&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NdGVt/btrvNGLMvkw/aOD0ohhDoe3RWGKHhXbink/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NdGVt/btrvNGLMvkw/aOD0ohhDoe3RWGKHhXbink/img.jpg&quot; data-alt=&quot;https://pixabay.com/ko/photos/글쓰기-작가-메모-펜-공책-923882&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NdGVt/btrvNGLMvkw/aOD0ohhDoe3RWGKHhXbink/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNdGVt%2FbtrvNGLMvkw%2FaOD0ohhDoe3RWGKHhXbink%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;1920&quot; height=&quot;1280&quot; data-filename=&quot;retrospective_post_image.jpg&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1280&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://pixabay.com/ko/photos/글쓰기-작가-메모-펜-공책-923882&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;webpack 기본기를 공부하기 시작!&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;웹팩을 공부하고 사용한 경험이 이번 미션의 가장 큰 수확이라고 생각합니다.&lt;/li&gt;
&lt;li&gt;webpack의 &lt;code&gt;CleanWebpackPlugin&lt;/code&gt;를 사용하면서 output path를 잘못 설정해서 .git 디렉터리가 삭제되는 경험을 했습니다.&lt;ul&gt;
&lt;li&gt;실패는 성공의 어머니라고 하듯이, 해당 경험이 webpack을 공부하는데 계기가 됐습니다.&lt;/li&gt;
&lt;li&gt;디렉터리가 삭제됐다면 mac 기준으로 cmd + z를 시도해 볼 필요가 있습니다.(저는 침착하지 못했고, 결국 다시 처음부터 작업을 시작했습니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;웹팩에 대해서는 블로그의 &lt;a href=&quot;https://coding-w00se.tistory.com/92&quot;&gt;게시글&lt;/a&gt;로 간략히 정리를 했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;h3&gt;랜덤 테스트에 대해서&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;랜덤은 테스트하기 힘들다&lt;/code&gt;라는 말을 자주 들었는데, 막상 랜덤과 관련 있는 테스트 코드를 작성할 때 어떤 식으로 테스트 코드를 작성할지 감이 잘 오지 않았습니다.&lt;ul&gt;
&lt;li&gt;예를 들면 이번 미션에서 &lt;code&gt;하나의 로또는 1 ~ 45 사이의 6개의 숫자를 랜덤으로 이루어져있다.&lt;/code&gt;라는 기능을 구현한 후 테스트를 어떤 방식으로 진행할지 명확하지 않았습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코드 리뷰를 통해 알게 된 점은 아래와 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;랜덤 값 자체는 테스트할 수 없다. 즉, 랜덤으로 생성하는 값의 범위를 테스트하는 건 적절하지 않다.&lt;br&gt;그러나 반환되는 데이터 형식(데이터의 길이 또는 중복 여부)는 테스트할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br&gt;

&lt;h3&gt;vscode extension을 활용해서 오타 예방하기&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이번 미션에서 유독 오타가 많았습니다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker&quot;&gt;해당 extension&lt;/a&gt;을 활용하면 어느 정도 오타를 방지할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;h3&gt;트리 쉐이킹 키워드에 대해서&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이번 미션에서 lodash를 처음 사용해봤는데, 피드백 받기 전에는 아래의 예시 코드처럼 lodash의 모든 메서드를 가져오는 방식으로 코드를 작성했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import * as _ form &amp;#39;lodash&amp;#39;;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;하지만 위 예시처럼 코드를 작성하면 사용하지 않는 메서드들도 모두 import하여 번들링 시 파일의 크기가 커진다는 사실을 배웠습니다.&lt;/li&gt;
&lt;li&gt;따라서 번들 파일의 크기를 최적화하기 위해서 필요한 메서드만 불러오는 습관이 필요하며 이는 트리 쉐이킹과 관련이 있다는 것을 알게 됐습니다.&lt;/li&gt;
&lt;li&gt;트리 쉐이킹에 대해서 &lt;a href=&quot;https://ui.toast.com/weekly-pick/ko_20180716&quot;&gt;이 글&lt;/a&gt;을 보면서 공부했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;h3&gt;조건문은 부정문보다는 긍정문으로&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;부정문보다는 긍정문이 읽는 사람에게 더 잘 읽힌다고 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;h3&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/CustomEvent/CustomEvent&quot;&gt;커스텀 이벤트&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이번 미션에서 커스텀 이벤트에 대해서 처음 접했습니다.&lt;/li&gt;
&lt;li&gt;커스텀 이벤트는 MVC 패턴에서 Controller와 View의 의존성을 끊기 위해 사용된다고 합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Retrospect</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/105</guid>
      <comments>https://coding-w00se.tistory.com/105#entry105comment</comments>
      <pubDate>Mon, 14 Mar 2022 00:58:04 +0900</pubDate>
    </item>
    <item>
      <title>[javascript] scroll event 사용해보기 with throttle 기법</title>
      <link>https://coding-w00se.tistory.com/104</link>
      <description>&lt;h3&gt;throttle 기법&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;일정 시간동안 발생한 이벤트를 한번만 처리하는 방법&lt;ul&gt;
&lt;li&gt;에를 들어 throttle을 활용하면 1총 동안 이벤트가 여러 번 발생해도 이벤트 내부 함수는 1번만 실행할 수 있게 구현 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;scroll 이벤트와 연속적으로 호출되는 이벤트를 처리할 때 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;

&lt;pre&gt;&lt;code&gt;let timerId;

const handleToDoListScrollEvent = (e) =&amp;gt; {
  // 스크롤이 맨 아래 쪽에 있지 않으면 early return
  if (e.target.offsetHeight + e.target.scrollTop &amp;lt; e.target.scrollHeight) {
    return;
  }

  // timerID가 Truthy한 값이면 early return
  if (timerId) {
    return;
  }

  timerId = setTimeout(() =&amp;gt; {
    timerId = null;

    console.log(
      &amp;#39;스크롤 이벤트 발생&amp;#39;,
      e.target.scrollHeight,
      &amp;#39;scrollTop: &amp;#39;,
      e.target.scrollTop,
      &amp;#39;offsetHeight: &amp;#39;,
      e.target.offsetHeight
    );
  }, 1000);
};

// $toDOWrapper은 id가 #to-do-wrapper인 div 태그
const $toDoWrapper = document.querySelector(&amp;#39;#to-do-wrapper&amp;#39;);

$toDoWrapper.addEventListener(&amp;#39;scroll&amp;#39;, handleToDoListScrollEvent);&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;참고 자료&lt;/strong&gt;&lt;br&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Document/scroll_event&quot;&gt;mdn web docs - scroll&lt;/a&gt;  &lt;/p&gt;
&lt;p&gt;읽어 주셔서 감사합니다 :)&lt;br&gt;잘못된 부분이 있다면 댓글로 편히 알려주세요.&lt;/p&gt;</description>
      <category>javascript</category>
      <category>SCROLL</category>
      <category>throttle</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/104</guid>
      <comments>https://coding-w00se.tistory.com/104#entry104comment</comments>
      <pubDate>Wed, 9 Mar 2022 11:59:11 +0900</pubDate>
    </item>
    <item>
      <title>[javascript] dotenv 사용해보기 with webpack</title>
      <link>https://coding-w00se.tistory.com/103</link>
      <description>&lt;h2&gt;&lt;a href=&quot;https://github.com/motdotla/dotenv#readme&quot;&gt;dotenv&lt;/a&gt;란?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.env&lt;/code&gt;라는 파일에서 변수를 불러와서 process.env에 변수를 삽입해주는 패키지&lt;/li&gt;
&lt;li&gt;웹팩의 &lt;a href=&quot;https://webpack.js.org/plugins/define-plugin/&quot;&gt;DefinePlugin&lt;/a&gt;과 함께 사용하면, 빌드된 파일에 환경 변수를 주입할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;설치&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;npm install --save dotenv&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;활용법&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;프로젝트의 root 디렉터리에 .env 파일 생성 후 환경 변수 선언&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// .env
API_KEY=&amp;quot;123&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;webpack.config.js 파일에서 DefinePlugin 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// webpack.config.js
require(&amp;#39;dotenv&amp;#39;).config();

module.exports = {
  ...
  plugins: [
    ...
    new webpack.DefinePlugin({
      // .env 파일의 API_KEY 값을 API_KEY 환경 변수로 삽입
      API_KEY: JSON.stringify(process.env.API_KEY),
    }),
    ...
  ],
  ...
};    &lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;환경 변수 사용하기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// index.js
console.log(API_KEY) // &amp;quot;123&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;참고 자료&lt;/strong&gt;&lt;br&gt;&lt;a href=&quot;https://www.daleseo.com/js-dotenv/&quot;&gt;DaleSeo: dotenv로 환경 변수 관리하기&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;읽어 주셔서 감사합니다 :)&lt;br&gt;잘못된 부분이 있다면 댓글로 편히 알려주세요&lt;/p&gt;</description>
      <category>DefinePlugin</category>
      <category>dotenv</category>
      <category>webpack</category>
      <author>w00se</author>
      <guid isPermaLink="true">https://coding-w00se.tistory.com/103</guid>
      <comments>https://coding-w00se.tistory.com/103#entry103comment</comments>
      <pubDate>Tue, 8 Mar 2022 23:19:34 +0900</pubDate>
    </item>
  </channel>
</rss>