<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>헬롤의 잡동사니</title>
    <link>https://hellol77.tistory.com/</link>
    <description>https://github.com/Hellol77</description>
    <language>ko</language>
    <pubDate>Thu, 7 May 2026 23:22:08 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>헬롤이다</managingEditor>
    <image>
      <title>헬롤의 잡동사니</title>
      <url>https://tistory1.daumcdn.net/tistory/4720280/attach/cf514bb3322b4d18b736fd11431ec2d6</url>
      <link>https://hellol77.tistory.com</link>
    </image>
    <item>
      <title>[React] 플리커링 현상 해결하기</title>
      <link>https://hellol77.tistory.com/105</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 거의 다 마친 상황..에서 마주한 한가지 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거슬리는 부분을 찾아냈다. 바로 플리커링(flickering) 현상이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐️&amp;nbsp; 왜 일어나고 있는 것인가?&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;일단 이 프로젝트는 suspense로 부모 컴포넌트가 싸여있고 빈 div를 fallback으로 두고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1717685414617&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  path: PATH.PLANNING,
  element: (
    &amp;lt;Suspense fallback={&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;}&amp;gt;
      &amp;lt;Lazy.PlanningPage /&amp;gt;
     &amp;lt;/Suspense&amp;gt;
  ),
 },&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열리는 모달에는 댓글들을 불러오는 api가 모달이 열려질때 불려진다. 이 댓글을 불러오는 api가 완료되길 기다리는 동안&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Suspense에서 설정해놓은 Callback인 빈 div 화면이 전체에 걸쳐서 플리커링 현상을 일으키는 것이다.&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;화면-기록-2024-05-29-오후-9.54.05.gif&quot; data-origin-width=&quot;2372&quot; data-origin-height=&quot;1418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZXbVd/btsHF2CIwzQ/KVq1gdD7lmUqKHp9a1hkV0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZXbVd/btsHF2CIwzQ/KVq1gdD7lmUqKHp9a1hkV0/img.gif&quot; data-alt=&quot;플리커링 현상&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZXbVd/btsHF2CIwzQ/KVq1gdD7lmUqKHp9a1hkV0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cZXbVd/btsHF2CIwzQ/KVq1gdD7lmUqKHp9a1hkV0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2372&quot; height=&quot;1418&quot; data-filename=&quot;화면-기록-2024-05-29-오후-9.54.05.gif&quot; data-origin-width=&quot;2372&quot; data-origin-height=&quot;1418&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  해결책&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Suspense를 CommentField에 씌워준다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1716988579231&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ScheduleModal 컴포넌트
// ...

      &amp;lt;Suspense fallback={&amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;}&amp;gt;
        &amp;lt;CommentField boardId={schedule.boardId} /&amp;gt;
      &amp;lt;/Suspense&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Suspense가 중첩된다면 가장 가까이에 있는 Suspense가 적용되는 것을 이용하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CommentField에서 comment들을 불러오고 있는 CommentField를 Suspense가 덮어 씌어준다면 플리커링 현상은 일어나지 않고 댓글 목록에서만 로딩이 일어날 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  또 다른 해결책?&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. prefetch를 해준다&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;prefetch란?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;사용자가 가까운 미래에 탐색할 가능성이 '있는' 페이지에 대해 백그라운드에서 리소스를 추론적으로 가져오는 방식&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;이렇게 하면 사용자가 해당 페이지로 이동하기로 선택한 경우 미리 가져온 페이지의 로드 시간을 크게 줄일 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;prefetch를 통해서 data를 미리 불러와 준다면 Suspense가 동작하지 않고 플리커링 현상을 해결할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1718841283556&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  // 현재 달력의 일정의 댓글을 prefetch 해준다.
  data?.result.scheduleList.forEach((schedule) =&amp;gt; {
      prefetchScheduleComment(schedule.boardId);
    });&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;화면-기록-2024-06-20-오전-8.48.24.gif&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;1474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zFeeb/btsH4LAgCEf/veWfDsvdasMiMYKXWTxYYk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zFeeb/btsH4LAgCEf/veWfDsvdasMiMYKXWTxYYk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zFeeb/btsH4LAgCEf/veWfDsvdasMiMYKXWTxYYk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/zFeeb/btsH4LAgCEf/veWfDsvdasMiMYKXWTxYYk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2268&quot; height=&quot;1474&quot; data-filename=&quot;화면-기록-2024-06-20-오전-8.48.24.gif&quot; data-origin-width=&quot;2268&quot; data-origin-height=&quot;1474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;결국 선택한 해결책은?&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 해결책중 선택한 해결책은 Suspense를 다시 쓰는 것이다. 일정이 많아졌을때는 생각 보다 어쩌면 필요없을 수도 있는 많은 수의 api 요청이 있었다. 또한 댓글을 불러오는 것은 비교적 간단한 get요청이기에 suspense를 써서 플리커링 현상을 막기로 결정했다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  만약 이렇게 한다면? (UX)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일정에 마우스를 hover할때 댓글을 가져오는 요청을 보내는 것이다. 일정 상세보기 및 댓글을 봐야한다면 결국 일정에 마우스를 가져다 댈수 밖에 없는데 이렇게 한다면 사용자는 로딩 없는 더욱 빠른 화면을 볼 수 있고 또한 불필요한 api요청을 없앨 수도 있을 것 같다.&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>flickering</category>
      <category>prefetch</category>
      <category>react-query</category>
      <category>Suspense</category>
      <category>플리커링 현상</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/105</guid>
      <comments>https://hellol77.tistory.com/105#entry105comment</comments>
      <pubDate>Mon, 24 Jun 2024 14:47:59 +0900</pubDate>
    </item>
    <item>
      <title>nextjs 프로젝트를 정적배포를 해보다 생긴일</title>
      <link>https://hellol77.tistory.com/92</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Nextjs 개발이 거의 다 마무리가 되가는 상황에 배포를 해보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s3와 cloudfront를 사용해서 배포를 해보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 잘못된 결정이었다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6IzGH/btsEfnwATGs/tU4d8jVu6h0QVXxTud0yH1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6IzGH/btsEfnwATGs/tU4d8jVu6h0QVXxTud0yH1/img.jpg&quot; data-alt=&quot;stay..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6IzGH/btsEfnwATGs/tU4d8jVu6h0QVXxTud0yH1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6IzGH%2FbtsEfnwATGs%2FtU4d8jVu6h0QVXxTud0yH1%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;886&quot; height=&quot;496&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;stay..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;정적 웹 호스팅이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 웹 호스팅은 HTML, JavaScript, 이미지, 동영상 및 기타 파일을 저장된 그대로 웹 사이트 방문자에게 단순히 제공할 뿐이며 애플리케이션 코드를 포함하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 웹 호스팅은 ssr 처럼 전체 프로세스가 각 사용자 요청에 수행되는 것이 아닌 빌드 시간에 수행된다. 따라서 속도가 더 빠르다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 seo 검색엔진 최적화에 이점이 있고 cdn에서 페이지를 수신하면 되기때문에 리소스와 비용을 절감할 수 있다.&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;&lt;b&gt;Nextjs의 정적 배포 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Nextjs에서 정적 배포를 하려면 한가지 설정이 필요하다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1706786377320&quot; class=&quot;ebnf&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;const nextConfig = {
  output: 'export',
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;output: 'export'옵션을 주어서 정적 빌드를 할 수 있게 만들어야한다.&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;Nextjs에서 정적 배포시 사용할 수 없는 기능&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 배포를 하게 된다면 프론트 서버가 없기 때문에 Nextjs에서 서버를 사용하는 기능은 사용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 것이 &lt;span style=&quot;background-color: #f89009;&quot;&gt;Cookies, Rewrites, 동적 라우팅 등 많은 기능을 사용 할 수 없게 된다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nextjs에서 항상 배포하기 전에 나에게 맞는 배포방식이 무엇인지 확인해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나의 경우에는 동적 라우팅과 rewirtes등 몇 가지 정적 배포 시 사용 할 수없는 기능들이 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 동적라우팅은 query string 형식으로 바꾸었으며 nextjs에서 할 수 있는 cors 해결 방법인 nextjs rewrite 기능도 AWS gateway의 cors를 통해서 해결했다.&lt;br /&gt;&lt;br /&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;b&gt;s3 와 cloudfront&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s3에서 직접 정적 웹 호스팅을 하는 것보다 cloudfront를 통해서 s3에 접근하는게 비용 측면에서 더 아낄수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;b&gt;데이터 전송 비용 절감&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;CloudFront는 전 세계에 분산된 엣지 로케이션을 통해 콘텐츠를 전달한다. 사용자가 s3버켓에 직접 접근하면 모든 요청이 s3가 위치한 리전으로 전송되어야 하므로 데이터 전송 비용이 발생한다. 하지만 cloudfront를 사용하면 엣지 로케이션에서 콘텐츠를 캐시하고 사용자에게 가장 가까운 엣지 로케이션에서 콘텐츠를 제공하여 데이터 전송 비용을 아낄 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt;2. &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;text-align: left;&quot;&gt;cloudfront는 자주 요청되는 콘텐츠를 엣지 로케이션에 캐시한다. s3에서 직접 가져오는 것보다 빠르게 제공되고 s3의 요청 비용을 절감할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 s3를 통한 접근을 막기 위해서 퍼블릭 접근은 우선 모두 막았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cloudfront origin에 들어가서 사용하고 싶은 s3 버킷을 선택한다.&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;1146&quot; data-origin-height=&quot;234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOlzKn/btsEmqTCgjj/GWAN06s993Ob7XIAktAiz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOlzKn/btsEmqTCgjj/GWAN06s993Ob7XIAktAiz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOlzKn/btsEmqTCgjj/GWAN06s993Ob7XIAktAiz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOlzKn%2FbtsEmqTCgjj%2FGWAN06s993Ob7XIAktAiz1%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;567&quot; height=&quot;116&quot; data-origin-width=&quot;1146&quot; data-origin-height=&quot;234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경고 창이 하나 뜨는데 클릭하면 origin domain이 website 엔드 포인트로 바뀐다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이렇게 사용하면 원본 엑세스 설정을 하지 못한다. 즉 s3을 통한 접근이 가능하다는 이야기이다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VRRvr/btsEoI6MAt9/o1rK8bVxk9kpY0C7Lqk6j1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VRRvr/btsEoI6MAt9/o1rK8bVxk9kpY0C7Lqk6j1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VRRvr/btsEoI6MAt9/o1rK8bVxk9kpY0C7Lqk6j1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVRRvr%2FbtsEoI6MAt9%2Fo1rK8bVxk9kpY0C7Lqk6j1%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;531&quot; height=&quot;152&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;336&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;cloudfront를 통한 접근만을 허용하기 위해 원본 엑세스 제어 설정을 선택한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 OAC(Origin access control)를 만든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;s3 와 cloudfront를 통한 배포를 하려고 많은 블로그와 자료들을 찾아다녔는데 옛날 자료들을 보면 oai를 설정하라고 나와있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;OAI(origin access identity)&lt;/b&gt; 는 &lt;b&gt;OAC(Origin access control) &lt;/b&gt;의 옛날 버전으로 &lt;span style=&quot;text-align: start;&quot;&gt;AWS Signature Version 4(SigV4), POST method를 사용하는 HTTP/HTTPS 요청, AWS KMS를 사용한 서버 측 암호화(SSE-KMS)를 적용을 지원하지 않는 등의 제한 사항이 있다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;OAI는 기존 aws 리전들과 2022년 12월 이전에 출시한 리전에서만 지원이 된다.&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;color: #333333;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;OAC(Origin access control)를 만들고 정책을 복사해서 s3에 cloudfront로만 접근이 가능하도록 권한을 설정하기 위해 버킷 권한에 붙여넣는다.&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;한가지 주의할 것은 혹시나 s3내의 파일이나 설정을 바꾸었을때 cloudfront 캐시 무효화를 해야한다. 하지 않으면 시간이 지나면 언젠가는 변경이 되겠지만 캐시 무효화를 통해 빠르게 적용시키자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;하지만.. 왜 redirect가 안되지?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한가지 문제가 있었다. 현재 카카오 Oauth 로그인을 적용중이다.&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;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;무제.png&quot; data-origin-width=&quot;551&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciZbfK/btsEkhJ9pj2/lbcvPP6KH5pAy9i0K7sHMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciZbfK/btsEkhJ9pj2/lbcvPP6KH5pAy9i0K7sHMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciZbfK/btsEkhJ9pj2/lbcvPP6KH5pAy9i0K7sHMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciZbfK%2FbtsEkhJ9pj2%2FlbcvPP6KH5pAy9i0K7sHMk%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;551&quot; height=&quot;452&quot; data-filename=&quot;무제.png&quot; data-origin-width=&quot;551&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 성공 시 인가코드를 카카오에서 부터 받아오는데 302 Redirect URI로 인가 코드를 전달한다. 하지만 이 302 Redirect 가 배포시 정상적으로 동작하지 않았다.&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;root 경로인 요청은 자동적으로 index.html을 붙여서 요청을 하지만 하위 디렉토리에 있는 요청은 자동적으로 index.html로 요청을 하지 않는다.&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;'/' 요청은 초반 배포할때의 설정 때문에 '/index.html'로 자동으로 요청이 가는데&amp;nbsp; '/blog' 요청은 그대로 '/blog'요청으로 가기 때문에 원하는 페이지를 볼 수없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;해결책 후보 : cloudfront functions 사용&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;cloudfront functions&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clundfront fuctions는 자바스크립트 런타임을 통해서 요청이 엣지 로케이션에 도착할 때 요청에 접근 할수 있다.&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;따라서 패키기 크기는 10KB로 제한 되며 비동기 패턴, 파일 시스템 접근 등이 불가능하다.&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;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;사용 예시&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;간단한 조건의 Redirect&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서&lt;/p&gt;
&lt;pre id=&quot;code_1706974262074&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function handler(event) {
    var request = event.request;
    var supportedCountries = [&amp;lsquo;de&amp;rsquo;, &amp;lsquo;it&amp;rsquo;, &amp;lsquo;fr&amp;rsquo;];

    if (request.uri.substr(3, 1) != &amp;lsquo;/&amp;rsquo;) {
        var headers = request.headers;
        var nextUri;

        if (headers[&amp;lsquo;cloudfront-viewer-country&amp;rsquo;]) {
            var countryCode = headers[&amp;lsquo;cloudfront-viewer-country&amp;rsquo;].value.toLowerCase();
            if (supportedCountries.includes(countryCode)) {
                nextUri = &amp;lsquo;/&amp;rsquo; + countryCode + request.uri;
            }
        }

        if (!nextUri) {
            nextUri = &amp;lsquo;/en&amp;rsquo; + request.uri;
        }

        return {
            statusCode: 302,
            statusDescription: &amp;lsquo;Found&amp;rsquo;,
            headers: {
                location: { value: newUri }
            }
        }
    }

    return request;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 함수는 특정 국가의 트래픽을 Redirect하는 코드이다. 만약 supportedContries에 없는 국가 코드라면 코드의 기본값은 en이 되는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- Cloudfront 캐시 키 수정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- CORS,CSP, X-Frame-Options 및 기타 보안 HTTP 헤더 관리&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;나의 문제는 redirect니 조건을 붙여 redirect를 해주면 해결 가능하다. 또한 url에 직접 입력해서 요청했을때 지금 현재 403, 404 요청이 root의 index.html로 가게 만들어 무조건 home화면이 나온다. 이. 문제도 cloudfront Function을 사용하면 해결 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;또 다른 간단한 해결책&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 간단한 해결책이지만 카카오 redirect uri에도 /index.html을 붙이자&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;&lt;/p&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;span style=&quot;background-color: #ffc1c8;&quot;&gt;nextjs를 배포할 때는 s3, cloudfront 정적 배포를 하지 말자.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배포 방식에 따라 장단점이 있는 것이지만 nextjs의 나름 중요한 몇가지 기능이 안된다는 것과 웹 어플리케이션 서버가 없다는 것은 장점을 뛰어 넘는 큰 단점인 것 같다.&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;이 배포를 진행하는데 정말 많은 시간이 걸렸다. (한.. 5일 정도... 온갖 블로그나 웹 사이트는 다 찾아본것 같다.)&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;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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1706975806189&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;CloudFront Functions vs. Lambda@Edge &amp;ndash; Which One Should You Choose? - TrackIt - Cloud Consulting &amp;amp; S/W Development&quot; data-og-description=&quot;The differences between CloudFront Functions and Lambda@Edge. Make the right choice for your AWS infrastructure.&quot; data-og-host=&quot;trackit.io&quot; data-og-source-url=&quot;https://trackit.io/cloudfront-functions-vs-lambdaedge/&quot; data-og-url=&quot;https://trackit.io/cloudfront-functions-vs-lambdaedge/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ci4BjM/hyVf9zNkqX/3YC9zdqVyaUJjZhVrKWCVk/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/CXKja/hyVb3H18FQ/lQwkyHVOJF8E5cAP2ZkXNK/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/UX8nX/hyVf723BiH/lzwTQQ6BUJ13yCaEerxDlK/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600&quot;&gt;&lt;a href=&quot;https://trackit.io/cloudfront-functions-vs-lambdaedge/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://trackit.io/cloudfront-functions-vs-lambdaedge/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ci4BjM/hyVf9zNkqX/3YC9zdqVyaUJjZhVrKWCVk/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/CXKja/hyVb3H18FQ/lQwkyHVOJF8E5cAP2ZkXNK/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_600,https://scrap.kakaocdn.net/dn/UX8nX/hyVf723BiH/lzwTQQ6BUJ13yCaEerxDlK/img.png?width=800&amp;amp;height=600&amp;amp;face=0_0_800_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;CloudFront Functions vs. Lambda@Edge &amp;ndash; Which One Should You Choose? - TrackIt - Cloud Consulting &amp;amp; S/W Development&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The differences between CloudFront Functions and Lambda@Edge. Make the right choice for your AWS infrastructure.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;trackit.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>aws</category>
      <category>AWS</category>
      <category>cloudfront</category>
      <category>nextjs</category>
      <category>OAC</category>
      <category>OAI</category>
      <category>S3</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/92</guid>
      <comments>https://hellol77.tistory.com/92#entry92comment</comments>
      <pubDate>Sat, 18 May 2024 12:15:56 +0900</pubDate>
    </item>
    <item>
      <title>[NPM 배포] SpeechBubble 배포하기 (1)</title>
      <link>https://hellol77.tistory.com/102</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 할때마다 만나는 ui가 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 말풍선입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4VPX0/btsHq6jieVG/j2KIW6P4MClyPx2t3KrMgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4VPX0/btsHq6jieVG/j2KIW6P4MClyPx2t3KrMgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4VPX0/btsHq6jieVG/j2KIW6P4MClyPx2t3KrMgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4VPX0%2FbtsHq6jieVG%2Fj2KIW6P4MClyPx2t3KrMgk%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;313&quot; height=&quot;608&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;608&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;보기에는 상당히 구현하기 매우 쉬워보입니다. 하지만 문제는 저 위에 뾰족 나와있는 저 삼각형이 문제입니다.&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;/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;이번 프로젝트는 번들링 사이즈 개선을 위해서 pnpm과 vite를 사용해서 배포하려고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 vite 프로젝트를 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 와 TypeScript를 사용하겠습니다.&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;528&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMoVvW/btsHq1WCq2w/yxkqmfYtZYRDZ7O4hSq2N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMoVvW/btsHq1WCq2w/yxkqmfYtZYRDZ7O4hSq2N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMoVvW/btsHq1WCq2w/yxkqmfYtZYRDZ7O4hSq2N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMoVvW%2FbtsHq1WCq2w%2FyxkqmfYtZYRDZ7O4hSq2N0%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;363&quot; height=&quot;262&quot; data-origin-width=&quot;528&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;필요 없는 파일은 다 지우고 말풍선에 필요한 Pointer 컴포넌트들을 만들었습니다. (Public도 함께 삭제해줍니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 index.ts에서 컴포넌트들을 한꺼번에 export해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715758166576&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//  src/lib/index.ts
export { default as PointerBorder } from &quot;./components/PointerBorder&quot;;
export { default as Pointer } from &quot;./components/Pointer&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. vite config 파일 수정&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;타입스크립트를 사용해서 개발하기 위해 d.ts파일 번들링 위한 vite-plugin-dts를 설치해줍니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;pnpm i -D @types/node vite-plugin-dts&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;insertTypesEntry옵션을 통해 package.json types 속성에 지정된 위치에 타입 정의 파일을 생성할 수 있게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 storybook을 사용해 ui test를 할 예정이기 때문에 storybook을 추가해보았습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715758561508&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { defineConfig } from &quot;vite&quot;;
import react from &quot;@vitejs/plugin-react&quot;;
import dts from &quot;vite-plugin-dts&quot;;

// https://vitejs.dev/config/
export default defineConfig({
  build: {
    lib: {
      entry: &quot;src/lib/index.ts&quot;,
      name: &quot;@hellol/speech-bubble&quot;,
      formats: [&quot;es&quot;, &quot;cjs&quot;],
      fileName: (format) =&amp;gt; `index.${format}.js`,
    },
    rollupOptions: {
      external: [&quot;react&quot;, &quot;react-dom&quot;, &quot;**/*.stories.tsx&quot;],
      output: {
        globals: {
          react: &quot;React&quot;,
          &quot;react-dom&quot;: &quot;ReactDOM&quot;,
        },
        banner: '&quot;use client&quot;;',
        interop: &quot;auto&quot;,
      },
    },
    commonjsOptions: {
      esmExternals: [&quot;react&quot;],
    },
  },
  plugins: [
    react({
      jsxImportSource: &quot;@emotion/react&quot;,
      babel: {
        plugins: [&quot;@emotion/babel-plugin&quot;],
      },
    }),
    dts({
      insertTypesEntry: true,
    }),
  ],
});&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;lib
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;entry: 라이브러리 진입점, 제공하고자 하는 컴포넌트 모두 export하는 부분&lt;/li&gt;
&lt;li&gt;name: 라이브러리 이름&lt;/li&gt;
&lt;li&gt;formats: 라이브러리를 어떤 형식으로 빌드할지 지정, es모듈과 commonjs 형식으로 빌드&lt;/li&gt;
&lt;li&gt;fileName: 출력 파일 이름 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;rollupOptions
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;external: 라이브러리에 포함하지 않을 dependency&lt;/li&gt;
&lt;li&gt;globals: 라이브러리 외부에 존재하는 dependency를 위해 번들링 시 사용될 전역 변수 명시&lt;/li&gt;
&lt;li&gt;banner: 번들 앞에 문자열을 추가함, &quot;use client&quot;;를 추가해 컴포넌트의 모든 사용을 클라이언트 컴포넌트로 보장&lt;/li&gt;
&lt;li&gt;interop: 외부 의존성과의 모듈 간 상호 작용 방식 설정 (기본 모드에서 Node.js 동작 방식을 따르며, TypeScript의 esModuleInterop 동작과 다르므로 auto로 설정하여 ES모듈과 CommonJS모듈 간의 상호 운용성 문제를 줄임)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. package.json 수정 및 npm 배포&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm에 배포하기 위해서 entry 와 다른 정보들을 적어줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715762852344&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;@hellol77/speech-bubble&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;description&quot;: &quot;Speech bubble component&quot;,
  &quot;main&quot;: &quot;dist/index.cjs.js&quot;,
  &quot;module&quot;: &quot;dist/index.es.js&quot;,
  &quot;types&quot;: &quot;dist/index.d.ts&quot;,
  &quot;type&quot;: &quot;module&quot;,
  &quot;exports&quot;: {
    &quot;.&quot;: {
      &quot;module&quot;: &quot;./dist/index.es.js&quot;,
      &quot;import&quot;: &quot;./dist/index.es.js&quot;,
      &quot;default&quot;: &quot;./dist/index.cjs.js&quot;
    }
  },
  &quot;author&quot;: {
    &quot;name&quot;: &quot;hellol77&quot;,
    &quot;email&quot;: email
  },
  &quot;repository&quot;: {
    &quot;type&quot;: &quot;git&quot;,
    &quot;url&quot;: &quot;https://github.com/Hellol77/speech-bubble&quot;
  },
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 파일 수정시 배포할때는 version을 다른 버전으로 적어줘야 에러가 뜨지 않고 배포를 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;npm publish --access=public&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 하면 npm 배포가 가능합니다!&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;5. npm 배포 테스트 해보기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm 배포가 잘 되었는지 로컬에서 테스트 해볼 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;pnpm pack&lt;br /&gt;pnpm i ./hellol77-speech-bubble-0.0.1.tgz&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pnpm pack을 통해서 package를 만든후 만든 패키지 파일을 프로젝트에 추가해 줍니다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 후&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715768248359&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Pointer } from &quot;@hellol77/speech-bubble&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import 해주면 로컬에서 테스트 해볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 본격적으로 speech bubble을 개발해보도록 하겠습니다.&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;Reference&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1715768586319&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;[npm 라이브러리 배포] React 컴포넌트 라이브러리 배포전 테스트 및 배포하기&quot; data-og-description=&quot;[react 컴포넌트 npm 라이브러리로 배포하기] React 컴포넌트 라이브러리 배포전 테스트 및 배포하기편&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@seonye-98/npm-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B0%B0%ED%8F%AC-React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B0%B0%ED%8F%AC%EC%A0%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%B0%8F-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@seonye-98/npm-라이브러리-배포-React-컴포넌트-라이브러리-배포전-테스트-및-배포하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qCuML/hyV2EACD0x/a6qcmhXMNFadDDLk2DH1Pk/img.png?width=393&amp;amp;height=128&amp;amp;face=0_0_393_128,https://scrap.kakaocdn.net/dn/bEBZVU/hyV2w3ErSw/9YN85G6Co9d2K7NTIo2gPK/img.png?width=393&amp;amp;height=128&amp;amp;face=0_0_393_128,https://scrap.kakaocdn.net/dn/odyL5/hyV6bwLMu1/KD980Cu8WdDgwYAAW2bzIk/img.png?width=1512&amp;amp;height=1512&amp;amp;face=0_0_1512_1512&quot;&gt;&lt;a href=&quot;https://velog.io/@seonye-98/npm-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B0%B0%ED%8F%AC-React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B0%B0%ED%8F%AC%EC%A0%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%B0%8F-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@seonye-98/npm-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B0%B0%ED%8F%AC-React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B0%B0%ED%8F%AC%EC%A0%84-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%B0%8F-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qCuML/hyV2EACD0x/a6qcmhXMNFadDDLk2DH1Pk/img.png?width=393&amp;amp;height=128&amp;amp;face=0_0_393_128,https://scrap.kakaocdn.net/dn/bEBZVU/hyV2w3ErSw/9YN85G6Co9d2K7NTIo2gPK/img.png?width=393&amp;amp;height=128&amp;amp;face=0_0_393_128,https://scrap.kakaocdn.net/dn/odyL5/hyV6bwLMu1/KD980Cu8WdDgwYAAW2bzIk/img.png?width=1512&amp;amp;height=1512&amp;amp;face=0_0_1512_1512');&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;[npm 라이브러리 배포] React 컴포넌트 라이브러리 배포전 테스트 및 배포하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;[react 컴포넌트 npm 라이브러리로 배포하기] React 컴포넌트 라이브러리 배포전 테스트 및 배포하기편&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.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;figure id=&quot;og_1715768597349&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;[React] vite와 함께 리액트 컴포넌트 npm에 배포하기&quot; data-og-description=&quot;우리가 흔히 사용하는 react, eslint와 같은 패키지를 우리도 npm에 직접 배포할 수 있습니다. react 함수와 컴포넌트 등을 작성한 라이브러리를 추후 프로젝트에 만들어서 사용할 예정이기 때문에, &quot; data-og-host=&quot;devpluto.tistory.com&quot; data-og-source-url=&quot;https://devpluto.tistory.com/entry/React-vite%EC%99%80-%ED%95%A8%EA%BB%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-npm%EC%97%90-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://devpluto.tistory.com/entry/React-vite%EC%99%80-%ED%95%A8%EA%BB%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-npm%EC%97%90-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bkqdWC/hyV2BDTvh4/yYukmwqYHjleGkkz62coDK/img.png?width=800&amp;amp;height=552&amp;amp;face=0_0_800_552,https://scrap.kakaocdn.net/dn/6i1hW/hyV2uLvgKO/vEB6rj6KFHP6ymovV52Xfk/img.png?width=800&amp;amp;height=552&amp;amp;face=0_0_800_552,https://scrap.kakaocdn.net/dn/fLMAU/hyV2AkHM8R/upPK9mJzWeNrhkxpipKATk/img.png?width=1336&amp;amp;height=282&amp;amp;face=0_0_1336_282&quot;&gt;&lt;a href=&quot;https://devpluto.tistory.com/entry/React-vite%EC%99%80-%ED%95%A8%EA%BB%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-npm%EC%97%90-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://devpluto.tistory.com/entry/React-vite%EC%99%80-%ED%95%A8%EA%BB%98-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-npm%EC%97%90-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bkqdWC/hyV2BDTvh4/yYukmwqYHjleGkkz62coDK/img.png?width=800&amp;amp;height=552&amp;amp;face=0_0_800_552,https://scrap.kakaocdn.net/dn/6i1hW/hyV2uLvgKO/vEB6rj6KFHP6ymovV52Xfk/img.png?width=800&amp;amp;height=552&amp;amp;face=0_0_800_552,https://scrap.kakaocdn.net/dn/fLMAU/hyV2AkHM8R/upPK9mJzWeNrhkxpipKATk/img.png?width=1336&amp;amp;height=282&amp;amp;face=0_0_1336_282');&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;[React] vite와 함께 리액트 컴포넌트 npm에 배포하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;우리가 흔히 사용하는 react, eslint와 같은 패키지를 우리도 npm에 직접 배포할 수 있습니다. react 함수와 컴포넌트 등을 작성한 라이브러리를 추후 프로젝트에 만들어서 사용할 예정이기 때문에,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;devpluto.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;</description>
      <category>자유글</category>
      <category>npm 배포</category>
      <category>pnpm</category>
      <category>ViTE</category>
      <category>ㅍvi</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/102</guid>
      <comments>https://hellol77.tistory.com/102#entry102comment</comments>
      <pubDate>Wed, 15 May 2024 19:22:32 +0900</pubDate>
    </item>
    <item>
      <title>짧게 알아보는 React 19 버전</title>
      <link>https://hellol77.tistory.com/101</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;React를 주로 사용하는 개발자로서 React 19 버전에서 새로 추가되는 내용과 수정되는 내용은 뭔지 간단히 알아보는 시간을 가지겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐️ Compiler(컴파일러)&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 중요한 사실은 React에&amp;nbsp;&lt;b&gt;Compiler&lt;/b&gt;를 추가한다는 사실입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 프레임워크인 Astro나 Svelte는 컴파일 단계가 있어 많은 일들을 개발자 대신 해주지만 React는 컴파일 하는 스텝 없이 오직 브라우저에서 실행되어 추가적인 성능을 위해서 더 많은 코드를 작성해야 했죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서 useMemo, memo, useCallback 같은 hook들 말이죠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 React 19 에서는 compiler가 추가되어 자동적으로 memorization을 제공합니다. 따라서 19버전에서는 이러한 성가신 hook들을 더이상 사용하지 않아도 되며, 개발자가 memorization이 필요한 곳을 놓치는 곳도 찾아서 자동적으로 memorization해주기 때문에 더 빠르게 동작할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 React 19 중에서 가장 기대하는 부분인데요. 개발할 때마다 불필요한 리렌더를 찾고 고치는데 고생했었는데 이제 그럴 필요가 없어질 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐️ &quot;use client&quot; &amp;amp; &quot;use server&quot;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nextjs에서 흔히 보던 방식입니다. 코드가 서버에서 실행되고 있는지 혹은 클라이언트에서 실행되고 있는지 구별하기 위해 nextjs 내부 파일 상단에 적어 놓는 것이죠. 지금은 nextjs 에서만 사용할 수 있지만 앞으로 나올 React 19 에서도 사용 가능하게 된다고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는 nextjs 같은 프레임워크 뿐만 아니라 앞으로 개발될 다양한 프레임워크에서 사용될 것으로 생각됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⭐️ &lt;b&gt;MetaData&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는 title , meta, link 태그를 컴포넌트 트리안에서 아무곳에 적어도 바로 작동하게 된다고 합니다. 또한 SSR이나 CSR 등 모든 환경에서 제공된다고 합니다. react에서 SEO(검색 엔진 최적화)를 편하게 하려면 react helmet 이나 다른 라이브러리가 필요했었지만 앞으로는 편하게 SEO 작업이 가능할 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⭐️ &lt;b&gt;forwardRef&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트에 ref를 전달하기 위해 사용했던 forwardRef가 이제 사라진다고 합니다. 이제 모든 구성요소에 자동적으로 ref가 전달 된다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⭐️ use&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 컴포넌트는 async를 사용하여 비동기 처리를 할 수 있는데 반해 클라이언트 컴포넌트에서 비동기 처리를 위해서는 useEffect에서 비동기 함수를 선언하고 실행하는, 무엇인가 부자연스러운 형태로 처리를 해야했습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1715323001522&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ClientComponent() {
  useEffect(() =&amp;gt; {
    async function somethingAsync() {
      await something('...')
    }

    somethingAsync()
  }, [])

  return &amp;lt;&amp;gt;...&amp;lt;/&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 React 19에서는 &quot;&lt;b&gt;use&quot;&lt;/b&gt;라는 새로운 훅이 등장했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;use는 await 과 비슷한 개념이라고 할 수 있습니다. async 함수안에서만 await를 사용하듯이 use는 리액트 컴포넌트와 훅 내부에서만 사용할 수 있는 훅입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;use는 특이하게도 조건부로 호출할 수 있습니다. 조건부로 호출할 수 있는 이유는 다른 훅들과 달리 상태를 추적할 필요가 없기 때문입니다. useState를 비롯한 다른 훅들은 이전 상태와 연관지을 수 있도록 조건부로 실행되는 일 없이 실행되어야 하지만, use는 컴포넌트를 일단 렌더링 한뒤에는 데이터를 저장할 필요가 없기 때문입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;use를 사용하면 비동기 식으로 로드할 수 있게 됩니다. Promise 뿐만 아니라 Context라 던지 모든 데이터 타입도 지원합니다.&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;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1715280908778&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;리액트의 신규 훅, &amp;quot;use&amp;quot;&quot; data-og-description=&quot;promise가 완료되기 전까지 잠시 일시정지 했다가 다시 컴포넌트를 렌더링하기: 만약 use로 넘겨받은 promise의 로딩이 끝나지 않았다면 예외를 던지고, 컴포넌트의 렌더링을 일시 중단한다. 그리고 &quot; data-og-host=&quot;yceffort.kr&quot; data-og-source-url=&quot;https://yceffort.kr/2023/06/react-use-hook&quot; data-og-url=&quot;https://yceffort.kr/2023/06/react-use-hook&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/2Vd98/hyV2syPbxa/QzJOMA9fHayu4bq4wIIBQk/img.png?width=1200&amp;amp;height=651&amp;amp;face=0_0_1200_651,https://scrap.kakaocdn.net/dn/canwKs/hyV2A4GH6t/u5fc58i8nuMBJgpP8AaDf0/img.png?width=1200&amp;amp;height=651&amp;amp;face=0_0_1200_651,https://scrap.kakaocdn.net/dn/bie17Z/hyV2voM0ie/N8AkGkNU43rovFPYkdXRG1/img.png?width=1532&amp;amp;height=248&amp;amp;face=0_0_1532_248&quot;&gt;&lt;a href=&quot;https://yceffort.kr/2023/06/react-use-hook&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yceffort.kr/2023/06/react-use-hook&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/2Vd98/hyV2syPbxa/QzJOMA9fHayu4bq4wIIBQk/img.png?width=1200&amp;amp;height=651&amp;amp;face=0_0_1200_651,https://scrap.kakaocdn.net/dn/canwKs/hyV2A4GH6t/u5fc58i8nuMBJgpP8AaDf0/img.png?width=1200&amp;amp;height=651&amp;amp;face=0_0_1200_651,https://scrap.kakaocdn.net/dn/bie17Z/hyV2voM0ie/N8AkGkNU43rovFPYkdXRG1/img.png?width=1532&amp;amp;height=248&amp;amp;face=0_0_1532_248');&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;리액트의 신규 훅, &quot;use&quot;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;promise가 완료되기 전까지 잠시 일시정지 했다가 다시 컴포넌트를 렌더링하기: 만약 use로 넘겨받은 promise의 로딩이 끝나지 않았다면 예외를 던지고, 컴포넌트의 렌더링을 일시 중단한다. 그리고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yceffort.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=v07gXY6ESEo&amp;amp;t=318s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=v07gXY6ESEo&amp;amp;t=318s&lt;/a&gt;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>Compiler</category>
      <category>forwardRef</category>
      <category>react</category>
      <category>react 19</category>
      <category>use</category>
      <category>use client</category>
      <category>use server</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/101</guid>
      <comments>https://hellol77.tistory.com/101#entry101comment</comments>
      <pubDate>Fri, 10 May 2024 15:51:59 +0900</pubDate>
    </item>
    <item>
      <title>[docker] vite 배포하면서 겪었던 시행착오</title>
      <link>https://hellol77.tistory.com/100</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;docker 란?&lt;/b&gt;&lt;/h4&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;284&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RWbzs/btsGB9Qn3if/Oka9HPoYEQwfmkfKnsTI8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RWbzs/btsGB9Qn3if/Oka9HPoYEQwfmkfKnsTI8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RWbzs/btsGB9Qn3if/Oka9HPoYEQwfmkfKnsTI8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRWbzs%2FbtsGB9Qn3if%2FOka9HPoYEQwfmkfKnsTI8k%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;412&quot; height=&quot;232&quot; data-origin-width=&quot;284&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;따라서..&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker를 사용해서 ec2에 배포하는 것이 목표인데요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ec2에 배포하기 전 연습을 위해 로컬에 docker를 사용해서 service 프로젝트 배포했어요&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;686&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kO2CD/btsGEhGCsEV/SB0RnCKKTDZEQkUIgHfc41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kO2CD/btsGEhGCsEV/SB0RnCKKTDZEQkUIgHfc41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kO2CD/btsGEhGCsEV/SB0RnCKKTDZEQkUIgHfc41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkO2CD%2FbtsGEhGCsEV%2FSB0RnCKKTDZEQkUIgHfc41%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;384&quot; height=&quot;549&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 모노레포 디렉토리는 위와 같은 구조로 되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 service 는 vite를 사용해서 빌드하고 있고 yarn berry를 사용해 개발하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금부터 만났던 에러들과 배포하는데 힘들었던 내용을 정리하고자 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;본 글은 로컬 배포일때의 겪었던 문제들을 적은 것임을 알립니다.&lt;/b&gt;&lt;/span&gt;&lt;/h3&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;  첫번째 문제 : vite Config&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 문제는 vite의 빌드 문제였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째로 해주어야 할 것은 vite.config.ts를 수정해주어야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713184740965&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default defineConfig({
  plugins: [react(), vitePluginSvgr(), tsconfigPaths()],
  base: &quot;/&quot;,
  server: {
    host: true,
    port: 3001,
  },
  test: {
    globals: true,
    environment: &quot;jsdom&quot;,
    setupFiles: &quot;/setupVitest.ts&quot;,
  },
} as VitestConfigExport);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;중요한 것은 server 부분입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vite는 서버가 수신할 IP 주소가 고정되어 있기 때문에 LAN과 공용 주소를 포함한 모든 주소를 수신하려면 이 값을 0.0.0.0 이나 true로 설정하여야 합니다.&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;&lt;b&gt;ERR_CONNECTION_RESET&lt;/b&gt; 이 뜬다면 이 설정을 안해놓았을 확률이 높습니다.&lt;span style=&quot;background-color: #202124; color: #e8eaed; text-align: left;&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;b&gt;  두번째 문제 : Zero install을 사용하고 있다면&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Zero install 을 사용하고 있다면 한번은 DockerFile에서 yarn install 이 필요합니다. 바로 .yarn/unplugged 에 있는 모듈 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yarn berry는 의존성 패키지들을 모두 zip으로 처리합니다. 그렇기 때문에 읽기 밖에 가능하지 않습니다. 하지만 의존성 패키지가 쓰여지거나 실행이 되어야 한다면 zip 파일은 압축 해제되어 .yarn/unplugged 디렉토리에 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일들은 압축 파일이 아닌 기존의 node_modules 와 비슷한 형태로 되어 있다고 합니다. 각 플랫폼에 맞는 바이너리를 로드해야 하기 때문에 최초 한번은 DockerFile 내에서 yarn install 이 필요합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt; &lt;b&gt; 세번째 문제 : platfrom :linux arn64를 사용할때 &lt;/b&gt;&lt;b&gt;@esbuild/linux-x64@npm Initiated Worker with invalid execArgv flags: --no-opt &lt;/b&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-04-14 오후 9.37.36.png&quot; data-origin-width=&quot;2480&quot; data-origin-height=&quot;1132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cItJ9m/btsGB49yJok/3wmivS5Uyzpxbuy3XlVSjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cItJ9m/btsGB49yJok/3wmivS5Uyzpxbuy3XlVSjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cItJ9m/btsGB49yJok/3wmivS5Uyzpxbuy3XlVSjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcItJ9m%2FbtsGB49yJok%2F3wmivS5Uyzpxbuy3XlVSjK%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;694&quot; height=&quot;317&quot; data-filename=&quot;스크린샷 2024-04-14 오후 9.37.36.png&quot; data-origin-width=&quot;2480&quot; data-origin-height=&quot;1132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 배포를 하면서 가장 골머리를 앓았던 에러메시지입니다. 맥북에서 작업한 도커 이미지는 linux 환경에서는 정상적으로 동작하지 않습니다. 따라서 platform linux/amd64 를 통해서 linux환경이라고 명시해주어야 동작을 원활히 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 에러는 바로 linux/arm64을 사용했을때 나왔던 에러입니다. 원인은 바로 esbuild의 특징 때문인데요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 코드로 작성되어 플랫폼 별로 바이너리 실행 파일을 설치해야하는 esbuild 특성 상 하나의 os에서 esbuild를 설치한 후 다시 설치하지 않고 디렉토리를 다른 os에 복사했을때 해당 다른 Os 에서 esbuild를 실행할 수 없기 때문입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 특히 window 나 맥북에 esbuild를 설치하고 해당 디렉토리를 linux를 실행하는 docker에서 많이 발생한다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제의 경우 yarnrc.yml을 사용하여 어떤 플랫폼에서 사용할 것인지 작성해주면 해결이 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713199596727&quot; class=&quot;haml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;// .yarnrc.yml

supportedArchitectures:
    os:
        - 'darwin'
        - 'linux'
        - 'win32'
    cpu:
        - 'arm'
        - 'arm64'
        - 'x64'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  네번째 문제: @rollup/rollup-linux-x64-musl Initiated Worker with invalid execArgv flags: --no-opt&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2430&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vSKoC/btsGFjKM8Eu/T7SZg7iKZjRSLdL9WIBfKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vSKoC/btsGFjKM8Eu/T7SZg7iKZjRSLdL9WIBfKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vSKoC/btsGFjKM8Eu/T7SZg7iKZjRSLdL9WIBfKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvSKoC%2FbtsGFjKM8Eu%2FT7SZg7iKZjRSLdL9WIBfKK%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;2430&quot; height=&quot;1000&quot; data-origin-width=&quot;2430&quot; data-origin-height=&quot;1000&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;이 에러는 vite 의 빌드 문제와 연관이 있습니다. github 에도 docker 컨테이너에서 rollup 을 찾을 수 없다는 이슈가 올라와 있습니다.&lt;/p&gt;
&lt;figure id=&quot;og_1713203371138&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;Error: Cannot find module @rollup/rollup-linux-x64-gnu on Docker container &amp;middot; vitejs vite &amp;middot; Discussion #15532&quot; data-og-description=&quot;Describe the bug I researched and tried on the sites below and on many different platforms, but I could not find a solution. I am using node:lts and also I cannot download this module in my docker ...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/vitejs/vite/discussions/15532&quot; data-og-url=&quot;https://github.com/vitejs/vite/discussions/15532&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/slLjc/hyVPNjjEW7/6d1BauIRHMVO36jZFIec6k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/vitejs/vite/discussions/15532&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/vitejs/vite/discussions/15532&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/slLjc/hyVPNjjEW7/6d1BauIRHMVO36jZFIec6k/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;Error: Cannot find module @rollup/rollup-linux-x64-gnu on Docker container &amp;middot; vitejs vite &amp;middot; Discussion #15532&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Describe the bug I researched and tried on the sites below and on many different platforms, but I could not find a solution. I am using node:lts and also I cannot download this module in my docker ...&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;p data-ke-size=&quot;size16&quot;&gt;이번 에러는 package.json에 yarn resolutions 옵션을 이용하면 해결이 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713203829316&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;./packages.json
    &quot;resolutions&quot;: {
        ...
        &quot;rollup&quot;: &quot;npm:@rollup/wasm-node@*&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yarn resolutions 는 의존하는 모듈을 특정 버전으로 고정시키고 싶을때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;yarn resolutions 가 필요할 수 있는 경우는 몇가지가 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 부정적인 영향을 끼칠 수 있는 문제 있는 업데이트가 있어 이전 버전으로 고정하고 싶은 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 하위 의존성 B에 업데이트가 있었는데 B에 의존하는 A가 이를 커버하지 않는 경우, A가 이 업데이트를 커버할 때까지 기다릴 수 없으므로 B의 버전을 이전 버전으로 고정하는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 하위 의존성 B에 업데이트가 있었는데 굳이 반영할 필요성을 못 느끼는 경우&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;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;간혹 가다가 이런 에러들 말고 다양한 에러들도 발생했는데 vite 업데이트와 각종 필요한 라이브러리 설치와 함께 해결했던 것 같습니다&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;이번 글은 로컬에서 docker 배포를 해보고 싶은 분들을 위해 로컬 배포를 진행하면서 겪었던 에러들을 적어본 글이었어요.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ec2에 배포를 할때는 platform 을 지정을 안해줘도 원활하게 배포가 가능해서 이런 에러들을 마주치지 않을 것이라고 생각이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;다음에는 드디어 docker + ngnix + ec2 와 함께 github action을 사용해서 CI/CD 를 구현해보도록 하겠습니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1713204873590&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;package.json의 resolutions&quot; data-og-description=&quot;resolutions package.json에 resolutions라는 필드가 있는데, 여기서는 package.json에 있는 의존성들의 특정 버전이나 range를 지정할 수 있다. package.json에는 일반적으로 semantic versioning을 사용하여 range로 패키&quot; data-og-host=&quot;developer-alle.tistory.com&quot; data-og-source-url=&quot;https://developer-alle.tistory.com/417&quot; data-og-url=&quot;https://developer-alle.tistory.com/417&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/X7nxd/hyVPVBERap/VpJkpaEbLP1dj2FN6MfWpk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ByS9Z/hyVPTw5Duz/EbZAZDKI6h2m5FjQr8y9oK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://developer-alle.tistory.com/417&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer-alle.tistory.com/417&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/X7nxd/hyVPVBERap/VpJkpaEbLP1dj2FN6MfWpk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/ByS9Z/hyVPTw5Duz/EbZAZDKI6h2m5FjQr8y9oK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;package.json의 resolutions&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;resolutions package.json에 resolutions라는 필드가 있는데, 여기서는 package.json에 있는 의존성들의 특정 버전이나 range를 지정할 수 있다. package.json에는 일반적으로 semantic versioning을 사용하여 range로 패키&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer-alle.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1713204889848&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;Nest.js에 Yarn Berry + Zero Install 적용하기&quot; data-og-description=&quot;Nest.js에 Yarn PnP + Zero Install을 적용하는 겪은 여러 이야기들을 담았습니다.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@biud436/Nest.js%EC%97%90-Yarn-PnP-Zero-Install-Docker-%EC%A0%81%EC%9A%A9&quot; data-og-url=&quot;https://velog.io/@biud436/Nest.js에-Yarn-PnP-Zero-Install-Docker-적용&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xcdc8/hyVPRzeJe1/eTx1kXfrVD2DQqKufms5v0/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200,https://scrap.kakaocdn.net/dn/IlG7W/hyVPYd4H9b/KxTkuxkLBTqzuaoIiSPcq1/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200,https://scrap.kakaocdn.net/dn/c4PQe8/hyVPNKomjN/KzsYdaplStkfB4IIzVBzlk/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200&quot;&gt;&lt;a href=&quot;https://velog.io/@biud436/Nest.js%EC%97%90-Yarn-PnP-Zero-Install-Docker-%EC%A0%81%EC%9A%A9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@biud436/Nest.js%EC%97%90-Yarn-PnP-Zero-Install-Docker-%EC%A0%81%EC%9A%A9&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xcdc8/hyVPRzeJe1/eTx1kXfrVD2DQqKufms5v0/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200,https://scrap.kakaocdn.net/dn/IlG7W/hyVPYd4H9b/KxTkuxkLBTqzuaoIiSPcq1/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200,https://scrap.kakaocdn.net/dn/c4PQe8/hyVPNKomjN/KzsYdaplStkfB4IIzVBzlk/img.png?width=1200&amp;amp;height=1200&amp;amp;face=0_0_1200_1200');&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;Nest.js에 Yarn Berry + Zero Install 적용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Nest.js에 Yarn PnP + Zero Install을 적용하는 겪은 여러 이야기들을 담았습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자유글</category>
      <category>docker</category>
      <category>Initiated Worker with invalid execArgv flags: --no-opt</category>
      <category>ROLLUP</category>
      <category>ViTE</category>
      <category>yarn berry</category>
      <category>yarn workspace</category>
      <category>zero install</category>
      <category>모노레포</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/100</guid>
      <comments>https://hellol77.tistory.com/100#entry100comment</comments>
      <pubDate>Tue, 16 Apr 2024 03:14:01 +0900</pubDate>
    </item>
    <item>
      <title>[#회고] 지금까지의 회고록</title>
      <link>https://hellol77.tistory.com/99</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 회고록&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대학교 수업을 모두 마치고 이제 개발 공부를 이어간지 언 반년 이상이 지났습니다. 그 때보단 아는 것도 많아지고 그랬습니다만은 아직도 느껴지는 부족함과 나약함. 지금 내가 이렇게 하는게 맞는 걸까 라는 생각을 수도 없이 합니다. 처음 혼자 개발할 때는 정말 재밌어서 내가 만들고 싶은 개인 프로젝트도 진행하고 전에 있던 동아리에서 해커톤도 진행했어요. 하지만 갑자기 이게 잘한게 맞는지 되돌아보면 모르겠습니다. 더 넓은 세상으로 예를 들어 다른 개발 동아리나 부트캠프라도 했어야 했나 라는 생각이 듭니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 잘했던 점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;개인 프로젝트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼자서 개인 프로젝트를 했던게 그래도 웹 구조가 어떻게 돌아가는지 알아 볼 수 있는 좋은 계기가 된것 같아요. 벡엔드와 디자인 모두 혼자 한번 해보았어요. 벡엔드는 최대한 비용을 줄이기 위해서 Lambda 도 써보고 피그마를 사용해서 디자인도 해보았어요. 모든걸 저 혼자서 하다 보니 정말 개발이 오래걸리더군요. 배포도 S3와 cloudfront로 해보았는데요. 정적 배포를 한 뒤 뒤늦게 정적 배포를 하면 Nextjs의 몇 가지 기능이 안된다는 것을 깨닫고 vercel로 배포했던 것이 기억에 오래 남습니다. 정적 배포는 4일 동안 온갖 블로그와 해외 블로그까지 다 뒤져가며 배포를 했었어요. 결국에는 설정 몇개 만 하면 끝나는 것이 였던이었는데 말이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 내용은 이미지 클라우드 게시판입니다. 댓글과 좋아요를 누를 수 있죠. 한로로 덕질을 하면서 뭔가 공부하면서 덕질을 할 수는 없을까 생각하다 만든 프로젝트였어요. 무한 스크롤도 넣어보고 framer motion도 사용해보았어요. 정말 재밌게 개발했던것 같아요.&lt;/p&gt;
&lt;figure id=&quot;og_1712213064006&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;SSR 과 CSR 눈으로 비교해 보기&quot; data-og-description=&quot;  프로젝트 중... next.js로 간단한 프로젝트를 하고 있는 와중 중대한(?) 결정에 놓였다. 최근 게시물을 보여주는 페이지를 SSR로 할 것인가 CSR로 할 것인가였다. ⭐️ SSR (Server Side Rendering) next.js&quot; data-og-host=&quot;hellol77.tistory.com&quot; data-og-source-url=&quot;https://hellol77.tistory.com/90&quot; data-og-url=&quot;https://hellol77.tistory.com/90&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c1p8RD/hyVJSLt1Ny/w2BkwiKsMur1NU4bxHG3u1/img.png?width=800&amp;amp;height=1149&amp;amp;face=156_204_417_947,https://scrap.kakaocdn.net/dn/d3K7Pq/hyVJZ4V58a/7zBMJHKLQPs7DxQ9dslFpK/img.png?width=800&amp;amp;height=1149&amp;amp;face=156_204_417_947,https://scrap.kakaocdn.net/dn/iMeVV/hyVJ6iGfXs/3B2DnkRKyllgcomubzqqz1/img.png?width=1320&amp;amp;height=1896&amp;amp;face=254_337_982_1679&quot;&gt;&lt;a href=&quot;https://hellol77.tistory.com/90&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hellol77.tistory.com/90&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c1p8RD/hyVJSLt1Ny/w2BkwiKsMur1NU4bxHG3u1/img.png?width=800&amp;amp;height=1149&amp;amp;face=156_204_417_947,https://scrap.kakaocdn.net/dn/d3K7Pq/hyVJZ4V58a/7zBMJHKLQPs7DxQ9dslFpK/img.png?width=800&amp;amp;height=1149&amp;amp;face=156_204_417_947,https://scrap.kakaocdn.net/dn/iMeVV/hyVJ6iGfXs/3B2DnkRKyllgcomubzqqz1/img.png?width=1320&amp;amp;height=1896&amp;amp;face=254_337_982_1679');&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;SSR 과 CSR 눈으로 비교해 보기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  프로젝트 중... next.js로 간단한 프로젝트를 하고 있는 와중 중대한(?) 결정에 놓였다. 최근 게시물을 보여주는 페이지를 SSR로 할 것인가 CSR로 할 것인가였다. ⭐️ SSR (Server Side Rendering) next.js&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hellol77.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 미숙한 점이 많고 아무래도 serverless 벡엔드로 돌아가다 보니 api 요청이 조금 늦는 경우가 있지만 나름 처음 했던 풀스택 프로젝트로써 자신감도 얻었던 프로젝트였었던 것 같아요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;팀 프로젝트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 팀 프로젝트에 참여하게 되었습니다. 팀 프로젝트는 처음 들어갈때는 이미 2개월 정도 진행된 상태였는데요. 팀원들도 상당히 열심히 하시고 열정적이셔서 참가했습니다. 올해 5월 까지는 이 프로젝트는 끝내는 것이 목표입니다.&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1712237753201&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 - teamWaggle/Waggle-front: 반려견에 대한 모든 것을 공유하고 소통하는 공간  &quot; data-og-description=&quot;반려견에 대한 모든 것을 공유하고 소통하는 공간  . Contribute to teamWaggle/Waggle-front development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/teamWaggle/Waggle-front&quot; data-og-url=&quot;https://github.com/teamWaggle/Waggle-front&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wORev/hyVJ3zFEKk/RteXLKgoLq0YZ2OmXKfOe1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/teamWaggle/Waggle-front&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/teamWaggle/Waggle-front&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wORev/hyVJ3zFEKk/RteXLKgoLq0YZ2OmXKfOe1/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 - teamWaggle/Waggle-front: 반려견에 대한 모든 것을 공유하고 소통하는 공간  &lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;반려견에 대한 모든 것을 공유하고 소통하는 공간  . Contribute to teamWaggle/Waggle-front 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;지금까지 했던 팀 프로젝트 중에 제일 체계적이고 소통하고 서로 맞춰가면서 이루어지는 프로젝트라서 기대가 됩니다. storybook과 test도 점진적으로 도입해나갈 예정이구요. 또한 나중에 팀프로젝트 디자인 시스템을 좀 더 다듬어서 npm 배포도 해볼 예정이에요.&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;3. 반성해야할 점과 고쳐야할 점&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;혼자 공부한다는 것은&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 회고록을 작성하면서 반성해야 할 점이 더욱 많은 것 같아요. 2023년 후반기에는 해커톤 활동과, 훕팅 팀 프로젝트를 제외하면 늘 저혼자 공부하면서 개발했던 것 같아요. 나름 열심히 한다고 공부를 해왔습니다만 이력서들을 쓰면서 깨달았습니다. 혼자서는 도저히 어필 할 내용이 떠오르지 않다는 것과 혼자 공부한 내용들을 어떻게 써야할지 감이 오지를 않았어요. 결국 취업을 목표로 한다면 협업을 해야하는 것인데 제가 너무 간과했던 것 같아요. 이번 4월 부터는 다른 네트워킹 활동을 한번 찾아보러 다닐 예정이에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&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;h4 data-ke-size=&quot;size20&quot;&gt;4. 앞으로 해야하는 것&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워킹&lt;/li&gt;
&lt;li&gt;스터디 시작하기&lt;/li&gt;
&lt;li&gt;블로그 글 꾸준히 쓰기&amp;nbsp;&lt;/li&gt;
&lt;li&gt;알고리즘 꾸준히 문제 풀기&lt;/li&gt;
&lt;li&gt;이력서 리펙토링(?)&lt;/li&gt;
&lt;li&gt;같이 일하고 싶은 사람이 되기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. 생각&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;난 왜 개발자가 되고 싶어 했나&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 어느샌가 부터 뭔가를 만들어 내는 사람이 되고 싶었어요. 무언가의 창조자가 되고 싶었습니다. 화가, 작곡가, 작가. 무에서 유를, 마음대로 창조하는 사람들이 정말 멋있어 보였고 존경하고 되고 싶었어요. 전 아쉽게도 그런쪽 재능은 없더군요. 그러다 찾게된 개발. 아무것도 없는 흰 화면에다 뭔가를 채워나가는 과정과 내 생각이 컴퓨터에서 실현되는 것이 저도 예술가로 만드는 것 같았고 여기까지 저를 이끌었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여전히 개발은 재밌고 제가 꾸준히 할 수있는 것 중에 하나입니다. 하지만 요즘은 조금 힘에 벅차는 것도 사실이에요. 취준 생활을 하면서 구글링을 하며 다양한 블로그글을 읽어보는 데 정말 대단하신 분들이 많습니다. 요런걸 어떻게 저렇게 생각했을까.. 어떻게 만들었을까.. 나는 취업을 할 수 있을까. 나는 개발할 능력이 안되었던걸까? 무력감이 몰려옵니다. 또한 그러다가 아냐 난 할 수 있다. 화이팅..! 그러다 또 다시 무력감... 이런 변덕스러운 생각이 온종일 맴돌아요.&lt;/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;</description>
      <category>자유글</category>
      <category>회고</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/99</guid>
      <comments>https://hellol77.tistory.com/99#entry99comment</comments>
      <pubDate>Thu, 4 Apr 2024 23:43:02 +0900</pubDate>
    </item>
    <item>
      <title>[React hook form] 제어 컴포넌트와 비제어 컴포넌트 동시에 사용하기</title>
      <link>https://hellol77.tistory.com/98</link>
      <description>&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;Reack hook form은 form 을 만들때 쓰는 대표적인 라이브러리 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;form 구조 떄문에 어쩔 수 없는 많은 state로 부터 우리를 지켜주는 존재입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 그에 따른 리렌더링 문제로 부터 자유롭게 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reack hook form는 대표적으로 비제어 컴포넌트를 사용하긴 하지만 제어 컴포넌트로도 form을 만들 수 있게 하는데요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 만들어보는 form은 react hook form을 사용해 사용자가 문자를 입력할때 마다 적합한 input인지 확인하기 위해 제어 컴포넌트로 만들고 굳이 입력할 때마다 유효성 검사가 필요없는 input에 관해서는 비제어 컴포넌트로도 만드려고 합니다.&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;2634&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yPHuu/btsGj5tcvv2/aH1i5hNPj7PqNvbfBjcKVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yPHuu/btsGj5tcvv2/aH1i5hNPj7PqNvbfBjcKVK/img.png&quot; data-alt=&quot;react-hook-form vs formik&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yPHuu/btsGj5tcvv2/aH1i5hNPj7PqNvbfBjcKVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyPHuu%2FbtsGj5tcvv2%2FaH1i5hNPj7PqNvbfBjcKVK%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;2634&quot; height=&quot;908&quot; data-origin-width=&quot;2634&quot; data-origin-height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;react-hook-form vs formik&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;b&gt;1. (Controlled Component)제어 컴포넌트란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어 컴포넌트는 React state를 사용해서 사용자 입력값을 제어하는 방식입니다. 사용자가 입력한 값과 State값이 실시간으로 동기화 됩니다. 흔히 useState를 사용해서 input에 연결하는 방식이죠&lt;/p&gt;
&lt;pre id=&quot;code_1712122684971&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState } from 'react';

function ControlledComponentExample() {
  const [inputValue, setInputValue] = useState('');

  const handleChange = (event) =&amp;gt; {
    setInputValue(event.target.value);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input
        type=&quot;text&quot;
        value={inputValue} 
        onChange={handleChange}
      /&amp;gt;
      &amp;lt;p&amp;gt;입력된 값: {inputValue}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default ControlledComponentExample;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실시간으로 유효성 검사나 특정한 입력 방식을 적용할때 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 단점이 존재합니다. Input값이 바뀔 때마다 리렌더링이 일어난다는 것입니다. 불필요한 리렌더링, api요청이 이루어질 수 있다는 것입니다. 이러한 단점을 해결하기위해 debounce , throttling 같은 방법들을 사용하기도 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. (Uncontrolled Component)비제어 컴포넌트&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비제어 컴포넌트는 ref를 사용해서 DOM에서 직접 form 값을 가져오는 방법입니다. 비제어 컴포넌트는 사용자가 직접 submit하기 전까지는 리렌더링을 발생시키지 않고 값을 동기화 하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react를 쓰지 않고 자바스크립트만 사용했을때와 같다고 생각하면 될것 같아요&lt;/p&gt;
&lt;pre id=&quot;code_1712123684528&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;import React, { useRef } from 'react';

function UncontrolledComponentExample() {
  const inputRef = useRef(null);

  const handleButtonClick = () =&amp;gt; {
    console.log('입력된 값:', inputRef.current.value);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input type=&quot;text&quot; ref={inputRef} /&amp;gt;
      &amp;lt;button onClick={handleButtonClick}&amp;gt;입력값 출력&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default UncontrolledComponentExample;&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;b&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 요구사항&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 개발 요구사항을 살펴보겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;form 구조
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;image file input
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지를 올리면 preview를 보여줘야 한다.&lt;/li&gt;
&lt;li&gt;이미지 초기화 버튼을 누르면 이미지가 초기화 되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;제목 입력 input과 내용 입력 textarea
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제목 입력값은 필수로 입력해야 한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;입력값은 30자 이내이어야 하고 특수문자는 submit 될수 없다&lt;/li&gt;
&lt;li&gt;입력할 때마다 이 값이 유효한지 확인해야 한다.&lt;/li&gt;
&lt;li&gt;첫 페이지 렌더링시에는 값이 비어있더라도 경고 메시지가 띄워지면 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;색깔 radiobox&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;radiobox는 한가지만 선택 할 수 있다.&lt;/li&gt;
&lt;li&gt;무조건 한개는 선택되어 있기 때문에 유효성 검사가 필요 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미리보기 또는 유효성 검사가 있는 image와 제목 , 내용 입력 Input은 제어 컴포넌트로 구현하여야 하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색깔 radiobox는 비제어 컴포넌트로 구현해야 불필요한 렌더링을 없앨 수 있을 것 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가독성과 유연함이 좋아 재사용성이 높다는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Compound 패턴의&lt;/span&gt; 장점을 가져가기 위해서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 저는 Compound 패턴으로 컴포넌트를 구현 해볼 생각이고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유효성 검사는 yup을 사용해보려고 합니다!&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Form 컴포넌트&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1712127275101&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Form = ({
	children,
	onSubmit,
	defaultValues,
	schema,
}: {
	children: React.ReactNode;
	onSubmit: (data: FieldValues) =&amp;gt; void;
	defaultValues: FieldValues;
	schema: yup.ObjectSchema&amp;lt;FieldValues&amp;gt;;
}) =&amp;gt; {
	const method = useForm&amp;lt;FieldValues&amp;gt;({
		defaultValues: defaultValues,
		resolver: yupResolver(schema),
	});
	const submit: SubmitHandler&amp;lt;FieldValues&amp;gt; = (data) =&amp;gt; {
		onSubmit(data);
	};
	return (
		&amp;lt;FormProvider {...method}&amp;gt;
			&amp;lt;form onSubmit={method.handleSubmit(submit)}&amp;gt;{children}&amp;lt;/form&amp;gt;
		&amp;lt;/FormProvider&amp;gt;
	);
};

Form.ColorRadioInputField = ColorRadioInputField;
Form.ImageInputField = ImageInputField;
Form.TitleInputField = TitleInputField;
Form.ContentInputField = ContentInputField;

export default Form;&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;react hook form 에서 제공하는 FormProvider를 통해서 useForm을 자식 컴포넌트에게 전해줍니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 useForm은 양식을 쉽게 관리하게 만드는 hook으로 값 추적,submit , 유효성 검사 등 form 에 필요한 모든 상태를 useForm 으로 한번에 처리할 수 있게 만들어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useForm 에서는 register라는 등록 메소드를 많이 이용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;register 메소드는 input이나 select 엘리먼트를 등록하고 유효성 검사 규칙을 적용할 수 있게 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이는 비제어 컴포넌트에서 많이 이용합니다. 물론 register 함수를 이용해서 제어컴포넌트를 만들 수 있지만 setState를 통해서 값을 수동으로 변경시켜줘야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1712132013163&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;register('firstName', { required: true, min: 8 });


&amp;lt;TextInput onTextChange={(value) =&amp;gt; setValue('lastChange', value))} /&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;따라서 제어 컴포넌트를 react hook form 에서 사용하려면 제공하는 Controller 컴포넌트를 사용하는 방법 혹은 useController hook을 사용해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useController hook은 Controller 컴포넌트의 props와 메소드들을 공유하여 재사용 가능한 제어 컴포넌트를 만드는데 유용하고 좀 더 가독성있는 코드를 작성 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yup을 사용해 작성된 유효성 검사를 props로 받아오게 설계해 쉽게 유효성 조건을 변경 가능하게 만들었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 컴포넌트의 한가지 단점이 있다면 react hook form , yup 라이브러리에 너무 의존하고 있다는 것이 있겠네요&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Text input field (제어 컴포넌트)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1712132224589&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const TextInputField = ({
	name,
	validateText,
	placeholder,
}: {
	name: FieldPath&amp;lt;FieldValues&amp;gt;;
	validateText: string;
	placeholder: string;
}) =&amp;gt; {
	const { control, trigger } = useFormContext();
	const { field: textField } = useController({
		control,
		name,
	});
	const { errors } = useFormState({ control, name });
	const isValid = !errors[name];

	const handleOnChange = (e: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
		textField.onChange({ target: { value: e.target.value } });
		trigger(name);
	};
	return (
		&amp;lt;&amp;gt;
			&amp;lt;input
				type=&quot;text&quot;
				onChange={handleOnChange}
				css={titleTextInputStyle(false)}
				placeholder={placeholder}
			/&amp;gt;
			&amp;lt;InputNotice isValid={isValid} message={(errors[name]?.message as string) || validateText} /&amp;gt;
		&amp;lt;/&amp;gt;
	);
};

export default TextInputField;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react hook form에서 제공하는 useFormContext를 사용해 useForm 을 불러옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 control는 form 을 컨트롤 할 수 있게 하는 메소드들이 담긴 객체로 useController를 통해 input에 접근합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;trigger는 양식 또는 입력 유효성 검사를 수동 트리거 입니다. name 변수에 해당하는 input 수동으로 검사합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onChange 를 등록해주어 값을 제어해 줍니다.&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;여기서 만약 input에 field만 주입해준다면 비제어 컴포넌트가 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1712138566961&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비제어 컴포넌트
const { field } = useController({ name: 'test' })

&amp;lt;input {...field} /&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;error[name].message의 경우에는 다른 타입은 쓰지 않고 오로지 string으로 에러메시지를 작성할 것이기 때문에 as string으로 타입 처리를 해주었습니다.&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;- ImageField (제어 컴포넌트)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1712138020875&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const ImageInputField = ({ name }: { name: FieldPath&amp;lt;FieldValues&amp;gt; }) =&amp;gt; {
	const { control } = useFormContext();
	const { field: imageField } = useController({
		control,
		name,
	});
	const { value } = imageField;

	const imagePreview = useImagePreview(value);

	const handleResetImage = () =&amp;gt; {
		imageField.onChange({ target: { value: null, name: &quot;image&quot; } });
	};

	const handleOnChange = (e: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
		const file = e.target.files;
		if (file) {
			imageField.onChange({ target: { value: file, name: &quot;image&quot; } });
		}
	};
	return (
		&amp;lt;Flex css={imageInputBoxStyle}&amp;gt;
			&amp;lt;input
				type=&quot;file&quot;
				multiple={false}
				accept=&quot;image/*&quot;
				css={imageInputStyle}
				id=&quot;image&quot;
				onChange={handleOnChange}
			/&amp;gt;
			&amp;lt;label css={imageBoxStyle(!!imagePreview)} htmlFor=&quot;image&quot;&amp;gt;
				{imagePreview &amp;amp;&amp;amp; &amp;lt;img css={imageStyle} src={imagePreview} alt=&quot;team image&quot; /&amp;gt;}
				{!imagePreview &amp;amp;&amp;amp; &amp;lt;PhotoIcon style={{ width: &quot;40px&quot; }} /&amp;gt;}
			&amp;lt;/label&amp;gt;
			&amp;lt;Flex onClick={handleResetImage} css={resetImageButtonStyle}&amp;gt;
				사진 초기화
			&amp;lt;/Flex&amp;gt;
		&amp;lt;/Flex&amp;gt;
	);
};
export default ImageInputField;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;textField랑 다른점이 있다면 preview 기능과 초기화 기능입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;preview 기능을 위해서 field 를 사용해서 파일을 가져온 후&amp;nbsp; 파일을 url로 변환하고 보여줍니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;url 변환 후에는 메모리 누수 방지를 위해 항상 revoke 하는 것을 잊으면 안됩니다&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;- ColorRadioInpuField (비제어 컴포넌트)&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1712133530187&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const ColorRadioInputField = ({ name }: { name: FieldPath&amp;lt;FieldValues&amp;gt; }) =&amp;gt; {
	const { register } = useFormContext();

	return (
		&amp;lt;Flex css={ColorRadioInputFieldStyle}&amp;gt;
			{TEAM_COLOR.map((color) =&amp;gt; {
				return &amp;lt;ColorRadioButton key={color} color={color} register={register(name)} /&amp;gt;;
			})}
		&amp;lt;/Flex&amp;gt;
	);
};
export default ColorRadioInputField;&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;비제어 컴포넌트를 구현하는것은 더 간단합니다. useForm의 register를 받아와 Button에 등록시켜주면 끝이 납니다.&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;h4 data-ke-size=&quot;size20&quot;&gt;- form 사용하기&lt;/h4&gt;
&lt;pre id=&quot;code_1712137773842&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const schema = yup
	.object({
		title: TEAM_TITLE.RULES(),
		content: TEAM_CONTENT.RULES(),
	})
	.required();

const Main = () =&amp;gt; {
	const navigate = useNavigate();

	const onSubmit = (data: FieldValues) =&amp;gt; {
// ...
	};
	return (
		&amp;lt;&amp;gt;
			&amp;lt;Form onSubmit={onSubmit} defaultValues={TEAM_DEFAULT_VALUES} schema={schema}&amp;gt;
				&amp;lt;Form.ImageInputField name=&quot;image&quot; /&amp;gt;
				&amp;lt;Form.TitleInputField
					placeholder={TEAM_TITLE.PLACEHOLDER}
					name={TEAM_TITLE.NAME}
					validateText={TEAM_TITLE.VALIDATE_TEXT()}
				/&amp;gt;
				&amp;lt;Form.ContentInputField
					placeholder={TEAM_CONTENT.PLACEHOLDER}
					name={TEAM_CONTENT.NAME}
					validateText={TEAM_CONTENT.VALIDATE_TEXT()}
				/&amp;gt;
				&amp;lt;Form.ColorRadioInputField name=&quot;teamColor&quot; /&amp;gt;
				&amp;lt;button css={submitButtonStyle} type=&quot;submit&quot;&amp;gt;
					팀 생성하기
				&amp;lt;/button&amp;gt;
			&amp;lt;/Form&amp;gt;
		&amp;lt;/&amp;gt;
	);
};
export default Main;&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;추가로 다른 form input이 필요하다면 언제든지 간단하게 추가 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react hook form 을 사용하지 않는다면 많은 ref와 state들을 사용해야 했을 텐데 한 곳에서 상태를 관리 한다는 것이 정말 큰 장점인 것 같습니다. 덤으로 리렌더링 최적화까지 해주니..와우&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;참조&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1712140975570&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;React Hook Form - performant, flexible and extensible form library&quot; data-og-description=&quot;Performant, flexible and extensible forms with easy-to-use validation.&quot; data-og-host=&quot;react-hook-form.com&quot; data-og-source-url=&quot;https://react-hook-form.com/&quot; data-og-url=&quot;https://react-hook-form.com/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://react-hook-form.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://react-hook-form.com/&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;React Hook Form - performant, flexible and extensible form library&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Performant, flexible and extensible forms with easy-to-use validation.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;react-hook-form.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1712141768879&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;React: 제어 컴포넌트와 비제어 컴포넌트의 차이점&quot; data-og-description=&quot;  제어 컴포넌트와 비제어 컴포넌트의 차이점, 활용 방법에 대해 알아봅시다&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@yukyung/React-%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%99%80-%EB%B9%84%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@yukyung/React-제어-컴포넌트와-비제어-컴포넌트의-차이점-톺아보기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jz9j8/hyVJYLpVYG/Xhd3PFOW8Gwh10c7OKV4L0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cmeKYd/hyVJUWxX35/qVjlLEIsTlOM87ykbbxJD0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bJQ4AH/hyVGOwVj4w/0KU5fGjnNwcTjWKX6lxaE1/img.jpg?width=1080&amp;amp;height=1080&amp;amp;face=0_0_1080_1080&quot;&gt;&lt;a href=&quot;https://velog.io/@yukyung/React-%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%99%80-%EB%B9%84%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@yukyung/React-%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%99%80-%EB%B9%84%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jz9j8/hyVJYLpVYG/Xhd3PFOW8Gwh10c7OKV4L0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cmeKYd/hyVJUWxX35/qVjlLEIsTlOM87ykbbxJD0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bJQ4AH/hyVGOwVj4w/0KU5fGjnNwcTjWKX6lxaE1/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;React: 제어 컴포넌트와 비제어 컴포넌트의 차이점&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;velog.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;figure id=&quot;og_1712142515757&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;react-hook-form을 선택한 이유와 적용 과정&quot; data-og-description=&quot;IT 채용 플랫폼 랠릿의 거대한 Form을 react-hook-form으로 마이그레이션한 이유와 과정을 공유합니다.&quot; data-og-host=&quot;tech.inflab.com&quot; data-og-source-url=&quot;https://tech.inflab.com/202207-rallit-form-refactoring/react-hook-form/#2-react-hook-form-%EB%8F%84%EC%9E%85-%EA%B3%BC%EC%A0%95&quot; data-og-url=&quot;https://tech.inflab.com/202207-rallit-form-refactoring/react-hook-form/#2-react-hook-form-%EB%8F%84%EC%9E%85-%EA%B3%BC%EC%A0%95&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bvZMjE/hyVGOXY5Mv/ZavSyKKPlZnpv8aVFotXiK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bOawtv/hyVJ4LCUs1/t4kGtIX0kURSivom1KktSK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/csr1bM/hyVJ5jsKnN/TlyVJNFMrUvwl8Puswkkvk/img.png?width=720&amp;amp;height=363&amp;amp;face=0_0_720_363&quot;&gt;&lt;a href=&quot;https://tech.inflab.com/202207-rallit-form-refactoring/react-hook-form/#2-react-hook-form-%EB%8F%84%EC%9E%85-%EA%B3%BC%EC%A0%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://tech.inflab.com/202207-rallit-form-refactoring/react-hook-form/#2-react-hook-form-%EB%8F%84%EC%9E%85-%EA%B3%BC%EC%A0%95&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bvZMjE/hyVGOXY5Mv/ZavSyKKPlZnpv8aVFotXiK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/bOawtv/hyVJ4LCUs1/t4kGtIX0kURSivom1KktSK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400,https://scrap.kakaocdn.net/dn/csr1bM/hyVJ5jsKnN/TlyVJNFMrUvwl8Puswkkvk/img.png?width=720&amp;amp;height=363&amp;amp;face=0_0_720_363');&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;react-hook-form을 선택한 이유와 적용 과정&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;IT 채용 플랫폼 랠릿의 거대한 Form을 react-hook-form으로 마이그레이션한 이유와 과정을 공유합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;tech.inflab.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>Frontend</category>
      <category>Controlled Component</category>
      <category>react</category>
      <category>react hook form</category>
      <category>uncontrolled conponent</category>
      <category>useController</category>
      <category>useForm</category>
      <category>비제어 컴포넌트</category>
      <category>제어 컴포넌트</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/98</guid>
      <comments>https://hellol77.tistory.com/98#entry98comment</comments>
      <pubDate>Wed, 3 Apr 2024 20:00:34 +0900</pubDate>
    </item>
    <item>
      <title>[React] 재조정(Reconciliation)과 key값</title>
      <link>https://hellol77.tistory.com/97</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에서 DatePicker를 직접 구현하게 되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;670&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P0edt/btsFW7krClX/SqaJ8PKPkT8xako0GTngmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P0edt/btsFW7krClX/SqaJ8PKPkT8xako0GTngmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P0edt/btsFW7krClX/SqaJ8PKPkT8xako0GTngmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP0edt%2FbtsFW7krClX%2FSqaJ8PKPkT8xako0GTngmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;261&quot; height=&quot;299&quot; data-origin-width=&quot;584&quot; data-origin-height=&quot;670&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;date-fns 라이브러리를 사용해서 날을 생성했고 다음달 이전달로 넘어갈때마다 날짜들을 재조정 시켜주었다.&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇번 달을 이동하다 보면 첫째주에서 시작해야할 1일은 다른 주에서 나오기 시작했다.&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;578&quot; data-origin-height=&quot;752&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2N67U/btsFW33sCiC/SXQriKQyGuLPdwdT0n9nfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2N67U/btsFW33sCiC/SXQriKQyGuLPdwdT0n9nfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2N67U/btsFW33sCiC/SXQriKQyGuLPdwdT0n9nfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2N67U%2FbtsFW33sCiC%2FSXQriKQyGuLPdwdT0n9nfK%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;258&quot; height=&quot;336&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;752&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;문제는 key 값이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 보니 key값을 중복값으로 입력하고 있었다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1710954025989&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const DatePickerModal = () =&amp;gt; {
	useEffect(() =&amp;gt; {
		editCurrentMonth(selectedDate);
	}, []);

	const {
		modalClose,
		currentMonth,
		selectedDate,
		handlePrevMonth,
		handleNextMonth,
		editCurrentMonth,
	} = useContext(DatePickerProvider);

	const CalendarDateCards = useMemo(() =&amp;gt; {
		const monthStart = startOfMonth(currentMonth);
		const daysInMonth = getDaysInMonth(currentMonth);
		const firstDayOfMonth = getDay(monthStart);
		const calendarArray = Array.from({ length: daysInMonth + firstDayOfMonth }, (_, i) =&amp;gt; {
			if (i &amp;lt; firstDayOfMonth) {
				return &quot;&quot;;
			}
			return addDays(monthStart, i - firstDayOfMonth);
		});
		return calendarArray.map((day) =&amp;gt; {
			return &amp;lt;DatePickerCalendarCard key={day.toString()} modalClose={modalClose} day={day} /&amp;gt;;
		});
	}, [currentMonth]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key값으로 Date또는 ''인 day를 toString으로 변환해서 주고 있는데 ''을 toString하면 그대로 이기때문에 중복되는 key값이 생겨버린다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 key값에 유니크한 값을 주기 위해서 '' 값은 첫째줄에만 나올때니 요일+'' 을 넣어주었고 문제는 해결되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1710956021511&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	const CalendarDateCards = useMemo(() =&amp;gt; {
		const monthStart = startOfMonth(currentMonth);
		const daysInMonth = getDaysInMonth(currentMonth);
		const firstDayOfMonth = getDay(monthStart);
		const calendarArray = Array.from({ length: daysInMonth + firstDayOfMonth }, (_, i) =&amp;gt; {
			if (i &amp;lt; firstDayOfMonth) {
				return &quot;&quot;;
			}
			return addDays(monthStart, i - firstDayOfMonth);
		});
		return calendarArray.map((day, index) =&amp;gt; {
			return (
				&amp;lt;DatePickerCalendarCard
					key={weekday[index] + day.toString()}
					modalClose={modalClose}
					day={day}
				/&amp;gt;
			);
		});
	}, [currentMonth]);&lt;/code&gt;&lt;/pre&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;b&gt;  왜 이런 문제가 나타날까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React는 state나 props가 갱신되면 새로운 React 엘리먼트 트리를 반환한다. 이때 하나의 트리를 가지고 다른 트리로 변환하기 위한 최소한의 연산수는 n개의 엘리먼트가 있는 트리에서 &lt;span style=&quot;text-align: start;&quot;&gt;O(n^3)&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;의 복잡도를 가진다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;만약 1000개의 엘리먼트를 그리기 위해서는 10억번의 비교 연산을 해야 한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;따라서 React는 O(n) 복잡도를 가진 비교 알고리즘을 구현했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 이용하는 것이 바로 key인데&amp;nbsp;React는 key를 통해서 기존 트리와 이후 트리의 자식들이 일치하는지 확인하고 효율적으로 수행한다.이렇게 수행하기 위해서는 key값은 무조건 유일한 값이어야 한다. 만약 중복이 된다면 react는 제대로 비교 알고리즘을 사용 할 수 없게 되고 렌더링하는 값들도 문제가 있을 수 있다.&amp;nbsp;나의 경우가 이 경우인것 같다.&lt;/p&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;span style=&quot;background-color: #ffc9af;&quot;&gt; key값을 index값으로 사용하는 경우가 많은데 절대 그러지 말도록 하자&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 공식 문서에도 친절히 예시까지 보여주며 경고 하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인덱스를 key값으로 사용하는 경우 문제가 발생하는 예시&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1710956090417&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;Create a New Pen&quot; data-og-description=&quot;Behavior Auto Save If active, Pens will autosave every 30 seconds after being saved once. Auto-Updating Preview If enabled, the preview panel updates automatically as you code. If disabled, use the &amp;quot;Run&amp;quot; button to update. Format on Save If enabled, your co&quot; data-og-host=&quot;codepen.io&quot; data-og-source-url=&quot;https://codepen.io/pen?&amp;amp;editors=0010&amp;amp;layout=left&quot; data-og-url=&quot;https://codepen.io/pen?editors=0010&amp;amp;layout=left&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://codepen.io/pen?&amp;amp;editors=0010&amp;amp;layout=left&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://codepen.io/pen?&amp;amp;editors=0010&amp;amp;layout=left&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;Create a New Pen&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Behavior Auto Save If active, Pens will autosave every 30 seconds after being saved once. Auto-Updating Preview If enabled, the preview panel updates automatically as you code. If disabled, use the &quot;Run&quot; button to update. Format on Save If enabled, your co&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;codepen.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 todo를 추가해주고 내용을 입력한 다음 정렬을 시켜보자. id 는 정렬이 되지만 우리가 기대한 대로 입력한 값까지 정렬되지 않는 모습을 보인다.&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;인덱스를 key값으로 사용하지 않는 경우&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1710956090725&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;Create a New Pen&quot; data-og-description=&quot;Behavior Auto Save If active, Pens will autosave every 30 seconds after being saved once. Auto-Updating Preview If enabled, the preview panel updates automatically as you code. If disabled, use the &amp;quot;Run&amp;quot; button to update. Format on Save If enabled, your co&quot; data-og-host=&quot;codepen.io&quot; data-og-source-url=&quot;https://codepen.io/pen?&amp;amp;editors=0010&amp;amp;layout=left&quot; data-og-url=&quot;https://codepen.io/pen?editors=0010&amp;amp;layout=left&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://codepen.io/pen?&amp;amp;editors=0010&amp;amp;layout=left&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://codepen.io/pen?&amp;amp;editors=0010&amp;amp;layout=left&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;Create a New Pen&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Behavior Auto Save If active, Pens will autosave every 30 seconds after being saved once. Auto-Updating Preview If enabled, the preview panel updates automatically as you code. If disabled, use the &quot;Run&quot; button to update. Format on Save If enabled, your co&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;codepen.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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>DatePicker</category>
      <category>index</category>
      <category>key</category>
      <category>map</category>
      <category>react</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/97</guid>
      <comments>https://hellol77.tistory.com/97#entry97comment</comments>
      <pubDate>Thu, 21 Mar 2024 02:38:09 +0900</pubDate>
    </item>
    <item>
      <title>기본적인 git 사용 패턴 정리</title>
      <link>https://hellol77.tistory.com/96</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 멋사 운영진을 할때 github 세션을 했었는데 관련 내용을 공유합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;git과 github의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;git&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;오픈 소스 버전 관리 시스템(Version Control System)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;로컬에서 버전 관리&lt;/li&gt;
&lt;li&gt;소프트웨어 개발 및 소스 코드 관리&lt;/li&gt;
&lt;li&gt;인터넷이 필요 없다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;github&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;git을 사용하는 클라우드 서비스&lt;/li&gt;
&lt;li&gt;git repository를 위한 웹 기반 호스팅 서비스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;repository : 저장소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;local : 자신의 컴퓨터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;origin : 원격 저장소(깃허브 페이지)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;첫번째 자신의 repository만들어 보기&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;자신의 프로필에서 [repository]&amp;rarr; [New]버튼&lt;/li&gt;
&lt;/ul&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;742&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYbUTP/btsFoK3ThlH/tg8WLZkbUEKURSJ2oz2nSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYbUTP/btsFoK3ThlH/tg8WLZkbUEKURSJ2oz2nSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYbUTP/btsFoK3ThlH/tg8WLZkbUEKURSJ2oz2nSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYbUTP%2FbtsFoK3ThlH%2Ftg8WLZkbUEKURSJ2oz2nSK%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;742&quot; height=&quot;551&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;551&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레포지토리를 만들어봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;692&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4IlKN/btsFqNTq9L5/ZkiBit1OMPZY0on6sC8K2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4IlKN/btsFqNTq9L5/ZkiBit1OMPZY0on6sC8K2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4IlKN/btsFqNTq9L5/ZkiBit1OMPZY0on6sC8K2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4IlKN%2FbtsFqNTq9L5%2FZkiBit1OMPZY0on6sC8K2k%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;745&quot; height=&quot;414&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1245&quot; data-origin-height=&quot;692&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;그리고 바탕화면에 폴더하나를 만들겠습니다. 저는 웬만하면 repository와 같은 이름으로 만드는걸 추천 드릴게요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Vscode를 연 다음 파일을 눌러서 새로만든 파일을 열어줍니다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;git init                                                    # git 초기화
git remote add origin &amp;lt;https://github.com/Hellol77/test.git&amp;gt;  # 원격 레포지토리 연결
git branch -M main                                          # 메인브랜치 main으로 초기화

	# 폴더안에 파일을 하나 만든후 commit까지 보낸후 

git push -u origin main       
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하시면 이제 github페이지에 자신의 변경사항이 올라간 것을 보실 수 있습니다.&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;github 시작하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 repository를 fork 하는 방식과 clone 하는 방식 두가지가 있는데 저희는 clone으로 해보도록 하겠습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;nbsp;  fork: 다른 사람의 repository를 내 github repository로 그대로 복제하는 기능입니다. clone:다른 사람의 repository를 내 컴퓨터에 복사하여 새로운 저장소를 만드는 기능&lt;/blockquote&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;1308&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lEeLr/btsFoJql84x/POdXdLodEUXZFK8KkY5LE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lEeLr/btsFoJql84x/POdXdLodEUXZFK8KkY5LE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lEeLr/btsFoJql84x/POdXdLodEUXZFK8KkY5LE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlEeLr%2FbtsFoJql84x%2FPOdXdLodEUXZFK8KkY5LE1%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;713&quot; height=&quot;416&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1709145604518&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone http://...&lt;/code&gt;&lt;/pre&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;브랜치 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브랜치를 통해서 하나의 프로젝트를 여러 갈래로 나누어서 관리 및 개발 가능&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;git branch 만들브랜치이름 뻗어나올브랜치이름   # 브랜치 생성
git checkout 만든브랜치이름               # 내가 사용할 브랜치를 지정
																			# (내 컴퓨터가 이제 만든브랜치의 정보를 보여줍니다)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 만든 브랜치를 사용하여 어떤 작업을 수행하려면, git checkout 으로 만든 브랜치를 사용 하겠다고 명시적으로 지정해 주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좌측 아래에 보면 자신이 보고있는 브랜치 이름이 나와있습니다. 눌러서 checkout 할 수도 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vscode 동기화가 좀 잘 안되어서 만든 브랜치 이름이 안보일 수 있으니 명령어를 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고) checkout 할 때는 이미 존재하는 파일의 변경사항을 전부 commit하거나 아니면 stash로 저장후 checkout 해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 브랜치를 만들어서 checkout 까지 완료했는데요. 이제 원격(remote) repository에도 내가 만든 브랜치를 알려줘야겠죠?&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;	git push # push 는 원격 repository에 내 컴퓨터와 같은 상태를 만든다(복사 된다) 
					 # 라고 생각하시면 됩니다. 
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러니까 원격repository에도 내가 컴퓨터에서 만든 브랜치가 똑같이 만들어지는거죠&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 하시면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLs4ix/btsFm4PnLLD/SlJ48jswDRk3ZzNFy2ujBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLs4ix/btsFm4PnLLD/SlJ48jswDRk3ZzNFy2ujBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLs4ix/btsFm4PnLLD/SlJ48jswDRk3ZzNFy2ujBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLs4ix%2FbtsFm4PnLLD%2FSlJ48jswDRk3ZzNFy2ujBK%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;652&quot; height=&quot;210&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요런 메시지가 나올텐데요. 복사해서 다시 쳐줍니다.&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;git push &amp;mdash;set-upstream origin branchTest
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sErc9/btsFjVsgPJ5/BTXXoOFKfNohWsXPbW2ix0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sErc9/btsFjVsgPJ5/BTXXoOFKfNohWsXPbW2ix0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sErc9/btsFjVsgPJ5/BTXXoOFKfNohWsXPbW2ix0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsErc9%2FbtsFjVsgPJ5%2FBTXXoOFKfNohWsXPbW2ix0%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;652&quot; height=&quot;311&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;publish branch 누르시면 사실 끝납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;파일 수정하기&lt;/b&gt;&lt;/h3&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;685&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U56MW/btsFme5M3Ye/8yRbSMmdptS75hqD6lCryk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U56MW/btsFme5M3Ye/8yRbSMmdptS75hqD6lCryk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U56MW/btsFme5M3Ye/8yRbSMmdptS75hqD6lCryk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU56MW%2FbtsFme5M3Ye%2F8yRbSMmdptS75hqD6lCryk%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;685&quot; height=&quot;363&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 수정하면 변경 사항에 변경한 파일명이 뜹니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAziV5/btsFjUUw92n/Y11HbDbioEhvthwvrzOQsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAziV5/btsFjUUw92n/Y11HbDbioEhvthwvrzOQsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAziV5/btsFjUUw92n/Y11HbDbioEhvthwvrzOQsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAziV5%2FbtsFjUUw92n%2FY11HbDbioEhvthwvrzOQsk%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;661&quot; height=&quot;438&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;538&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨간 박스의 왼쪽 버튼 부터 설명하면 &lt;span data-token-index=&quot;1&quot;&gt;파일 열기, 변경 내용 취소하기, staged 상태로 만들기(git add 하기)&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;b&gt;파일 staged 상태로 만들기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;staged 상태라는 것은 쉽게 말하면 앞으로 commit 할 파일을 정하는 것이라고 생각하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+버튼을 누르시거나&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwqC3C/btsFjYvPR4e/y2hdyIhKcNu8R66gyylWP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwqC3C/btsFjYvPR4e/y2hdyIhKcNu8R66gyylWP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwqC3C/btsFjYvPR4e/y2hdyIhKcNu8R66gyylWP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwqC3C%2FbtsFjYvPR4e%2Fy2hdyIhKcNu8R66gyylWP0%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;674&quot; height=&quot;343&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;git add . # 변경한 전체 파일 staged상태로 변경하기
# 혹은
git add 파일이름 # 파일 staged상태로 변경하기
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 입력해서 변경된 파일을 staged상태로 변경합니다.&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;commit 보내기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 staged 했던 파일들을 commit 할겁니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커밋을 보내면 staged 했던 파일들만 commit이 됩니다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYURCF/btsFkGPhw71/wmjlzOae4dGp0fWMupBbSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYURCF/btsFkGPhw71/wmjlzOae4dGp0fWMupBbSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYURCF/btsFkGPhw71/wmjlzOae4dGp0fWMupBbSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYURCF%2FbtsFkGPhw71%2FwmjlzOae4dGp0fWMupBbSK%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;672&quot; height=&quot;210&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;git commit -m 커밋메시지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고) 커밋 컨벤션 예시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 기능을 추가했는지 버그 수정을 했는지.. 커밋메시지로 무엇을 변경했는지 편하게 알기 위해서 정하는 일종의 커밋메시지 규칙입니다. 이 규칙은 사용하는 그룹마다 다 다릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;feat : 새로운 기능 추가&lt;/li&gt;
&lt;li&gt;fix : 버그 수정&lt;/li&gt;
&lt;li&gt;hotfix : 급하게 치명적인 버그 수정&lt;/li&gt;
&lt;li&gt;docs : 문서 수정&lt;/li&gt;
&lt;li&gt;style : 코드 포맷팅, 세미콜론 등의 스타일 수정(코드 자체 수정 X)&lt;/li&gt;
&lt;li&gt;refactor : 프로덕션 코드 리팩토링&lt;/li&gt;
&lt;li&gt;test : 테스트 코드, 테스트 코드 리팩토링&lt;/li&gt;
&lt;li&gt;chore : 빌드 과정 또는 보조 기능(문서 생성 기능 등) 수정&lt;/li&gt;
&lt;li&gt;rename : 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우&lt;/li&gt;
&lt;li&gt;remove : 파일을 삭제하는 작업만 수행한 경우&lt;/li&gt;
&lt;li&gt;comment : 필요한 주석 추가 및 변경&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.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K7OPC/btsFm3iF0aQ/Ve3S0JDzvnlV8TZ8z3YDhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K7OPC/btsFm3iF0aQ/Ve3S0JDzvnlV8TZ8z3YDhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K7OPC/btsFm3iF0aQ/Ve3S0JDzvnlV8TZ8z3YDhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK7OPC%2FbtsFm3iF0aQ%2FVe3S0JDzvnlV8TZ8z3YDhK%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;617&quot; height=&quot;375&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;원격 repository에 push 하기&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;git push --force-with-lease  # push 를 해줍니다. 항상 push --force..를 할때는 조심
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;mdash;force-with-lease 를 사용하면 혹시나 원격 repository에 다른사람의 커밋이 새롭게 추가된 경우 덮어쓰기 작업을 취소시키기 때문에 안전합니다. 그냥 &amp;mdash;force 대신 이거 쓰시는 것을 추천드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력을 완료하시고 깃허브 페이지를 가셔서 pull request 쪽을 들어가보시면 자신이 push 했다고 알림이 옵니다.&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;2436&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brFFjX/btsFogWi3tL/kair2MEU24DKYeNhOvSX5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brFFjX/btsFogWi3tL/kair2MEU24DKYeNhOvSX5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brFFjX/btsFogWi3tL/kair2MEU24DKYeNhOvSX5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrFFjX%2FbtsFogWi3tL%2Fkair2MEU24DKYeNhOvSX5K%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;2436&quot; height=&quot;214&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;원격 repository에 자신의 변경사항 적용시키기 (깃허브에 업로드하기)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격 repository에 자신의 변경사항을 적용시키려면 &lt;b&gt;pull request&lt;/b&gt; 를 보내야합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;compare &amp;amp; pull request 버튼을 눌러줍니다! (만약 compare &amp;amp; pull request)버튼이 안생긴다면 new pull request 버튼을 눌러줍니다.&lt;/li&gt;
&lt;li&gt;자신의 편집한 브랜치 이름과 base 브랜치를 확인합니다.&lt;/li&gt;
&lt;/ol&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;2480&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJq5sI/btsFoIyfYRW/lJdClFaCVirDL7nbhWSfZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJq5sI/btsFoIyfYRW/lJdClFaCVirDL7nbhWSfZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJq5sI/btsFoIyfYRW/lJdClFaCVirDL7nbhWSfZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJq5sI%2FbtsFoIyfYRW%2FlJdClFaCVirDL7nbhWSfZK%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;2480&quot; height=&quot;310&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;2480&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 이미지를 보시면 main 브랜치에 branchTest브랜치에 있는 수정사항을 추가한다라고 보시면 됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;혹시나 합치는 과정에서 알아야할 참고사항이나 특별한 특이사항들을 적어줍니다.&lt;/li&gt;
&lt;li&gt;create pull request를 눌러줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고) &lt;a href=&quot;https://im-developer.tistory.com/182&quot;&gt;https://im-developer.tistory.com/182&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;merge (합치기) 에는 세가지 방법이 있는데요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rebase 를 한 후 merge 하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 일반적인 merge 가 있습니다. (한가지는 몰라도 됩니다. 잘 안써서요 &lt;s&gt;squash merge&lt;/s&gt;라나..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희는 그냥 일반적인 merge를 하는것으로 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;merge버튼을 누르시고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 main 브랜치를 보시면&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;**git checkout main**
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 맞습니다 아무것도 안바뀌어 있습니다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;원격 repository에서 데이터 받아오기(동기화하기)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;git branch -D 자신이만든브랜치이름   # 이제 merge를 했으니 로컬에서의 브랜치를 지워줍니다.
																
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 로컬(컴퓨터) 에 있는 브랜치를 지워줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(주의 : 자신이 만든 브랜치에 checkout 되어있으면 삭제가 안됩니다. main이나 다른 브랜치에 checkout 후 삭제하세요!)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격 repository에서는 merge후 자동으로 브랜치를 지우게 설정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pr(pull request) 보내고 merge 까지 했는데 왜 안바뀌어있죠??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 우리 컴퓨터는 똑똑하지 않아서요 직접 그 정보를 가져와야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 fetch 를 하시기 전에 지금은 없겠지만 작업하시고 있는 것이 있다면&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;git stash        # staged 된 변경사항이나 staged 안한 변경사항들을 전부다 임시저장합니다.
								 # 파일들은 전부 변경하지 전 상태로 돌아갑니다.

git stash pop    # 제일 마지막에 stash 한 사항들을 다시 컴퓨터로 불러옵니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git stash를 해주신 후 fetch 를 하는것을 추천드립니다. (사실은 아무상관이 없지만&amp;hellip; 제 징크스입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 있던 파일을 수정한것들은 stash가 되는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 만든 파일은 stash가 안됩니다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(git rebase main을 추천 드립니다)&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;git fetch --all   # 원격 repository에서 데이터 동기화
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 fetch 를 하시면 네 맞습니다. 또 아무것도 변하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch는 원격 repository의 최신이력을 내 컴퓨터에 가져온다 라고 생각하시면 됩니다. 절대 내 컴퓨터에 저장하는 것이 아니라 그냥 데이터만 가져온다. 그래서 내 컴퓨터에 저장 할지 아니면 저장을 안할지 선택 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 fetch를 했으니 원격 repository의 정보를 컴퓨터에 저장시켜 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째는 git pull을 사용한 방식입니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1709145761193&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git pull&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경사항이 현재 없는 상태라면 다음의 명령어로 불러올 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dsconfig&quot;&gt;&lt;code&gt;git reset --hard origin/main  # 강제로 원격 repository의 main 브랜치의 
															#	정보를 컴퓨터에 저장합니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;mdash;hard 가 붙은 명령어는 항상 조심하셔야 합니다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제야 main 브랜치에 여러분이 변경한 내용들이 보일 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;충돌작업 해보기&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGsvgK/btsFqKh6BI3/KfsM5RFkbbt3gRYHDwqdj0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGsvgK/btsFqKh6BI3/KfsM5RFkbbt3gRYHDwqdj0/img.gif&quot; data-alt=&quot;그냥 웃겨서 넣어봤어요..&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGsvgK/btsFqKh6BI3/KfsM5RFkbbt3gRYHDwqdj0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bGsvgK/btsFqKh6BI3/KfsM5RFkbbt3gRYHDwqdj0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;296&quot; data-filename=&quot;image.gif&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;그냥 웃겨서 넣어봤어요..&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;1274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czDo84/btsFqMAcM3q/KuKm9FYhFBddt5kNbjUjpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czDo84/btsFqMAcM3q/KuKm9FYhFBddt5kNbjUjpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czDo84/btsFqMAcM3q/KuKm9FYhFBddt5kNbjUjpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczDo84%2FbtsFqMAcM3q%2FKuKm9FYhFBddt5kNbjUjpK%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;629&quot; height=&quot;421&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;1274&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;일단 이 화면을 목격하시면&amp;hellip;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬(자신의 컴퓨터)에서의 main 브랜치를 업데이트 해줍니다!&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;git checkout main
git fetch --all
git reset --hard origin/main
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하시면 main 이 최신으로 업데이트 되겠죠?&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;git checkout 자신이작업한브랜치
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 해서 자신의 브랜치로 이동한후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 자신의 브랜치(&lt;b&gt;git checkout 자신이 작업한 브랜치&lt;/b&gt;)로 가서 &lt;b&gt;git merge main(git rebase main)&lt;/b&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;(참고) git rebase(merge) main 이라는 뜻은 지금 로컬 환경에 있는 main 브랜치에 합쳐주겠다 라는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 main을 최신화하지 않으면 예전 버전의 main에 합쳐주는게 되겠죠.. 그래서 main을 최신화 하고 rebase 를 해줍니다.&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;1402&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccFTXa/btsFqMNJ6sK/QLoGfYUnCrgyMUNAwxaJ2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccFTXa/btsFqMNJ6sK/QLoGfYUnCrgyMUNAwxaJ2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccFTXa/btsFqMNJ6sK/QLoGfYUnCrgyMUNAwxaJ2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccFTXa%2FbtsFqMNJ6sK%2FQLoGfYUnCrgyMUNAwxaJ2k%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;728&quot; height=&quot;132&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;친절하게도 어떤 것을 수정사항으로 받아드릴지 알려줍니다.&lt;/p&gt;
&lt;p 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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmVyy4/btsFpPqr644/FS4i3MCpwUGWFz9JxKUBp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmVyy4/btsFpPqr644/FS4i3MCpwUGWFz9JxKUBp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmVyy4/btsFpPqr644/FS4i3MCpwUGWFz9JxKUBp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmVyy4%2FbtsFpPqr644%2FFS4i3MCpwUGWFz9JxKUBp0%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;579&quot; height=&quot;488&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;724&quot; data-origin-height=&quot;610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정하셨으면&lt;/p&gt;
&lt;p 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;로컬 컴퓨터로 병합을 해주었으니까 원격 repository의 브랜치에도 그 정보들을 알려줘야 합니다.&lt;/p&gt;
&lt;pre class=&quot;verilog&quot;&gt;&lt;code&gt;git push --force-with-lease
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 해서 우리가 병합했던 내용들을 알려줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 우리는 다시 github페이지에 가서 merge 할 수 있게됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(merge했으니까 항상 로컬에서 branch 지우는 건 잊지말아주세요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;자신이 push 한 제일 최근 커밋 되돌리기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/36wNu/btsFm42UCD9/jtkkOvfwbYPdmEQ7uR1Xz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/36wNu/btsFm42UCD9/jtkkOvfwbYPdmEQ7uR1Xz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/36wNu/btsFm42UCD9/jtkkOvfwbYPdmEQ7uR1Xz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F36wNu%2FbtsFm42UCD9%2FjtkkOvfwbYPdmEQ7uR1Xz0%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;1822&quot; height=&quot;396&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test 브랜치에 2개의 commit있습니다. 지금 Pull request를 보낸 상황이구요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 최근 커밋인 &amp;lsquo;삭제할 커밋입니다.&amp;rsquo; 를 취소해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 최근 커밋을 취소하는 것은 아주 간단합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;git reset HEAD^   # 가장 최근의 commit을 취소합니다.
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;을 입력해주면 로컬(컴퓨터)에서는 다시 커밋메세지를 보내기 전으로 돌아옵니다. 아예 수정하기 전 상태가 아닌, 수정은 되어있지만 커밋을 보내지 않은 상태입니다. (add도 안되어있음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 컴퓨터에서 커밋이 취소되었으니 원격 repository에도 로컬 컴퓨터의 상태를 복사해줍니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;git push --force-with-lease&lt;/code&gt;&lt;/pre&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;1724&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpjfVC/btsFjXwWp4U/9efjiHaC17yH0RF2AjGSUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpjfVC/btsFjXwWp4U/9efjiHaC17yH0RF2AjGSUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpjfVC/btsFjXwWp4U/9efjiHaC17yH0RF2AjGSUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpjfVC%2FbtsFjXwWp4U%2F9efjiHaC17yH0RF2AjGSUK%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;1724&quot; height=&quot;122&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1724&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커밋이 취소된 모습입니다.&lt;/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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;내가 커밋하지 않았는데 다른 사람의 커밋이 나의 pull request에 있을때&lt;/b&gt;&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;git checkout main   # main으로 가준다음
git fetch --all     
git reset --hard origin/main  # main을 최신화 시켜준뒤
git checkout 자신의브랜치   
git rebase main         					
git push --force-with-lease  # 이제 중복된 커밋을 로컬에서 제거했으니 원격에도 업데이트 시켜줍니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자신의&amp;nbsp;브랜치로&amp;nbsp;간다음&amp;nbsp;내&amp;nbsp;pull&amp;nbsp;request에&amp;nbsp;있는&amp;nbsp;다른사람의&amp;nbsp;커밋은&amp;nbsp;보통&amp;nbsp;main에도&amp;nbsp;똑같이&amp;nbsp;있습니다.&amp;nbsp;그래서&amp;nbsp;git&amp;nbsp;rebase&amp;nbsp;main을&amp;nbsp;해서&amp;nbsp;중복된&amp;nbsp;커밋을&amp;nbsp;없애줍니다.&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;만약 다른사람의 커밋이 내 pull request에 있는데 이 작업을 안하고 merge를 누르면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른사람의 커밋이 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;&amp;nbsp;&lt;/p&gt;</description>
      <category>git</category>
      <category>GIT</category>
      <category>Github</category>
      <category>Merge</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/96</guid>
      <comments>https://hellol77.tistory.com/96#entry96comment</comments>
      <pubDate>Thu, 29 Feb 2024 04:02:07 +0900</pubDate>
    </item>
    <item>
      <title>[React] 불필요한 리렌더링을 막아서 6배가 빨라졌다고?</title>
      <link>https://hellol77.tistory.com/95</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;현재 프로젝트에서 calendar 컴포넌트를 맡고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;calendar 컴포넌트에는 추가한 일정들을 볼 수가 있다. 각 일정들을 클릭하면 일정 상세보기 모달이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일정이 2개를 초과하면 더보기 버튼이 생기는데 클릭하면 달력이 미쳐 나오지 못한 일정이 모달을 통해 나오게 되는 구조이다.&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;화면-기록-2024-02-22-오후-9.19.04.gif&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5hTL6/btsFffJllwQ/MRVoChYkzhtcmFewMFsZDK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5hTL6/btsFffJllwQ/MRVoChYkzhtcmFewMFsZDK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5hTL6/btsFffJllwQ/MRVoChYkzhtcmFewMFsZDK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b5hTL6/btsFffJllwQ/MRVoChYkzhtcmFewMFsZDK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;779&quot; height=&quot;376&quot; data-filename=&quot;화면-기록-2024-02-22-오후-9.19.04.gif&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불필요한 너무 많은 곳에 리렌더가 일어나는 것이다.&lt;/p&gt;
&lt;p 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;  &lt;b&gt;문제점&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. recoil로 modal 상태 관리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 modal을 열고 닫는데는 recoil을 사용중이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1708605958999&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const moreModalState = atom&amp;lt;MoreModalStateType&amp;gt;({
	key: &quot;moreModalState&quot;,
	default: {
		day: null,
	},
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더보기를 클릭한다면 그 날자가 day에 설정되고 더보기 modal 이 열리는 구조이다.&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;day에 맞는 더보기 modal을 열기 위해 달력의 각 날짜들은 recoil을 구독하게 되므로 modal이 열리고 닫힐때마다 리렌더링이 일어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;recoil로 구현을 했던 이유는 더보기 모달은 달력에서 딱 하나만 열려야 한다. 한개가 열리면 한개가 닫혀야 하는데&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;하지만 생각해보니 현재도 ref를 통해 다른 곳을 클릭하면 modal이 닫히게 되어 있는데 굳이 하나의 상태로 관리하면서 리렌더링을 모든 날짜에게 일어나게 하지 않아도 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 코드를 바꿔보기로 하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  변경 전과 변경 후의 성능 변화&lt;/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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 변경 전&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yIWtv/btsFcmJGA3T/vqB8eUKC7AOIiM3oZv4KF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yIWtv/btsFcmJGA3T/vqB8eUKC7AOIiM3oZv4KF1/img.png&quot; data-alt=&quot;변경 전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yIWtv/btsFcmJGA3T/vqB8eUKC7AOIiM3oZv4KF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyIWtv%2FbtsFcmJGA3T%2FvqB8eUKC7AOIiM3oZv4KF1%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;428&quot; height=&quot;398&quot; data-origin-width=&quot;688&quot; data-origin-height=&quot;640&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;클릭시 이벤트가 반응하는데 43.55ms 가 걸렸다.&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;/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;2. 변경 후&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;변경점은 recoil을 사용하지 않고 더보기를 하나의 버튼으로써 마치 dropdown 처럼 생각하고 다시 코드를 작성했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 useState와 useContext를 사용했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pxQ4G/btsFedZyiea/5gQVlZPet0tit6O9QPioiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pxQ4G/btsFedZyiea/5gQVlZPet0tit6O9QPioiK/img.png&quot; data-alt=&quot;변경 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pxQ4G/btsFedZyiea/5gQVlZPet0tit6O9QPioiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpxQ4G%2FbtsFedZyiea%2F5gQVlZPet0tit6O9QPioiK%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;442&quot; height=&quot;444&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;732&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;무려 7.18ms만에 완료되었다. 약 6배의 속도 차이이다.&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;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;화면-기록-2024-02-22-오후-10.34.36.gif&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MMAGQ/btsFdn2DGrm/79JQYFs6aRHJ6KxC1iwvEK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MMAGQ/btsFdn2DGrm/79JQYFs6aRHJ6KxC1iwvEK/img.gif&quot; data-alt=&quot;변경 후 리렌더링 변화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MMAGQ/btsFdn2DGrm/79JQYFs6aRHJ6KxC1iwvEK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/MMAGQ/btsFdn2DGrm/79JQYFs6aRHJ6KxC1iwvEK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;391&quot; data-filename=&quot;화면-기록-2024-02-22-오후-10.34.36.gif&quot; data-origin-width=&quot;2436&quot; data-origin-height=&quot;1176&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;변경 후 리렌더링 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;&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;또한 상태관리 라이브러리를 사용하는 것도 좋지만 본래의 react를 잘 활용하는 것이 기본이고 정말 중요하다는 것을 느낀다.&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>calenar 컴포넌트</category>
      <category>dropdown</category>
      <category>modal</category>
      <category>Recoil</category>
      <category>useContext</category>
      <category>useState</category>
      <category>리렌더링</category>
      <author>헬롤이다</author>
      <guid isPermaLink="true">https://hellol77.tistory.com/95</guid>
      <comments>https://hellol77.tistory.com/95#entry95comment</comments>
      <pubDate>Thu, 22 Feb 2024 22:43:35 +0900</pubDate>
    </item>
  </channel>
</rss>