<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>찬희의 Frontend Notes</title>
    <description>프론트엔드 개발 전문 기술 블로그입니다. React, Next.js, Angular, TypeScript 등 
최신 웹 기술 스택과 JavaScript 기초부터 심화까지, 실무 경험을 바탕으로 한 
개발 지식을 공유합니다.
</description>
    <link>https://beam307.github.io/</link>
    <atom:link href="https://beam307.github.io/rss" rel="self" type="application/rss+xml"/>
    <pubDate>Sun, 05 Apr 2026 19:17:12 +0900</pubDate>
    <lastBuildDate>Sun, 05 Apr 2026 19:17:12 +0900</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>JavaScript 2026 정리: ES2025, Node.js, React, Next.js에서 알아야 할 변화</title>
        <description>&lt;p&gt;2026년에도 자바스크립트를 공부할 때&lt;br /&gt;
언어 문법만 보면 부족하다.&lt;/p&gt;

&lt;p&gt;실제로는 아래 네 가지를 같이 봐야 한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;언어 자체가 어디까지 바뀌었는가&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;브라우저와 런타임이 무엇을 기본 지원하는가&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;프레임워크가 어떤 방향으로 이동하는가&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;실무에서 지금 당장 체감되는 변화가 무엇인가&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frontend Masters의 글&lt;br /&gt;
&lt;a href=&quot;https://frontendmasters.com/blog/what-to-know-in-javascript-2026-edition/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;What To Know in JavaScript (2026 Edition)&lt;/code&gt;&lt;/a&gt;를 바탕으로 다시 정리해보겠다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2026년-자바스크립트에서-먼저-봐야-할-변화&quot;&gt;2026년 자바스크립트에서 먼저 봐야 할 변화&lt;/h2&gt;

&lt;p&gt;핵심만 먼저 정리하면 이렇다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Node.js는 TypeScript 실행 경험을 더 직접적으로 제공하고 있다&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Vite, Next.js, React는 모두 빌드 속도와 서버 중심 구조를 밀고 있다&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Temporal API는 매우 중요하지만, 2026년 4월 5일 기준 Stage 4이다&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;이제 자바스크립트 실력은 언어 + 런타임 + 툴링 이해까지 포함한다&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;es2025에서-알아둘-기능&quot;&gt;ES2025에서 알아둘 기능&lt;/h2&gt;

&lt;h3 id=&quot;1-iterator-helpers&quot;&gt;1. Iterator Helpers&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterator Helpers&lt;/code&gt;는 2025년에 들어와&lt;br /&gt;
배열 중심 처리와 다른 흐름을 보여주는 기능이 됐다.&lt;/p&gt;

&lt;p&gt;기존에는 배열을 여러 번 변환하면서&lt;br /&gt;
중간 배열이 계속 만들어지는 코드가 흔했다.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 방식은 읽기 쉽지만&lt;br /&gt;
중간 결과가 계속 생긴다.&lt;/p&gt;

&lt;p&gt;반면 이터레이터 헬퍼는&lt;br /&gt;
&lt;strong&gt;지연 평가(lazy evaluation)&lt;/strong&gt; 기반으로 동작한다.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Iterator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 패턴이 중요한 이유는 두 가지다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;큰 데이터에서 불필요한 중간 배열 생성을 줄일 수 있다&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt;뿐 아니라 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Map&lt;/code&gt;, generator 등 iterable 전반에 같은 흐름을 적용할 수 있다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MDN 기준 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterator&lt;/code&gt; 관련 helper는&lt;br /&gt;
2025년부터 최신 브라우저 기준으로 사용 범위가 넓어지고 있다.&lt;/p&gt;

&lt;h3 id=&quot;2-set-methods&quot;&gt;2. Set Methods&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set&lt;/code&gt;은 예전부터 있었지만&lt;br /&gt;
이제는 교집합, 합집합, 차집합 같은 연산을&lt;br /&gt;
더 직접적으로 쓸 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;frontend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;frontend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;intersection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Set { &quot;js&quot;, &quot;ts&quot; }&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;frontend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Set { &quot;js&quot;, &quot;ts&quot;, &quot;react&quot;, &quot;node&quot; }&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;difference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;frontend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Set { &quot;node&quot; }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 변화는 작아 보이지만&lt;br /&gt;
실무에서는 권한 집합, 태그 집합, 기능 플래그 집합 처리에서 꽤 유용하다.&lt;/p&gt;

&lt;h3 id=&quot;3-regexpescape&quot;&gt;3. RegExp.escape()&lt;/h3&gt;

&lt;p&gt;이 기능은 실무 체감이 아주 큰 편이다.&lt;/p&gt;

&lt;p&gt;사용자 입력값을 그대로 정규식에 넣으면&lt;br /&gt;
특수문자 때문에 의도하지 않은 동작이 생길 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;userInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pattern&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;escape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;직접 escape 유틸을 만들어 쓰던 코드보다&lt;br /&gt;
훨씬 안전하고 명확하다.&lt;/p&gt;

&lt;p&gt;MDN 기준 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegExp.escape()&lt;/code&gt;는&lt;br /&gt;
2025년 5월부터 최신 브라우저에서 폭넓게 동작하는 흐름으로 소개되고 있다.&lt;/p&gt;

&lt;h3 id=&quot;4-promisetry&quot;&gt;4. Promise.try()&lt;/h3&gt;

&lt;p&gt;동기 예외와 비동기 예외를 따로 처리하던 코드를&lt;br /&gt;
조금 더 단순하게 만들 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;loadUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;showError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 패턴의 장점은&lt;br /&gt;
함수가 동기로 터지든, Promise rejection으로 터지든&lt;br /&gt;
에러 경로를 한 번에 모을 수 있다는 점이다.&lt;/p&gt;

&lt;h3 id=&quot;5-import-attributes&quot;&gt;5. Import Attributes&lt;/h3&gt;

&lt;p&gt;JSON이나 CSS 같은 리소스를&lt;br /&gt;
더 명시적으로 import하는 흐름도 중요하다.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./data.json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sheet&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;./styles.css&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;문법은 깔끔하지만&lt;br /&gt;
JSON import는 실패 시 모듈 그래프 전체에 영향이 갈 수 있어서&lt;br /&gt;
항상 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt;보다 낫다고 보긴 어렵다.&lt;/p&gt;

&lt;p&gt;즉, 이 기능은 “새 문법”으로 보기보다&lt;br /&gt;
&lt;strong&gt;리소스를 자바스크립트 모듈 시스템에 어떻게 연결할 것인가&lt;/strong&gt;의 문제로 보는 편이 맞다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;es2026-후보-중에서-특히-중요한-것&quot;&gt;ES2026 후보 중에서 특히 중요한 것&lt;/h2&gt;

&lt;h3 id=&quot;1-temporal-api&quot;&gt;1. Temporal API&lt;/h3&gt;

&lt;p&gt;원문에서도 가장 크게 다룬 기능이 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Temporal&lt;/code&gt;이다.&lt;br /&gt;
이건 이유가 분명하다.&lt;br /&gt;
JavaScript의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Date&lt;/code&gt;는 오랫동안 불편했다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;timezone 처리&lt;/li&gt;
  &lt;li&gt;월 단위 계산&lt;/li&gt;
  &lt;li&gt;immutable하지 않은 API&lt;/li&gt;
  &lt;li&gt;비교와 연산의 혼란&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Temporal&lt;/code&gt;은 이 문제를 거의 정면으로 해결하려는 API다.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Temporal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;zonedDateTimeISO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Asia/Seoul&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLocaleString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;월 계산도 더 직관적이다.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;jan31&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Temporal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PlainDate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;2026-01-31&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nextMonth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;jan31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;months&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;nextMonth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 2026-02-28&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;다만 여기서는 날짜를 분명히 봐야 한다.&lt;br /&gt;
&lt;strong&gt;2026년 4월 5일 기준 TC39의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proposal-temporal&lt;/code&gt;은 Stage 4&lt;/strong&gt;이다.&lt;br /&gt;
즉, 매우 중요하고 거의 표준처럼 다뤄지지만&lt;br /&gt;
아직 확정된 ECMAScript 본편이라고 단정하면 안 된다.&lt;/p&gt;

&lt;p&gt;그래서 지금의 실무 기준은 이렇다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;개념은 반드시 익혀둘 것&lt;/li&gt;
  &lt;li&gt;브라우저/런타임 지원 범위를 확인하고 도입할 것&lt;/li&gt;
  &lt;li&gt;기존 날짜 라이브러리 교체는 점진적으로 판단할 것&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;2-explicit-resource-management&quot;&gt;2. Explicit Resource Management&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;using&lt;/code&gt;과 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symbol.dispose&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symbol.asyncDispose&lt;/code&gt; 계열은&lt;br /&gt;
리소스 정리를 더 명시적으로 만든다.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FileHandle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;output.txt&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 기능이 중요한 이유는&lt;br /&gt;
파일, DB 연결, 임시 리소스처럼&lt;br /&gt;
“반드시 정리해야 하는 것”을 언어 차원에서 다룰 수 있기 때문이다.&lt;/p&gt;

&lt;p&gt;자바스크립트가 점점&lt;br /&gt;
스크립트 언어를 넘어서 애플리케이션 언어로 가고 있다는 신호로 볼 수 있다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2026년-런타임에서-봐야-할-것&quot;&gt;2026년 런타임에서 봐야 할 것&lt;/h2&gt;

&lt;h3 id=&quot;1-nodejs는-이제-typescript-실행-경험을-직접-제공한다&quot;&gt;1. Node.js는 이제 TypeScript 실행 경험을 직접 제공한다&lt;/h3&gt;

&lt;p&gt;Node.js 22.18.0부터는&lt;br /&gt;
타입 스트리핑 기반 TypeScript 실행이 기본 동작으로 들어왔다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;node script.ts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이건 꽤 큰 변화다.&lt;br /&gt;
예전에는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsx&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ts-node&lt;/code&gt;, 빌드 설정이 거의 기본 전제였다.&lt;br /&gt;
이제는 간단한 스크립트나 도구성 코드에서&lt;br /&gt;
Node 자체만으로도 시작이 쉬워졌다.&lt;/p&gt;

&lt;p&gt;다만 한계는 분명하다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;TypeScript 전체 기능을 다 지원하는 것은 아니다&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsconfig.json&lt;/code&gt; 기반의 완전한 타입 검사 도구는 아니다&lt;/li&gt;
  &lt;li&gt;“실행 편의성”이 커진 것이지 “타입 안정성”이 자동으로 해결된 것은 아니다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node script.ts&lt;/code&gt;는&lt;br /&gt;
개발 경험 개선이지 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsc&lt;/code&gt;의 대체는 아니다.&lt;/p&gt;

&lt;h3 id=&quot;2-bun과-deno는-계속-node를-압박하고-있다&quot;&gt;2. Bun과 Deno는 계속 Node를 압박하고 있다&lt;/h3&gt;

&lt;p&gt;2026년에도 기본 축은 Node.js다.&lt;br /&gt;
하지만 런타임 경쟁은 더 흥미로워졌다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Bun&lt;/strong&gt;: 빠른 설치 속도와 빠른 실행 속도&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Deno&lt;/strong&gt;: 보안 기본값과 안정성&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Node.js&lt;/strong&gt;: 생태계 규모와 호환성&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;특히 Deno는 “secure by default” 철학이 분명하고,&lt;br /&gt;
Bun은 개발 서버 경험과 속도를 강하게 밀고 있다.&lt;/p&gt;

&lt;p&gt;중요한 건 이제 세 런타임 모두&lt;br /&gt;
예전보다 훨씬 비슷해지고 있다는 점이다.&lt;br /&gt;
즉, 차이는 문법보다 &lt;strong&gt;운영 철학과 생태계 선택&lt;/strong&gt;에 더 가깝다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;빌드-도구는-결국-vite-중심으로-모이고-있다&quot;&gt;빌드 도구는 결국 Vite 중심으로 모이고 있다&lt;/h2&gt;

&lt;h3 id=&quot;1-vite-8&quot;&gt;1. Vite 8&lt;/h3&gt;

&lt;p&gt;2026년 3월 12일에 공개된 Vite 8은&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rolldown&lt;/code&gt; 기반 단일 번들러 구조를 더 강하게 밀고 있다.&lt;/p&gt;

&lt;p&gt;이 변화가 중요한 이유는&lt;br /&gt;
Vite가 더 이상 “빠른 dev server” 정도가 아니라&lt;br /&gt;
&lt;strong&gt;통합 툴체인 중심축&lt;/strong&gt;이 되고 있기 때문이다.&lt;/p&gt;

&lt;p&gt;실무 관점에서 보면 의미는 명확하다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;개발 서버&lt;/li&gt;
  &lt;li&gt;번들링&lt;/li&gt;
  &lt;li&gt;플러그인 생태계&lt;/li&gt;
  &lt;li&gt;테스트 도구와의 연결&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 모든 것이 Vite 중심으로 더 강하게 묶인다.&lt;/p&gt;

&lt;h3 id=&quot;2-webpack은-끝난-것이-아니라-기본값에서-밀려난-것이다&quot;&gt;2. webpack은 끝난 것이 아니라, 기본값에서 밀려난 것이다&lt;/h3&gt;

&lt;p&gt;webpack은 여전히 크고 중요하다.&lt;br /&gt;
다만 2026년 흐름에서 “새 프로젝트의 기본 선택지” 자리는&lt;br /&gt;
대체로 Vite와 Turbopack 쪽으로 이동했다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;프레임워크-흐름도-같이-봐야-한다&quot;&gt;프레임워크 흐름도 같이 봐야 한다&lt;/h2&gt;

&lt;h3 id=&quot;1-react-192&quot;&gt;1. React 19.2&lt;/h3&gt;

&lt;p&gt;React는 2025년 10월 1일에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;React 19.2&lt;/code&gt;를 공개했다.&lt;br /&gt;
이 버전에서 눈에 띄는 키워드는 아래와 같다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffectEvent&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Activity /&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Partial Pre-rendering&lt;/li&gt;
  &lt;li&gt;성능 분석용 Performance Tracks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, React는 여전히&lt;br /&gt;
&lt;strong&gt;렌더링 우선순위와 서버 렌더링 구조를 더 정교하게 다루는 방향&lt;/strong&gt;으로 가고 있다.&lt;/p&gt;

&lt;h3 id=&quot;2-nextjs-16&quot;&gt;2. Next.js 16&lt;/h3&gt;

&lt;p&gt;Next.js 16은 2025년 10월 21일 공개됐다.&lt;br /&gt;
이 버전에서 가장 중요한 변화는&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Turbopack&lt;/code&gt;이 기본 번들러가 되었다는 점이다.&lt;/p&gt;

&lt;p&gt;그리고 같이 봐야 할 변화가 있다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;React 19.2 지원&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy.ts&lt;/code&gt; 전환&lt;/li&gt;
  &lt;li&gt;Cache Components&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;revalidateTag()&lt;/code&gt; 시그니처 변화&lt;/li&gt;
  &lt;li&gt;비동기 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;searchParams&lt;/code&gt; 사용 강화&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, Next.js는 여전히&lt;br /&gt;
“React를 서버 중심 아키텍처로 실용화하는 프레임워크” 방향을 더 밀고 있다.&lt;/p&gt;

&lt;h3 id=&quot;3-프레임워크를-볼-때-중요한-질문&quot;&gt;3. 프레임워크를 볼 때 중요한 질문&lt;/h3&gt;

&lt;p&gt;2026년에는&lt;br /&gt;
프레임워크를 비교할 때 기능 목록보다 이 질문이 더 중요하다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;서버를 기본 전제로 두는가&lt;/li&gt;
  &lt;li&gt;캐싱 모델이 명확한가&lt;/li&gt;
  &lt;li&gt;빌드 도구와 얼마나 잘 통합되는가&lt;/li&gt;
  &lt;li&gt;React Compiler 같은 자동 최적화와 얼마나 맞물리는가&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;typescript도-같이-봐야-한다&quot;&gt;TypeScript도 같이 봐야 한다&lt;/h2&gt;

&lt;p&gt;2026년 3월 23일 TypeScript 6.0이 공개됐다.&lt;br /&gt;
이 버전은 “엄청난 기능 추가”보다&lt;br /&gt;
&lt;strong&gt;TypeScript 7 전환을 준비하는 정리 단계&lt;/strong&gt; 성격이 강하다.&lt;/p&gt;

&lt;p&gt;중요한 이유는&lt;br /&gt;
다음 세대 TypeScript가 Go 기반 컴파일러로 이동하면서&lt;br /&gt;
속도 개선 기대치가 매우 커졌기 때문이다.&lt;/p&gt;

&lt;p&gt;결국 2026년의 자바스크립트 생태계는&lt;br /&gt;
JavaScript 단독이라기보다&lt;br /&gt;
&lt;strong&gt;JavaScript + TypeScript + 빌드 체인 전체&lt;/strong&gt;로 이해해야 한다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;그래서-지금-무엇을-공부해야-할까&quot;&gt;그래서 지금 무엇을 공부해야 할까&lt;/h2&gt;

&lt;p&gt;2026년 기준으로 우선순위를 잡는다면&lt;br /&gt;
나는 아래 순서가 실용적이라고 본다.&lt;/p&gt;

&lt;h3 id=&quot;1-es2025-실전-기능&quot;&gt;1. ES2025 실전 기능&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterator Helpers&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Set&lt;/code&gt; 메서드&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RegExp.escape()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Import Attributes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이건 지금 바로 코드 품질과 표현력을 바꾼다.&lt;/p&gt;

&lt;h3 id=&quot;2-날짜와-시간-처리의-새-기준&quot;&gt;2. 날짜와 시간 처리의 새 기준&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Temporal&lt;/code&gt; 개념&lt;/li&gt;
  &lt;li&gt;timezone 처리&lt;/li&gt;
  &lt;li&gt;immutable date API 감각&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이건 곧 실무 기본기가 될 가능성이 크다.&lt;/p&gt;

&lt;h3 id=&quot;3-런타임-감각&quot;&gt;3. 런타임 감각&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Node.js에서 TypeScript를 어디까지 기본 지원하는가&lt;/li&gt;
  &lt;li&gt;Bun과 Deno는 무엇이 다른가&lt;/li&gt;
  &lt;li&gt;보안 모델과 실행 모델이 어떻게 다른가&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;4-빌드-도구와-프레임워크-연결&quot;&gt;4. 빌드 도구와 프레임워크 연결&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Vite 8&lt;/li&gt;
  &lt;li&gt;Next.js 16&lt;/li&gt;
  &lt;li&gt;React 19.2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;프론트엔드 개발자라면&lt;br /&gt;
이 세 축은 거의 같이 봐야 한다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;정리&quot;&gt;정리&lt;/h2&gt;

&lt;p&gt;2026년 자바스크립트에서 중요한 것은&lt;br /&gt;
“새 문법 몇 개를 외우는가”가 아니다.&lt;/p&gt;

&lt;p&gt;더 중요한 것은 아래다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;어떤 기능이 이제 실무 기본값이 되었는가&lt;/li&gt;
  &lt;li&gt;어떤 API가 아직 후보 단계인가&lt;/li&gt;
  &lt;li&gt;어떤 런타임과 도구가 실제 개발 경험을 바꾸는가&lt;/li&gt;
  &lt;li&gt;프레임워크가 서버 중심 구조로 얼마나 이동했는가&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, 지금 자바스크립트를 공부한다는 말은&lt;br /&gt;
언어만 공부하는 것이 아니라&lt;br /&gt;
&lt;strong&gt;언어, 런타임, 빌드 도구, 프레임워크의 연결 구조를 같이 이해하는 것&lt;/strong&gt;에 가깝다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;참고-자료&quot;&gt;참고 자료&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://frontendmasters.com/blog/what-to-know-in-javascript-2026-edition/&quot;&gt;Frontend Masters - What To Know in JavaScript (2026 Edition)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 05 Apr 2026 14:20:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/04/05/javascript-2026/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/04/05/javascript-2026/</guid>
        
        <category>javascript</category>
        
        <category>frontend</category>
        
        <category>web-development</category>
        
        <category>react</category>
        
        <category>nextjs</category>
        
        
      </item>
    
      <item>
        <title>antd vs shadcn/ui 비교: 어떤 React UI를 선택해야 할까?</title>
        <description>&lt;p&gt;React 프로젝트를 시작할 때 UI 선택지는 많지만, 실무에서 자주 비교되는 조합은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;다.&lt;/p&gt;

&lt;p&gt;둘 다 React에서 빠르게 화면을 만들 수 있다는 점은 같지만, 접근 방식은 꽤 다르다.&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;는 &lt;strong&gt;완성형 컴포넌트 라이브러리&lt;/strong&gt;에 가깝고, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;는 &lt;strong&gt;내 코드베이스 안으로 가져와 직접 소유하는 UI 세트&lt;/strong&gt;에 가깝다.&lt;/p&gt;

&lt;p&gt;현재 공식 문서 기준 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;는 &lt;strong&gt;6.3.4&lt;/strong&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init/add&lt;/code&gt; CLI와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Radix/Base UI&lt;/code&gt; 선택 구조를 제공하고 있다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;요약&quot;&gt;요약&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;관리자 화면, 대시보드, 폼/테이블 중심 서비스라면: &lt;strong&gt;antd&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;마케팅 페이지, SaaS 프로덕트, 브랜드 커스터마이징이 중요한 서비스라면: &lt;strong&gt;shadcn/ui&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;팀이 “일단 빨리 많이 만들어야 한다”면: &lt;strong&gt;antd&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;팀이 “디자인 시스템을 우리 코드로 오래 가져가야 한다”면: &lt;strong&gt;shadcn/ui&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;antd와-shadcnui의-핵심-차이&quot;&gt;antd와 shadcn/ui의 핵심 차이&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;antd&lt;/th&gt;
      &lt;th&gt;shadcn/ui&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;기본 성격&lt;/td&gt;
      &lt;td&gt;완성형 React UI 라이브러리&lt;/td&gt;
      &lt;td&gt;코드 배포형 UI 세트&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;설치 방식&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install antd&lt;/code&gt; 후 import&lt;/td&gt;
      &lt;td&gt;CLI로 필요한 컴포넌트 코드를 프로젝트에 추가&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;기본 스타일&lt;/td&gt;
      &lt;td&gt;일관된 Ant Design 스타일&lt;/td&gt;
      &lt;td&gt;미니멀한 기본 스타일, 직접 수정 전제&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;커스터마이징&lt;/td&gt;
      &lt;td&gt;토큰/테마 중심&lt;/td&gt;
      &lt;td&gt;코드 직접 수정 중심&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;컴포넌트 수&lt;/td&gt;
      &lt;td&gt;매우 많음&lt;/td&gt;
      &lt;td&gt;필요한 것만 선택 추가&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;러닝커브&lt;/td&gt;
      &lt;td&gt;낮음&lt;/td&gt;
      &lt;td&gt;낮지 않음(Tailwind/CSS 변수 구조 이해 필요)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;업데이트 방식&lt;/td&gt;
      &lt;td&gt;라이브러리 버전 업&lt;/td&gt;
      &lt;td&gt;내 코드와 의존성 같이 관리&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;잘 맞는 제품&lt;/td&gt;
      &lt;td&gt;백오피스, 대시보드, CRUD&lt;/td&gt;
      &lt;td&gt;제품 UI, 디자인시스템 적용된 서비스&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-설치와-시작-속도&quot;&gt;1. 설치와 시작 속도&lt;/h2&gt;

&lt;h3 id=&quot;antd-바로-쓸-수-있는-완성형&quot;&gt;antd: 바로 쓸 수 있는 완성형&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;는 설치 후 바로 import해서 사용할 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DatePicker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;antd&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;공식 소개 페이지 기준으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;는 엔터프라이즈 웹 애플리케이션용 UI 프레임워크를 지향하고,&lt;br /&gt;
컴포넌트 수가 매우 많다. 실제로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Form&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Table&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DatePicker&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Modal&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Drawer&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Upload&lt;/code&gt;처럼&lt;br /&gt;
실무에서 자주 쓰는 조합이 이미 잘 갖춰져 있다.&lt;/p&gt;

&lt;p&gt;즉, &lt;strong&gt;관리자 화면을 빨리 만드는 속도&lt;/strong&gt;는 대체로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt; 쪽이 더 좋다.&lt;/p&gt;

&lt;h3 id=&quot;shadcnui-필요한-것만-가져오는-방식&quot;&gt;shadcn/ui: 필요한 것만 가져오는 방식&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;는 전통적인 “패키지 import” 스타일과 다르다.&lt;br /&gt;
CLI로 필요한 컴포넌트를 내 프로젝트에 추가하는 방식이다.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pnpm dlx shadcn@latest init
pnpm dlx shadcn@latest add button dialog form
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;공식 문서도 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;를 &lt;strong&gt;전통적인 컴포넌트 라이브러리라기보다, 코드 배포 방식&lt;/strong&gt;으로 설명한다.&lt;br /&gt;
즉, 가져다 쓰는 게 아니라 &lt;strong&gt;내 프로젝트 안에 컴포넌트 파일이 들어온다&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;이 방식은 초반 세팅은 조금 더 필요하지만, 장기적으로는 유연하다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-커스터마이징은-누가-더-쉬운가&quot;&gt;2. 커스터마이징은 누가 더 쉬운가?&lt;/h2&gt;

&lt;h3 id=&quot;antd-토큰-기반-커스터마이징&quot;&gt;antd: 토큰 기반 커스터마이징&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ConfigProvider&lt;/code&gt;와 디자인 토큰 기반 커스터마이징이 강하다.&lt;/p&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ConfigProvider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;antd&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConfigProvider&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;colorPrimary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;#0f766e&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;borderRadius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;cm&quot;&gt;/* app */&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ConfigProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;공식 문서 기준으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt; 5+는 CSS-in-JS 기반 테마 시스템을 제공하고,&lt;br /&gt;
6.x에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zeroRuntime&lt;/code&gt; 모드도 지원한다.&lt;br /&gt;
즉, &lt;strong&gt;“브랜드 컬러/둥근 모서리/밀도” 같은 전역 규칙&lt;/strong&gt;을 적용하는 데 강하다.&lt;/p&gt;

&lt;p&gt;다만 컴포넌트 내부 DOM 구조까지 강하게 바꾸고 싶을 때는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;보다 답답할 수 있다.&lt;/p&gt;

&lt;h3 id=&quot;shadcnui-코드-직접-수정&quot;&gt;shadcn/ui: 코드 직접 수정&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;의 강점은 여기서 나온다.&lt;br /&gt;
버튼, 다이얼로그, 셀렉트 같은 UI가 &lt;strong&gt;내 코드로 들어오기 때문에 그대로 수정&lt;/strong&gt;할 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// components/ui/button.tsx&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ButtonProps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;inline-flex h-10 items-center rounded-md px-4 text-sm font-medium&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;className&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;공식 문서 기준으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;는 CSS Variables 기반 테마를 권장하고,&lt;br /&gt;
Tailwind CSS를 전제로 스타일링한다.&lt;/p&gt;

&lt;p&gt;즉, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;는 &lt;strong&gt;디자인 시스템을 라이브러리 밖에서 억지로 덮는 구조가 아니라, 아예 코드 자체를 팀 자산으로 가져가는 방식&lt;/strong&gt;이다.&lt;/p&gt;

&lt;p&gt;결론적으로:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;전역 테마 수정&lt;/strong&gt;은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;가 편하다&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;컴포넌트 구조 자체를 수정&lt;/strong&gt;하려면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;가 더 편하다&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-디자인-일관성과-기본-완성도&quot;&gt;3. 디자인 일관성과 기본 완성도&lt;/h2&gt;

&lt;h3 id=&quot;antd는-정해진-시스템이-강하다&quot;&gt;antd는 “정해진 시스템”이 강하다&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;는 처음부터 디자인 언어가 강하다.&lt;br /&gt;
버튼, 입력창, 테이블, 모달, 네비게이션이 한 세트처럼 움직인다.&lt;/p&gt;

&lt;p&gt;이건 특히 아래 같은 상황에서 유리하다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;백오피스 화면이 많다&lt;/li&gt;
  &lt;li&gt;폼과 테이블이 많다&lt;/li&gt;
  &lt;li&gt;디자인보다 기능 우선이다&lt;/li&gt;
  &lt;li&gt;여러 개발자가 동시에 화면을 찍어내야 한다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, &lt;strong&gt;디자인 판단 비용을 줄여준다&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;shadcnui는-좋은-출발점에-가깝다&quot;&gt;shadcn/ui는 “좋은 출발점”에 가깝다&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;는 기본 스타일이 깔끔하지만, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;처럼 강한 제품 언어를 밀어붙이진 않는다.&lt;br /&gt;
오히려 그 점이 장점이다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;제품 톤앤매너를 직접 만들기 쉽다&lt;/li&gt;
  &lt;li&gt;Tailwind/CSS 변수 기반으로 미세 조정이 쉽다&lt;/li&gt;
  &lt;li&gt;마케팅 페이지와 앱 화면을 같은 언어로 묶기 좋다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;대신 팀이 디자인 원칙 없이 들어가면, 화면마다 느낌이 달라질 수 있다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;4-컴포넌트-범위와-실무-생산성&quot;&gt;4. 컴포넌트 범위와 실무 생산성&lt;/h2&gt;

&lt;h3 id=&quot;antd는-특히-formtable-계열이-강하다&quot;&gt;antd는 특히 Form/Table 계열이 강하다&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;의 가장 큰 실무 장점은 풍부한 컴포넌트다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Form&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Table&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DatePicker&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tree&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Select&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Upload&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Drawer&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Modal&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Notification&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이런 조합을 직접 설계하지 않고 빠르게 연결할 수 있다.&lt;br /&gt;
관리자 시스템, 내부 운영 도구, B2B SaaS 백오피스라면 이 장점이 매우 크다.&lt;/p&gt;

&lt;h3 id=&quot;shadcnui는-필요한-것만-쌓는-방식&quot;&gt;shadcn/ui는 필요한 것만 쌓는 방식&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;도 폼, 다이얼로그, 드롭다운, 시트, 토스트 등 주요 조각은 충분히 제공한다.&lt;br /&gt;
게다가 최근 문서 기준으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Radix&lt;/code&gt;와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Base UI&lt;/code&gt; 선택도 가능하다.&lt;/p&gt;

&lt;p&gt;하지만 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;처럼 거대한 완성형 세트라기보다,&lt;br /&gt;
&lt;strong&gt;“팀이 필요한 조각을 가져와 조립하는 방식”&lt;/strong&gt;에 더 가깝다.&lt;/p&gt;

&lt;p&gt;그래서 제품이 아래에 가까울수록 잘 맞는다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;커스텀 UI가 많다&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;hr /&gt;

&lt;h2 id=&quot;5-번들-런타임-성능-관점&quot;&gt;5. 번들, 런타임, 성능 관점&lt;/h2&gt;

&lt;p&gt;이 비교는 “무조건 누가 더 빠르다”로 단순화하기 어렵다.&lt;br /&gt;
대신 &lt;strong&gt;어디서 비용이 생기는가&lt;/strong&gt;를 봐야 한다.&lt;/p&gt;

&lt;h3 id=&quot;antd&quot;&gt;antd&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;장점: 검증된 컴포넌트가 많아 직접 구현 비용이 줄어듦&lt;/li&gt;
  &lt;li&gt;장점: ES modules 기반 tree shaking 지원&lt;/li&gt;
  &lt;li&gt;장점: 6.x에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;zeroRuntime&lt;/code&gt; 모드 지원&lt;/li&gt;
  &lt;li&gt;주의점: 무거운 화면에서 많은 고수준 컴포넌트를 한 번에 쓰면 체감 비용이 생길 수 있음&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;shadcnui&quot;&gt;shadcn/ui&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;장점: 필요한 컴포넌트만 추가 가능&lt;/li&gt;
  &lt;li&gt;장점: 코드 소유권이 내 쪽에 있어 불필요한 추상화 제거가 쉬움&lt;/li&gt;
  &lt;li&gt;장점: Tailwind/CSS 변수 조합으로 스타일 제어가 단순한 편&lt;/li&gt;
  &lt;li&gt;주의점: 성능이 자동으로 좋아지는 것은 아님&lt;/li&gt;
  &lt;li&gt;주의점: 결국 팀이 추가한 코드 품질에 따라 차이가 커짐&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, 성능만 보고 고르면 안 된다.&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;는 &lt;strong&gt;완성도와 생산성의 비용 구조&lt;/strong&gt;,&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;는 &lt;strong&gt;유연성과 소유권의 비용 구조&lt;/strong&gt;라고 보는 편이 맞다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;6-어떤-팀에-무엇이-맞을까&quot;&gt;6. 어떤 팀에 무엇이 맞을까?&lt;/h2&gt;

&lt;h3 id=&quot;antd가-더-잘-맞는-팀&quot;&gt;antd가 더 잘 맞는 팀&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;운영툴, 관리자, 대시보드 개발이 많다&lt;/li&gt;
  &lt;li&gt;폼/테이블/권한 관리 화면이 많다&lt;/li&gt;
  &lt;li&gt;디자이너 리소스가 제한적이다&lt;/li&gt;
  &lt;li&gt;팀원마다 UI 구현 편차를 줄이고 싶다&lt;/li&gt;
  &lt;li&gt;빠르게 MVP가 아니라 빠르게 “업무용 완성 화면”을 내야 한다&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;shadcnui가-더-잘-맞는-팀&quot;&gt;shadcn/ui가 더 잘 맞는 팀&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;제품 UI를 브랜드에 맞게 세밀하게 만들고 싶다&lt;/li&gt;
  &lt;li&gt;Tailwind 기반 개발 문화가 이미 있다&lt;/li&gt;
  &lt;li&gt;컴포넌트를 우리 코드 자산으로 관리하고 싶다&lt;/li&gt;
  &lt;li&gt;Next.js/Vite 기반 프론트엔드 제품팀이다&lt;/li&gt;
  &lt;li&gt;장기적으로 사내 디자인 시스템을 키우고 싶다&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;7-내-기준-추천&quot;&gt;7. 내 기준 추천&lt;/h2&gt;

&lt;p&gt;제가 실무에서 선택한다면 기준은 꽤 단순하다.&lt;/p&gt;

&lt;h3 id=&quot;antd를-고를-때&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;를 고를 때&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;“이번 분기 안에 관리자 화면을 많이 만들어야 한다”&lt;/li&gt;
  &lt;li&gt;“Table/Form/DatePicker가 핵심이다”&lt;/li&gt;
  &lt;li&gt;“팀 전체가 같은 UI 언어를 빠르게 공유해야 한다”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 경우는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;가 훨씬 실용적이다.&lt;/p&gt;

&lt;h3 id=&quot;shadcnui를-고를-때&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;를 고를 때&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;“디자인 시스템을 우리가 소유해야 한다”&lt;/li&gt;
  &lt;li&gt;“컴포넌트를 계속 뜯어고칠 가능성이 높다”&lt;/li&gt;
  &lt;li&gt;“브랜드 경험이 제품 경쟁력이다”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 경우는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;가 더 오래 버틴다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;최종-정리&quot;&gt;최종 정리&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd vs shadcn/ui&lt;/code&gt; 비교는 단순히 “예쁜가?”의 문제가 아니다.&lt;br /&gt;
결국 아래 두 질문에 대한 답이다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;우리는 &lt;strong&gt;완성형 라이브러리&lt;/strong&gt;가 필요한가?&lt;/li&gt;
  &lt;li&gt;아니면 &lt;strong&gt;우리 코드베이스 안에서 디자인 시스템을 직접 키울 것인가?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;빠른 실무 생산성, 풍부한 기본 컴포넌트, 관리자 화면 중심이라면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;antd&lt;/code&gt;.&lt;br /&gt;
브랜드 커스터마이징, 코드 소유권, 제품 UI 확장성을 더 중시한다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shadcn/ui&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;둘 중 무엇이 더 좋다고 단정하기보다,&lt;br /&gt;
&lt;strong&gt;“우리 팀의 제품 성격과 유지보수 방식에 더 맞는가”&lt;/strong&gt;로 고르는 게 맞다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;참고-자료&quot;&gt;참고 자료&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://ant.design/&quot;&gt;Ant Design&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ui.shadcn.com/&quot;&gt;shadcn/ui&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Sat, 28 Mar 2026 16:07:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/03/28/antd-vs-shadcn/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/03/28/antd-vs-shadcn/</guid>
        
        <category>react</category>
        
        <category>frontend</category>
        
        <category>web-development</category>
        
        
      </item>
    
      <item>
        <title>React와 Next.js를 위한 완벽한 Codex 설정</title>
        <description>&lt;p&gt;AI 코딩 도구는 이제 많다.&lt;br /&gt;
하지만 생산성이 정말 오르는 팀은 모델이 좋아서가 아니라&lt;br /&gt;
&lt;strong&gt;에이전트가 참고할 규칙과 검증 루프를 먼저 세팅한 팀&lt;/strong&gt;이다.&lt;/p&gt;

&lt;p&gt;Builder.io의 글&lt;br /&gt;
&lt;a href=&quot;https://www.builder.io/blog/cursor-ai-tips-react-nextjs&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;The Perfect Cursor AI setup for React and Next.js&lt;/code&gt;&lt;/a&gt;도&lt;br /&gt;
결국 같은 얘기를 한다.&lt;br /&gt;
핵심은 기능 목록이 아니라, &lt;strong&gt;문맥을 고정하고 결과를 자동으로 검증하는 흐름&lt;/strong&gt;이다.&lt;/p&gt;

&lt;p&gt;이 글에서는 그 원칙을 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cursor&lt;/code&gt;가 아니라&lt;br /&gt;
&lt;strong&gt;Codex를 기준으로 React/Next.js 프로젝트에 맞게 다시 정리&lt;/strong&gt;해보겠다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;결론부터-codex는-설정보다-작업-환경-설계가-중요하다&quot;&gt;결론부터: Codex는 “설정”보다 “작업 환경 설계”가 중요하다&lt;/h2&gt;

&lt;p&gt;React와 Next.js에서 Codex를 잘 쓰려면 아래 네 가지가 먼저 잡혀야 한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;프로젝트 규칙을 한 곳에 적어둘 것&lt;/li&gt;
  &lt;li&gt;어떤 문서를 우선 신뢰해야 하는지 정해둘 것&lt;/li&gt;
  &lt;li&gt;lint, typecheck, test를 검증 루프로 묶을 것&lt;/li&gt;
  &lt;li&gt;에이전트가 한 번에 너무 큰 변경을 하지 않게 작업 단위를 작게 나눌 것&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, Codex를 잘 쓰는 방법은&lt;br /&gt;
프롬프트를 화려하게 쓰는 게 아니라&lt;br /&gt;
&lt;strong&gt;좋은 기본 규칙 + 짧은 피드백 루프 + 명확한 코드베이스 문맥&lt;/strong&gt;을 만드는 것이다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;왜-react와-nextjs에서-특히-설정이-중요할까&quot;&gt;왜 React와 Next.js에서 특히 설정이 중요할까&lt;/h2&gt;

&lt;p&gt;React와 Next.js 프로젝트는 AI가 실수하기 쉬운 지점이 꽤 분명하다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;서버 컴포넌트와 클라이언트 컴포넌트 경계를 흐릴 수 있다&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt;를 과하게 추가할 수 있다&lt;/li&gt;
  &lt;li&gt;최신 라우팅 구조를 무시하고 예전 페이지 라우터 방식으로 코드를 만들 수 있다&lt;/li&gt;
  &lt;li&gt;Tailwind 클래스만 맞추고 접근성이나 상태 흐름은 놓칠 수 있다&lt;/li&gt;
  &lt;li&gt;타입은 맞는 것처럼 보이지만 실제 런타임 흐름은 깨질 수 있다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;그래서 이 스택에서는&lt;br /&gt;
AI가 “대충 돌아가는 코드”를 내놓는 것보다&lt;br /&gt;
&lt;strong&gt;우리 프로젝트 규칙을 어기지 않게 만드는 장치&lt;/strong&gt;가 더 중요하다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-가장-먼저-해야-할-일-agentsmd를-제대로-적기&quot;&gt;1. 가장 먼저 해야 할 일: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AGENTS.md&lt;/code&gt;를 제대로 적기&lt;/h2&gt;

&lt;p&gt;Codex 계열 에이전트를 쓸 때 가장 먼저 손봐야 하는 파일은 보통 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AGENTS.md&lt;/code&gt;다.&lt;br /&gt;
이 파일은 “우리 프로젝트에서 어떻게 일해야 하는가”를 고정하는 역할을 한다.&lt;/p&gt;

&lt;p&gt;여기에는 최소한 아래 내용이 들어가야 한다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;패키지 매니저는 무엇인지 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pnpm&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;개발 서버/테스트/린트 명령어가 무엇인지&lt;/li&gt;
  &lt;li&gt;React와 Next.js에서 반드시 지켜야 할 규칙&lt;/li&gt;
  &lt;li&gt;폴더 구조와 컴포넌트 배치 원칙&lt;/li&gt;
  &lt;li&gt;스타일링 방식 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tailwind&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSS Modules&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;styled-components&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;데이터 패칭 원칙&lt;/li&gt;
  &lt;li&gt;PR 전에 반드시 통과해야 하는 체크 항목&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;예를 들면 이런 식이다.&lt;/p&gt;

&lt;div class=&quot;language-md highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gu&quot;&gt;## 프로젝트 규칙&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;
-&lt;/span&gt; Next.js는 App Router만 사용한다.
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; 기본 컴포넌트는 서버 컴포넌트로 작성한다.
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; 브라우저 API나 클라이언트 상태가 필요할 때만 &lt;span class=&quot;sb&quot;&gt;`&quot;use client&quot;`&lt;/span&gt;를 추가한다.
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; 데이터 패칭은 가능한 서버에서 먼저 처리하고, 꼭 필요하지 않다면 &lt;span class=&quot;sb&quot;&gt;`useEffect`&lt;/span&gt;로 옮기지 않는다.
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; 스타일링은 Tailwind CSS를 사용한다.
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; 새 컴포넌트를 만들기 전에 &lt;span class=&quot;sb&quot;&gt;`components/ui`&lt;/span&gt;의 기존 컴포넌트를 먼저 재사용한다.
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; 의미 있는 변경 후에는 &lt;span class=&quot;sb&quot;&gt;`pnpm lint`&lt;/span&gt;, &lt;span class=&quot;sb&quot;&gt;`pnpm typecheck`&lt;/span&gt;, &lt;span class=&quot;sb&quot;&gt;`pnpm test`&lt;/span&gt;를 실행한다.
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; 명시적으로 요청받지 않았다면 새 의존성을 추가하지 않는다.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 정도만 있어도 Codex의 출력 품질은 꽤 달라진다.&lt;br /&gt;
AI는 똑똑한 것 같아도, 기준이 없으면 매번 다른 스타일로 코드를 쓴다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-무엇을-기억해야-하는가를-규칙으로-분리하기&quot;&gt;2. “무엇을 기억해야 하는가”를 규칙으로 분리하기&lt;/h2&gt;

&lt;p&gt;Builder 글이 Cursor의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rules&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Docs&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Memories&lt;/code&gt;를 강조한 이유는 단순하다.&lt;br /&gt;
AI는 코드만 읽고도 어느 정도 일할 수 있지만,&lt;br /&gt;
팀의 취향과 예외 규칙은 코드만으로는 잘 못 배운다.&lt;/p&gt;

&lt;p&gt;Codex에서도 같은 원칙을 적용하면 된다.&lt;/p&gt;

&lt;p&gt;정리 기준은 이렇다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;오래 유지될 규칙: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AGENTS.md&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;특정 폴더/기능에만 적용되는 규칙: 각 디렉터리의 문서 파일&lt;/li&gt;
  &lt;li&gt;자주 틀리는 구현 포인트: 예제 코드와 테스트&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;예를 들어 Next.js 프로젝트라면 아래 항목은 문서로 명시해두는 편이 좋다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/&lt;/code&gt; 아래 라우트 구성 규칙&lt;/li&gt;
  &lt;li&gt;서버 액션 사용 기준&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loading.tsx&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error.tsx&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;not-found.tsx&lt;/code&gt; 처리 원칙&lt;/li&gt;
  &lt;li&gt;캐싱/재검증 정책&lt;/li&gt;
  &lt;li&gt;인증 상태를 어디서 읽는지&lt;/li&gt;
  &lt;li&gt;공통 UI 컴포넌트의 import 경로&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;중요한 건&lt;br /&gt;
설명을 길게 쓰는 게 아니라 &lt;strong&gt;에이전트가 반복해서 참고할 수 있게 짧고 단정하게 적는 것&lt;/strong&gt;이다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-공식-문서를-우선-참조하게-만들어라&quot;&gt;3. 공식 문서를 우선 참조하게 만들어라&lt;/h2&gt;

&lt;p&gt;AI가 가장 자주 헷갈리는 순간은&lt;br /&gt;
“예전에 맞던 답”을 지금도 맞다고 가정할 때다.&lt;/p&gt;

&lt;p&gt;특히 React와 Next.js는 변화가 빠르기 때문에&lt;br /&gt;
다음 우선순위를 미리 정해두는 게 좋다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;React 공식 문서&lt;/li&gt;
  &lt;li&gt;Next.js 공식 문서&lt;/li&gt;
  &lt;li&gt;팀 내부 규칙 문서&lt;/li&gt;
  &lt;li&gt;현재 저장소의 실제 코드&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;실제로 Codex에 일을 시킬 때도 이렇게 요청하는 편이 낫다.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;이 저장소의 현재 라우팅 구조를 기준으로 작업해줘.
기존의 서버 우선 데이터 패칭 방식을 유지해줘.
프레임워크 동작이 애매하면 React와 Next.js 공식 문서 기준에 맞춰줘.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 한 줄이 중요한 이유는&lt;br /&gt;
AI가 “비슷한 예제”가 아니라 &lt;strong&gt;이 프로젝트와 맞는 정답&lt;/strong&gt;을 찾도록 방향을 잡아주기 때문이다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;4-eslint와-typescript는-선택이-아니라-필수다&quot;&gt;4. ESLint와 TypeScript는 선택이 아니라 필수다&lt;/h2&gt;

&lt;p&gt;Builder 글에서도 lint 자동 수정 루프를 강하게 밀고 있는데,&lt;br /&gt;
이건 Codex에서도 거의 그대로 적용된다.&lt;/p&gt;

&lt;p&gt;AI가 만든 코드는 얼핏 그럴듯해 보여도&lt;br /&gt;
실제로는 아래 같은 문제가 자주 생긴다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;사용하지 않는 import&lt;/li&gt;
  &lt;li&gt;잘못된 hook 사용&lt;/li&gt;
  &lt;li&gt;타입 단언 남용&lt;/li&gt;
  &lt;li&gt;client/server 경계 위반&lt;/li&gt;
  &lt;li&gt;접근성 속성 누락&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;그래서 React/Next.js 프로젝트에서 Codex를 쓴다면&lt;br /&gt;
적어도 아래 스크립트는 정리되어 있어야 한다.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;next dev&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;next build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lint&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;next lint&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;typecheck&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tsc --noEmit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;vitest run&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;그리고 에이전트에게도 명시적으로 요구하는 편이 좋다.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;변경 후에는 lint와 typecheck를 실행해줘.
수정한 영역에 테스트가 있으면 함께 실행해줘.
코드만 생성하고 끝내지 말고, 검증이 통과할 때까지 수정해줘.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;핵심은 “코드 생성”에서 끝내지 않는 것이다.&lt;br /&gt;
&lt;strong&gt;생성 -&amp;gt; 검증 -&amp;gt; 수정&lt;/strong&gt;이 한 묶음이어야 한다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;5-테스트-기반-루프를-만들면-codex가-훨씬-안정적이다&quot;&gt;5. 테스트 기반 루프를 만들면 Codex가 훨씬 안정적이다&lt;/h2&gt;

&lt;p&gt;React와 Next.js에서 AI가 제일 잘하는 일 중 하나는&lt;br /&gt;
빈 화면에서 처음부터 모든 걸 맞히는 일이 아니라,&lt;br /&gt;
&lt;strong&gt;실패하는 조건을 보고 점진적으로 고치는 일&lt;/strong&gt;이다.&lt;/p&gt;

&lt;p&gt;그래서 새 기능을 만들 때는 아래 순서가 좋다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;먼저 테스트나 기대 동작을 적는다&lt;/li&gt;
  &lt;li&gt;Codex에게 그 조건을 만족하는 최소 구현을 시킨다&lt;/li&gt;
  &lt;li&gt;lint/typecheck/test를 돌린다&lt;/li&gt;
  &lt;li&gt;실패 원인을 보고 다시 수정한다&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;예를 들면 이렇게 요청할 수 있다.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;`SignupForm` 컴포넌트 테스트를 먼저 작성해줘.
로딩 상태, 유효성 검사 오류 상태, 성공 상태를 포함해줘.
그 다음 테스트를 통과하는 최소 구현을 만들어줘.
컴포넌트는 작게 유지하고 기존 UI 컴포넌트를 재사용해줘.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 방식이 좋은 이유는&lt;br /&gt;
AI가 멋있어 보이는 코드를 쓰는 대신&lt;br /&gt;
&lt;strong&gt;통과해야 할 기준을 중심으로 움직이게 만들기 때문&lt;/strong&gt;이다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;6-작업-단위는-작게-쪼개야-한다&quot;&gt;6. 작업 단위는 작게 쪼개야 한다&lt;/h2&gt;

&lt;p&gt;Codex를 포함한 대부분의 에이전트는&lt;br /&gt;
한 번에 너무 많은 걸 시키면 품질이 급격히 흔들린다.&lt;/p&gt;

&lt;p&gt;특히 아래 요청은 실패 확률이 높다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“대시보드 전체를 새 디자인으로 갈아줘”&lt;/li&gt;
  &lt;li&gt;“Next.js 구조 전부 최신 방식으로 바꿔줘”&lt;/li&gt;
  &lt;li&gt;“상태 관리도 정리하고 성능도 개선하고 테스트도 추가해줘”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;반대로 이런 요청은 훨씬 안정적이다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“이 페이지를 서버 컴포넌트로 유지한 채 데이터 패칭만 정리해줘”&lt;/li&gt;
  &lt;li&gt;“이 폼에서 유효성 검사와 제출 상태만 분리해줘”&lt;/li&gt;
  &lt;li&gt;“이 파일의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt;를 줄이고 불필요한 상태를 제거해줘”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;좋은 에이전트 사용법은&lt;br /&gt;
큰 일을 한 번에 맡기는 것이 아니라&lt;br /&gt;
&lt;strong&gt;작은 단위를 연속적으로 위임하는 것&lt;/strong&gt;에 가깝다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;7-reactnextjs용-codex-프롬프트는-이렇게-바꾸는-게-좋다&quot;&gt;7. React/Next.js용 Codex 프롬프트는 이렇게 바꾸는 게 좋다&lt;/h2&gt;

&lt;p&gt;아래처럼 막연하게 시키면 결과가 흔들리기 쉽다.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;이 페이지를 더 좋게 만들어줘.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;반면 이렇게 조건을 걸면 훨씬 낫다.&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;이 페이지를 현재 Next.js 라우팅 구조에 맞게 리팩터링해줘.
클라이언트 컴포넌트로 바꾸지 말고 지금의 서버 렌더링 구조를 유지해줘.
새 의존성은 추가하지 마.
`components/ui`의 기존 컴포넌트를 우선 재사용해줘.
추상화를 늘리기보다 상태 흐름을 단순하게 유지해줘.
변경 후에는 lint와 typecheck를 실행해줘.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;포인트는 두 가지다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;기술 제약을 먼저 적는다&lt;/li&gt;
  &lt;li&gt;검증 조건을 마지막에 붙인다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 두 줄만 붙여도&lt;br /&gt;
AI가 “예쁜 코드”보다 &lt;strong&gt;프로젝트에 맞는 코드&lt;/strong&gt;를 낼 확률이 올라간다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;8-내가-추천하는-reactnextjs용-codex-기본-체크리스트&quot;&gt;8. 내가 추천하는 React/Next.js용 Codex 기본 체크리스트&lt;/h2&gt;

&lt;p&gt;새 프로젝트든 기존 프로젝트든, 시작점은 아래 정도면 충분하다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AGENTS.md&lt;/code&gt;에 라우팅 구조, 스타일링, 데이터 패칭, 테스트 규칙 작성&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lint&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;typecheck&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; 스크립트 정리&lt;/li&gt;
  &lt;li&gt;공통 컴포넌트 경로와 재사용 원칙 명시&lt;/li&gt;
  &lt;li&gt;서버 컴포넌트와 클라이언트 컴포넌트 경계 규칙 명시&lt;/li&gt;
  &lt;li&gt;의존성 추가 금지 또는 승인 기준 명시&lt;/li&gt;
  &lt;li&gt;큰 작업은 작은 단계로 나눠 요청&lt;/li&gt;
  &lt;li&gt;구현 후 반드시 검증 명령 실행&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;여기까지 맞추면 Codex는 단순 코드 생성기가 아니라&lt;br /&gt;
&lt;strong&gt;팀 규칙 안에서 움직이는 보조 개발자&lt;/strong&gt;에 가까워진다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;마무리&quot;&gt;마무리&lt;/h2&gt;

&lt;p&gt;Builder의 Cursor 설정 글을 읽고 가장 동의했던 부분은&lt;br /&gt;
“좋은 AI 활용은 모델 선택보다 작업 환경 설계에 가깝다”는 점이다.&lt;/p&gt;

&lt;p&gt;Codex도 마찬가지다.&lt;br /&gt;
React와 Next.js에서 정말 중요한 건&lt;br /&gt;
에이전트를 똑똑하게 보이게 만드는 프롬프트가 아니라,
&lt;strong&gt;틀리기 어려운 환경을 먼저 만드는 것&lt;/strong&gt;이다.&lt;/p&gt;

&lt;p&gt;정리하면 React/Next.js에서 Codex 설정의 핵심은 아래다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;규칙은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AGENTS.md&lt;/code&gt;에 고정한다&lt;/li&gt;
  &lt;li&gt;공식 문서와 저장소 패턴을 우선 참조하게 만든다&lt;/li&gt;
  &lt;li&gt;lint/typecheck/test를 작업 루프에 포함한다&lt;/li&gt;
  &lt;li&gt;한 번에 큰 작업보다 작은 수정 단위로 맡긴다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 네 가지만 지켜도&lt;br /&gt;
AI 코딩 경험은 꽤 안정적으로 바뀐다.&lt;/p&gt;
</description>
        <pubDate>Fri, 27 Mar 2026 16:32:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/03/27/react-nextjs-codex-setup/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/03/27/react-nextjs-codex-setup/</guid>
        
        <category>ai</category>
        
        <category>react</category>
        
        <category>nextjs</category>
        
        <category>frontend</category>
        
        <category>web-development</category>
        
        
      </item>
    
      <item>
        <title>ChatGPT vs Gemini 비교: 프론트엔드 개발자에게 어떤 AI가 더 좋을까?</title>
        <description>&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT vs Gemini 비교&lt;/code&gt;를 찾는 개발자는 대체로 같은 질문을 한다.&lt;br /&gt;
“코딩은 누가 더 잘하나?”, “프론트엔드 개발에는 뭐가 더 잘 맞나?”, “React나 Next.js 작업에서는 어느 쪽이 덜 답답한가?”&lt;/p&gt;

&lt;p&gt;이 글은 그런 검색 의도에 맞춰 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;챗GPT vs 제미나이 비교&lt;/code&gt;를&lt;br /&gt;
&lt;strong&gt;프론트엔드 개발자 실사용 관점&lt;/strong&gt;으로 정리한 글이다.&lt;br /&gt;
특히 아래 작업을 기준으로 나눠서 본다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;React 컴포넌트 리팩터링&lt;/li&gt;
  &lt;li&gt;UI 초안 생성과 반복 수정&lt;/li&gt;
  &lt;li&gt;긴 문서, 긴 로그, 긴 스펙 요약&lt;/li&gt;
  &lt;li&gt;Next.js에 AI 기능 붙이는 프로토타이핑&lt;/li&gt;
&lt;/ul&gt;

&lt;!--more--&gt;

&lt;p&gt;결론만 먼저 말하면 이렇다.&lt;br /&gt;
&lt;strong&gt;코드를 세밀하게 고치고 UI를 여러 번 다듬는 흐름은 ChatGPT가 편하고, 긴 문서를 한 번에 읽히거나 실험용 API 흐름을 빠르게 잡는 일은 Gemini가 편한 경우가 많다.&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;요약&quot;&gt;요약&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;는 기존 코드 수정, React 리팩터링, UI 반복 편집에 강점이 있다&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;는 긴 문서 요약, 긴 로그 분석, 여러 자료를 한 번에 읽히는 작업에 강점이 있다&lt;/li&gt;
  &lt;li&gt;프론트엔드 개발자라면 하나만 고르기보다 &lt;strong&gt;편집은 ChatGPT, 탐색과 대용량 입력은 Gemini&lt;/strong&gt;로 나눠 쓰는 전략이 실용적이다&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;chatgpt-vs-gemini-한눈에-비교&quot;&gt;ChatGPT vs Gemini 한눈에 비교&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;작업&lt;/th&gt;
      &lt;th&gt;추천&lt;/th&gt;
      &lt;th&gt;이유&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;React 컴포넌트 리팩터링&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;기존 코드를 붙여 넣고 단계적으로 수정하기 편하다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;UI 초안 만들기&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;시안을 만든 뒤 여러 번 수정 지시를 주는 흐름이 자연스럽다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;긴 문서/긴 로그 읽히기&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;긴 입력을 한 번에 다루는 쪽이 더 편하다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Next.js에 AI 기능 붙이기&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;프로토타입을 빠르게 만들고 비교 실험하기 좋다&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;모델 응답 비교/PoC 만들기&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;여러 조건을 바꿔가며 실험할 때 효율적이다&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;chatgpt와-gemini-차이-프론트엔드-개발자-기준-비교-포인트&quot;&gt;ChatGPT와 Gemini 차이: 프론트엔드 개발자 기준 비교 포인트&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;는 둘 다 “코드도 만들고 문서도 읽는 AI”처럼 보이지만,&lt;br /&gt;
실제로 붙잡고 일해보면 결이 다르다.&lt;/p&gt;

&lt;p&gt;프론트엔드 개발자 기준으로는 아래 차이가 체감된다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;이미 있는 코드를 조금씩 고쳐야 하는가&lt;/li&gt;
  &lt;li&gt;화면 초안을 만든 뒤 여러 번 수정해야 하는가&lt;/li&gt;
  &lt;li&gt;긴 문서나 큰 로그를 한 번에 읽혀야 하는가&lt;/li&gt;
  &lt;li&gt;Next.js에서 빠르게 AI 기능 PoC를 붙여야 하는가&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT vs Gemini&lt;/code&gt;는 모델 성능 한 줄 비교보다&lt;br /&gt;
&lt;strong&gt;어떤 작업 흐름에 더 잘 맞는가&lt;/strong&gt;로 보는 편이 정확하다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;chatgpt-vs-gemini-코딩-비교-react-리팩터링은-누가-더-편할까&quot;&gt;ChatGPT vs Gemini 코딩 비교: React 리팩터링은 누가 더 편할까?&lt;/h2&gt;

&lt;p&gt;프론트엔드에서 AI를 가장 자주 쓰는 순간은&lt;br /&gt;
새 프로젝트를 처음부터 만드는 때보다 &lt;strong&gt;이미 있는 코드를 손볼 때&lt;/strong&gt;다.&lt;/p&gt;

&lt;p&gt;예를 들면 이런 작업이다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;상태가 꼬인 컴포넌트 정리&lt;/li&gt;
  &lt;li&gt;과하게 늘어난 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt; 제거&lt;/li&gt;
  &lt;li&gt;반복되는 폼 로직 분리&lt;/li&gt;
  &lt;li&gt;길어진 Tailwind 클래스 정리&lt;/li&gt;
  &lt;li&gt;접근성(a11y) 보강&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이 구간에서는 대체로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;가 더 손에 잘 붙는다.&lt;br /&gt;
이유는 결과물 자체보다 &lt;strong&gt;수정 대화의 템포&lt;/strong&gt;에 있다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“문제부터 짚어줘”&lt;/li&gt;
  &lt;li&gt;“가장 작은 수정안부터 보여줘”&lt;/li&gt;
  &lt;li&gt;“before / after로 비교해줘”&lt;/li&gt;
  &lt;li&gt;“이건 유지하고 이 부분만 바꿔줘”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이런 식으로 한 단계씩 밀고 가는 흐름이 자연스럽다.&lt;/p&gt;

&lt;p&gt;React 코드 정리가 잦다면&lt;br /&gt;
&lt;a href=&quot;/2026/02/08/use-effect/&quot;&gt;useEffect 정리 글&lt;/a&gt;과&lt;br /&gt;
&lt;a href=&quot;/2026/03/27/react-nextjs-codex-setup/&quot;&gt;React/Next.js용 Codex 설정 글&lt;/a&gt;도 같이 보면&lt;br /&gt;
프롬프트 품질을 더 끌어올리기 쉽다.&lt;/p&gt;

&lt;p&gt;반대로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;는 코드 생성 자체는 충분히 되지만,&lt;br /&gt;
한두 줄씩 집요하게 다듬는 편집감보다는&lt;br /&gt;
&lt;strong&gt;넓은 맥락을 한 번에 보고 정리하는 느낌&lt;/strong&gt;이 더 강하다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;ui-초안-생성은-chatgpt가-더-빠른-편이다&quot;&gt;UI 초안 생성은 ChatGPT가 더 빠른 편이다&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT vs Gemini&lt;/code&gt;를 UI 기준으로 비교하면&lt;br /&gt;
초안 하나를 만든 뒤 계속 수정해 나가는 작업은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;가 대체로 편하다.&lt;/p&gt;

&lt;p&gt;예를 들면 이런 요청이다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“간격을 더 줄여줘”&lt;/li&gt;
  &lt;li&gt;“모바일에서는 카드 순서를 바꿔줘”&lt;/li&gt;
  &lt;li&gt;“빈 상태와 로딩 상태를 추가해줘”&lt;/li&gt;
  &lt;li&gt;“너무 평범하지 않게 다시 구성해줘”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이런 식의 반복 수정은&lt;br /&gt;
초안을 계속 이어서 고치는 방식이 중요하기 때문에 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt; 쪽이 유리하다.&lt;/p&gt;

&lt;p&gt;특히 아래 조건이 많을수록 체감 차이가 난다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;React 또는 Next.js 기반 UI&lt;/li&gt;
  &lt;li&gt;Tailwind나 CSS 변수 사용&lt;/li&gt;
  &lt;li&gt;시안보다 구현 가능한 코드가 중요&lt;/li&gt;
  &lt;li&gt;디자이너 전달용이 아니라 개발 초안이 목적&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;도 UI 코드를 만들 수는 있다.&lt;br /&gt;
다만 UI 하나를 세밀하게 다듬는 것보다&lt;br /&gt;
요구사항이 긴 화면이나 데모 성격의 기능 화면을 한 번에 잡는 쪽이 더 잘 맞는다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;긴-문서-요약과-긴-로그-분석은-gemini가-더-잘-맞는다&quot;&gt;긴 문서 요약과 긴 로그 분석은 Gemini가 더 잘 맞는다&lt;/h2&gt;

&lt;p&gt;문서가 길어지고 입력량이 커질수록 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;가 편하다고 느끼는 경우가 많다.&lt;/p&gt;

&lt;p&gt;대표적으로 아래 작업이다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Next.js 마이그레이션 문서 통째로 읽히기&lt;/li&gt;
  &lt;li&gt;OpenAPI 문서를 바탕으로 타입 초안 만들기&lt;/li&gt;
  &lt;li&gt;긴 에러 로그에서 반복 패턴 찾기&lt;/li&gt;
  &lt;li&gt;여러 파일을 한 번에 넣고 구조 정리 받기&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;프론트엔드 실무에서는 이런 일이 생각보다 자주 생긴다.&lt;br /&gt;
특히 프레임워크 변경점을 따라가야 할 때 체감이 크다.&lt;/p&gt;

&lt;p&gt;예를 들어 Next.js를 쓰고 있다면&lt;br /&gt;
&lt;a href=&quot;/2026/02/15/nextjs-app-router/&quot;&gt;Next.js App Router 정리&lt;/a&gt;나&lt;br /&gt;
&lt;a href=&quot;/2026/03/18/nextjs16-vs15/&quot;&gt;Next.js 16 vs 15 변경점&lt;/a&gt; 같은 긴 문서를 볼 때,&lt;br /&gt;
핵심만 먼저 추려 달라고 하기 좋다.&lt;/p&gt;

&lt;p&gt;반대로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;는 긴 입력을 한 번에 몰아 넣기보다&lt;br /&gt;
문맥을 대화로 이어가며 점점 좁혀가는 방식에 더 강점이 있다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;nextjs에서-ai-기능-붙일-때는-gemini가-실험-속도가-빠르다&quot;&gt;Next.js에서 AI 기능 붙일 때는 Gemini가 실험 속도가 빠르다&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini vs ChatGPT 개발자 추천&lt;/code&gt; 질문이 나올 때&lt;br /&gt;
의외로 갈리는 지점이 바로 &lt;strong&gt;프로토타이핑 속도&lt;/strong&gt;다.&lt;/p&gt;

&lt;p&gt;Next.js에서 AI 응답을 붙이는 최소 예제를 만들고&lt;br /&gt;
응답 형식을 바꿔 보고&lt;br /&gt;
에러 처리 구조를 바꿔 보고&lt;br /&gt;
스트리밍 확장 여지를 남기는 식의 PoC를 할 때는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;가 편한 경우가 많다.&lt;/p&gt;

&lt;p&gt;이유는 복잡한 제품 설계보다&lt;br /&gt;
빠르게 붙여 보고 확인하는 흐름에 잘 맞기 때문이다.&lt;/p&gt;

&lt;p&gt;특히 아래 조건이면 더 그렇다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Route Handler&lt;/code&gt; 기준 최소 예제가 필요하다&lt;/li&gt;
  &lt;li&gt;환경변수 분리와 에러 처리까지 같이 보고 싶다&lt;/li&gt;
  &lt;li&gt;여러 모델 응답 형태를 빠르게 비교하고 싶다&lt;/li&gt;
  &lt;li&gt;아직 제품 코드보다 실험 코드 비중이 높다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;다만 PoC가 끝나고 실제 화면 안에서 다듬기 시작하면&lt;br /&gt;
그다음부터는 다시 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;가 더 편하게 느껴질 수 있다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;그래서-프론트엔드-개발자는-무엇을-먼저-써야-할까&quot;&gt;그래서 프론트엔드 개발자는 무엇을 먼저 써야 할까?&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT와 Gemini 중 어떤 AI가 더 좋나요?&lt;/code&gt;라는 질문에&lt;br /&gt;
프론트엔드 개발자 기준으로 답하면 아래에 가깝다.&lt;/p&gt;

&lt;h3 id=&quot;chatgpt가-더-잘-맞는-사람&quot;&gt;ChatGPT가 더 잘 맞는 사람&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;기존 React 코드를 자주 고친다&lt;/li&gt;
  &lt;li&gt;UI 초안을 만든 뒤 수정 반복이 많다&lt;/li&gt;
  &lt;li&gt;한 번에 완성 답변보다 대화형 수정이 편하다&lt;/li&gt;
  &lt;li&gt;코드 리뷰처럼 문제를 짚고 고쳐 나가는 흐름을 선호한다&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;gemini가-더-잘-맞는-사람&quot;&gt;Gemini가 더 잘 맞는 사람&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;긴 문서와 긴 입력을 자주 다룬다&lt;/li&gt;
  &lt;li&gt;API 실험과 PoC를 빠르게 돌려본다&lt;/li&gt;
  &lt;li&gt;여러 자료를 한 번에 넣고 구조를 잡고 싶다&lt;/li&gt;
  &lt;li&gt;Next.js에 AI 기능을 붙이는 초기 탐색을 자주 한다&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;둘을-같이-쓰는-것이-맞는-사람&quot;&gt;둘을 같이 쓰는 것이 맞는 사람&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;구현과 실험을 모두 많이 한다&lt;/li&gt;
  &lt;li&gt;코드는 세밀하게 고치고 문서는 크게 읽어야 한다&lt;/li&gt;
  &lt;li&gt;한 도구로 모든 상황을 해결하려다 자주 막힌다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;실무에서는 이 세 번째가 가장 많다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;비교할-때-바로-써먹기-좋은-프롬프트&quot;&gt;비교할 때 바로 써먹기 좋은 프롬프트&lt;/h2&gt;

&lt;p&gt;아래 프롬프트는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;를 같은 조건으로 비교할 때 유용하다.&lt;br /&gt;
같은 입력을 넣으면 두 도구의 답변 스타일 차이가 꽤 선명하게 드러난다.&lt;/p&gt;

&lt;h3 id=&quot;1-react-컴포넌트-리팩터링&quot;&gt;1) React 컴포넌트 리팩터링&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;아래 React 컴포넌트를 리팩터링해줘.

조건:
- 관심사 분리
- derived state 제거
- 불필요한 useEffect 제거
- 접근성(a11y) 개선
- TypeScript 타입 보강

코드:
[여기에 컴포넌트 코드 붙여넣기]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-nextjs-페이지-구조-개선&quot;&gt;2) Next.js 페이지 구조 개선&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;이 Next.js App Router 코드를 보고 서버 컴포넌트와 클라이언트 컴포넌트 경계를 다시 설계해줘.

조건:
- use client 최소화
- 서버 패칭 우선
- hydration mismatch 가능성 체크
- provider 전염 여부 체크
- before / after 코드로 보여줘

코드:
[여기에 페이지 코드 붙여넣기]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-typescript-에러-분석&quot;&gt;3) TypeScript 에러 분석&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;아래 TypeScript 에러 원인을 설명하고, 가장 단순한 수정안과 구조적으로 더 나은 수정안을 각각 제안해줘.

에러:
[에러 메시지]

코드:
[관련 코드]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-ui-초안-생성&quot;&gt;4) UI 초안 생성&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Next.js + Tailwind 기준으로 반응형 대시보드 페이지 초안을 만들어줘.

조건:
- 모바일/태블릿/데스크톱 대응
- 카드 4개
- 최근 활동 리스트
- 빈 상태와 로딩 상태 포함
- CSS 변수 사용
- 너무 평범하지 않게 구성
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;5-긴-문서-요약&quot;&gt;5) 긴 문서 요약&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;아래 공식 문서를 읽고 프론트엔드 개발자가 알아야 할 핵심만 정리해줘.

조건:
- 바뀐 점
- 기존 코드가 깨질 수 있는 지점
- 바로 적용할 수 있는 예시
- 10줄 요약 + 상세 설명

문서:
[공식 문서 본문 또는 링크 내용 요약]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;6-gemini-api--openai-api-프로토타입&quot;&gt;6) Gemini API / OpenAI API 프로토타입&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Next.js Route Handler에서 사용자 질문을 받아 AI 응답을 반환하는 최소 예제를 만들어줘.

조건:
- TypeScript
- 에러 처리 포함
- 환경변수 분리
- fetch 또는 공식 SDK 사용
- 추후 스트리밍으로 확장하기 쉽게 구조화
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;자주-묻는-질문&quot;&gt;자주 묻는 질문&lt;/h2&gt;

&lt;h3 id=&quot;chatgpt와-gemini-중-코딩은-뭐가-더-좋나요&quot;&gt;ChatGPT와 Gemini 중 코딩은 뭐가 더 좋나요?&lt;/h3&gt;

&lt;p&gt;기존 코드를 고치고, 문제를 짚고, 작은 수정안을 반복해서 받는 흐름은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;가 더 편한 경우가 많다.&lt;br /&gt;
반면 긴 문서와 큰 입력을 함께 읽혀가며 구조를 잡는 흐름은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;가 더 편할 수 있다.&lt;/p&gt;

&lt;h3 id=&quot;프론트엔드-개발자는-어떤-ai부터-써보면-좋나요&quot;&gt;프론트엔드 개발자는 어떤 AI부터 써보면 좋나요?&lt;/h3&gt;

&lt;p&gt;React 컴포넌트 수정, UI 초안 생성, 코드 품질 개선이 많다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;부터 시작하는 편이 무난하다.&lt;br /&gt;
긴 문서 요약, 로그 분석, AI 기능 PoC가 많다면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;부터 써보는 것도 괜찮다.&lt;/p&gt;

&lt;h3 id=&quot;nextjs-프로젝트에는-chatgpt와-gemini-중-무엇이-더-잘-맞나요&quot;&gt;Next.js 프로젝트에는 ChatGPT와 Gemini 중 무엇이 더 잘 맞나요?&lt;/h3&gt;

&lt;p&gt;구현 단계에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;, 탐색 단계와 긴 문서 요약 단계에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;가 편한 경우가 많다.&lt;br /&gt;
즉, Next.js에서는 둘 중 하나를 고르기보다 작업 단계에 따라 분리해서 쓰는 전략이 실용적이다.&lt;/p&gt;

&lt;h3 id=&quot;chatgpt-vs-gemini-비교에서-가장-중요한-기준은-무엇인가요&quot;&gt;ChatGPT vs Gemini 비교에서 가장 중요한 기준은 무엇인가요?&lt;/h3&gt;

&lt;p&gt;모델 이름보다 &lt;strong&gt;내가 자주 하는 작업의 형태&lt;/strong&gt;가 더 중요하다.&lt;br /&gt;
세밀한 편집이 많은지, 긴 입력을 자주 다루는지, 프로토타입 비중이 높은지를 먼저 보면 선택이 쉬워진다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;결론&quot;&gt;결론&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT vs Gemini 비교&lt;/code&gt;를 한 줄로 끝내면 이렇다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;코드를 세밀하게 고치고 UI를 반복 수정해야 하면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ChatGPT&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;긴 문서와 큰 입력을 빠르게 정리하고 실험해야 하면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemini&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;프론트엔드 실무에서는 둘을 분리해서 쓰는 쪽이 가장 덜 답답하다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;챗GPT vs 제미나이&lt;/code&gt;의 답은 “누가 무조건 더 좋다”가 아니다.&lt;br /&gt;
&lt;strong&gt;프론트엔드 개발에서 내가 자주 하는 일이 무엇인지&lt;/strong&gt;가 답을 정한다.&lt;/p&gt;
</description>
        <pubDate>Sun, 22 Mar 2026 10:08:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/03/22/chatgpt-vs-gemini/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/03/22/chatgpt-vs-gemini/</guid>
        
        <category>ai</category>
        
        <category>chatgpt</category>
        
        <category>gemini</category>
        
        <category>react</category>
        
        <category>nextjs</category>
        
        <category>frontend</category>
        
        <category>web-development</category>
        
        
      </item>
    
      <item>
        <title>Next.js 16 vs 15 변경점: 무엇이 달라졌나?</title>
        <description>&lt;p&gt;2025년 10월 21일에 Next.js 16이 공개되었다.&lt;br /&gt;
이미 15를 쓰고 있는 팀이라면 “무엇이 좋아졌는가?”보다 먼저&lt;br /&gt;
&lt;strong&gt;“어떤 부분이 바뀌었고 왜 바뀌었는가?”&lt;/strong&gt;를 확인하는 게 중요하다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;한눈에-보는-nextjs-16-vs-15&quot;&gt;한눈에 보는 Next.js 16 vs 15&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;영역&lt;/th&gt;
      &lt;th&gt;Next.js 15&lt;/th&gt;
      &lt;th&gt;Next.js 16&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;요청 관련 API (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;searchParams&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cookies&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;headers&lt;/code&gt;)&lt;/td&gt;
      &lt;td&gt;비동기 전환 시작, 동기 접근 임시 허용(경고)&lt;/td&gt;
      &lt;td&gt;동기 접근 제거, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt; 기반 사용이 사실상 필수&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;번들러&lt;/td&gt;
      &lt;td&gt;Turbopack 개발 안정화(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next dev --turbo&lt;/code&gt;)&lt;/td&gt;
      &lt;td&gt;Turbopack이 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dev/build&lt;/code&gt; 기본값&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Cache Components&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;experimental.ppr&lt;/code&gt; 기반 흐름 + 암묵적 캐싱 이해 필요&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cacheComponents: true&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;use cache&quot;&lt;/code&gt; 중심의 명시적 opt-in 캐싱&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;라우팅/프리패치&lt;/td&gt;
      &lt;td&gt;기존 prefetch 동작&lt;/td&gt;
      &lt;td&gt;레이아웃 중복 제거 + incremental prefetching&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;미들웨어&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;middleware.ts&lt;/code&gt; 중심&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy.ts&lt;/code&gt; 권장(네트워크 경계 명확화), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;middleware.ts&lt;/code&gt;는 deprecated&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;캐시 API&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;revalidateTag(tag)&lt;/code&gt; 단일 인자 사용 가능&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;revalidateTag(tag, profile)&lt;/code&gt; 권장/갱신, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updateTag()&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;refresh()&lt;/code&gt; 추가&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Next.js Devtools MCP&lt;/td&gt;
      &lt;td&gt;별도 MCP 연동 없음&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next-devtools-mcp&lt;/code&gt;로 AI 에이전트 디버깅 연동&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Logging Improvements&lt;/td&gt;
      &lt;td&gt;개발/빌드 로그 정보가 상대적으로 제한적&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Compile&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Render&lt;/code&gt; 구분 + 빌드 단계별 소요 시간 표시&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;버전 요구사항&lt;/td&gt;
      &lt;td&gt;Node 18 호환 구간 존재&lt;/td&gt;
      &lt;td&gt;Node.js 20.9+ / TypeScript 5.1+&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;16에서-먼저-확인할-변경점&quot;&gt;16에서 먼저 확인할 변경점&lt;/h2&gt;

&lt;h3 id=&quot;1-async-request-apis-최종-정리&quot;&gt;1) Async Request APIs 최종 정리&lt;/h3&gt;

&lt;p&gt;15에서 경고로 넘어가던 동기 접근은 16에서 더 엄격해졌다.&lt;br /&gt;
다음 패턴을 우선 점검한다.&lt;/p&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// before&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// after&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;slug&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;같은 맥락으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cookies()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;headers()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;draftMode()&lt;/code&gt;도 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt; 패턴을 기준으로 마이그레이션한다.&lt;/p&gt;

&lt;h3 id=&quot;2-middlewarets---proxyts&quot;&gt;2) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;middleware.ts&lt;/code&gt; -&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy.ts&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;16에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy.ts&lt;/code&gt;가 표준 경로다.&lt;br /&gt;
기존 로직은 대부분 유지하고, 파일명/함수명만 전환하면 된다.&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// proxy.ts&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NextRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NextResponse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;next/server&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;proxy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NextRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;NextResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-revalidatetag-시그니처-변경&quot;&gt;3) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;revalidateTag()&lt;/code&gt; 시그니처 변경&lt;/h3&gt;

&lt;p&gt;단일 인자 호출은 deprecated 흐름이다.&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;revalidateTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updateTag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;next/cache&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// SWR 성격의 재검증&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;revalidateTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Server Action에서 read-your-writes 보장&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;updateTag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;user-profile&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;4-parallel-routes의-defaultjs-요구&quot;&gt;4) Parallel Routes의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.js&lt;/code&gt; 요구&lt;/h3&gt;

&lt;p&gt;병렬 슬롯은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.js&lt;/code&gt;가 없으면 빌드 실패 가능성이 있다.&lt;br /&gt;
기존에 암묵 동작하던 슬롯 구조를 명시적으로 보강해야 한다.&lt;/p&gt;

&lt;h3 id=&quot;5-이미지보안-기본값-변화&quot;&gt;5) 이미지/보안 기본값 변화&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next/image&lt;/code&gt; 관련 기본 정책(캐시 TTL, quality, redirect 제한, local src 보안 제한 등)이 조정됐다.&lt;br /&gt;
이미지 최적화 커스텀을 많이 한 프로젝트는 회귀 테스트가 필요하다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;16에서-체감되는-좋아진-점&quot;&gt;16에서 체감되는 “좋아진 점”&lt;/h2&gt;

&lt;h3 id=&quot;cache-components-use-cache&quot;&gt;Cache Components (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;use cache&quot;&lt;/code&gt;)&lt;/h3&gt;

&lt;p&gt;15까지는 App Router에서 캐싱 동작을 규칙/예외로 이해해야 하는 구간이 많았고,&lt;br /&gt;
16은 캐시할 대상을 코드에서 직접 선언하는 방식으로 정리했다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;기본 동작: 페이지/레이아웃/라우트의 동적 코드는 요청 시점에 실행&lt;/li&gt;
  &lt;li&gt;선택 동작: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;use cache&quot;&lt;/code&gt;를 선언한 페이지/컴포넌트/함수만 캐시&lt;/li&gt;
  &lt;li&gt;캐시 키: 컴파일러가 사용 위치 기준으로 키 생성을 보조&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;또한 Cache Components는 PPR(Partial Prerendering) 흐름을 공식 모델로 통합한다.&lt;br /&gt;
즉, URL 전체를 정적/동적으로 이분법 처리하는 대신,&lt;br /&gt;
정적 껍질 + 필요한 부분만 동적으로 렌더링하는 구성이 자연스러워진다.&lt;/p&gt;

&lt;p&gt;설정은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next.config.ts&lt;/code&gt;에서 활성화한다.&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nextConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;cacheComponents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nextConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;기존에 쓰던 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;experimental.ppr&lt;/code&gt; 계열 설정은 제거되었고,&lt;br /&gt;
Cache Components 설정으로 일원화되었다.&lt;/p&gt;

&lt;h3 id=&quot;turbopack-기본화&quot;&gt;Turbopack 기본화&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next dev&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;next build&lt;/code&gt;에서 Turbopack이 기본이 되며&lt;br /&gt;
프로젝트에 따라 빌드/리프레시 체감 개선이 크다.&lt;br /&gt;
단, 커스텀 webpack 의존이 강하면 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--webpack&lt;/code&gt; 경로를 함께 유지해 단계적으로 전환하는 것이 안전하다.&lt;/p&gt;

&lt;h3 id=&quot;라우팅프리패치-개선&quot;&gt;라우팅/프리패치 개선&lt;/h3&gt;

&lt;p&gt;레이아웃 중복 다운로드를 줄이고 incremental prefetch를 적용해&lt;br /&gt;
요청 수는 늘어도 총 전송량을 낮추는 방향으로 최적화가 들어갔다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;nextjs-devtools-mcp&quot;&gt;Next.js Devtools MCP&lt;/h2&gt;

&lt;p&gt;Next.js 16은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next.js Devtools MCP&lt;/code&gt;를 소개했다.&lt;br /&gt;
MCP(Model Context Protocol)를 통해 AI 코딩 에이전트가 실행 중인 앱 상태와 로그에 더 직접적으로 접근할 수 있다.&lt;/p&gt;

&lt;p&gt;공식 가이드 기준 최소 설정 예시는 다음과 같다.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;mcpServers&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;next-devtools&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;npx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-y&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;next-devtools-mcp@latest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이후 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pnpm dev&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run dev&lt;/code&gt;처럼 개발 서버를 실행하면 MCP 서버가 앱을 감지해 연결된다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;logging-improvements&quot;&gt;Logging Improvements&lt;/h2&gt;

&lt;p&gt;Next.js 16에서는 로그가 “시간이 어디에 쓰였는지”를 더 잘 보여준다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;개발 요청 로그: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Compile&lt;/code&gt;(라우팅/컴파일)과 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Render&lt;/code&gt;(코드 실행/React 렌더링) 구분&lt;/li&gt;
  &lt;li&gt;빌드 로그: TypeScript, 페이지 데이터 수집, 정적 페이지 생성, 최적화 단계별 소요 시간 표시&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/nextjs/log_time.png&quot; alt=&quot;log_time.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;이 변경으로 dev/build 단계에서 병목 위치를 빠르게 파악하기 쉬워졌다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;참고-자료&quot;&gt;참고 자료&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://nextjs.org/blog/next-16&quot;&gt;https://nextjs.org/blog/next-16&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 18 Mar 2026 18:10:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/03/18/nextjs16-vs15/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/03/18/nextjs16-vs15/</guid>
        
        <category>nextjs</category>
        
        <category>react</category>
        
        <category>frontend</category>
        
        <category>web-development</category>
        
        
      </item>
    
      <item>
        <title>Angular DI(Dependency Injection)에 모든 것: 기초, 계층형 Injector</title>
        <description>&lt;h1 id=&quot;angular-di-이제는-서비스-가져다-쓰기에서-끝내지-말자&quot;&gt;Angular DI, 이제는 “서비스 가져다 쓰기”에서 끝내지 말자&lt;/h1&gt;

&lt;p&gt;Angular를 쓰다 보면 DI를 이렇게 이해하기 쉽습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;constructor&lt;/code&gt;에 서비스 넣기&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;처음엔 이 정도면 충분합니다.
하지만 프로젝트가 커지면 금방 한계가 옵니다.&lt;/p&gt;

&lt;p&gt;이 글에서는 DI를 문법이 아니라 &lt;strong&gt;설계 도구&lt;/strong&gt;로 설명합니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Angular 의존성 주입(DI)&lt;/strong&gt;은 단순한 문법이 아니라, 테스트 용이성과 유지보수성을 동시에 높이는 아키텍처 핵심입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;1-도입-di란-무엇인가&quot;&gt;1. 도입: DI란 무엇인가?&lt;/h2&gt;

&lt;p&gt;DI(Dependency Injection)는
객체가 필요한 의존성을 직접 만들지 않고,
&lt;strong&gt;외부에서 주입받는 방식&lt;/strong&gt;입니다.&lt;/p&gt;

&lt;p&gt;핵심은 두 가지입니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;IoC(제어의 역전)&lt;/strong&gt;: 객체 생성/생명주기 제어를 프레임워크에 위임&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Loose Coupling(느슨한 결합)&lt;/strong&gt;: 구체 구현과의 결합을 낮춰 교체를 쉽게 만듦&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;bad-di-미사용&quot;&gt;Bad (DI 미사용)&lt;/h3&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PaymentComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// 컴포넌트가 직접 생성&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;paymentService&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PaymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;checkout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;paymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;문제:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;테스트에서 Mock으로 바꾸기 어렵습니다.&lt;/li&gt;
  &lt;li&gt;구현 교체 시 컴포넌트까지 수정됩니다.&lt;/li&gt;
  &lt;li&gt;컴포넌트가 UI + 생성 책임까지 떠안습니다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;good-di-사용&quot;&gt;Good (DI 사용)&lt;/h3&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Injectable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;providedIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PaymentService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;pay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 결제 로직&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-payment&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`&amp;lt;button (click)=&quot;checkout()&quot;&amp;gt;결제&amp;lt;/button&amp;gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PaymentComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;paymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PaymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;checkout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;paymentService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;실무에서 DI가 필수인 이유는 단순합니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;테스트가 쉬워짐&lt;/strong&gt;: Mock 주입이 자연스럽다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;유지보수가 쉬워짐&lt;/strong&gt;: 구현을 갈아껴도 소비자 코드는 안정적이다.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;협업이 쉬워짐&lt;/strong&gt;: 역할이 분리되어 충돌이 줄어든다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-angular-di의-3가지-핵심-요소&quot;&gt;2. Angular DI의 3가지 핵심 요소&lt;/h2&gt;

&lt;p&gt;Angular DI는 아래 흐름으로 보면 쉽습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Consumer&lt;/strong&gt;: 의존성을 필요로 하는 쪽 (컴포넌트/서비스)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Dependency&lt;/strong&gt;: 주입되는 대상 (서비스/설정 객체)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Injector + Provider&lt;/strong&gt;: 무엇을 어떻게 만들지 결정하고 전달하는 주체&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Injectable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;providedIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UserService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;getUsers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-user-list&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`&amp;lt;p&amp;gt;Users&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UserListComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Consumer가 Dependency를 요청&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;userService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UserService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;동작 순서:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Consumer가 토큰(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserService&lt;/code&gt;)을 요청&lt;/li&gt;
  &lt;li&gt;Injector가 Provider 설정을 확인&lt;/li&gt;
  &lt;li&gt;인스턴스를 생성 또는 재사용해 전달&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-핵심-계층적-주입hierarchical-injectors&quot;&gt;3. [핵심] 계층적 주입(Hierarchical Injectors)&lt;/h2&gt;

&lt;p&gt;여기서 Angular DI의 실전 감각이 갈립니다.
&lt;strong&gt;어디에 Provider를 두느냐&lt;/strong&gt;가 핵심입니다.&lt;/p&gt;

&lt;h3 id=&quot;providedin-root-vs-component-providers--&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providedIn: &apos;root&apos;&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Component({ providers: [...] })&lt;/code&gt;&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;항목&lt;/th&gt;
      &lt;th&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providedIn: &apos;root&apos;&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Component({ providers: [...] })&lt;/code&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;범위&lt;/td&gt;
      &lt;td&gt;애플리케이션 전역&lt;/td&gt;
      &lt;td&gt;해당 컴포넌트 인스턴스 트리&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;인스턴스&lt;/td&gt;
      &lt;td&gt;보통 1개(싱글톤)&lt;/td&gt;
      &lt;td&gt;컴포넌트마다 독립 인스턴스&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;적합한 대상&lt;/td&gt;
      &lt;td&gt;인증, 공통 API, 로깅&lt;/td&gt;
      &lt;td&gt;탭 상태, 위저드 상태, 샌드박스 세션&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;주의점&lt;/td&gt;
      &lt;td&gt;전역 상태 오염 가능&lt;/td&gt;
      &lt;td&gt;과도하게 쪼개면 구조가 복잡해짐&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;4-예시&quot;&gt;4. 예시&lt;/h2&gt;

&lt;h3 id=&quot;4-1-탭tab-위젯-상태-분리&quot;&gt;4-1. 탭(Tab) 위젯 상태 분리&lt;/h3&gt;

&lt;h4 id=&quot;bad-전역-싱글톤-사용&quot;&gt;Bad (전역 싱글톤 사용)&lt;/h4&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Injectable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;providedIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TabStateService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;activeTab&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;overview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;페이지에 탭 위젯이 여러 개 있으면
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;activeTab&lt;/code&gt;이 서로 영향을 줄 수 있습니다.&lt;/p&gt;

&lt;h4 id=&quot;good-컴포넌트-레벨-주입&quot;&gt;Good (컴포넌트 레벨 주입)&lt;/h4&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Injectable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TabStateService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;activeTab&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;overview&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-tab-group&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;TabStateService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
    &amp;lt;button (click)=&quot;state.activeTab = &apos;overview&apos;&quot;&amp;gt;Overview&amp;lt;/button&amp;gt;
    &amp;lt;button (click)=&quot;state.activeTab = &apos;settings&apos;&quot;&amp;gt;Settings&amp;lt;/button&amp;gt;
    &amp;lt;p&amp;gt;현재 탭: &amp;lt;/p&amp;gt;
  `&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TabGroupComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;TabStateService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이제 탭 그룹 인스턴스마다 상태가 분리됩니다.&lt;/p&gt;

&lt;h3 id=&quot;42-샌드박스-패턴&quot;&gt;4.2. 샌드박스 패턴&lt;/h3&gt;

&lt;p&gt;샌드박스 패턴은 쉽게 말해
&lt;strong&gt;“같은 화면의 기능을 서로 독립된 작업실처럼 분리하는 방식”&lt;/strong&gt;입니다.&lt;/p&gt;

&lt;p&gt;코드 실행기, 폼 빌더, 탭형 편집기에서 특히 중요합니다.
한 화면에 인스턴스가 여러 개 떠도 상태가 섞이지 않아야 하기 때문입니다.&lt;/p&gt;

&lt;h4 id=&quot;bad-전역-싱글톤-공유&quot;&gt;Bad (전역 싱글톤 공유)&lt;/h4&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Injectable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;providedIn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SandboxStateService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;logs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 상태에서 샌드박스가 2개 있으면
왼쪽에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reset&lt;/code&gt;했을 때 오른쪽 로그도 같이 지워질 수 있습니다.&lt;/p&gt;

&lt;h4 id=&quot;good-컴포넌트-단위-분리&quot;&gt;Good (컴포넌트 단위 분리)&lt;/h4&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Injectable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SandboxStateService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;logs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;logs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`run: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;logs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-sandbox&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SandboxStateService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
    &amp;lt;app-sandbox-editor&amp;gt;&amp;lt;/app-sandbox-editor&amp;gt;
    &amp;lt;app-sandbox-console&amp;gt;&amp;lt;/app-sandbox-console&amp;gt;
  `&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SandboxComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;정리하면:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;같은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app-sandbox&lt;/code&gt; 내부 자식은 같은 상태를 공유&lt;/li&gt;
  &lt;li&gt;다른 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app-sandbox&lt;/code&gt; 인스턴스와는 완전히 분리&lt;/li&gt;
  &lt;li&gt;인스턴스별 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reset&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt;이 가능해 디버깅이 쉬워짐&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;5-고급-di-설정-방법&quot;&gt;5. 고급 DI 설정 방법&lt;/h2&gt;

&lt;p&gt;기본 생성자 주입만으로는 실무 요구를 다 못 맞춥니다.
여기서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Provider&lt;/code&gt; 옵션을 꺼내 쓰게 됩니다.&lt;/p&gt;

&lt;h3 id=&quot;5-1-useclass-구현-교체가-필요할-때&quot;&gt;5-1. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useClass&lt;/code&gt;: 구현 교체가 필요할 때&lt;/h3&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Injectable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ConsoleLogger&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;[console]&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;Injectable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RemoteLogger&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 서버 전송&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;appProviders&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;useClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ConsoleLogger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;언제 쓰나:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;개발/운영 구현 교체&lt;/li&gt;
  &lt;li&gt;레거시 구현에서 새 구현으로 점진 이전&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;5-2-usevalue-설정값config-상수-주입&quot;&gt;5-2. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useValue&lt;/code&gt;: 설정값(Config), 상수 주입&lt;/h3&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;apiBaseUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;enableNewCheckout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;APP_CONFIG&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;InjectionToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;APP_CONFIG&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;apiBaseUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://api.example.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;enableNewCheckout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;bootstrapApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AppComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;APP_CONFIG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;useValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;언제 쓰나:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;환경별 API URL&lt;/li&gt;
  &lt;li&gt;피처 플래그&lt;/li&gt;
  &lt;li&gt;빌드 타임/런타임 상수&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;5-3-usefactory-실행-시점에-값-골라서-주입&quot;&gt;5-3. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useFactory&lt;/code&gt;: 실행 시점에 값 골라서 주입&lt;/h3&gt;

&lt;p&gt;쉽게 말하면,
&lt;strong&gt;앱이 실행될 때 현재 상황을 보고 값을 결정한 뒤 주입하는 방식&lt;/strong&gt;입니다.&lt;/p&gt;

&lt;p&gt;예를 들어:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;로컬 개발이면 로컬 API 주소&lt;/li&gt;
  &lt;li&gt;스테이징이면 스테이징 API 주소&lt;/li&gt;
  &lt;li&gt;운영이면 운영 API 주소&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이처럼 “미리 고정하기 어려운 값”은 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useValue&lt;/code&gt;보다 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useFactory&lt;/code&gt;가 더 맞습니다.&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;API_BASE_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;InjectionToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;API_BASE_URL&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;apiBaseUrlFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;staging&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://staging-api.example.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://api.example.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;bootstrapApplication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AppComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;API_BASE_URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;useFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;apiBaseUrlFactory&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;동작은 이렇게 이해하면 됩니다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Angular가 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;API_BASE_URL&lt;/code&gt;이 필요하다고 판단&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useFactory&lt;/code&gt;에 등록된 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apiBaseUrlFactory()&lt;/code&gt;를 실행&lt;/li&gt;
  &lt;li&gt;함수가 현재 환경을 보고 URL을 계산해 반환&lt;/li&gt;
  &lt;li&gt;반환된 값을 의존성으로 주입&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;언제 쓰나:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;호스트/플랫폼에 따라 값 분기&lt;/li&gt;
  &lt;li&gt;여러 의존성 조합으로 객체 생성&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;5-4-injectiontoken-인터페이스값-주입의-핵심&quot;&gt;5-4. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InjectionToken&lt;/code&gt;: 인터페이스/값 주입의 핵심&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interface&lt;/code&gt;는 런타임에 사라집니다.
그래서 Injector 토큰으로 사용할 수 없습니다.&lt;/p&gt;

&lt;h4 id=&quot;bad-문자열-토큰&quot;&gt;Bad (문자열 토큰)&lt;/h4&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CONFIG&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;useValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;good-injectiontoken&quot;&gt;Good (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InjectionToken&lt;/code&gt;)&lt;/h4&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;APP_CONFIG&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;InjectionToken&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AppConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;APP_CONFIG&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nl&quot;&gt;providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;APP_CONFIG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;useValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;장점:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;타입 안정성&lt;/li&gt;
  &lt;li&gt;토큰 충돌 방지&lt;/li&gt;
  &lt;li&gt;리팩터링 안정성&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;6-정리&quot;&gt;6. 정리&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;전역 공유가 필요한가? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providedIn: &apos;root&apos;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;컴포넌트 인스턴스마다 분리돼야 하는가? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Component.providers&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;구현을 바꿔 끼워야 하는가? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useClass&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;상수/설정 주입인가? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useValue + InjectionToken&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;런타임 계산이 필요한가? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useFactory&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;테스트에서 Mock 주입이 쉬운 구조인가? &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;같은 토큰에 Mock Provider를 주입&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 01 Mar 2026 14:40:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/03/01/angular-di/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/03/01/angular-di/</guid>
        
        <category>angular</category>
        
        <category>frontend</category>
        
        <category>oop</category>
        
        <category>web-development</category>
        
        <category>javascript</category>
        
        
      </item>
    
      <item>
        <title>Capacitor vs React Native 작동 원리 비교</title>
        <description>&lt;p&gt;저는 실무에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Capacitor&lt;/code&gt;를 주로 사용해 왔고, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;React Native&lt;/code&gt;도 한 번 실제 프로젝트에 적용해봤습니다.&lt;/p&gt;

&lt;p&gt;이 글은 제가 다시 공식 문서와 기술 블로그를 확인하면서, 두 기술의 작동 원리와 JS-Native 비동기 통신 메커니즘을 한 번에 비교해 정리한 내용입니다.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Capacitor&lt;/code&gt;와 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;React Native&lt;/code&gt;는 둘 다 iOS/Android 앱을 만들 수 있지만, 내부 동작 방식은 꽤 다릅니다.&lt;/p&gt;

&lt;h2 id=&quot;한눈에-보는-결론&quot;&gt;한눈에 보는 결론&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;웹 코드를 최대한 재사용하고 싶다면: &lt;strong&gt;Capacitor&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;네이티브 UI 성능/일관성을 더 중요하게 본다면: &lt;strong&gt;React Native&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;이미 React 웹팀이 있고 빠르게 앱을 내야 한다면: &lt;strong&gt;Capacitor&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;장기적으로 모바일 UX를 네이티브 수준으로 밀고 싶다면: &lt;strong&gt;React Native&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1-capacitor의-작동-원리&quot;&gt;1. Capacitor의 작동 원리&lt;/h2&gt;

&lt;p&gt;Ionic 팀 설명에 따르면 Capacitor는 “웹 앱을 네이티브 앱으로 감싸서 실행”하는 런타임 접근입니다.&lt;br /&gt;
즉, 코드를 네이티브로 변환하는 크로스 컴파일러가 아니라, &lt;strong&gt;WebView + 네이티브 브리지&lt;/strong&gt; 구조에 가깝습니다.&lt;/p&gt;

&lt;h3 id=&quot;구조&quot;&gt;구조&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;웹 앱(HTML/CSS/JS)
  -&amp;gt; iOS WKWebView / Android WebView
      -&amp;gt; Capacitor Bridge
          -&amp;gt; Native Plugin (Swift / Kotlin / Java)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;빌드-흐름&quot;&gt;빌드 흐름&lt;/h3&gt;

&lt;p&gt;제가 Capacitor 프로젝트를 세팅할 때는 보통 아래 순서로 진행합니다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx cap add ios&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx cap add android&lt;/code&gt;로 네이티브 프로젝트를 한 번 생성&lt;/li&gt;
  &lt;li&gt;웹 앱 빌드 후 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx cap sync&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sync&lt;/code&gt;는 내부적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;copy&lt;/code&gt;(웹 자산 복사) + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt;(플러그인/네이티브 의존성 반영) 수행&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;capacitor의-요약&quot;&gt;Capacitor의 요약&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;UI 렌더링 주체: 브라우저 엔진(WebView)&lt;/li&gt;
  &lt;li&gt;네이티브 기능 접근: 플러그인 브리지 호출(Promise 기반)&lt;/li&gt;
  &lt;li&gt;강점: 기존 웹 코드 재사용성, 빠른 개발 속도&lt;/li&gt;
  &lt;li&gt;한계: 복잡한 애니메이션/리스트에서 WebView 특성의 한계를 만날 수 있음&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-react-native의-작동-원리&quot;&gt;2. React Native의 작동 원리&lt;/h2&gt;

&lt;p&gt;React Native는 WebView 위에서 DOM을 그리는 방식이 아니라, &lt;strong&gt;네이티브 UI 컴포넌트 트리&lt;/strong&gt;를 직접 다루는 방식입니다.&lt;/p&gt;

&lt;p&gt;공식 문서 기준으로 &lt;strong&gt;React Native 0.82(2025-10-08)&lt;/strong&gt;부터는 Legacy Architecture와 기존 Bridge가 제거되고, New Architecture가 기본이 되었습니다.&lt;/p&gt;

&lt;h3 id=&quot;new-architecture-핵심-구성&quot;&gt;New Architecture 핵심 구성&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;React 컴포넌트
  -&amp;gt; JS Runtime(Hermes 등)
      -&amp;gt; JSI
          -&amp;gt; TurboModules (Native API)
          -&amp;gt; Fabric (Native Rendering)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JSI&lt;/code&gt;: JS와 네이티브 사이 통신을 기존 직렬화 브리지보다 직접적으로 처리&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TurboModules&lt;/code&gt;: 필요 시점 로딩(lazy loading) 가능한 네이티브 모듈 시스템&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fabric&lt;/code&gt;: React의 최신 렌더링 모델과 맞춰 네이티브 UI 렌더링을 수행&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;react-native의-요약&quot;&gt;React Native의 요약&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;UI 렌더링 주체: 네이티브 컴포넌트&lt;/li&gt;
  &lt;li&gt;네이티브 기능 접근: JSI + TurboModules&lt;/li&gt;
  &lt;li&gt;강점: 네이티브에 가까운 UI/성능, 대규모 모바일 앱 확장성&lt;/li&gt;
  &lt;li&gt;한계: 네이티브 빌드/모듈 이해도 요구, 러닝커브 존재&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;3-js와-native가-비동기로-통신하는-메커니즘&quot;&gt;3. JS와 Native가 비동기로 통신하는 메커니즘&lt;/h2&gt;

&lt;p&gt;핵심은 두 프레임워크 모두 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;요청 ID&lt;/code&gt;를 기준으로, JS의 Promise와 Native 작업 결과를 다시 연결한다는 점입니다.&lt;/p&gt;

&lt;h3 id=&quot;3-1-capacitor의-비동기-통신-흐름&quot;&gt;3-1. Capacitor의 비동기 통신 흐름&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;JS(Plugin.call)
  -&amp;gt; Bridge에 plugin/method/options 전달
  -&amp;gt; Native Plugin 실행(백그라운드 작업 가능)
  -&amp;gt; 결과와 callbackId로 resolve/reject 반환
  -&amp;gt; WebView JS 런타임에서 Promise 완료
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;실제 흐름을 풀어서 보면 다음 순서입니다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;JS에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Camera.getPhoto()&lt;/code&gt; 같은 플러그인 메서드를 호출하면 Promise가 생성됩니다.&lt;/li&gt;
  &lt;li&gt;Bridge는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pluginId&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;methodName&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;options&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callbackId&lt;/code&gt;를 Native 쪽으로 전달합니다.&lt;/li&gt;
  &lt;li&gt;Native 플러그인이 실제 OS API를 비동기로 수행합니다.&lt;/li&gt;
  &lt;li&gt;완료 시 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callbackId&lt;/code&gt;를 포함해 성공/실패 결과를 Bridge로 돌려보냅니다.&lt;/li&gt;
  &lt;li&gt;JS 런타임이 해당 Promise를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resolve/reject&lt;/code&gt;합니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;즉, WebView 메인 스레드를 오래 점유하지 않도록 Native 작업을 비동기로 처리하고, 완료 시점에만 JS 이벤트 루프로 결과를 되돌립니다.&lt;/p&gt;

&lt;h3 id=&quot;3-2-react-nativenew-architecture의-비동기-통신-흐름&quot;&gt;3-2. React Native(New Architecture)의 비동기 통신 흐름&lt;/h3&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;JS(TurboModule method)
  -&amp;gt; JSI 경유 C++/Native 영역 진입
  -&amp;gt; Native에서 작업 큐로 비동기 처리
  -&amp;gt; CallInvoker 등을 통해 JS 런타임으로 완료 콜백 전달
  -&amp;gt; Promise resolve/reject + 필요 시 Fabric으로 UI 반영
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;New Architecture에서는 Legacy Bridge의 JSON 직렬화 왕복 대신 JSI 기반 통신을 사용합니다.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;JS가 TurboModule 메서드를 호출합니다.&lt;/li&gt;
  &lt;li&gt;JSI를 통해 Native 코드에 진입합니다.&lt;/li&gt;
  &lt;li&gt;시간이 걸리는 작업은 Native 스레드/큐에서 비동기로 수행합니다.&lt;/li&gt;
  &lt;li&gt;완료 콜백을 JS 런타임 큐로 다시 보내 Promise를 완료합니다.&lt;/li&gt;
  &lt;li&gt;결과로 UI 상태가 바뀌면 Fabric 렌더링 파이프라인에서 네이티브 UI 업데이트가 적용됩니다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;참고로 React Native는 일부 가벼운 API는 동기 호출도 가능하지만, 네트워크/파일 I/O/디스크 접근처럼 시간이 걸리는 작업은 비동기로 처리하는 것이 기본입니다. JS 스레드를 막지 않기 위해서입니다.&lt;/p&gt;

&lt;h3 id=&quot;3-3-공통-원리&quot;&gt;3-3. 공통 원리&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;요청-응답 상관관계&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;callbackId&lt;/code&gt; 또는 내부 요청 핸들로 매칭&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;스레드 분리&lt;/code&gt;: 오래 걸리는 Native 작업은 JS 메인 흐름 밖에서 처리&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Promise 기반 복귀&lt;/code&gt;: JS 쪽에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt; 패턴으로 동일하게 소비&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;이벤트 통신&lt;/code&gt;: 단발 응답 외에 센서/위치처럼 스트리밍은 listener 패턴 사용&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;4-capacitor-vs-react-native-비교&quot;&gt;4. Capacitor vs React Native 비교&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;비교 항목&lt;/th&gt;
      &lt;th&gt;Capacitor&lt;/th&gt;
      &lt;th&gt;React Native&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;UI 렌더링&lt;/td&gt;
      &lt;td&gt;WebView 내부 DOM/CSS&lt;/td&gt;
      &lt;td&gt;네이티브 UI 컴포넌트&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;네이티브 API 호출&lt;/td&gt;
      &lt;td&gt;Capacitor Bridge + Plugin&lt;/td&gt;
      &lt;td&gt;JSI + TurboModules&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;JS-Native 비동기 통신&lt;/td&gt;
      &lt;td&gt;callbackId 기반 Promise 완료&lt;/td&gt;
      &lt;td&gt;JSI+TurboModule+CallInvoker 기반 Promise 완료&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;웹 코드 재사용&lt;/td&gt;
      &lt;td&gt;매우 높음(기존 웹 앱 활용 용이)&lt;/td&gt;
      &lt;td&gt;로직 재사용은 좋지만 UI는 별도 구성&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;UI 성능 특성&lt;/td&gt;
      &lt;td&gt;일반 앱은 충분, 고난도 UI에서 제약 가능&lt;/td&gt;
      &lt;td&gt;고난도 인터랙션/리스트에서 유리&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;개발 진입장벽&lt;/td&gt;
      &lt;td&gt;웹 개발자에게 낮음&lt;/td&gt;
      &lt;td&gt;모바일/네이티브 개념 학습 필요&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;팀 구성 적합성&lt;/td&gt;
      &lt;td&gt;웹팀 중심 조직&lt;/td&gt;
      &lt;td&gt;모바일 제품 조직(장기 운영)에 유리&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;5-어떤-경우에-무엇을-선택할까&quot;&gt;5. 어떤 경우에 무엇을 선택할까?&lt;/h2&gt;

&lt;h3 id=&quot;capacitor가-맞는-경우&quot;&gt;Capacitor가 맞는 경우&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;기존 React/Vue/Angular 웹 서비스를 앱으로 빠르게 확장해야 함&lt;/li&gt;
  &lt;li&gt;한 팀이 웹과 앱을 동시에 운영해야 함&lt;/li&gt;
  &lt;li&gt;앱의 핵심 가치가 복잡한 네이티브 인터랙션보다 서비스 확장 속도에 있음&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;react-native가-맞는-경우&quot;&gt;React Native가 맞는 경우&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;모바일 UX 품질과 퍼포먼스가 제품 경쟁력 핵심임&lt;/li&gt;
  &lt;li&gt;장기적으로 모바일 전용 기능과 고성능 화면이 계속 늘어날 예정임&lt;/li&gt;
  &lt;li&gt;네이티브 모듈 연동/최적화를 감당할 팀 역량이 있음&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;제 경험으로는, 초기 출시 속도와 웹 자산 재사용이 중요할 때는 Capacitor가 정말 빠르게 성과를 냈고, 화면 복잡도와 인터랙션 품질을 더 밀어야 하는 상황에서는 React Native 접근이 더 설득력 있었습니다.&lt;/p&gt;

&lt;h2 id=&quot;참고-자료&quot;&gt;참고 자료&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Ionic Blog: &lt;a href=&quot;https://ionic.io/blog/how-capacitor-works-2&quot;&gt;How Capacitor Works&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Capacitor Docs: &lt;a href=&quot;https://capacitorjs.com/docs/core-apis/web#javascript-to-native-bridge&quot;&gt;JavaScript to Native&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;React Native Docs: &lt;a href=&quot;https://reactnative.dev/architecture/landing-page&quot;&gt;React Native Architecture&lt;/a&gt;, &lt;a href=&quot;https://reactnative.dev/docs/getting-started&quot;&gt;What is React Native?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;React Native Blog: &lt;a href=&quot;https://reactnative.dev/blog/2024/10/23/the-new-architecture-is-here&quot;&gt;The New Architecture is here&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 22 Feb 2026 10:30:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/02/22/capacitor-vs-react-native/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/02/22/capacitor-vs-react-native/</guid>
        
        <category>react</category>
        
        <category>react-native</category>
        
        <category>capacitor</category>
        
        <category>mobile</category>
        
        <category>javascript</category>
        
        
      </item>
    
      <item>
        <title>[Next.js] App Router에서 서버/클라 경계 설계 실수 TOP 7 (실무 패턴 + 샘플)</title>
        <description>&lt;h2 id=&quot;왜-서버클라-경계가-app-router의-핵심인가&quot;&gt;왜 “서버/클라 경계”가 App Router의 핵심인가?&lt;/h2&gt;

&lt;p&gt;App Router(Next.js 13+)는 기본이 &lt;strong&gt;Server Component&lt;/strong&gt;입니다.&lt;br /&gt;
즉, 컴포넌트가 &lt;strong&gt;어디서 실행되는지(서버/브라우저)&lt;/strong&gt;에 따라 가능한 API, 번들 크기, 렌더링 방식이 크게 달라집니다.&lt;/p&gt;

&lt;p&gt;실무에서 문제는 보통 이 셋으로 귀결됩니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;클라에서 해야 할 일을 서버에서 하려다 깨짐&lt;/li&gt;
  &lt;li&gt;서버에서 할 수 있는 일을 클라로 끌고 와서 성능/보안 손해&lt;/li&gt;
  &lt;li&gt;Provider/상태/이벤트 때문에 경계가 무너짐&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;아래 TOP 7은 가장 자주 터지는 패턴 + 바로 적용 가능한 해결책입니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-1-use-client를-습관처럼-붙여서-페이지-전체를-클라로-만들어버림&quot;&gt;TOP 1. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use client&lt;/code&gt;를 습관처럼 붙여서 “페이지 전체를 클라”로 만들어버림&lt;/h2&gt;

&lt;h3 id=&quot;증상&quot;&gt;증상&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;페이지 전체 번들이 커짐(초기 로딩/INP 악화)&lt;/li&gt;
  &lt;li&gt;서버 패칭/캐시/SEO 이점이 사라짐&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;안-좋은-예샘플&quot;&gt;안 좋은 예(샘플)&lt;/h3&gt;
&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/dashboard/page.tsx&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;use client&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DashboardPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/api/stats&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;pre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;pre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;권장-패턴-서버는-데이터조합-클라는-인터랙션만샘플&quot;&gt;권장 패턴: 서버는 데이터/조합, 클라는 인터랙션만(샘플)&lt;/h3&gt;
&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/dashboard/page.tsx (Server Component)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DashboardClient&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./DashboardClient&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://internal-api.example.com/stats&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;no-store&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DashboardPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stats&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;DashboardClient&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;initialStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stats&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/dashboard/DashboardClient.tsx (Client Component)&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;use client&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;DashboardClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;initialStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;initialStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;initialStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Dashboard&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setStats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;refreshedAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()})&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        새로고침(샘플)
      &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;pre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stats&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;pre&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;핵심:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use client&lt;/code&gt;는 “페이지 전체”가 아니라 &lt;strong&gt;딱 필요한 컴포넌트에만&lt;/strong&gt;. 
하지만 꼭 최적화를 할 필요는 없다 작은 페이지/컴포넌트는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use client&lt;/code&gt;로 빠르게 개발해도 무방&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-2-server-component에-이벤트상태브라우저-api를-섞어버림&quot;&gt;TOP 2. Server Component에 이벤트/상태/브라우저 API를 섞어버림&lt;/h2&gt;

&lt;h3 id=&quot;증상-1&quot;&gt;증상&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onClick&lt;/code&gt; 넣자마자 에러&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt; 접근 시 런타임 에러&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;올바른-분리샘플&quot;&gt;올바른 분리(샘플)&lt;/h3&gt;
&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/products/page.tsx (Server)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FilterBar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./FilterBar&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ProductsPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;initialFilters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onlySale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;Products&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;FilterBar&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;initialFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;initialFilters&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/products/FilterBar.tsx (Client)&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;use client&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FilterBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;initialFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;initialFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;initialFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;input&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;검색&quot;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;input&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;checked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onlySale&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;onChange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setFilters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onlySale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;checked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
        세일만
      &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-3-데이터-패칭을-전부-클라로-내려서-ssr캐시-이점을-다-날림&quot;&gt;TOP 3. 데이터 패칭을 전부 클라로 내려서 SSR/캐시 이점을 다 날림&lt;/h2&gt;

&lt;h3 id=&quot;증상-2&quot;&gt;증상&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;초기 화면은 스피너만 보임(UX 저하)&lt;/li&gt;
  &lt;li&gt;중복 호출/레이스 컨디션이 늘어남&lt;/li&gt;
  &lt;li&gt;SEO/공유 메타에서 빈 화면처럼 보일 수 있음&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;서버에서-패칭샘플&quot;&gt;서버에서 패칭(샘플)&lt;/h3&gt;
&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/posts/page.tsx&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PostsPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://api.example.com/posts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 상황에 따라 선택:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// cache: &apos;force-cache&apos;,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// next: { revalidate: 60 },&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;no-store&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-4-provider를-root에-박아서-전부-client로-전염시킴&quot;&gt;TOP 4. Provider를 Root에 박아서 전부 Client로 “전염”시킴&lt;/h2&gt;

&lt;h3 id=&quot;증상-3&quot;&gt;증상&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;layout까지 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use client&lt;/code&gt; 필요 → 앱 전체가 클라화&lt;/li&gt;
  &lt;li&gt;번들/JS 실행 비용 증가&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;권장-패턴-providers를-전용-파일로-분리샘플&quot;&gt;권장 패턴: Providers를 전용 파일로 분리(샘플)&lt;/h3&gt;
&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/providers.tsx&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;use client&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ReactNode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;QueryClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;QueryClientProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@tanstack/react-query&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ReactNode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;QueryClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;QueryClientProvider&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;QueryClientProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/layout.tsx (Server)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Providers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./providers&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RootLayout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;React&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ReactNode&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ko&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Providers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-5-server-action을-만능-api처럼-쓰다가-ux경계가-꼬임&quot;&gt;TOP 5. Server Action을 “만능 API”처럼 쓰다가 UX/경계가 꼬임&lt;/h2&gt;

&lt;h3 id=&quot;현실적인-정리&quot;&gt;현실적인 정리&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;Server Actions는 특히 &lt;strong&gt;폼 기반 뮤테이션(저장/수정)&lt;/strong&gt;에 적합&lt;/li&gt;
  &lt;li&gt;조회(Read)는 서버 패칭/캐시 설계와 조합하는 편이 안정적&lt;/li&gt;
  &lt;li&gt;TanStack Query 같은 클라이언트 데이터 라이브러리로도 충분히 커버 가능&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;폼-업데이트샘플&quot;&gt;폼 업데이트(샘플)&lt;/h3&gt;
&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/settings/actions.ts&lt;/span&gt;
&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;use server&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updateProfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;FormData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nickname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// await db.user.update(...)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// app/settings/page.tsx&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;updateProfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./actions&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SettingsPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;updateProfile&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
        닉네임
        &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nickname&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;저장&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-6-processenv를-클라에서-써서-비밀값-노출빌드-문제-발생&quot;&gt;TOP 6. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;process.env&lt;/code&gt;를 클라에서 써서 비밀값 노출/빌드 문제 발생&lt;/h2&gt;

&lt;h3 id=&quot;규칙&quot;&gt;규칙&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;클라에 노출해도 되는 값만 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NEXT_PUBLIC_&lt;/code&gt; 접두사&lt;/li&gt;
  &lt;li&gt;그 외는 서버에서만 사용(Server Component/Route Handler/Server Action)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;좋은-예샘플&quot;&gt;좋은 예(샘플)&lt;/h3&gt;
&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 공개 가능&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;publicKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NEXT_PUBLIC_MAP_KEY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 서버에서만&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;secret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;API_SECRET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;top-7-hydration-mismatch를-운빨로-치부하고-땜질만-함&quot;&gt;TOP 7. Hydration mismatch를 운빨로 치부하고 땜질만 함&lt;/h2&gt;

&lt;h3 id=&quot;대표-원인&quot;&gt;대표 원인&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;서버 렌더와 클라 렌더 결과가 달라지는 값
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new Date()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Math.random()&lt;/code&gt;, locale 포맷 등&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;localStorage 기반 테마 같은 “클라 전용 상태”를 즉시 렌더&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;해결-패턴-1-마운트-후에만-값-반영샘플&quot;&gt;해결 패턴 1: 마운트 후에만 값 반영(샘플)&lt;/h3&gt;
&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;use client&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;setNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toISOString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;해결-패턴-2-서버에서-값-고정해서-내려주기샘플&quot;&gt;해결 패턴 2: 서버에서 값 고정해서 내려주기(샘플)&lt;/h3&gt;
&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Server Component&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;serverNow&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toISOString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ClientNow&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;serverNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serverNow&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-tsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;use client&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ClientNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serverNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;serverNow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;serverNow&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;실무-팁-코드리뷰에서-바로-쓰는-체크-질문&quot;&gt;실무 팁: 코드리뷰에서 바로 쓰는 체크 질문&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;이 컴포넌트는 정말 Client여야 하나? (이벤트/상태/브라우저 API가 있나?)&lt;/li&gt;
  &lt;li&gt;데이터 패칭을 클라로 내린 이유가 있는가? (SEO/TTFB/중복 호출 고려했나?)&lt;/li&gt;
  &lt;li&gt;Provider 범위는 최소인가? (Root까지 전염시키지 않았나?)&lt;/li&gt;
  &lt;li&gt;env는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NEXT_PUBLIC_&lt;/code&gt;만 클라로 나갔나?&lt;/li&gt;
  &lt;li&gt;Hydration mismatch 가능성이 있는 값(Date/Random/Locale)은 어디서 계산했나?&lt;/li&gt;
  &lt;li&gt;“수정”은 Server Action, “조회”는 서버 패칭으로 분리했나?&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 15 Feb 2026 11:03:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/02/15/nextjs-app-router/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/02/15/nextjs-app-router/</guid>
        
        <category>nextjs</category>
        
        <category>react</category>
        
        <category>frontend</category>
        
        <category>web-development</category>
        
        
      </item>
    
      <item>
        <title>React useEffect 실수 TOP3 정리 + useEffectEvent로 의존성/스테일 클로저 해결하기</title>
        <description>&lt;h2 id=&quot;useeffect-한-줄-정의-외부-시스템과-동기화&quot;&gt;useEffect 한 줄 정의: “외부 시스템과 동기화”&lt;/h2&gt;
&lt;p&gt;React 공식 문서의 핵심 관점은 단순합니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt;는 &lt;strong&gt;React 바깥(외부 시스템)&lt;/strong&gt; 과 컴포넌트를 &lt;strong&gt;동기화&lt;/strong&gt;할 때 쓰는 “탈출구(escape hatch)”다.&lt;/li&gt;
  &lt;li&gt;외부 시스템이 없다면, &lt;strong&gt;Effect가 필요 없을 가능성이 높다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(공식 문서: &lt;a href=&quot;https://react.dev/learn/you-might-not-need-an-effect&quot;&gt;You Might Not Need an Effect&lt;/a&gt;)&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;실무에서-자주-하는-useeffect-실수-top3&quot;&gt;실무에서 자주 하는 useEffect 실수 TOP3&lt;/h1&gt;

&lt;h2 id=&quot;1-불필요한-useeffect-사용-derived-state--렌더-계산을-effect로-처리&quot;&gt;1) 불필요한 useEffect 사용 (derived state / 렌더 계산을 effect로 처리)&lt;/h2&gt;
&lt;p&gt;가장 흔한 패턴이 “상태를 또 다른 상태로 동기화”하는 코드입니다.&lt;/p&gt;

&lt;h3 id=&quot;-안-좋은-예-state를-effect로-복제&quot;&gt;❌ 안 좋은 예: state를 effect로 ‘복제’&lt;/h3&gt;
&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setFirst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setLast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setFullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;setFullName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;이 경우 외부 시스템이 없고, 단순 계산이라서 &lt;strong&gt;렌더링 중 계산&lt;/strong&gt;이 더 적합합니다.&lt;br /&gt;
불필요한 Effect는 코드 흐름을 복잡하게 만들고 버그(렌더 타이밍/의존성)를 늘립니다.
간단한 state는 가독성이 괜찮지만 프로젝트가 복잡할 시 가독성은 급격히 떨어지고 사이드이펙트가 늘어나 디버깅하기가 어렵습니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“렌더링을 위한 데이터 변환”은 Effect가 아니라 &lt;strong&gt;렌더 계산&lt;/strong&gt;으로 해결하는 게 우선입니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(공식 문서: &lt;a href=&quot;https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state&quot;&gt;Updating state based on props or state&lt;/a&gt;)&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;2-의존성-배열deps-실수-과도한-재실행--누락--객체함수-deps&quot;&gt;2) 의존성 배열(deps) 실수 (과도한 재실행 / 누락 / 객체·함수 deps)&lt;/h2&gt;
&lt;p&gt;의존성 배열 관련 실수는 크게 2가지로 나뉩니다.&lt;/p&gt;

&lt;h3 id=&quot;a-객체함수-deps로-인해-effect가-쓸데없이-자주-재실행&quot;&gt;(A) 객체/함수 deps로 인해 effect가 “쓸데없이 자주” 재실행&lt;/h3&gt;
&lt;p&gt;컴포넌트 내부에서 만든 객체/함수는 렌더마다 새로 만들어질 수 있어, deps에 넣으면 effect가 불필요하게 반복됩니다.&lt;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// options가 렌더마다 새 객체면 매번 실행될 수 있음&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;✅ 개선 방향(공식 문서 권장 요지)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;불필요한 객체/함수 의존성 제거&lt;/li&gt;
  &lt;li&gt;외부로 빼거나, 필요한 “원시 값”만 deps에 넣기&lt;/li&gt;
  &lt;li&gt;“반응형(reactive) 로직”과 “비반응형(non-reactive) 로직” 분리 고려&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(공식 문서: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt; &lt;a href=&quot;https://react.dev/learn/you-might-not-need-an-effect#caching-expensive-calculations&quot;&gt;Caching expensive calculations&lt;/a&gt;)&lt;/p&gt;

&lt;h3 id=&quot;b-deps-누락으로-인한-stale-closure스테일-클로저--의도치-않은-동작&quot;&gt;(B) deps 누락으로 인한 stale closure(스테일 클로저) / 의도치 않은 동작&lt;/h3&gt;
&lt;p&gt;deps를 “비워서([])” 고정하거나, 필요한 값을 빠뜨리면
Effect 내부에서 &lt;strong&gt;오래된 state/props&lt;/strong&gt; 를 참조하는 문제가 생길 수 있습니다.&lt;/p&gt;

&lt;p&gt;이 지점에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffectEvent&lt;/code&gt;가 매우 유용합니다. (아래에서 자세히)&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;3-cleanup-누락-특히-strict-mode에서-더-잘-드러남&quot;&gt;3) cleanup 누락 (특히 Strict Mode에서 더 잘 드러남)&lt;/h2&gt;
&lt;p&gt;구독/이벤트/타이머처럼 “설치(setup)”를 했다면, &lt;strong&gt;반드시 해제(cleanup)&lt;/strong&gt; 해야 합니다.&lt;/p&gt;

&lt;h3 id=&quot;대표-케이스&quot;&gt;대표 케이스&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setInterval&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clearInterval&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addEventListener&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;removeEventListener&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;소켓 연결 / 해제&lt;/li&gt;
  &lt;li&gt;서드파티 위젯 초기화 / 제거&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;setInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;clearInterval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;또한 React Strict Mode(개발 모드)에서는
&lt;strong&gt;setup + cleanup을 한 번 더 실행&lt;/strong&gt;해서 cleanup이 제대로 “대칭”인지 점검합니다.&lt;br /&gt;
cleanup이 빠져 있으면 개발 환경에서 문제(중복 구독/중복 타이머)가 더 빨리 튀어나옵니다.&lt;/p&gt;

&lt;p&gt;(공식 문서: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt; &lt;a href=&quot;https://react.dev/learn/you-might-not-need-an-effect#sharing-logic-between-event-handlers&quot;&gt;Sharing logic between event handlers&lt;/a&gt;)&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;useeffectevent-추가-최신-stateprops는-읽고-싶지만-effect를-재실행하진-않게&quot;&gt;useEffectEvent 추가: “최신 state/props는 읽고 싶지만, effect를 재실행하진 않게”&lt;/h1&gt;
&lt;p&gt;문서: https://react.dev/reference/react/useEffectEvent&lt;/p&gt;

&lt;h2 id=&quot;useeffectevent란&quot;&gt;useEffectEvent란?&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffectEvent&lt;/code&gt;는 Effect 내부에서 사용하는 로직 중,
&lt;strong&gt;반응형(reactive)으로 다루고 싶지 않은 부분&lt;/strong&gt;을 분리해
“Effect Event”라는 함수로 만들 수 있게 해주는 Hook입니다.&lt;/p&gt;

&lt;p&gt;공식 문서가 강조하는 대표 목적은:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Effect 안에서 최신 props/state 읽기&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;하지만 그 값 때문에 &lt;strong&gt;Effect 자체가 다시 실행되게 만들지 않기&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(공식 문서: &lt;a href=&quot;https://react.dev/reference/react/useEffectEvent&quot;&gt;useEffectEvent&lt;/a&gt;)&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;언제-유용한가-실전-시나리오&quot;&gt;언제 유용한가? (실전 시나리오)&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;“roomId가 바뀔 때만” 서버 연결을 다시 하고 싶다&lt;/li&gt;
  &lt;li&gt;그런데 연결 과정에서 “최신 알림 설정/유저 정보” 같은 값도 쓰고 싶다&lt;/li&gt;
  &lt;li&gt;그 값들을 deps에 넣으면 effect가 너무 자주 재실행됨 → 원치 않는 재연결/재구독&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이런 상황에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffectEvent&lt;/code&gt;로 “최신 값 읽기”를 분리하면,
deps는 깔끔하게 유지하면서 stale closure를 줄일 수 있습니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;예시-패턴-reactive-deps는-최소화--최신-값은-effect-event로-읽기&quot;&gt;예시 패턴: reactive deps는 최소화 + 최신 값은 Effect Event로 읽기&lt;/h2&gt;
&lt;p&gt;(아래 예시는 “형태”를 보여주는 샘플입니다)&lt;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useEffectEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ChatRoom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roomId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;soundEnabled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ✅ 최신 값을 읽되, 이것 때문에 effect가 재실행되진 않도록 분리&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;onMessage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;useEffectEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;soundEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// 최신 soundEnabled를 사용&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;playSound&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;showToast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;roomId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;onMessage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 최신 값을 안전하게 사용&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;disconnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;roomId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// ✅ roomId만 바뀔 때 재연결&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;핵심 포인트:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Effect는 “외부 시스템과의 동기화(연결/해제)”만 담당&lt;/li&gt;
  &lt;li&gt;“최신 옵션/상태 읽기”는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffectEvent&lt;/code&gt;로 분리&lt;/li&gt;
  &lt;li&gt;deps가 과도하게 커지지 않아서 의도한 타이밍만 재실행됨&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;체크리스트-useeffect-쓰기-전에-한번-생각-해보기&quot;&gt;체크리스트: useEffect 쓰기 전에 한번 생각 해보기&lt;/h1&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;외부 시스템 동기화인가?&lt;/strong&gt; (DOM API/구독/네트워크/타이머/서드파티 위젯)&lt;/li&gt;
  &lt;li&gt;단순 계산/파생 값이면 &lt;strong&gt;렌더에서 계산&lt;/strong&gt;할 수 없는가?&lt;/li&gt;
  &lt;li&gt;유저 클릭/입력 등 “이벤트”라면 &lt;strong&gt;이벤트 핸들러&lt;/strong&gt;에서 처리하는 게 맞지 않은가?&lt;/li&gt;
  &lt;li&gt;setup을 했다면 &lt;strong&gt;cleanup이 대칭&lt;/strong&gt;으로 존재하는가?&lt;/li&gt;
  &lt;li&gt;deps에 &lt;strong&gt;객체/함수&lt;/strong&gt;가 들어가서 불필요한 재실행을 만들고 있지 않은가?&lt;/li&gt;
  &lt;li&gt;deps 누락으로 &lt;strong&gt;stale closure&lt;/strong&gt; 위험이 있지 않은가?&lt;/li&gt;
  &lt;li&gt;“최신 값은 필요하지만 재실행은 싫다”면 &lt;strong&gt;useEffectEvent로 분리&lt;/strong&gt;할 수 있는가?&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;요약&quot;&gt;요약&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;useEffect&lt;/code&gt;는 “외부 시스템 동기화”를 위한 도구다.&lt;/li&gt;
  &lt;li&gt;실수 TOP3는 보통 &lt;strong&gt;불필요한 Effect&lt;/strong&gt;, &lt;strong&gt;deps 실수&lt;/strong&gt;, &lt;strong&gt;cleanup 누락&lt;/strong&gt;에서 터진다.&lt;/li&gt;
  &lt;li&gt;deps가 커지거나 stale closure가 고민이라면, &lt;strong&gt;useEffectEvent로 비반응형 로직을 분리&lt;/strong&gt;해서
“재실행 타이밍”과 “최신 값 읽기”를 동시에 만족시킬 수 있다.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;공식-문서&quot;&gt;공식 문서&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;useEffectEvent: https://react.dev/reference/react/useEffectEvent&lt;/li&gt;
  &lt;li&gt;useEffect: https://react.dev/reference/react/useEffect&lt;/li&gt;
  &lt;li&gt;You Might Not Need an Effect: https://react.dev/learn/you-might-not-need-an-effect&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 08 Feb 2026 16:33:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/02/08/use-effect/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/02/08/use-effect/</guid>
        
        <category>react</category>
        
        <category>frontend</category>
        
        <category>web-development</category>
        
        
      </item>
    
      <item>
        <title>[React] react-error-boundary 완벽 정리: ErrorBoundary는 만능이 아니다 (언제 써야 할까?)</title>
        <description>&lt;h2 id=&quot;react-errorboundaryerror-boundary란&quot;&gt;React ErrorBoundary(Error Boundary)란?&lt;/h2&gt;

&lt;p&gt;React에서 컴포넌트가 렌더링 중 에러를 던지면, 화면 전체가 깨지거나(화이트 스크린) 앱이 멈춰 보일 수 있습니다.&lt;br /&gt;
&lt;strong&gt;ErrorBoundary&lt;/strong&gt;는 이런 상황에서 &lt;strong&gt;하위 컴포넌트 트리에서 발생한 렌더링 에러를 포착&lt;/strong&gt;하고, 앱 전체가 죽지 않도록 &lt;strong&gt;대체 UI(fallback UI)&lt;/strong&gt; 를 보여주는 “UI 안전장치”입니다.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;핵심: ErrorBoundary는 &lt;strong&gt;에러 처리 로직&lt;/strong&gt;이 아니라, &lt;strong&gt;렌더링 붕괴를 막는 UI 보호막&lt;/strong&gt;에 가깝습니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;errorboundary가-잡아주는-에러-범위--되는-것&quot;&gt;ErrorBoundary가 잡아주는 에러 범위 (✅ 되는 것)&lt;/h2&gt;

&lt;p&gt;ErrorBoundary가 포착할 수 있는 대표 케이스는 아래와 같습니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;컴포넌트 &lt;strong&gt;렌더링(render) 과정&lt;/strong&gt;에서 발생한 에러&lt;/li&gt;
  &lt;li&gt;클래스 컴포넌트 기준 &lt;strong&gt;생명주기(lifecycle)&lt;/strong&gt; 에서 발생한 에러&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;자식 컴포넌트 트리&lt;/strong&gt; 내부에서 발생한 에러&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;예시:&lt;/p&gt;

&lt;div class=&quot;language-jsx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ErrorBoundary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;MyComponent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;ErrorBoundary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyComponent&lt;/code&gt;가 렌더링 중 예외를 던지면, ErrorBoundary가 fallback UI로 대체 렌더링합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;errorboundary는-만능이-아니다--안-되는-것&quot;&gt;ErrorBoundary는 만능이 아니다 (❌ 안 되는 것)&lt;/h2&gt;

&lt;p&gt;많이 하는 오해는 “ErrorBoundary로 감싸면 앱 에러가 다 잡힌다”인데, &lt;strong&gt;그렇지 않습니다.&lt;/strong&gt;&lt;br /&gt;
ErrorBoundary는 &lt;strong&gt;React 렌더링 사이클 안에서&lt;/strong&gt; 발생한 에러만 안전하게 다룰 수 있습니다.&lt;/p&gt;

&lt;h3 id=&quot;errorboundary가-잡지-못하는-대표-케이스&quot;&gt;ErrorBoundary가 잡지 못하는 대표 케이스&lt;/h3&gt;

&lt;p&gt;1) &lt;strong&gt;이벤트 핸들러 내부 에러&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onClick&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onChange&lt;/code&gt; 등 사용자 액션 핸들러에서 발생한 예외&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;strong&gt;비동기 코드의 에러&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setTimeout&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Promise&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;async/await&lt;/code&gt; 내부에서 발생한 예외&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;strong&gt;SSR(서버 사이드 렌더링) 중 에러&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;4) &lt;strong&gt;ErrorBoundary 자신에서 발생한 에러&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;왜-이벤트비동기-에러는-errorboundary로-안-잡힐까&quot;&gt;왜 이벤트/비동기 에러는 ErrorBoundary로 안 잡힐까?&lt;/h2&gt;

&lt;p&gt;ErrorBoundary는 &lt;strong&gt;렌더링 중&lt;/strong&gt; 발생한 예외를 기준으로 “fallback UI로 교체”하는 방식입니다.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;렌더링 중 에러: React가 UI 트리를 다시 그리면서 대체 가능 ✅&lt;/li&gt;
  &lt;li&gt;이벤트/비동기 에러: 렌더링이 끝난 뒤 발생 → React가 자동으로 UI를 교체할 컨텍스트가 없음 ❌&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;즉, &lt;strong&gt;ErrorBoundary는 “렌더링 단계의 안전장치”&lt;/strong&gt; 이고,&lt;br /&gt;
&lt;strong&gt;이벤트/비동기 단계의 에러는 별도 전략&lt;/strong&gt;이 필요합니다.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;react-에러-처리-실전-전략-errorboundary--직접-처리&quot;&gt;React 에러 처리 실전 전략 (ErrorBoundary + 직접 처리)&lt;/h2&gt;

&lt;h3 id=&quot;1-이벤트-핸들러-에러-처리-trycatch로-직접-잡기&quot;&gt;1) 이벤트 핸들러 에러 처리: try/catch로 직접 잡기&lt;/h3&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 위험한 로직&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 필요하면 사용자에게 토스트/알림 표시&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-비동기-에러-처리-promise-체인async-처리로-잡기&quot;&gt;2) 비동기 에러 처리: Promise 체인/async 처리로 잡기&lt;/h3&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetchData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/api/data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Network error&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// 상태로 에러를 관리하거나, 재시도 UI를 제공&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;네트워크 에러/비동기 에러는 대개 ErrorBoundary보다&lt;br /&gt;
&lt;strong&gt;TanStack Query&lt;/strong&gt;이나 &lt;strong&gt;로딩/에러 상태(state) 기반 UI 패턴&lt;/strong&gt;으로 처리하는 쪽이 가독성이 좋습니다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;언제-errorboundary를-써야-할까-판단표&quot;&gt;언제 ErrorBoundary를 써야 할까? (판단표)&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;상황&lt;/th&gt;
      &lt;th&gt;ErrorBoundary 사용&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;렌더링 에러(컴포넌트 JSX 평가 중)&lt;/td&gt;
      &lt;td&gt;✅&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;생명주기 에러(클래스 컴포넌트 기준)&lt;/td&gt;
      &lt;td&gt;✅&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;이벤트 핸들러(onClick 등) 에러&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;비동기(Promise/async/timeout) 에러&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;API 요청 실패/서버 응답 오류&lt;/td&gt;
      &lt;td&gt;❌ (직접 처리 권장)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;특정 섹션만 깨져도 되는 UI 보호&lt;/td&gt;
      &lt;td&gt;✅ (강력 추천)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;errorboundary-적용-위치-전체-감싸기보다-구역-감싸기&quot;&gt;ErrorBoundary 적용 위치: “전체 감싸기”보다 “구역 감싸기”&lt;/h2&gt;

&lt;p&gt;실무에서는 App 전체를 1개 ErrorBoundary로 감싸기보다, &lt;strong&gt;깨졌을 때 영향 범위를 줄이고 싶은 구역&lt;/strong&gt;에 배치하는 게 효율적입니다.&lt;/p&gt;

&lt;p&gt;추천 단위:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;라우트(페이지) 단위&lt;/li&gt;
  &lt;li&gt;탭/패널 단위&lt;/li&gt;
  &lt;li&gt;위젯/카드 리스트 단위&lt;/li&gt;
  &lt;li&gt;외부 라이브러리 의존도가 높은 영역(차트, 에디터 등)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;목표: “앱 전체 다운”이 아니라 “부분 장애”로 격리하기&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;요약&quot;&gt;요약&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;ErrorBoundary는 &lt;strong&gt;렌더링 에러 전용&lt;/strong&gt; 안전장치다.&lt;/li&gt;
  &lt;li&gt;이벤트/비동기/SSR 에러는 &lt;strong&gt;별도 처리&lt;/strong&gt;가 필요하다.&lt;/li&gt;
  &lt;li&gt;실무에서는 &lt;strong&gt;페이지/섹션 단위로 적용&lt;/strong&gt;해 장애 범위를 줄이는 게 핵심이다.&lt;/li&gt;
  &lt;li&gt;ErrorBoundary는 “에러 처리 만능키”가 아니라 “UI 붕괴 방지용 보호막”이다.&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 07 Feb 2026 14:20:00 +0900</pubDate>
        <link>https://beam307.github.io/2026/02/07/react-error-boundary/</link>
        <guid isPermaLink="true">https://beam307.github.io/2026/02/07/react-error-boundary/</guid>
        
        <category>react</category>
        
        <category>frontend</category>
        
        <category>web-development</category>
        
        
      </item>
    
  </channel>
</rss>
