<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>김신건의 로그 - 위키 / frameworks</title><description>&apos;frameworks&apos; 카테고리 위키 페이지 최신 업데이트</description><link>https://shinkeonkim.com/</link><language>ko-KR</language><item><title>Pagefind: 정적 사이트 전문 검색</title><link>https://shinkeonkim.com/wiki/frameworks/pagefind/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/pagefind/</guid><pubDate>Mon, 29 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Pagefind&lt;/strong&gt; 는 정적 사이트 (SSG) 용 클라이언트 사이드 전문 검색 엔진. &lt;strong&gt;빌드 후 정적 인덱스&lt;/strong&gt; 생성, 브라우저에서 fetch 로 검색.&lt;/p&gt;
&lt;h2 id=&quot;특징&quot;&gt;특징&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;서버 없음&lt;/strong&gt;: JS + fragment index 만&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;다국어 자동 감지&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;작은 인덱스&lt;/strong&gt;: 40MB HTML → 300KB index 정도&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stemming&lt;/strong&gt;: Snowball 알고리즘&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;astro-통합&quot;&gt;Astro 통합&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; i&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -D&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; astro-pagefind&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// astro.config.mjs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; defineConfig&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    integrations: [&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pagefind&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;astro&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;#x3C;!-- mdx-component: PagefindSearch --&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;사용-직접&quot;&gt;사용 (직접)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; pagefind&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/pagefind/pagefind.js&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; search&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; pagefind.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;search&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;rails&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; r&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; search.results) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; r.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(data.url, data.excerpt);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;필터링&quot;&gt;필터링&lt;/h2&gt;
&lt;p&gt;HTML 에 &lt;code&gt;data-pagefind-filter&lt;/code&gt; 속성으로 카테고리 필터:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; data-pagefind-filter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;category:blog&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;pagefind.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;search&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;rails&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, { filters: { category: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;blog&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[astro|Astro]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>frameworks</category><category>search</category><category>astro</category><category>static</category><author>koa (김신건)</author></item><item><title>[Frontend] React 19.x: Actions, Activity, RSC, Foundation</title><link>https://shinkeonkim.com/wiki/frameworks/react/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/react/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;React 19&lt;/strong&gt; (&lt;code&gt;2024-12-05&lt;/code&gt;) 는 &lt;em&gt;Server Components 의 정식화&lt;/em&gt; 와 &lt;em&gt;Actions&lt;/em&gt; 라는 새 패러다임을 들고 나온 메이저 릴리즈. 이후 &lt;code&gt;19.1&lt;/code&gt; (&lt;code&gt;2025-06&lt;/code&gt;), &lt;code&gt;19.2&lt;/code&gt; (&lt;code&gt;2025-10-01&lt;/code&gt;) 까지 &lt;em&gt;minor 릴리즈에서 Activity / Performance Tracks / Partial Pre-rendering 같은 굵직한 기능&lt;/em&gt; 이 계속 추가되었다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2026-06 시점 기준 &lt;em&gt;현재 LTS 줄기는 19.x&lt;/em&gt;. React 20 / 19.3 은 &lt;em&gt;아직 공식 발표 전&lt;/em&gt;. 이 페이지는 &lt;em&gt;2026-06 의 운영자 시점&lt;/em&gt; 에서 정리한 React 19.x 의 &lt;em&gt;작은 백과&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;버전-매트릭스&quot;&gt;버전 매트릭스&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;19.0&lt;/td&gt;&lt;td&gt;2024-12-05&lt;/td&gt;&lt;td&gt;Actions, ref-as-prop, Document Metadata, Asset Loading, &lt;code&gt;use()&lt;/code&gt;, &lt;code&gt;useActionState&lt;/code&gt; / &lt;code&gt;useFormStatus&lt;/code&gt; / &lt;code&gt;useOptimistic&lt;/code&gt;, Server Components 안정화&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;19.1&lt;/td&gt;&lt;td&gt;2025-06&lt;/td&gt;&lt;td&gt;점진 안정화 + 버그 픽스 (큰 신 기능 없음)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;19.2&lt;/td&gt;&lt;td&gt;2025-10-01&lt;/td&gt;&lt;td&gt;`&lt;!-- mdx-component: Activity --&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;, &lt;/code&gt;useEffectEvent&lt;code&gt;, &lt;/code&gt;cacheSignal&lt;code&gt;, Performance Tracks, Partial Pre-rendering, Web Streams for Node, &lt;/code&gt;useId&lt;code&gt;prefix&lt;/code&gt;&lt;em&gt;r&lt;/em&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;19.0.1 / 19.1.2 / 19.2.1&lt;/td&gt;&lt;td&gt;2025-12-03&lt;/td&gt;&lt;td&gt;&lt;em&gt;RSC RCE 보안 패치&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;actions-폼네트워크-워크플로의-새-표준&quot;&gt;Actions: 폼/네트워크 워크플로의 새 표준&lt;/h2&gt;
&lt;p&gt;폼 제출 같은 &lt;em&gt;비동기 전이&lt;/em&gt; 를 &lt;code&gt;useTransition&lt;/code&gt; 위에서 &lt;em&gt;정식화&lt;/em&gt; 한 것이 Actions. 함수 자체에 &lt;code&gt;async&lt;/code&gt; 가 들어가도 &lt;code&gt;isPending&lt;/code&gt; 이 자동으로 잡힌다.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; UpdateName&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setName&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setError&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;isPending&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;startTransition&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useTransition&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; handleSubmit&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    startTransition&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; err&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; updateName&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(name);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (err) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;        setError&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(err);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      redirect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/path&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{name} &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onChange&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setName&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(e.target.value)} /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{handleSubmit} &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;disabled&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{isPending}&gt;Update&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {error &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{error}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 패턴 위에 &lt;em&gt;3 종 hook&lt;/em&gt; 이 얹힌다:&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Hook&lt;/th&gt;&lt;th&gt;역할&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https://react.dev/reference/react/useActionState&quot;&gt;&lt;code&gt;useActionState(action, initial)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Action 의 &lt;em&gt;마지막 결과&lt;/em&gt; + &lt;code&gt;isPending&lt;/code&gt; 보관. 폼 결과 처리의 표준&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https://react.dev/reference/react-dom/hooks/useFormStatus&quot;&gt;&lt;code&gt;useFormStatus()&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;#x3C;form&gt;&lt;/code&gt; 의 &lt;em&gt;자손 컴포넌트&lt;/em&gt; 에서 부모 폼의 pending 상태 조회 (prop drilling 없이)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href=&quot;https://react.dev/reference/react/useOptimistic&quot;&gt;&lt;code&gt;useOptimistic(state, updater)&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;낙관적 업데이트&lt;/em&gt; (서버 응답 전 미리 UI 갱신)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;&amp;#x3C;form action={fn}&gt;&lt;/code&gt; 형태로 &lt;em&gt;DOM 액션 prop&lt;/em&gt; 도 추가. Server Actions (&lt;code&gt;&apos;use server&apos;&lt;/code&gt;) 와 합쳐지면 &lt;em&gt;클라이언트 → 서버 mutation&lt;/em&gt; 의 일등 시민이 된다.&lt;/p&gt;
&lt;h2 id=&quot;server-components-정식화&quot;&gt;Server Components 정식화&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&apos;use client&apos;&lt;/code&gt; / &lt;code&gt;&apos;use server&apos;&lt;/code&gt; 디렉티브 기반의 &lt;em&gt;분할&lt;/em&gt;. 19 에서 안정 API 로 진입.&lt;/p&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&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;&lt;code&gt;&apos;use client&apos;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;번들러에게 &lt;em&gt;클라이언트 경계&lt;/em&gt; 알림. 별도 &lt;code&gt;&amp;#x3C;script&gt;&lt;/code&gt; 청크로 묶임&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;&apos;use server&apos;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;번들러에게 &lt;em&gt;서버 endpoint&lt;/em&gt; 알림. 클라이언트에서 호출하면 &lt;em&gt;POST 가 일어남&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;번들러 기능&lt;/em&gt; 이지 React 의 런타임 기능이 아니다. Next.js, Waku, RedwoodJS 같은 &lt;em&gt;프레임워크&lt;/em&gt; 가 묶어서 제공.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;ref-가-prop-이-되다&quot;&gt;ref 가 prop 이 되다&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;forwardRef&lt;/code&gt; 는 더 이상 필요 없다. 함수 컴포넌트가 &lt;em&gt;그냥 &lt;code&gt;ref&lt;/code&gt; prop 을 받을 수 있다&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 19 이전&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; Button&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; forwardRef&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;ref&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ref&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{ref} {&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;props} /&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 19+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;ref&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ref&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{ref} {&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;props} /&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;forwardRef&lt;/code&gt; 자체는 &lt;em&gt;deprecated&lt;/em&gt;. 새 코드는 바로 prop 으로 받자.&lt;/p&gt;
&lt;h2 id=&quot;document-metadata&quot;&gt;Document Metadata&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&amp;#x3C;title&gt;&lt;/code&gt;, &lt;code&gt;&amp;#x3C;meta&gt;&lt;/code&gt;, &lt;code&gt;&amp;#x3C;link&gt;&lt;/code&gt; 를 &lt;em&gt;컴포넌트 트리 어디에서나&lt;/em&gt; 렌더 가능. React 가 자동으로 &lt;code&gt;&amp;#x3C;head&gt;&lt;/code&gt; 로 올린다. &lt;a href=&quot;https://github.com/nfl/react-helmet&quot;&gt;React Helmet&lt;/a&gt; 같은 라이브러리가 사실상 &lt;em&gt;불필요&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; BlogPost&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{post.title}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{post.title}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; content&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Josh&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;https://twitter.com/joshcstory/&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;keywords&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; content&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{post.keywords} /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SSR / RSC / CSR 어디서나 같은 방식. 중복 태그는 React 가 알아서 dedupe.&lt;/p&gt;
&lt;h2 id=&quot;asset-loading-과-preload--preinit&quot;&gt;Asset Loading 과 &lt;code&gt;preload&lt;/code&gt; / &lt;code&gt;preinit&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;스타일시트, 스크립트, 폰트, 이미지의 &lt;em&gt;로딩 라이프사이클&lt;/em&gt; 을 Suspense 와 연결. 데이터처럼 &lt;em&gt;준비될 때까지 일관되게 기다림&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 곧 쓸 자원을 미리 로드 (priority hint)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;preload&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;https://example.com/font.woff2&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, { as: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;font&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 다운로드 + 초기화까지&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;preinit&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;https://example.com/script.js&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, { as: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;script&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;라우터 / 코드 스플리트 시점에 미리 호출해두면 &lt;em&gt;TTI 개선&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;use-api-promise--context-의-일관된-read&quot;&gt;&lt;code&gt;use()&lt;/code&gt; API: Promise / Context 의 일관된 read&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://react.dev/reference/react/use&quot;&gt;&lt;code&gt;use(promise)&lt;/code&gt;&lt;/a&gt; 는 &lt;em&gt;렌더 중 Promise 결과&lt;/em&gt; 를 읽는다. Suspense 가 받아서 &lt;em&gt;fallback&lt;/em&gt; 을 띄운다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://react.dev/reference/react/use&quot;&gt;&lt;code&gt;use(context)&lt;/code&gt;&lt;/a&gt; 는 &lt;code&gt;useContext&lt;/code&gt; 와 거의 같지만 &lt;em&gt;if 안에서 호출 가능&lt;/em&gt;. Rules of Hooks 의 &lt;em&gt;최상위 호출&lt;/em&gt; 제약을 &lt;em&gt;조건부 사용 영역에서 풀어준 첫 hook&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Message&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;messagePromise&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; msg&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; use&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(messagePromise); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Suspense 가 받음&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{msg}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;192-의-큰-4가지&quot;&gt;19.2 의 큰 4가지&lt;/h2&gt;
&lt;h3 id=&quot;1-activity-pre-render--상태-보존&quot;&gt;1. &lt;code&gt;&amp;#x3C;Activity&gt;&lt;/code&gt;: pre-render + 상태 보존&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 이전&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{isVisible &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Page &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 19.2+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Activity&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; mode&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{isVisible &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;visible&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;hidden&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;!-- mdx-component: Page --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;mode&lt;/th&gt;&lt;th&gt;효과&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;visible&lt;/code&gt;&lt;/td&gt;&lt;td&gt;정상 렌더 + effect mount&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;hidden&lt;/code&gt;&lt;/td&gt;&lt;td&gt;렌더는 &lt;em&gt;유지&lt;/em&gt; 하되 effect 는 unmount, 업데이트는 &lt;em&gt;우선순위 낮춤&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;다음 페이지로 갈 가능성이 높은 영역을 &lt;em&gt;백그라운드로 미리 그려두기&lt;/em&gt;. 뒤로가기 시 &lt;em&gt;입력값/스크롤 등 상태 보존&lt;/em&gt;. SPA 라우터의 &lt;em&gt;워킹 디스플레이&lt;/em&gt; 패턴.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;2-useeffectevent&quot;&gt;2. &lt;code&gt;useEffectEvent&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;useEffect&lt;/code&gt; 의 &lt;em&gt;의존성으로 들어가야 하는 값&lt;/em&gt; 과 &lt;em&gt;그 안에서 단순히 읽기만 하는 값&lt;/em&gt; 을 &lt;em&gt;문법적으로 분리&lt;/em&gt;. &lt;em&gt;eslint-plugin-react-hooks@latest&lt;/em&gt; 가 디펜던시 배열에 자동으로 넣지 않도록 처리한다.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ChatRoom&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;roomId&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // theme 가 바뀌어도 effect 는 재실행 안 됨&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; onConnected&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useEffectEvent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    showNotification&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Connected!&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, theme);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; conn&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; createConnection&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(serverUrl, roomId);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;connected&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onConnected&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;disconnect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, [roomId]); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// theme 는 의존성이 *아니다*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;“이건 이벤트인데 effect 안에서 발생한다” 라는 &lt;em&gt;드문 사례&lt;/em&gt; 만 사용. 모든 함수를 wrap 하는 &lt;em&gt;남발 금지&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;3-performance-tracks-chrome-devtools&quot;&gt;3. Performance Tracks (Chrome DevTools)&lt;/h3&gt;
&lt;p&gt;Chrome DevTools 의 &lt;em&gt;custom track&lt;/em&gt; 으로 React 의 &lt;em&gt;Scheduler&lt;/em&gt; / &lt;em&gt;Components&lt;/em&gt; 두 트랙 추가.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scheduler ⚛&lt;/strong&gt;: blocking / transition / idle 우선순위별 작업 흐름.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Components ⚛&lt;/strong&gt;: 컴포넌트 트리의 mount / render / effect 시점.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;기존 &lt;em&gt;React DevTools Profiler&lt;/em&gt; 보다 &lt;em&gt;브라우저 전체 timing&lt;/em&gt; 과 함께 볼 수 있어서 &lt;em&gt;interaction → first paint&lt;/em&gt; 까지 일관된 그림. &lt;a href=&quot;https://react.dev/reference/dev-tools/react-performance-tracks&quot;&gt;&lt;code&gt;reference/dev-tools/react-performance-tracks&lt;/code&gt;&lt;/a&gt; 참고.&lt;/p&gt;
&lt;h3 id=&quot;4-partial-pre-rendering&quot;&gt;4. Partial Pre-rendering&lt;/h3&gt;
&lt;p&gt;정적 부분은 &lt;em&gt;미리 빌드해 CDN&lt;/em&gt; 에서, 동적 부분은 &lt;em&gt;런타임에 이어서&lt;/em&gt; 렌더.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 빌드 시&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;prelude&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;postponed&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; prerender&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: App &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  signal: controller.signal,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; savePostponedState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(postponed);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// prelude 를 CDN 으로&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 런타임&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; postponed&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; getPostponedState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(request);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; resumeStream&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; resume&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: App &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, postponed);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 클라이언트로 스트림&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next.js / Vercel 의 &lt;em&gt;Partial Pre-rendering (PPR)&lt;/em&gt; 이 같은 의도. React 가 &lt;em&gt;직접 지원&lt;/em&gt; 하게 됨.&lt;/p&gt;
&lt;h2 id=&quot;사라지는-것들&quot;&gt;사라지는 것들&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;forwardRef&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;deprecated&lt;/em&gt;. &lt;code&gt;ref&lt;/code&gt; prop 사용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;defaultProps&lt;/code&gt; (함수 컴포넌트)&lt;/td&gt;&lt;td&gt;&lt;em&gt;제거&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;String refs (&lt;code&gt;&amp;#x3C;X ref=&quot;foo&quot;&gt;&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;&lt;em&gt;제거&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Module Pattern Factories&lt;/td&gt;&lt;td&gt;&lt;em&gt;제거&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;createFactory&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;제거&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;react-test-renderer/shallow&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;제거&lt;/em&gt; (Enzyme 종료 흐름)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Create React App&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://react.dev/blog/2025/02/14/sunsetting-create-react-app&quot;&gt;2025-02-14 sunsetting&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;CRA 는 &lt;em&gt;공식 종료&lt;/em&gt;. 새 프로젝트는 Vite, Next.js, Parcel, RSBuild, Expo 중 선택. &lt;em&gt;프레임워크 없이 시작&lt;/em&gt; 도 공식 가이드가 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;보안-2025-12-rsc-사건&quot;&gt;보안: 2025-12 RSC 사건&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;2025-12-03&lt;/td&gt;&lt;td&gt;&lt;strong&gt;RSC 의 unauthenticated RCE&lt;/strong&gt; 공개. 19.0.1 / 19.1.2 / 19.2.1 으로 즉시 업그레이드 권장&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2025-12-11&lt;/td&gt;&lt;td&gt;동일 패치 시도 중 &lt;em&gt;추가 취약점&lt;/em&gt; 2건 (DoS, Source Code Exposure) 발견&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;em&gt;Server Components 를 &lt;em&gt;프로덕션&lt;/em&gt; 에서 쓰는 모든 곳&lt;/em&gt; 이 영향. RSC 위에 &lt;em&gt;입력 검증 / 권한 체크&lt;/em&gt; 가 반드시 들어가야 한다는 점이 다시 부각되었다.&lt;/p&gt;
&lt;h2 id=&quot;react-foundation&quot;&gt;React Foundation&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;2025-10-07&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://react.dev/blog/2025/10/07/introducing-the-react-foundation&quot;&gt;React Foundation 발표&lt;/a&gt;. &lt;em&gt;Meta 단독&lt;/em&gt; 에서 &lt;em&gt;재단 거버넌스&lt;/em&gt; 로&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2026-02-24&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://react.dev/blog/2026/02/24/the-react-foundation&quot;&gt;Linux Foundation 산하&lt;/a&gt; 정식 출범. AWS, Amazon, Callstack, Expo, Microsoft, Software Mansion, Vercel 합류&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;Meta 의 &lt;em&gt;내부 release process&lt;/em&gt; 가 &lt;em&gt;외부 stakeholder&lt;/em&gt; 와 &lt;em&gt;공동 운영&lt;/em&gt; 으로 전환. Vue 와 비슷한 &lt;em&gt;재단 거버넌스&lt;/em&gt; 모델.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;react-1718-에서-19-로&quot;&gt;React 17/18 에서 19 로&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;19 Upgrade Guide&lt;/code&gt; 의 골자:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;모든 변경&lt;/em&gt; 이 codemod 로 자동화되어 있다 (&lt;code&gt;@codemod/react-19-codemod&lt;/code&gt; 류).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Suspense 안의 사이드 이펙트&lt;/em&gt; 가 더 엄격해짐. &lt;code&gt;useEffect&lt;/code&gt; 의 클린업 누락 등이 새로 잡힘.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Strict Mode&lt;/em&gt; 가 더 강력해짐 (double-invoke 가 &lt;em&gt;effect&lt;/em&gt; 까지 확장).&lt;/li&gt;
&lt;li&gt;&lt;em&gt;peer dep&lt;/em&gt; 으로 react@18 을 요구하는 라이브러리들이 &lt;em&gt;대부분 19 호환&lt;/em&gt; 되었다. 안 된 곳만 알림.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;김신건의-현장-메모&quot;&gt;김신건의 현장 메모&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Asset Loading&lt;/em&gt; 은 SSR 페이지에서 &lt;em&gt;FOUC&lt;/em&gt; 줄이는 효과가 즉시 보인다. 라우트 진입 직전 &lt;code&gt;preinit(&apos;app.css&apos;, { as: &apos;style&apos; })&lt;/code&gt; 한 줄.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Actions&lt;/em&gt; 는 &lt;code&gt;&amp;#x3C;form action={...}&gt;&lt;/code&gt; 형태로 &lt;em&gt;progressive enhancement&lt;/em&gt; 가 그대로 가능. JS off 인 환경에서도 폼이 동작하는 것이 &lt;em&gt;오랜만에 부활한 정통&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Activity&lt;/em&gt; 는 &lt;em&gt;탭 전환이 많은&lt;/em&gt; admin 같은 곳에서 강력. &lt;em&gt;상태 보존&lt;/em&gt; 이 직접 코드보다 깔끔.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Performance Tracks&lt;/em&gt; 는 &lt;em&gt;언제 effect 가 늦게 mount 되었는지&lt;/em&gt; 가 Chrome 화면 안에서 그대로 보여서 &lt;em&gt;문제 재현&lt;/em&gt; 의 grep 가능 영역이 커졌다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[React Compiler]] (19 와 함께 권장되는 &lt;em&gt;자동 memoization&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;[[SPA Architecture]] (Server Components 와의 대비)&lt;/li&gt;
&lt;li&gt;[[Zero Downtime Deployment]] (RSC 보안 패치를 &lt;em&gt;무중단&lt;/em&gt; 으로 흘려보내는 패턴)&lt;/li&gt;
&lt;li&gt;[[JS Async Await]] (Actions 의 underlying 모델)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;</content:encoded><category>react</category><category>frontend</category><category>javascript</category><category>server-components</category><category>ssr</category><author>koa (김신건)</author></item><item><title>[Frontend] React Compiler 1.0: 자동 memoization 의 새 표준</title><link>https://shinkeonkim.com/wiki/frameworks/react-compiler/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/react-compiler/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;React Compiler&lt;/strong&gt; 는 React 컴포넌트와 hook 의 &lt;em&gt;불필요한 재계산&lt;/em&gt; 을 빌드 타임에 자동으로 잡아주는 &lt;em&gt;optimizing compiler&lt;/em&gt;. 손으로 &lt;a href=&quot;https://react.dev/reference/react/useMemo&quot;&gt;&lt;code&gt;useMemo&lt;/code&gt;&lt;/a&gt; / &lt;a href=&quot;https://react.dev/reference/react/useCallback&quot;&gt;&lt;code&gt;useCallback&lt;/code&gt;&lt;/a&gt; / &lt;a href=&quot;https://react.dev/reference/react/memo&quot;&gt;&lt;code&gt;React.memo&lt;/code&gt;&lt;/a&gt; 를 박는 대신, 컴파일러가 &lt;em&gt;값과 함수의 변경 여부&lt;/em&gt; 를 분석해 자동으로 캐싱한다.&lt;/p&gt;
&lt;p&gt;핵심 약속 4가지:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;자동 memoization&lt;/strong&gt;: 손으로 쓰지 않아도 정확하게 박힌다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;조건부 memoization&lt;/strong&gt;: &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;return&lt;/code&gt; &lt;em&gt;이후의 값&lt;/em&gt; 까지 memoize 가능 (수동 hook 으로는 불가능했던 영역).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rules of React 강제&lt;/strong&gt;: ESLint 와 함께 위반을 &lt;em&gt;조용히 잡아낸다&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;점진 도입&lt;/strong&gt;: &lt;code&gt;&apos;use memo&apos;&lt;/code&gt; 디렉티브 하나로 &lt;em&gt;컴포넌트 단위&lt;/em&gt; 부터 켤 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;v1.0&lt;/code&gt; 은 &lt;a href=&quot;https://react.dev/blog/2025/10/07/react-compiler-1&quot;&gt;2025년 10월 7일&lt;/a&gt; 출시. 거의 &lt;em&gt;10년에 걸친 컴파일러 실험&lt;/em&gt; 의 첫 안정 결과물이다.&lt;/p&gt;
&lt;h2 id=&quot;왜-컴파일러인가&quot;&gt;왜 컴파일러인가?&lt;/h2&gt;
&lt;p&gt;React 의 &lt;em&gt;멘탈 모델은 “매 렌더마다 새로 계산”&lt;/em&gt;. 깔끔하지만, &lt;em&gt;비싼 계산&lt;/em&gt; 이나 &lt;em&gt;자식의 reference equality&lt;/em&gt; 가 필요한 곳에서는 다음 같은 코드가 불가피했다:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; filtered&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useMemo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; items.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;it&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; it.tag &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; activeTag),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  [items, activeTag],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; handleClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useCallback&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onSelect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(id),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  [onSelect],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; Child&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; memo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Child&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;문제는 3가지:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;디펜던시 배열을 사람이 관리&lt;/strong&gt;: 빠뜨리면 stale, 너무 많이 넣으면 무의미.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;memoize 한 함수도 변하면 자식이 다시 렌더&lt;/strong&gt;: 한 군데서 끊기면 사슬 전체가 무너진다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useMemo&lt;/code&gt; 자체가 비용&lt;/strong&gt;: dependency 비교 + 캐시 슬롯 관리. &lt;em&gt;오히려 무겁다&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;자동화는 &lt;em&gt;비용 자체를 줄이는 것이 아니라 사람의 일관성을 보장&lt;/em&gt; 하기 위한 것. 컴파일러는 &lt;em&gt;조건부 early-return 이후&lt;/em&gt; 의 값까지 memoize 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;컴파일러는-어떻게-동작하는가&quot;&gt;컴파일러는 어떻게 동작하는가&lt;/h2&gt;
&lt;p&gt;Babel 이 만든 &lt;em&gt;Abstract Syntax Tree (AST)&lt;/em&gt; 를 받아서, &lt;em&gt;자체 IR (HIR, High-Level Intermediate Representation)&lt;/em&gt; 로 lowering. HIR 은 &lt;em&gt;Control Flow Graph (CFG)&lt;/em&gt; 기반이라 정확한 &lt;em&gt;data-flow analysis&lt;/em&gt; 와 &lt;em&gt;mutability inference&lt;/em&gt; 가 가능하다.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Source (JSX/TS)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓ Babel parse&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;AST&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓ React Compiler lowering&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;HIR (CFG-based)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓ multi-pass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├ data-flow analysis&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├ mutability inference&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ├ scope formation (memoization 단위)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    └ rules-of-react validation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓ codegen&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Optimized output (AST)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ↓ Babel print&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;JS&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 분리 덕에 다음이 가능:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;조건부 분기 이후 memoize&lt;/strong&gt;: &lt;code&gt;useMemo&lt;/code&gt; 의 &lt;em&gt;최상위&lt;/em&gt; 위치 제약이 사라진다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;세분화 (granular) scope&lt;/strong&gt;: 거대한 &lt;code&gt;useMemo&lt;/code&gt; 하나가 아니라 &lt;em&gt;필요한 값마다 따로&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;lint 가 같은 분석을 재활용&lt;/strong&gt;: &lt;code&gt;eslint-plugin-react-hooks&lt;/code&gt; 의 새 규칙들이 compiler 의 HIR 분석 결과를 그대로 쓴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;공식 예시 (조건부 return 후의 &lt;code&gt;theme&lt;/code&gt; 값까지 memoize):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ThemeProvider&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;props.children) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 컴파일러는 conditional return 이후의 값도 memoize 한다&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; theme&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; mergeTheme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(props.theme, &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(ThemeContext));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;수동 &lt;code&gt;useMemo&lt;/code&gt; 로는 &lt;em&gt;불가능한 위치&lt;/em&gt;. 컴파일러만의 강점.&lt;/p&gt;
&lt;h2 id=&quot;컴파일-모드와-디렉티브&quot;&gt;컴파일 모드와 디렉티브&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;&lt;code&gt;infer&lt;/code&gt; (기본)&lt;/td&gt;&lt;td&gt;컴포넌트 / hook 으로 &lt;em&gt;추론&lt;/em&gt; 되면 자동 컴파일&lt;/td&gt;&lt;td&gt;새 코드, 전체 도입&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;annotation&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&apos;use memo&apos;&lt;/code&gt; 가 명시된 함수만 컴파일&lt;/td&gt;&lt;td&gt;점진 도입 초기&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;all&lt;/code&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;code&gt;syntax&lt;/code&gt;&lt;/td&gt;&lt;td&gt;parse 만 하고 실제 변환은 하지 않음&lt;/td&gt;&lt;td&gt;debugging&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;함수 단위 디렉티브 (모드와 무관하게 우선):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; StableComponent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;use memo&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;      &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 이 컴포넌트는 *반드시* 컴파일&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ProblematicComponent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &apos;use no memo&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 이 컴포넌트는 *반드시* 컴파일에서 제외&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 보통 Rules of React 위반을 발견한 임시 escape&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;권장 도입 흐름:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;compilationMode: &apos;annotation&apos;&lt;/code&gt; 으로 켜고, &lt;em&gt;잘 검증된&lt;/em&gt; 컴포넌트부터 &lt;code&gt;&apos;use memo&apos;&lt;/code&gt; 추가.&lt;/li&gt;
&lt;li&gt;ESLint 새 규칙으로 &lt;em&gt;위반 코드&lt;/em&gt; 식별 + 수정.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compilationMode: &apos;infer&apos;&lt;/code&gt; 로 전환.&lt;/li&gt;
&lt;li&gt;문제 발견된 컴포넌트만 &lt;code&gt;&apos;use no memo&apos;&lt;/code&gt; 로 &lt;em&gt;임시 격리&lt;/em&gt; + 원인 수정 후 제거.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;설정-babelconfigjs&quot;&gt;설정 (&lt;code&gt;babel.config.js&lt;/code&gt;)&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// babel.config.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  plugins: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      &apos;babel-plugin-react-compiler&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        compilationMode: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;annotation&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        target: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;18&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,                  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// &apos;17&apos; | &apos;18&apos; | &apos;19&apos; (기본 &apos;19&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        panicThreshold: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;none&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,        &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// &apos;all_errors&apos; | &apos;critical_errors&apos; | &apos;none&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        logger: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;          logEvent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (event.kind &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;CompileSuccess&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;              console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Compiled:&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, filename);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        gating: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          source: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;my-feature-flags&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,           &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 모듈 경로&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          importSpecifierName: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;isCompilerEnabled&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 토글 함수&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;주요 옵션:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;target: &apos;17&apos; | &apos;18&apos;&lt;/code&gt;&lt;/strong&gt;: React 19 미만이면 &lt;a href=&quot;https://www.npmjs.com/package/react-compiler-runtime&quot;&gt;&lt;code&gt;react-compiler-runtime&lt;/code&gt;&lt;/a&gt; 을 의존성에 추가하고 target 을 명시. v1.0 의 &lt;em&gt;놀라운 약속&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;panicThreshold&lt;/code&gt;&lt;/strong&gt;: 일반적으로 &lt;code&gt;&apos;none&apos;&lt;/code&gt; 권장. 컴파일에 실패한 함수는 &lt;em&gt;그대로 두고 넘어감&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;gating&lt;/code&gt;&lt;/strong&gt;: A/B 테스트나 feature flag 로 &lt;em&gt;환경에 따라 켜고 끄기&lt;/em&gt;. Meta 가 내부적으로 쓰던 방식.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;빌드-도구-매트릭스&quot;&gt;빌드 도구 매트릭스&lt;/h2&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;빌드 도구&lt;/th&gt;&lt;th&gt;지원 상태 (2026-06)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Babel&lt;/td&gt;&lt;td&gt;1차 (&lt;code&gt;babel-plugin-react-compiler&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Vite&lt;/td&gt;&lt;td&gt;&lt;code&gt;@vitejs/plugin-react&lt;/code&gt; 의 Babel 옵션 경유, 또는 &lt;a href=&quot;https://github.com/vitejs/vite-plugin-react-compiler&quot;&gt;&lt;code&gt;vite-plugin-react-compiler&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Next.js&lt;/td&gt;&lt;td&gt;&lt;code&gt;next.config.js&lt;/code&gt; 의 &lt;code&gt;reactCompiler: true&lt;/code&gt;. 15.3.1+ 에서 swc plugin 으로 빌드 속도 크게 개선&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;swc&lt;/td&gt;&lt;td&gt;&lt;em&gt;experimental&lt;/em&gt;. &lt;a href=&quot;https://x.com/kdy1dev&quot;&gt;@kdy1dev&lt;/a&gt; 가 작업. Next.js 가 먼저 활용&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;oxc&lt;/td&gt;&lt;td&gt;&lt;em&gt;작업 중&lt;/em&gt; (&lt;a href=&quot;https://github.com/oxc-project/oxc/issues/10048&quot;&gt;oxc-project/oxc#10048&lt;/a&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rsbuild&lt;/td&gt;&lt;td&gt;1차 지원&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Expo&lt;/td&gt;&lt;td&gt;SDK 54 부터 &lt;em&gt;기본 enable&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;새 프로젝트는 &lt;code&gt;create-vite&lt;/code&gt;, &lt;code&gt;create-next-app&lt;/code&gt;, &lt;code&gt;create-expo-app&lt;/code&gt; 모두 컴파일러 &lt;em&gt;enable 옵션&lt;/em&gt; 을 제공한다. 새 코드면 거의 무조건 켜는 것이 권장.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;eslint-통합&quot;&gt;ESLint 통합&lt;/h2&gt;
&lt;p&gt;v1.0 부터 &lt;em&gt;별도 &lt;code&gt;eslint-plugin-react-compiler&lt;/code&gt; 는 폐지&lt;/em&gt;. &lt;code&gt;eslint-plugin-react-hooks&lt;/code&gt; 의 &lt;code&gt;recommended&lt;/code&gt; preset 안으로 통합되었다.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// eslint.config.js (Flat Config)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; defineConfig&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  reactHooks.configs.flat.recommended,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;새 규칙 일부:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://react.dev/reference/eslint-plugin-react-hooks/lints/set-state-in-render&quot;&gt;&lt;code&gt;set-state-in-render&lt;/code&gt;&lt;/a&gt;: 렌더 중 &lt;code&gt;setState&lt;/code&gt; 호출로 인한 &lt;em&gt;무한 루프&lt;/em&gt; 패턴.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://react.dev/reference/eslint-plugin-react-hooks/lints/set-state-in-effect&quot;&gt;&lt;code&gt;set-state-in-effect&lt;/code&gt;&lt;/a&gt;: Effect 안에서 &lt;em&gt;불필요한 state 동기화&lt;/em&gt; 를 잡음.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://react.dev/reference/eslint-plugin-react-hooks/lints/refs&quot;&gt;&lt;code&gt;refs&lt;/code&gt;&lt;/a&gt;: 렌더 중 ref 접근 같은 &lt;em&gt;unsafe&lt;/em&gt; 사용을 잡음.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;ESLint 만 켜는 것도 &lt;em&gt;유의미&lt;/em&gt;: 컴파일러를 실제로 도입하지 않아도, &lt;em&gt;Rules of React&lt;/em&gt; 위반을 컴파일러의 IR 수준 분석으로 잡아낼 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;usememo--usecallback--reactmemo-는-이제-필요-없나&quot;&gt;&lt;code&gt;useMemo&lt;/code&gt; / &lt;code&gt;useCallback&lt;/code&gt; / &lt;code&gt;React.memo&lt;/code&gt; 는 이제 필요 없나?&lt;/h2&gt;
&lt;p&gt;대부분의 경우 &lt;em&gt;그렇다&lt;/em&gt;. 컴파일러는 손으로 쓴 것보다 &lt;em&gt;더 정밀하게&lt;/em&gt; memoize 한다.&lt;/p&gt;
&lt;p&gt;다만 다음 세 경우는 &lt;em&gt;escape hatch&lt;/em&gt; 로 그대로 둘 가치가 있다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useEffect&lt;/code&gt; 의 dependency 가 &lt;em&gt;정확히&lt;/em&gt; memoize 된 값이어야 할 때&lt;/strong&gt;: 컴파일러가 &lt;em&gt;언제 reference 가 바뀌는지&lt;/em&gt; 의 의도를 표현할 곳이 코드에 명시되어 있어야 안전.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;외부 라이브러리에 &lt;em&gt;stable reference&lt;/em&gt; 를 넘겨야 할 때&lt;/strong&gt;: 라이브러리의 hook 내부가 dep array 를 보는 경우.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;이미 깔린 기존 &lt;code&gt;useMemo&lt;/code&gt; 를 제거&lt;/strong&gt;: &lt;em&gt;컴파일 결과가 미세하게 달라질 수 있어&lt;/em&gt;, 코드를 건드리지 않고 두는 편이 안전. 새 코드는 안 써도 OK.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;프로덕션-결과&quot;&gt;프로덕션 결과&lt;/h2&gt;
&lt;p&gt;Meta 가 자체 앱들에 &lt;em&gt;수년간&lt;/em&gt; 적용한 결과 (공식 글):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Meta Quest Store&lt;/strong&gt;: 초기 로드와 페이지 이동 &lt;em&gt;최대 12% 개선&lt;/em&gt;. 일부 인터랙션은 &lt;em&gt;2.5배 이상&lt;/em&gt; 빨라짐.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;메모리 사용량은 중립&lt;/strong&gt;: memoize 추가 비용이 &lt;em&gt;그만한 가치&lt;/em&gt; 가 있도록 균형.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;오픈소스 사례:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sanity Studio&lt;/strong&gt;: 거대한 &lt;em&gt;content editor&lt;/em&gt; 의 복잡한 트리에서 도입 회고 (&lt;a href=&quot;https://github.com/reactwg/react-compiler/discussions/33&quot;&gt;discussion #33&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wakelet&lt;/strong&gt;: &lt;em&gt;측정된 수치&lt;/em&gt; 와 함께 도입 회고 (&lt;a href=&quot;https://github.com/reactwg/react-compiler/discussions/52&quot;&gt;discussion #52&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;v10-까지의-타임라인&quot;&gt;v1.0 까지의 타임라인&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;2017&lt;/td&gt;&lt;td&gt;Meta 의 &lt;a href=&quot;https://github.com/facebookarchive/prepack&quot;&gt;Prepack&lt;/a&gt; 실험. 종료되었지만 &lt;em&gt;Hooks 설계의 토대&lt;/em&gt; 가 됨&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2021&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://x.com/Huxpro&quot;&gt;Xuan Huang&lt;/a&gt; 의 &lt;a href=&quot;https://www.youtube.com/watch?v=lGEMwh32soc&quot;&gt;첫 prototype 데모&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2024-02&lt;/td&gt;&lt;td&gt;React Labs 글, 본격적인 &lt;em&gt;Compiler 작업 상황&lt;/em&gt; 공개&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2024-05&lt;/td&gt;&lt;td&gt;React Conf 2024 에서 &lt;em&gt;experimental&lt;/em&gt; 공개&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2024-10&lt;/td&gt;&lt;td&gt;Beta 출시. Meta 사내 사용량 확대&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2025-10-07&lt;/td&gt;&lt;td&gt;&lt;strong&gt;v1.0 stable&lt;/strong&gt;, ESLint 통합, 새 프로젝트 기본 enable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2025-10-16&lt;/td&gt;&lt;td&gt;React Conf 2025 에서 리뷰&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;다음&quot;&gt;다음&lt;/h2&gt;
&lt;p&gt;v1.0 은 &lt;em&gt;시작점&lt;/em&gt;. 공식 글이 명시하는 방향:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;더 정밀한 memoization (현재보다 &lt;em&gt;세분화&lt;/em&gt; + &lt;em&gt;예측 가능성&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;더 많은 &lt;em&gt;Rules of React&lt;/em&gt; 를 정적으로 검증.&lt;/li&gt;
&lt;li&gt;swc / oxc 와의 통합 강화 → 빌드 속도.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Library precompilation&lt;/em&gt;: 라이브러리 저작자가 &lt;em&gt;미리 컴파일된 결과&lt;/em&gt; 를 패키지에 동봉.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[React]] (React 19/19.x 핵심 기능)&lt;/li&gt;
&lt;li&gt;[[SPA Architecture]] (memoization 의 역사적 맥락)&lt;/li&gt;
&lt;li&gt;[[JS Closure]] (memoize 가 의존하는 어휘 환경)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;</content:encoded><category>react</category><category>compiler</category><category>performance</category><category>memoization</category><category>frontend</category><author>koa (김신건)</author></item><item><title>[React] Component Composition: Props, Children, Render Prop, HOC, Compound</title><link>https://shinkeonkim.com/wiki/frameworks/react-component-composition/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/react-component-composition/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Component Composition&lt;/strong&gt; 은 &lt;em&gt;작은 컴포넌트를 조합&lt;/em&gt; 해 &lt;em&gt;큰 UI 를 만드는 React 의 핵심 빌드 블록&lt;/em&gt;. 객체지향의 &lt;em&gt;상속 (inheritance)&lt;/em&gt; 대신 &lt;em&gt;Composition&lt;/em&gt; 이 &lt;em&gt;React 의 정통&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
React 의 공식 입장은 명확하다: &lt;em&gt;&lt;strong&gt;“Composition over Inheritance”&lt;/strong&gt;&lt;/em&gt;. &lt;em&gt;컴포넌트 상속 (&lt;code&gt;extends Modal&lt;/code&gt;)&lt;/em&gt; 같은 패턴은 &lt;em&gt;처음부터 권장하지 않는다&lt;/em&gt;. 모든 재사용은 &lt;em&gt;props / children / Hook / Composition&lt;/em&gt; 으로.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;stateless-vs-stateful&quot;&gt;Stateless vs Stateful&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;위 애니메이션은 stateless / stateful 시스템의 일반 직관. React 도 &lt;em&gt;상태 없는 컴포넌트 (presentational)&lt;/em&gt; 와 &lt;em&gt;상태 있는 컴포넌트 (container)&lt;/em&gt; 의 분리가 &lt;em&gt;대규모 코드 정리의 기본&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Stateless (presentational)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Card&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;card&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{title}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{body}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Stateful (container)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; CardWithLike&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;liked&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setLiked&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Card&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setLiked&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;liked)}&gt;Toggle&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;패턴-카탈로그&quot;&gt;패턴 카탈로그&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Comp[Composition 패턴]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Comp --&gt; P[Props]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Comp --&gt; C[Children]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Comp --&gt; RP[Render Prop]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Comp --&gt; HOC[HOC]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Comp --&gt; CC[Compound Component]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Comp --&gt; Slots[Slots / Portal]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Comp --&gt; Ctx[Context]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Comp --&gt; Hook[Custom Hook]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&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;Props&lt;/td&gt;&lt;td&gt;값 전달&lt;/td&gt;&lt;td&gt;가장 기본&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Children&lt;/td&gt;&lt;td&gt;임의 JSX 임베드&lt;/td&gt;&lt;td&gt;Card, Layout, Modal&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Render Prop&lt;/td&gt;&lt;td&gt;자식 &lt;em&gt;렌더 함수&lt;/em&gt;&lt;/td&gt;&lt;td&gt;데이터 + UI 분리 (옛 패턴)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HOC&lt;/td&gt;&lt;td&gt;컴포넌트 &lt;em&gt;감싸기&lt;/em&gt;&lt;/td&gt;&lt;td&gt;인증, 권한, 로깅 (옛 패턴)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Compound Component&lt;/td&gt;&lt;td&gt;&lt;em&gt;짝 컴포넌트&lt;/em&gt; (&lt;code&gt;Tabs.Tab&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Tabs, Select, Disclosure&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Slot&lt;/td&gt;&lt;td&gt;특정 &lt;em&gt;지정 영역&lt;/em&gt; 에 임베드&lt;/td&gt;&lt;td&gt;Layout, Drawer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Portal&lt;/td&gt;&lt;td&gt;DOM 트리 &lt;em&gt;밖&lt;/em&gt; 으로 렌더&lt;/td&gt;&lt;td&gt;Modal, Tooltip, Toast&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Context&lt;/td&gt;&lt;td&gt;트리 전체에 &lt;em&gt;값 공유&lt;/em&gt;&lt;/td&gt;&lt;td&gt;Theme, Auth, i18n&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Custom Hook&lt;/td&gt;&lt;td&gt;&lt;em&gt;동작&lt;/em&gt; 만 추출&lt;/td&gt;&lt;td&gt;API fetch, 폼 상태&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;1-props-가장-기본&quot;&gt;1. Props: 가장 기본&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Avatar&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;size&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;md&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;alt&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{src} &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;alt&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{alt} &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`avatar avatar-${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;size&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;} /&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Avatar &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
React 19+ 에서는 &lt;code&gt;ref&lt;/code&gt; 도 &lt;em&gt;그냥 prop&lt;/em&gt;. &lt;code&gt;forwardRef&lt;/code&gt; 불필요. 기본 props 의 &lt;em&gt;지위&lt;/em&gt; 가 더 강력해졌다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;2-children-임의-jsx-임베드&quot;&gt;2. Children: 임의 JSX 임베드&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Card&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;card&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{title}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;card-body&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{children}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Card &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Anything I want.&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Click me&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“내가 디자인한 슬롯에 너가 무엇을 넣을지 결정해”&lt;/em&gt;. &lt;em&gt;컴포지션의 가장 강력한 한 줄&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;다중-children-명명-slot&quot;&gt;다중 children: 명명 slot&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Layout&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;header&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;sidebar&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;footer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;layout&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;header&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{header}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;header&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;aside&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{sidebar}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;aside&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{children}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;footer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{footer}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;footer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Layout &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  sidebar&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&amp;#x3C;!-- mdx-component: Sidebar &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  footer&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&amp;#x3C;!-- mdx-component: Footer &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Article &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Layout&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;3-render-prop-옛-패턴&quot;&gt;3. Render Prop (옛 패턴)&lt;/h2&gt;
&lt;p&gt;자식이 &lt;em&gt;렌더 함수&lt;/em&gt;. 부모가 &lt;em&gt;데이터&lt;/em&gt; 를 만들고 자식이 &lt;em&gt;UI&lt;/em&gt; 를 결정.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: MouseTracker &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  {({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Mouse at ({x}, {y})&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MouseTracker&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;pos&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setPos&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ x: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, y: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; handler&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setPos&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ x: e.clientX, y: e.clientY });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    window.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;mousemove&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, handler);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; window.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;removeEventListener&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;mousemove&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, handler);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, []);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(pos);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
Render Prop 는 &lt;em&gt;Hook 이전 시대의 패턴&lt;/em&gt;. 현재는 &lt;em&gt;custom Hook (&lt;code&gt;useMouse&lt;/code&gt;)&lt;/em&gt; 이 &lt;em&gt;더 깔끔&lt;/em&gt;. 새 코드에서는 &lt;em&gt;피하는 게&lt;/em&gt; 표준.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 같은 의도, 현대적 형태&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useMouse&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;pos&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setPos&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ x: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, y: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* same */&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }, []);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; pos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Tracker&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;x&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;y&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useMouse&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Mouse at ({x}, {y})&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;4-hoc-higher-order-component-옛-패턴&quot;&gt;4. HOC (Higher Order Component) (옛 패턴)&lt;/h2&gt;
&lt;p&gt;컴포넌트를 &lt;em&gt;함수에 넣고 새 컴포넌트&lt;/em&gt; 를 받는다.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; withAuth&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;Component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; AuthGuarded&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; user&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;user) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: Login &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: Component &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ProtectedPage&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; withAuth&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(Page);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
HOC 도 &lt;em&gt;Hook 이전의 패턴&lt;/em&gt;. 현대적 대안:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인증 가드 → &lt;em&gt;Route 레벨&lt;/em&gt; 또는 &lt;em&gt;layout 컴포넌트&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;데이터 주입 → &lt;em&gt;Hook 또는 Context&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;prop 추가 → &lt;em&gt;Composition + Hook&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;5-compound-component-짝-컴포넌트&quot;&gt;5. Compound Component: 짝 컴포넌트&lt;/h2&gt;
&lt;p&gt;부모와 &lt;em&gt;지정된 자식들&lt;/em&gt; 이 &lt;em&gt;암묵적 계약&lt;/em&gt;. 사용자가 &lt;em&gt;자식의 배치만&lt;/em&gt; 결정.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Tabs &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.List&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.Trigger&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;profile&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Profile&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.Trigger&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.Trigger&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;settings&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Settings&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.Trigger&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.List&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.Panel&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;profile&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&amp;#x3C;!-- mdx-component: Profile --&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.Panel&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.Panel&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;settings&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&amp;#x3C;!-- mdx-component: Settings --&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Tabs.Panel&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 구현 (Context 로 짝 묶기)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; TabsContext&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; createContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Tabs&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;defaultValue&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;active&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setActive&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(defaultValue);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; TabsContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Tabs.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;tabs-list&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{children}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Tabs.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Trigger&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;active&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setActive&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; use&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(TabsContext);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; data-active&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{active &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; value} &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setActive&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(value)}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {children}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Tabs.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;Panel&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;active&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; use&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(TabsContext);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; active &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; value &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{children}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt; &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
라이브러리 (Radix, Reach UI, Headless UI, shadcn/ui) 가 &lt;em&gt;모두&lt;/em&gt; 이 패턴을 사용. &lt;em&gt;flexible + 타입 안전 + 자기 documentation&lt;/em&gt;. 모던 React 의 &lt;em&gt;주력 패턴&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;6-slot--portal&quot;&gt;6. Slot / Portal&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 트리 안의 *지정 위치* 에 임베드&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Modal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; createPortal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;modal-backdrop&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;modal-content&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{children}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    document.body          &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ← DOM 트리 *밖* 의 &amp;#x3C;body&gt; 로&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;em&gt;Portal 은 DOM 트리는 다르지만 React 트리는 같다&lt;/em&gt;. &lt;em&gt;Context 가 portal 자식까지 전파&lt;/em&gt;. &lt;em&gt;event bubbling 도 React 트리 따라 흐름&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;7-context-트리-전체에-값-공유&quot;&gt;7. Context: 트리 전체에 값 공유&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; createContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;light&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; App&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setTheme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;light&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;   // ← React 19+ &amp;#x3C;Context.Provider&gt; 줄임&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Layout&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &amp;#x3C;!-- mdx-component: Article --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Layout&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Article&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; theme&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; use&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(ThemeContext);   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// React 19 의 use() API&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{theme}&gt;...&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
Context 는 &lt;em&gt;상위 값이 바뀌면 &lt;em&gt;Context 를 읽는 모든 자손이 재렌더&lt;/em&gt;&lt;/em&gt;. &lt;em&gt;자주 변하는 값&lt;/em&gt; 을 Context 에 두면 &lt;em&gt;성능 사고&lt;/em&gt; 의 원인. &lt;em&gt;theme / auth&lt;/em&gt; 처럼 &lt;em&gt;드물게 변하는 값&lt;/em&gt; 에만.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;8-custom-hook-로직만-추출&quot;&gt;8. Custom Hook: 로직만 추출&lt;/h2&gt;
&lt;p&gt;UI 가 없는 &lt;em&gt;재사용 가능한 동작&lt;/em&gt;. &lt;em&gt;Composition 의 비-시각적 형태&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useFetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setData&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;loading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setLoading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setError&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; alive &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(url)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; r.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;d&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; alive &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setData&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(d))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; alive &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setError&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(e))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;finally&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; alive &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setLoading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { alive &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, [url]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { data, loading, error };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; UserCard&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;data&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;loading&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useFetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`/api/users/${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (loading) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: Skeleton &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (error) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: Error &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: Card &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{user.name};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Custom Hook 의 이름&lt;/em&gt; 은 &lt;em&gt;&lt;code&gt;use&lt;/code&gt; 로 시작&lt;/em&gt;. ESLint 가 &lt;em&gt;Hook 규칙 (최상위 호출, 컴포넌트 내부) 을 강제&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;패턴-선택-가이드&quot;&gt;패턴 선택 가이드&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1{재사용 대상}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|값 / 데이터 전달| Props[Props]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|자식 JSX 자유| Children[children]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|레이아웃 슬롯| NamedSlots[Named props as JSX]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|로직만| Hook[Custom Hook]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|UI + 동작 묶음| Q2{관련 컴포넌트 여러개?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|예| Compound[Compound Component]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|아니오| Standalone[일반 컴포넌트]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|DOM 밖 렌더| Portal[Portal]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|트리 전체 값 공유| Context[Context]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;inheritance-가-왜-안-좋은가&quot;&gt;Inheritance 가 &lt;em&gt;왜 안 좋은가&lt;/em&gt;&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 안티패턴&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Modal&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; BaseModal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // BaseModal 의 protected method 를 호출?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 부모-자식의 의미적 결합이 *코드를 강하게 묶음*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 정통: composition&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Modal&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: BaseModal &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{children};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;항목&lt;/th&gt;&lt;th&gt;Inheritance&lt;/th&gt;&lt;th&gt;Composition&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;em&gt;강함&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;약함&lt;/em&gt; (props 만)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;테스트 단위&lt;/td&gt;&lt;td&gt;&lt;em&gt;상속 체인 전체&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;조립 단위&lt;/em&gt; 별&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;재사용 단위&lt;/td&gt;&lt;td&gt;&lt;em&gt;클래스&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;컴포넌트 + Hook&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;변경 영향 범위&lt;/td&gt;&lt;td&gt;&lt;em&gt;모든 자식&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;해당 인스턴스&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;React 의 모든 공식 문서가 inheritance 를 권장하지 않는다&lt;/em&gt;. &lt;em&gt;예외가 거의 없는&lt;/em&gt; 원칙.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;컴포넌트-계층의-얕음&quot;&gt;컴포넌트 계층의 &lt;em&gt;얕음&lt;/em&gt;&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph Deep[깊은 계층, 안 좋음]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        D1[App] --&gt; D2[Layout]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        D2 --&gt; D3[Section]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        D3 --&gt; D4[CardGroup]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        D4 --&gt; D5[Card]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        D5 --&gt; D6[CardBody]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        D6 --&gt; D7[CardText]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    subgraph Flat[얕은 계층, 좋음]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        F1[App] --&gt; F2[Layout]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        F2 --&gt; F3[Section]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        F3 --&gt; F4[Card]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;계층이 &lt;em&gt;깊으면&lt;/em&gt; prop drilling 이 &lt;em&gt;고통&lt;/em&gt;. &lt;em&gt;Composition + children + Context&lt;/em&gt; 로 &lt;em&gt;얕게 유지&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;19-의-새-기능들과-composition&quot;&gt;19+ 의 새 기능들과 composition&lt;/h2&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;React 19+ 기능&lt;/th&gt;&lt;th&gt;composition 에 미친 영향&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;ref&lt;/code&gt; as prop&lt;/td&gt;&lt;td&gt;&lt;code&gt;forwardRef&lt;/code&gt; 의 &lt;em&gt;상속 같은 wrapping&lt;/em&gt; 사라짐&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Server Components&lt;/td&gt;&lt;td&gt;&lt;em&gt;서버 데이터 + 클라이언트 UI&lt;/em&gt; 의 &lt;em&gt;깔끔한 분리&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;use()&lt;/code&gt; API&lt;/td&gt;&lt;td&gt;&lt;em&gt;Promise / Context&lt;/em&gt; 의 &lt;em&gt;조건부 사용&lt;/em&gt; 가능&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;#x3C;Activity&gt;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;상태 보존 + effect 분리&lt;/em&gt; 의 새 컴포지션 단위&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Document Metadata&lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;#x3C;title&gt;&lt;/code&gt;, &lt;code&gt;&amp;#x3C;meta&gt;&lt;/code&gt; 가 &lt;em&gt;컴포넌트 내부에서&lt;/em&gt; 동작&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;HOC 남용&lt;/strong&gt; = 디버깅 hell (트리에 &lt;code&gt;withX(withY(withZ(Component)))&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Render Prop 남용&lt;/strong&gt; = nested function 의 무한 들여쓰기. Custom Hook 으로.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context 의 &lt;em&gt;모든 값을 한 곳&lt;/em&gt; 에&lt;/strong&gt; = 자주 변하는 값이 &lt;em&gt;모든 자식 재렌더&lt;/em&gt;. &lt;em&gt;분리된 Context&lt;/em&gt; 로.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;부모가 자식의 내부 state 를 직접 조작&lt;/strong&gt;&lt;/em&gt; = ref + imperative handle. &lt;em&gt;피해야&lt;/em&gt; 한다. &lt;em&gt;state lift up&lt;/em&gt; 또는 &lt;em&gt;event 전달&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compound Component 의 &lt;em&gt;암묵 계약&lt;/em&gt;&lt;/strong&gt; = 잘못된 자식이 들어오면 &lt;em&gt;조용히 깨짐&lt;/em&gt;. &lt;em&gt;Context 사용 + 자식 검증&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;김신certificate의-현장-메모&quot;&gt;김신certificate의 현장 메모&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Compound Component&lt;/em&gt; 가 &lt;em&gt;모던 React 의 가장 강력한 무기&lt;/em&gt;. Radix / Headless UI 가 모두 이 패턴 → &lt;em&gt;Tailwind + Compound&lt;/em&gt; 가 &lt;em&gt;오늘날의 표준 디자인 시스템 빌드 블록&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Custom Hook 의 &lt;em&gt;시그니처&lt;/em&gt; 가 컴포넌트보다 더 신중&lt;/em&gt; 해야 한다. &lt;em&gt;반환 모양 (object vs tuple)&lt;/em&gt;, &lt;em&gt;의존성 입력 방식&lt;/em&gt;, &lt;em&gt;에러 처리 일관성&lt;/em&gt; 이 &lt;em&gt;팀 전체&lt;/em&gt; 의 사용성을 결정.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Children 만으로도 거의 모든 합성&lt;/em&gt; 이 가능. &lt;em&gt;팀에 &lt;em&gt;children 으로 끝낼 수 있는 곳에 HOC / render prop 도입 금지&lt;/em&gt; 라는 규칙&lt;/em&gt; 만 있으면 &lt;em&gt;복잡도 폭증을 막을 수&lt;/em&gt; 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context 의 읽기 / 쓰기 분리&lt;/strong&gt; (&lt;code&gt;useTheme&lt;/code&gt; / &lt;code&gt;useThemeUpdate&lt;/code&gt;) 가 &lt;em&gt;자주 변하는 값의 성능 사고&lt;/em&gt; 를 막는 &lt;em&gt;간단한 패턴&lt;/em&gt;. 별도 라이브러리 없이 &lt;em&gt;Context 두 개&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[React]] (19.x 전반)&lt;/li&gt;
&lt;li&gt;[[React useEffect]] (effect 도 composition 단위)&lt;/li&gt;
&lt;li&gt;[[React useMemo useCallback]] (composition + memo)&lt;/li&gt;
&lt;li&gt;[[React Lifecycle]] (mount / Activity / RSC)&lt;/li&gt;
&lt;li&gt;[[React Compiler]] (자동 memo 시대의 composition)&lt;/li&gt;
&lt;li&gt;[[SPA Architecture]] (얕은 계층과 라우팅)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;공식: &lt;a href=&quot;https://react.dev/learn/passing-props-to-a-component&quot;&gt;Passing Props&lt;/a&gt;, &lt;a href=&quot;https://react.dev/learn/passing-data-deeply-with-context&quot;&gt;Children&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;레거시 (참고): &lt;a href=&quot;https://legacy.reactjs.org/docs/composition-vs-inheritance.html&quot;&gt;Composition vs Inheritance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Compound: &lt;a href=&quot;https://kentcdodds.com/blog/compound-components-with-react-hooks&quot;&gt;Kent C. Dodds&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>react</category><category>component</category><category>composition</category><category>pattern</category><category>hoc</category><category>render-prop</category><category>compound</category><author>koa (김신건)</author></item><item><title>[React] Lifecycle: Mount / Update / Unmount, StrictMode, Activity, RSC</title><link>https://shinkeonkim.com/wiki/frameworks/react-lifecycle/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/react-lifecycle/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;React Lifecycle&lt;/strong&gt; 은 &lt;em&gt;컴포넌트의 생성 → 갱신 → 제거&lt;/em&gt; 의 흐름. 함수 컴포넌트에서는 &lt;em&gt;클래스의 lifecycle method 가 사라지고&lt;/em&gt; &lt;em&gt;render + effect + cleanup&lt;/em&gt; 의 조합으로 대체된다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
함수 컴포넌트 시대의 멘탈 모델: **&lt;em&gt;“라이프사이클” 이 아니라 “동기화의 한 사이클”&lt;/em&gt;. &lt;em&gt;render&lt;/em&gt; (UI 계산) + &lt;em&gt;effect&lt;/em&gt; (외부 시스템 동기화) + &lt;em&gt;cleanup&lt;/em&gt; (정리). 이 3 단계가 &lt;em&gt;mount / update / unmount&lt;/em&gt; 시점마다 &lt;em&gt;다른 조합&lt;/em&gt; 으로 일어난다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;일반-라이프사이클의-직관&quot;&gt;일반 라이프사이클의 직관&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Spring Bean 의 라이프사이클 직관. React 도 &lt;em&gt;생성 → 의존성/props 주입 → 초기화 콜백 (effect setup) → 사용 → 소멸 콜백 (cleanup) → 제거&lt;/em&gt; 의 거의 같은 흐름.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;함수-컴포넌트의-3-단계&quot;&gt;함수 컴포넌트의 3 단계&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;stateDiagram-v2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    [*] --&gt; Mount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Mount --&gt; Mounted: render + effect setup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Mounted --&gt; Updating: state/props 변경&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Updating --&gt; Mounted: render + cleanup (옛 effect) + effect setup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Mounted --&gt; Unmount: 부모가 제거&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Unmount --&gt; [*]: cleanup (마지막)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;1-mount&quot;&gt;1. Mount&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. constructor 같은 *초기 state 설정* (useState, useReducer 초기값)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. render() = JSX 반환&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. DOM 에 commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. useLayoutEffect 실행 (동기)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5. paint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;6. useEffect 실행 (비동기)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-update&quot;&gt;2. Update&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. state / props 변경 트리거&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. render() = 새 JSX 계산 (Reconciliation)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. DOM diff 후 commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. useLayoutEffect cleanup → 새 useLayoutEffect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5. paint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;6. useEffect cleanup → 새 useEffect&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-unmount&quot;&gt;3. Unmount&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. 부모 트리에서 제거 결정&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. useEffect cleanup 호출 (마지막)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. useLayoutEffect cleanup 호출&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. DOM 제거&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
Cleanup 은 &lt;em&gt;옛 effect 를 지우고 새 effect 를 시작하기 직전&lt;/em&gt;. &lt;em&gt;언마운트 시점&lt;/em&gt; 만의 일이 아니다. 모든 &lt;em&gt;update&lt;/em&gt; 시 &lt;em&gt;cleanup + setup&lt;/em&gt; 이 &lt;em&gt;짝&lt;/em&gt; 으로 일어난다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;클래스-vs-함수-컴포넌트-매핑&quot;&gt;클래스 vs 함수 컴포넌트 매핑&lt;/h2&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;클래스 (legacy)&lt;/th&gt;&lt;th&gt;함수 (현재)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;constructor&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useReducer&lt;/code&gt; 초기값&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;componentDidMount&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;useEffect(() =&gt; {}, [])&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;componentDidUpdate&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;useEffect(() =&gt; {}, [deps])&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;componentWillUnmount&lt;/code&gt;&lt;/td&gt;&lt;td&gt;cleanup 함수 &lt;code&gt;return () =&gt; {}&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;shouldComponentUpdate&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;React.memo&lt;/code&gt; 의 비교 함수&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;getDerivedStateFromProps&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;없음&lt;/em&gt; (props 로부터 유도 = render 중 계산)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;getSnapshotBeforeUpdate&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;useLayoutEffect&lt;/code&gt; cleanup 의 &lt;em&gt;직전 측정&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;componentDidCatch&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;ErrorBoundary&lt;/code&gt; (여전히 클래스 필요)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;em&gt;ErrorBoundary&lt;/em&gt; 는 &lt;em&gt;2026 시점에도 여전히 클래스 컴포넌트 필요&lt;/em&gt;. &lt;code&gt;componentDidCatch&lt;/code&gt; 와 &lt;code&gt;getDerivedStateFromError&lt;/code&gt; 가 &lt;em&gt;함수 API 로 옮겨지지 않았다&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;strict-mode-의-double-invoke&quot;&gt;Strict Mode 의 double invoke&lt;/h2&gt;
&lt;p&gt;React 18+ 의 개발 모드에서 &lt;em&gt;render + effect 가 2 번 실행&lt;/em&gt; 된다. &lt;em&gt;cleanup 의 정합성 검증&lt;/em&gt; 도구.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant SM as StrictMode (dev)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant C as Component&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SM-&gt;&gt;C: mount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;C: render() (1번)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;C: render() (2번 - 같은 결과 검증)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;C: setup (1번)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SM-&gt;&gt;C: simulated unmount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;C: cleanup (1번)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    C-&gt;&gt;C: setup (2번)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over C: 진짜 사용 시작&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;무엇이-잡히는가&quot;&gt;무엇이 잡히는가&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;순수하지 않은 render&lt;/strong&gt;: 두 번 호출에서 &lt;em&gt;다른 결과&lt;/em&gt; 가 나오면 &lt;em&gt;Side effect in render&lt;/em&gt; 위반.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;불완전한 cleanup&lt;/strong&gt;: setup 에서 &lt;em&gt;전역 카운터 ++&lt;/em&gt;, cleanup 에서 &lt;em&gt;—&lt;/em&gt; 안 하면 &lt;em&gt;2 가 누적&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stale closure 오용&lt;/strong&gt;: 첫 render 의 closure 가 두 번째에서 &lt;em&gt;예상과 다르게 동작&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;production-에서는&quot;&gt;Production 에서는?&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: StrictMode &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: App &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;→ 개발 모드 &lt;em&gt;only&lt;/em&gt;. 프로덕션 번들에서는 &lt;em&gt;single invoke&lt;/em&gt;. 즉 &lt;em&gt;개발 시 잡지 못한 버그가 프로덕션에서 발현될 수 있다&lt;/em&gt;. &lt;em&gt;Strict Mode 의 모든 경고를 진지하게&lt;/em&gt; 받아들이는 게 &lt;em&gt;원칙&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;react-192-의-&quot;&gt;React 19.2 의 `&lt;!-- mdx-component: Activity --&gt;&lt;/h2&gt;
&lt;p&gt;` 모드&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;#x3C;Activity mode=&quot;hidden | visible&quot;&gt;&lt;/code&gt; 가 lifecycle 의 &lt;em&gt;4번째 상태&lt;/em&gt; 를 추가한다.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;stateDiagram-v2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    [*] --&gt; Mounted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Mounted --&gt; Hidden: Activity mode=&apos;hidden&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Hidden --&gt; Mounted: Activity mode=&apos;visible&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Hidden --&gt; Unmounted: 트리에서 제거&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Mounted --&gt; Unmounted: 트리에서 제거&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Unmounted --&gt; [*]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;상태&lt;/th&gt;&lt;th&gt;render&lt;/th&gt;&lt;th&gt;effect&lt;/th&gt;&lt;th&gt;DOM&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Mounted&lt;/code&gt; (visible)&lt;/td&gt;&lt;td&gt;일반&lt;/td&gt;&lt;td&gt;mounted&lt;/td&gt;&lt;td&gt;live&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Hidden&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;유지&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;cleanup&lt;/em&gt; (unmount 처럼)&lt;/td&gt;&lt;td&gt;&lt;em&gt;유지 (display:none)&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Unmounted&lt;/code&gt;&lt;/td&gt;&lt;td&gt;없음&lt;/td&gt;&lt;td&gt;cleanup&lt;/td&gt;&lt;td&gt;제거&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Activity&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; mode&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{isVisible &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;visible&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;hidden&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;!-- mdx-component: Page --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;em&gt;탭 전환 / 라우터&lt;/em&gt; 에서 &lt;em&gt;상태 보존이 필요&lt;/em&gt; 하지만 &lt;em&gt;effect 는 멈춰야&lt;/em&gt; 할 때. 옛 코드에서는 &lt;em&gt;직접 보관하다가 다시 mount&lt;/em&gt; 하는 &lt;em&gt;복잡한 패턴&lt;/em&gt; 이 &lt;em&gt;Activity 한 줄&lt;/em&gt; 로 대체.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;server-components-rsc-의-lifecycle&quot;&gt;Server Components (RSC) 의 lifecycle&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&apos;use server&apos;&lt;/code&gt; 가 박힌 서버 컴포넌트는 &lt;em&gt;서버에서 1번 render&lt;/em&gt;, &lt;em&gt;결과만 클라이언트로&lt;/em&gt;. &lt;em&gt;클라이언트에서 effect 없음&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Client&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant DB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client-&gt;&gt;Server: HTTP request&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server-&gt;&gt;DB: fetch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    DB--&gt;&gt;Server: data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server-&gt;&gt;Server: ServerComponent.render() (1번)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Server--&gt;&gt;Client: RSC payload (직렬화된 트리)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client-&gt;&gt;Client: 클라이언트 트리에 hydrate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Client: 클라이언트 컴포넌트 (&apos;use client&apos;) 만 effect 실행&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;csr--ssr--ssg--rsc-모드-비교&quot;&gt;CSR / SSR / SSG / RSC 모드 비교&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;4 가지 렌더링 모드의 시각. &lt;em&gt;각 모드별 lifecycle 의 위치&lt;/em&gt; 가 어떻게 다른지 직관.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;hydration-서버-html-과-클라이언트의-합체&quot;&gt;Hydration: 서버 HTML 과 클라이언트의 합체&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant SSR as Server-Side Render&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Browser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Client as Client React&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SSR-&gt;&gt;SSR: render() → HTML&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SSR--&gt;&gt;Browser: HTML (정적, no interactivity)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Browser-&gt;&gt;Browser: HTML 파싱 + 보여줌 (interactive 0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Browser-&gt;&gt;Client: JS 번들 load&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client-&gt;&gt;Client: hydrate(root, &amp;#x3C;!-- mdx-component: App --&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client-&gt;&gt;Client: DOM 트리와 React tree 매칭&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client-&gt;&gt;Client: event listener 부착&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Browser: 이제 interactive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Client-&gt;&gt;Client: useEffect 실행 (mount)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;hydration-mismatch&quot;&gt;Hydration mismatch&lt;/h3&gt;
&lt;p&gt;서버 render 결과와 클라이언트 첫 render 결과가 &lt;em&gt;다르면&lt;/em&gt; 에러:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ 서버와 클라이언트가 다른 시간&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Now&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Date&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;().&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;toISOString&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ 서버 render 결과 그대로 사용, 클라이언트만 갱신&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Now&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;now&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setNow&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setNow&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Date&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()), []);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{now?.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;toISOString&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;??&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;...&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;browser-렌더링-vs-react-렌더링&quot;&gt;Browser 렌더링 vs React 렌더링&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;브라우저 자체의 &lt;em&gt;DOM → Layout → Paint → Composite&lt;/em&gt; 흐름. React 의 render → commit → effect 가 &lt;em&gt;어디에 끼는지&lt;/em&gt; 의 큰 그림.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;render 에서 사이드 이펙트&lt;/strong&gt; = &lt;code&gt;useEffect&lt;/code&gt; 로 옮기거나 &lt;em&gt;이벤트 핸들러&lt;/em&gt; 로. render 는 &lt;em&gt;순수해야&lt;/em&gt; 한다 (StrictMode 가 강제로 검증).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;setState 가 즉시 반영되지 않음&lt;/strong&gt; = 같은 render 사이클 안에서는 &lt;em&gt;옛 값&lt;/em&gt;. &lt;em&gt;batched&lt;/em&gt; update.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concurrent rendering 의 &lt;em&gt;interrupted render&lt;/em&gt;&lt;/strong&gt; = render 가 &lt;em&gt;중간에 버려질 수 있다&lt;/em&gt;. &lt;em&gt;render 안에서 외부 state 변경 금지&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strict Mode 의 &lt;em&gt;double invoke 를 production 으로 오해&lt;/em&gt;&lt;/strong&gt; = 개발 도구. 프로덕션에서는 일어나지 않는다. 그러나 &lt;em&gt;그 경고를 무시하면&lt;/em&gt; 다른 버그.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;함수-컴포넌트의-호출-횟수-헷갈림&quot;&gt;함수 컴포넌트의 &lt;em&gt;호출 횟수&lt;/em&gt; 헷갈림&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Mount[mount] --&gt;|&quot;render() 1번&quot;| Render1[render]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Render1 --&gt;|Strict Mode dev| Render2[render 2번]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Render1 --&gt;|commit + effect| Effect1[setup]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Update1[state change] --&gt;|&quot;render() 1번&quot;| Render3[render]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Render3 --&gt;|commit| Cleanup1[cleanup]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Cleanup1 --&gt; Effect2[setup]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;시점&lt;/th&gt;&lt;th&gt;render 호출&lt;/th&gt;&lt;th&gt;effect setup&lt;/th&gt;&lt;th&gt;effect cleanup&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;첫 mount (prod)&lt;/td&gt;&lt;td&gt;1번&lt;/td&gt;&lt;td&gt;1번&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;첫 mount (StrictMode dev)&lt;/td&gt;&lt;td&gt;2번&lt;/td&gt;&lt;td&gt;2번 (1번 + 시뮬레이션 cleanup + 1번)&lt;/td&gt;&lt;td&gt;1번&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;1번 (deps 변경 시)&lt;/td&gt;&lt;td&gt;1번 (deps 변경 시)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;unmount&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1번 (마지막)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;컴포넌트-수명-추적&quot;&gt;컴포넌트 &lt;em&gt;수명&lt;/em&gt; 추적&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; mountCount&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useRef&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; renderCount&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useRef&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  renderCount.current &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    mountCount.current &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`mounted: ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;mountCount&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;current&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} times`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;unmounted&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, []);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`rendered: ${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;renderCount&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;current&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;} times`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;개발 시 &lt;em&gt;얼마나 자주 mount / re-render&lt;/em&gt; 되는지 측정. &lt;em&gt;prop 변경 → re-render&lt;/em&gt; 와 &lt;em&gt;key 변경 → unmount + mount&lt;/em&gt; 의 &lt;em&gt;큰 차이&lt;/em&gt; 를 직관으로.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;key-prop-의-lifecycle-영향&quot;&gt;Key prop 의 lifecycle 영향&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 같은 위치, key 같음 → React 가 *재사용* (update)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Component &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Component &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 같은 위치, key 다름 → *unmount + remount* (state 리셋)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Component &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;em&gt;key 가 바뀌면&lt;/em&gt; React 는 &lt;em&gt;완전히 새 컴포넌트&lt;/em&gt; 로 본다. &lt;em&gt;state 가 모두 리셋&lt;/em&gt;. &lt;em&gt;prop 변경 시 state reset&lt;/em&gt; 패턴은 &lt;em&gt;useEffect 보다 key&lt;/em&gt; 가 정석.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;192-activity--strict-mode--rsc-정리&quot;&gt;19.2 Activity + Strict Mode + RSC: 정리&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;Strict Mode&lt;/td&gt;&lt;td&gt;16.3&lt;/td&gt;&lt;td&gt;cleanup 검증 (double invoke 18+)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Server Components&lt;/td&gt;&lt;td&gt;18 / 19 정식&lt;/td&gt;&lt;td&gt;서버에서만 render (effect 없음)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Concurrent Rendering&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt;render 중단/재시작&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Activity&lt;/td&gt;&lt;td&gt;19.2&lt;/td&gt;&lt;td&gt;hidden / visible 의 4번째 상태&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;useEffectEvent&lt;/td&gt;&lt;td&gt;19.2&lt;/td&gt;&lt;td&gt;effect dep 분리&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;김신건의-현장-메모&quot;&gt;김신건의 현장 메모&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;함수 컴포넌트 시대에 &lt;em&gt;componentDidMount&lt;/em&gt; 라는 표현을 &lt;em&gt;팀에서 쓰지 않도록&lt;/em&gt; 정리한 게 &lt;em&gt;멘탈 모델 교체에 도움&lt;/em&gt;. &lt;em&gt;“mount effect” / “update effect” / “cleanup”&lt;/em&gt; 의 &lt;em&gt;함수 시대 어휘&lt;/em&gt; 로.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Strict Mode 켠 직후&lt;/em&gt; 가장 자주 본 버그: &lt;em&gt;전역 store 구독&lt;/em&gt; 의 &lt;em&gt;cleanup 누락&lt;/em&gt;. 두 번 setup → &lt;em&gt;구독 2 개&lt;/em&gt; → &lt;em&gt;알림 2 번&lt;/em&gt;. Strict Mode 가 &lt;em&gt;없었으면&lt;/em&gt; 프로덕션에서 &lt;em&gt;알 수 없는 중복&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Activity&lt;/em&gt; 가 도입된 뒤 &lt;em&gt;탭 전환의 state 보존&lt;/em&gt; 이 &lt;em&gt;완전히 다른 패턴&lt;/em&gt;. 옛 &lt;em&gt;별도 store 에 입력값 복제&lt;/em&gt; 같은 &lt;em&gt;모종의 패턴&lt;/em&gt; 이 &lt;em&gt;Activity 한 줄&lt;/em&gt; 로 대체.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Hydration mismatch&lt;/em&gt; 의 &lt;em&gt;가장 자주 본 원인&lt;/em&gt; 은 &lt;em&gt;Math.random()&lt;/em&gt; 또는 &lt;em&gt;Date.now()&lt;/em&gt;. SSR 와 클라이언트 첫 render 사이의 시간차. &lt;em&gt;시간 의존 값은 effect 안&lt;/em&gt; 에서.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[React]] (19.x 전반)&lt;/li&gt;
&lt;li&gt;[[React useEffect]] (lifecycle 의 핵심 표현)&lt;/li&gt;
&lt;li&gt;[[React useMemo useCallback]] (render 비용 줄이기)&lt;/li&gt;
&lt;li&gt;[[React Component Composition]] (lifecycle 단순화)&lt;/li&gt;
&lt;li&gt;[[SPA Architecture]] (CSR vs SSR vs SSG vs RSC)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;공식: &lt;a href=&quot;https://react.dev/reference/react/useEffect&quot;&gt;useEffect&lt;/a&gt;, &lt;a href=&quot;https://react.dev/reference/react/StrictMode&quot;&gt;Strict Mode&lt;/a&gt;, &lt;a href=&quot;https://react.dev/reference/react/Activity&quot;&gt;Activity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Class legacy: &lt;a href=&quot;https://react.dev/reference/react/Component&quot;&gt;Component&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>react</category><category>lifecycle</category><category>mount</category><category>unmount</category><category>strict-mode</category><category>activity</category><category>rsc</category><category>hydration</category><author>koa (김신건)</author></item><item><title>[React] useEffect: 동기화의 본질, dep array, cleanup, useEffectEvent</title><link>https://shinkeonkim.com/wiki/frameworks/react-useeffect/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/react-useeffect/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;useEffect(setup, deps?)&lt;/code&gt;&lt;/strong&gt; 는 &lt;em&gt;컴포넌트의 렌더 결과&lt;/em&gt; 와 &lt;em&gt;외부 시스템&lt;/em&gt; 을 &lt;em&gt;동기화&lt;/em&gt; 하는 hook.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;외부 시스템&lt;/em&gt;: 브라우저 DOM, WebSocket, 구독, 타이머, 외부 라이브러리 (D3, Mapbox 등), 외부 store.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;동기화&lt;/em&gt;: “&lt;em&gt;렌더가 끝났을 때 외부 상태도 이 컴포넌트 상태와 맞아야&lt;/em&gt; 한다” 라는 규칙 적용.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
useEffect 의 &lt;em&gt;공식 멘탈 모델은 “동기화”&lt;/em&gt;. &lt;em&gt;생명주기 이벤트&lt;/em&gt; 가 아니다. &lt;em&gt;왜 이 effect 가 살아 있어야 하는지&lt;/em&gt; 를 &lt;em&gt;외부 시스템 관점&lt;/em&gt; 에서 설명할 수 있어야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;라이프사이클의-직관&quot;&gt;라이프사이클의 직관&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;위 애니메이션은 Spring Bean 의 라이프사이클이지만 &lt;em&gt;컴포넌트 라이프사이클의 일반 직관&lt;/em&gt; 을 정확히 보여준다. &lt;em&gt;생성 → 의존성 주입 → init 콜백 → 사용 → destroy&lt;/em&gt;. React 도 &lt;em&gt;mount → effect → re-render (deps 변경시) → cleanup → unmount&lt;/em&gt; 의 유사 흐름.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;가장-단순한-형태&quot;&gt;가장 단순한 형태&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // setup: 렌더 후 동작&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; conn&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; createConnection&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(url);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // cleanup: 다음 effect 전에, 그리고 unmount 시&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;disconnect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [url]);   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// dep: 이 값들이 변하면 cleanup + setup 다시&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;4가지-패턴&quot;&gt;4가지 패턴&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q{deps?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|&quot;deps 없음&quot;| EveryRender[&quot;매 렌더마다 실행&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|&quot;[] 빈 배열&quot;| Once[&quot;mount 시 1회 + unmount 시 cleanup&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|&quot;[dep1, dep2]&quot;| OnChange[&quot;mount + dep 변경 시&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|&quot;[state, props]&quot;| Sync[&quot;state/props 의존 동기화&quot;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;deps&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;&lt;code&gt;useEffect(fn)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;매 렌더&lt;/em&gt;&lt;/td&gt;&lt;td&gt;거의 &lt;em&gt;피해야&lt;/em&gt; 한다&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;useEffect(fn, [])&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;mount 1회&lt;/em&gt;&lt;/td&gt;&lt;td&gt;구독, 초기 fetch, 외부 라이브러리 init&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;useEffect(fn, [a, b])&lt;/code&gt;&lt;/td&gt;&lt;td&gt;a, b 변경 시&lt;/td&gt;&lt;td&gt;검색 query 변경 → 새 fetch&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;useEffect(fn, [state])&lt;/code&gt;&lt;/td&gt;&lt;td&gt;state 동기화&lt;/td&gt;&lt;td&gt;라우터, 외부 store&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;cleanup-왜-이렇게-동작하는가&quot;&gt;Cleanup: &lt;em&gt;왜 이렇게 동작하는가&lt;/em&gt;&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Render as Render&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Effect as Effect setup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Cleanup as Effect cleanup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Render: 첫 mount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Render-&gt;&gt;Effect: setup() 실행&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Render: prop 변경 → re-render&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Render-&gt;&gt;Cleanup: cleanup() (옛 effect)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Render-&gt;&gt;Effect: setup() (새 effect)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Render: unmount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Render-&gt;&gt;Cleanup: cleanup() (마지막)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
Cleanup 은 &lt;em&gt;setup 의 거울&lt;/em&gt;. &lt;em&gt;setup 에서 시작한 모든 것&lt;/em&gt; 을 &lt;em&gt;cleanup 이 멈춰야&lt;/em&gt; 한다. 그래야 &lt;em&gt;다음 setup&lt;/em&gt; 이 &lt;em&gt;깨끗한 상태&lt;/em&gt; 에서 시작.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;cleanup-시각화&quot;&gt;Cleanup 시각화&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;위는 &lt;em&gt;AbortController&lt;/em&gt; 의 동작 직관. effect 의 cleanup 에서 &lt;em&gt;진행 중인 비동기&lt;/em&gt; 를 abort 하는 표준 패턴.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ac&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; AbortController&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`/api/users/${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, { signal: ac.signal })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; r.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(setUser)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;      if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (err.name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!==&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;AbortError&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;throw&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; err;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ac.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;abort&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 새 id 로 바뀌면 옛 요청 취소&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [id]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;strict-mode-의-double-invoke&quot;&gt;Strict Mode 의 double invoke&lt;/h2&gt;
&lt;p&gt;React 18+ 의 development 모드에서 effect 가 &lt;em&gt;2 번 실행&lt;/em&gt; 된다. &lt;em&gt;cleanup 을 제대로 짰는지&lt;/em&gt; 강제로 검사하는 도구.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant SM as StrictMode (dev)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Component&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SM-&gt;&gt;Component: mount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Component-&gt;&gt;Component: setup()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SM-&gt;&gt;Component: unmount (시뮬레이션)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Component-&gt;&gt;Component: cleanup()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    SM-&gt;&gt;Component: remount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Component-&gt;&gt;Component: setup() (2번째)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Component: 진짜 mount&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;em&gt;cleanup 이 비대칭이면&lt;/em&gt; 두 번째 setup 후 &lt;em&gt;불일치 상태&lt;/em&gt;. 예: setup 에서 &lt;em&gt;전역 카운터 ++&lt;/em&gt;, cleanup 에서 &lt;em&gt;—&lt;/em&gt; 안 하면 &lt;em&gt;2 가 남는다&lt;/em&gt;. &lt;em&gt;StrictMode 의 의도가 정확히 이런 버그를 잡는 것&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;effect-가-필요-없는-패턴&quot;&gt;”Effect 가 &lt;em&gt;필요 없는&lt;/em&gt;” 패턴&lt;/h2&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;h3 id=&quot;1-다른-state-로부터-유도된-값--usememo-또는-그냥-계산&quot;&gt;1. 다른 state 로부터 &lt;em&gt;유도된 값&lt;/em&gt; → useMemo (또는 그냥 계산)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ Effect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;fullName&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setFullName&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  setFullName&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(firstName &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos; &apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; lastName);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [firstName, lastName]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ 렌더 중 계산&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; fullName&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; firstName &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos; &apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; lastName;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-event-응답-로직--event-handler&quot;&gt;2. &lt;em&gt;event 응답 로직&lt;/em&gt; → event handler&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ Effect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (submitted) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/cart&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, cart);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    setSubmitted&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [submitted]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ handler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; handleSubmit&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;/api/cart&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, cart);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-상위-prop-변경-시-state-reset--key-prop&quot;&gt;3. &lt;em&gt;상위 prop 변경 시 state reset&lt;/em&gt; → key prop&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ Effect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  setComment&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [userId]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ key reset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;component&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: Profile &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;4-외부-store-구독--usesyncexternalstore&quot;&gt;4. &lt;em&gt;외부 store 구독&lt;/em&gt; → useSyncExternalStore&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ Effect + 직접 구독&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; unsubscribe&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; store.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;subscribe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(setData);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; unsubscribe;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, []);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ useSyncExternalStore&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; data&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useSyncExternalStore&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(store.subscribe, store.getSnapshot);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;진짜-effect-가-필요한-경우&quot;&gt;진짜 Effect 가 필요한 경우&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q{외부 시스템과&amp;#x3C;br/&gt;동기화?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|예| Yes[Effect 사용]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q --&gt;|아니오| Q2{event 응답?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|예| Handler[Event Handler]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|아니오| Q3{유도된 값?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|예| Memo[렌더 중 계산]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|아니오| Q4{key reset?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q4 --&gt;|예| Key[key prop]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q4 --&gt;|아니오| Q5{외부 구독?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q5 --&gt;|예| Sync[useSyncExternalStore]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q5 --&gt;|아니오| Rare[정말 드물게 Effect]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;진짜 Effect 사용처&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WebSocket / SSE 연결&lt;/li&gt;
&lt;li&gt;외부 라이브러리 (D3, Mapbox, Chart.js) instance 의 attach/detach&lt;/li&gt;
&lt;li&gt;브라우저 이벤트 listener (&lt;code&gt;window.scroll&lt;/code&gt;, &lt;code&gt;resize&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;타이머 (&lt;code&gt;setInterval&lt;/code&gt;, &lt;code&gt;setTimeout&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;document.title&lt;/code&gt; 같은 &lt;em&gt;외부 mutation&lt;/em&gt; (단, React 19 의 &lt;em&gt;Document Metadata&lt;/em&gt; 도 고려)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;useeffectevent-react-192&quot;&gt;&lt;code&gt;useEffectEvent&lt;/code&gt; (React 19.2)&lt;/h2&gt;
&lt;p&gt;Effect 안에서 &lt;em&gt;최신 prop/state&lt;/em&gt; 를 &lt;em&gt;의존성 없이&lt;/em&gt; 읽고 싶을 때.&lt;/p&gt;
&lt;p&gt;문제:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ChatRoom&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;roomId&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; conn&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; createConnection&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(serverUrl, roomId);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;connected&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;      showNotification&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Connected!&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, theme);   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ theme 가 dep 에 없으면 stale&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;disconnect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, [roomId, theme]);   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// theme 도 넣으면 *theme 바뀔 때마다 재연결*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;해결 (React 19.2+):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ChatRoom&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;roomId&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; onConnected&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useEffectEvent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // 항상 *최신 theme* 사용. dep array 에 들어가지 *않는다*.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    showNotification&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Connected!&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, theme);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; conn&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; createConnection&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(serverUrl, roomId);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;connected&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onConnected&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; conn.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;disconnect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, [roomId]);   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ theme 는 dep 가 아니다&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;code&gt;useEffectEvent&lt;/code&gt; 는 &lt;em&gt;남용하면 안 된다&lt;/em&gt;. &lt;em&gt;“이건 이벤트인데 effect 안에서 발생한다”&lt;/em&gt; 라는 &lt;em&gt;드문 케이스&lt;/em&gt; 만. 일반 함수를 &lt;em&gt;dep 에서 빼려고 wrap&lt;/em&gt; 하는 건 &lt;em&gt;반패턴&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;effect-의-타이밍&quot;&gt;Effect 의 타이밍&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant React&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant DOM&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Browser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    React-&gt;&gt;React: render() (Virtual DOM 계산)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    React-&gt;&gt;DOM: commit (실제 DOM 갱신)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    DOM-&gt;&gt;Browser: paint (화면 그림)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    React-&gt;&gt;React: useEffect setup (paint 후, 비동기)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;useeffect-vs-uselayouteffect-vs-useinsertioneffect&quot;&gt;&lt;code&gt;useEffect&lt;/code&gt; vs &lt;code&gt;useLayoutEffect&lt;/code&gt; vs &lt;code&gt;useInsertionEffect&lt;/code&gt;&lt;/h3&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Hook&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;&lt;code&gt;useEffect&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;paint 후&lt;/em&gt;&lt;/td&gt;&lt;td&gt;대부분의 동기화&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;useLayoutEffect&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;DOM mutation 후, paint 전&lt;/em&gt; (동기)&lt;/td&gt;&lt;td&gt;&lt;em&gt;측정 + 즉시 mutation&lt;/em&gt; (예: tooltip 위치)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;useInsertionEffect&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;DOM mutation 전&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;CSS-in-JS 라이브러리 전용&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;gantt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    title 렌더 사이클의 effect 타이밍&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    dateFormat ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    axisFormat %S.%L&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    section React&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    render             :r1, 000, 5ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    useInsertionEffect :i1, after r1, 1ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    commit (DOM)       :c1, after i1, 3ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    useLayoutEffect    :le, after c1, 2ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    section Browser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    paint              :p1, after le, 8ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    section React&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    useEffect          :e1, after p1, 1ms&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
&lt;code&gt;useLayoutEffect&lt;/code&gt; 는 &lt;em&gt;동기적으로 paint 를 막는다&lt;/em&gt;. 비싼 작업 (큰 DOM 측정 등) 을 넣으면 &lt;em&gt;애니메이션 끊김&lt;/em&gt;. &lt;em&gt;반드시 필요한 경우 (FOUC 방지, 즉시 위치 조정) 만&lt;/em&gt; 사용.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;h3 id=&quot;1-race-condition-오래된-응답이-새-응답-덮음&quot;&gt;1. Race condition (오래된 응답이 새 응답 덮음)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ id 가 빠르게 변하면 옛 응답이 마지막에 도착&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`/api/users/${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; r.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(setUser);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [id]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ cleanup 플래그 또는 AbortController&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  let&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; alive &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  fetch&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`/api/users/${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; r.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;u&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (alive) &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(u);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { alive &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [id]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-무한-루프-effect-안에서-setstate&quot;&gt;2. 무한 루프 (effect 안에서 setState)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ 무한 루프&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  setUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;user, lastSeen: Date.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;now&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [user]);   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// user 가 deps 에 있고 effect 가 user 변경&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;→ &lt;em&gt;유도된 값이면 렌더 중 계산&lt;/em&gt;. &lt;em&gt;외부 시스템 동기화&lt;/em&gt; 라면 &lt;em&gt;어떤 조건&lt;/em&gt; 으로 막을지 명시.&lt;/p&gt;
&lt;h3 id=&quot;3-stale-closure&quot;&gt;3. Stale closure&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setInterval&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(count);   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 처음 mount 시점의 count 만 영원히&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; clearInterval&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(id);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, []);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;→ deps 에 &lt;em&gt;count&lt;/em&gt; 추가, 또는 &lt;em&gt;useEffectEvent / setState 함수형 갱신&lt;/em&gt; 으로.&lt;/p&gt;
&lt;h3 id=&quot;4-의존성을-수동으로-빠뜨림&quot;&gt;4. 의존성을 &lt;em&gt;수동으로 빠뜨림&lt;/em&gt;&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  fetchPosts&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(query);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // eslint-disable-next-line   ❌&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, []);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;→ &lt;em&gt;해결의 시작이 아니라 끝&lt;/em&gt; 이다. 의존성이 &lt;em&gt;진짜 필요 없음&lt;/em&gt; 을 증명하거나, &lt;em&gt;useEffectEvent&lt;/em&gt; 또는 &lt;em&gt;useRef&lt;/em&gt; 로 의도 명시.&lt;/p&gt;
&lt;h2 id=&quot;event-loop-와의-관계&quot;&gt;Event Loop 와의 관계&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;useEffect 는 &lt;em&gt;paint 후 마이크로태스크 큐&lt;/em&gt; 에서 실행. 위 두 애니메이션은 &lt;em&gt;언제 어떤 작업이 실행되는지&lt;/em&gt; 의 일반 직관. &lt;em&gt;effect cleanup 과 setup&lt;/em&gt; 의 &lt;em&gt;순서&lt;/em&gt; 가 &lt;em&gt;왜 그렇게 정해졌는지&lt;/em&gt; 의 배경.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;react-192-의-변화&quot;&gt;React 19.2 의 변화&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;useEffectEvent&lt;/code&gt; 정식&lt;/td&gt;&lt;td&gt;dep array 분리&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;#x3C;Activity&gt;&lt;/code&gt; 의 &lt;code&gt;hidden&lt;/code&gt; 모드&lt;/td&gt;&lt;td&gt;effect &lt;em&gt;unmount&lt;/em&gt; 시뮬레이션 (배경 렌더)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ESLint 새 규칙&lt;/td&gt;&lt;td&gt;&lt;code&gt;set-state-in-effect&lt;/code&gt;, &lt;code&gt;refs&lt;/code&gt; 등 &lt;em&gt;effect 안의 흔한 버그&lt;/em&gt; 자동 검출&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;React Compiler 1.0&lt;/td&gt;&lt;td&gt;&lt;em&gt;effect 안의 reference&lt;/em&gt; 자동 memoize → dep array stable&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;김신건의-현장-메모&quot;&gt;김신건의 현장 메모&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Effect 의 80% 는 &lt;em&gt;필요 없는&lt;/em&gt; 경우&lt;/em&gt;. 첫 코드 리뷰 라운드에서 “이거 진짜 외부 시스템과 동기화 인가?” 를 물어보면 거의 &lt;em&gt;반 이상&lt;/em&gt; 이 &lt;em&gt;제거됨&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;AbortController&lt;/em&gt; 가 &lt;em&gt;fetch + effect&lt;/em&gt; 의 &lt;em&gt;기본 패턴&lt;/em&gt;. 옛 axios.cancel 같은 &lt;em&gt;라이브러리별 API&lt;/em&gt; 보다 &lt;em&gt;web 표준&lt;/em&gt; 이 깔끔.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useEffectEvent&lt;/code&gt; 는 &lt;em&gt;알림 전송 / 로깅&lt;/em&gt; 같은 &lt;em&gt;side effect 하나만&lt;/em&gt; 분리할 때 가장 빛난다. &lt;em&gt;비즈니스 로직&lt;/em&gt; 을 wrap 하는 용도로 &lt;em&gt;남용하면&lt;/em&gt; 추후 디버깅이 더 어렵다.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;React Compiler 1.0&lt;/em&gt; 이 잡힌 뒤 &lt;em&gt;useEffect 의 의존성 누락 버그&lt;/em&gt; 가 &lt;em&gt;컴파일 타임에 잡힘&lt;/em&gt;. &lt;em&gt;런타임 버그가 빌드 에러로&lt;/em&gt; 옮겨감.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[React]] (19.x 전반)&lt;/li&gt;
&lt;li&gt;[[React Compiler]] (자동 memoization 과 effect 의 dep 안정화)&lt;/li&gt;
&lt;li&gt;[[React useMemo useCallback]] (memoization 의 동반)&lt;/li&gt;
&lt;li&gt;[[React Lifecycle]] (mount / unmount / strict mode)&lt;/li&gt;
&lt;li&gt;[[React Component Composition]] (effect 가 &lt;em&gt;왜 안 필요한&lt;/em&gt; 컴포지션 패턴)&lt;/li&gt;
&lt;li&gt;[[JS Async Await]] (effect 안의 비동기)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;공식: &lt;a href=&quot;https://react.dev/reference/react/useEffect&quot;&gt;useEffect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;멘탈 모델: &lt;a href=&quot;https://react.dev/learn/synchronizing-with-effects&quot;&gt;Synchronizing with Effects&lt;/a&gt;&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;/ul&gt;</content:encoded><category>react</category><category>hook</category><category>effect</category><category>lifecycle</category><category>react19</category><category>frontend</category><author>koa (김신건)</author></item><item><title>[React] useMemo / useCallback / memo: 자동 시대의 수동 도구</title><link>https://shinkeonkim.com/wiki/frameworks/react-usememo-usecallback/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/react-usememo-usecallback/</guid><pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;세 가지 도구는 &lt;em&gt;동일한 목표 (불필요한 재계산 / 재생성 / 재렌더 방지)&lt;/em&gt; 를 &lt;em&gt;서로 다른 위치&lt;/em&gt; 에서 잡는다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useMemo(fn, deps)&lt;/code&gt;&lt;/strong&gt;: &lt;em&gt;값&lt;/em&gt; 의 메모이제이션. deps 가 같으면 &lt;em&gt;같은 값&lt;/em&gt; 반환.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useCallback(fn, deps)&lt;/code&gt;&lt;/strong&gt;: &lt;em&gt;함수&lt;/em&gt; 의 메모이제이션. deps 가 같으면 &lt;em&gt;같은 함수 reference&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;React.memo(Component)&lt;/code&gt;&lt;/strong&gt;: &lt;em&gt;컴포넌트&lt;/em&gt; 의 메모이제이션. props 의 &lt;em&gt;얕은 비교&lt;/em&gt; 가 같으면 &lt;em&gt;재렌더 skip&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
React Compiler 1.0 (2025-10-07) 이후로 &lt;em&gt;대부분의 새 코드&lt;/em&gt; 는 이 세 가지를 &lt;em&gt;수동으로 쓰지 않는다&lt;/em&gt;. &lt;em&gt;컴파일러가 자동으로&lt;/em&gt; 최적 memoization 을 박는다. 이 페이지는 &lt;em&gt;왜 / 언제 / 어떻게 수동으로 쓰는지&lt;/em&gt; 와 &lt;em&gt;Compiler 시대의 escape hatch&lt;/em&gt; 를 정리.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;자동-시대-react-compiler-10&quot;&gt;자동 시대 (React Compiler 1.0+)&lt;/h2&gt;
&lt;p&gt;기본 사고는 &lt;em&gt;“손 안 댄다”&lt;/em&gt;. 컴파일러가 &lt;em&gt;조건부 return 이후&lt;/em&gt; 까지 memoize 가능. 사람이 직접 작성한 &lt;em&gt;조잡한 useMemo&lt;/em&gt; 보다 &lt;em&gt;정밀&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// React Compiler 가 켜진 환경&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; UserList&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;users&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;query&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 컴파일러가 자동으로 filter 결과를 memoize&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; filtered&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; users.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;filter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;u&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; u.name.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;includes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(query));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 컴파일러가 자동으로 onClick 을 stable reference 로&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; navigate&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`/users/${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; filtered.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;u&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: Item &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자세한 컴파일러 동작은 [[React Compiler]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;그래도-수동이-필요한-4-경우&quot;&gt;그래도 수동이 &lt;em&gt;필요한&lt;/em&gt; 4 경우&lt;/h2&gt;
&lt;h3 id=&quot;1-effect-의-dep-로-stable-reference-보장&quot;&gt;1. Effect 의 dep 로 &lt;em&gt;stable reference&lt;/em&gt; 보장&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 외부 라이브러리에 객체를 넘기는데, 매 렌더 새 객체면 effect 재실행&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useMemo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({ url, headers }), [url, headers]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  client.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;subscribe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(config);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; client.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;unsubscribe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(config);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [config]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;React Compiler 도 &lt;em&gt;값이 effect dep 에 쓰이면&lt;/em&gt; 알아서 stable 화. 하지만 &lt;em&gt;명시적 의도&lt;/em&gt; 가 필요할 때.&lt;/p&gt;
&lt;h3 id=&quot;2-외부-라이브러리에-동일-reference-약속&quot;&gt;2. 외부 라이브러리에 &lt;em&gt;동일 reference&lt;/em&gt; 약속&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 어떤 라이브러리가 &quot;callback 이 바뀌면 재구독&quot; 하는 경우&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; onMessage&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useCallback&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;msg&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(msg, user.name);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [user.name]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useExternalSubscription&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(onMessage);   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 라이브러리가 reference 비교&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-매우-비싼-계산의-명시적-캐시&quot;&gt;3. 매우 비싼 계산의 &lt;em&gt;명시적 캐시&lt;/em&gt;&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// O(n²) 알고리즘 같은 비싼 계산&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; layout&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useMemo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; expensiveLayoutCompute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(items);   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 100ms+&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [items]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;컴파일러도 자동 memoize 하지만, &lt;em&gt;의도&lt;/em&gt; (이건 비싸니까 절대 다시 안 하길 바람) 를 &lt;em&gt;코드로&lt;/em&gt; 표현.&lt;/p&gt;
&lt;h3 id=&quot;4-기존-코드의-변경-위험-최소화&quot;&gt;4. 기존 코드의 &lt;em&gt;변경 위험 최소화&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;기존 &lt;code&gt;useMemo&lt;/code&gt; 가 박혀 있고 &lt;em&gt;광범위한 영향&lt;/em&gt; 이 의심되면 &lt;em&gt;제거하지 말고 유지&lt;/em&gt;. 컴파일러 출력이 &lt;em&gt;미세하게 달라질 수 있음&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
기존 코드의 &lt;code&gt;useMemo&lt;/code&gt; 를 &lt;em&gt;컴파일러 도입했다고 일괄 제거&lt;/em&gt; 하면 &lt;em&gt;동작 변경&lt;/em&gt; 가능. &lt;em&gt;새 코드에서만 안 쓰는&lt;/em&gt; 게 안전.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;직관-closure-와-메모이제이션&quot;&gt;직관: closure 와 메모이제이션&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useCallback&lt;/code&gt; 이 잡는 &lt;em&gt;closure&lt;/em&gt; 의 동작 직관:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Closure 는 &lt;em&gt;함수가 자기 외부 변수 reference&lt;/em&gt; 를 &lt;em&gt;잡고 살아남게&lt;/em&gt; 한다. &lt;code&gt;useCallback&lt;/code&gt; 이 같은 reference 를 유지한다는 건 &lt;em&gt;closure 안에 잡힌 변수가 stale&lt;/em&gt; 일 수 있다는 의미. dep 가 &lt;em&gt;정확히 필요&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;referential-equality&quot;&gt;referential equality&lt;/h2&gt;
&lt;p&gt;React 가 &lt;em&gt;재렌더가 필요한지&lt;/em&gt; 판단하는 기준:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Object.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(prevProp, nextProp)   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 얕은 비교, 객체는 reference 비교&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Parent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: Child &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 매 렌더 새 객체 → memo 컴포넌트도 매번 재렌더&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Parent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useMemo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ({ theme: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }), []);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: Child &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 같은 reference → memo 컴포넌트는 skip&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;code&gt;Object.is(a, b)&lt;/code&gt; 는 &lt;code&gt;===&lt;/code&gt; 와 거의 같지만 &lt;code&gt;NaN === NaN&lt;/code&gt; (false) / &lt;code&gt;0 === -0&lt;/code&gt; (true) 의 &lt;em&gt;두 가지 경계 다르다&lt;/em&gt;. React 의 의도는 &lt;em&gt;“의미적 동등”&lt;/em&gt; 에 가까움.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;reactmemo-의-메커니즘&quot;&gt;React.memo 의 메커니즘&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;sequenceDiagram&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    autonumber&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant Parent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    participant MemoChild&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Parent-&gt;&gt;Parent: re-render&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Note over Parent: 새 props 계산&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Parent-&gt;&gt;MemoChild: props (new reference)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    MemoChild-&gt;&gt;MemoChild: 얕은 비교 (Object.is)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    alt props 같음&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        MemoChild-&gt;&gt;MemoChild: skip render&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        MemoChild--&gt;&gt;Parent: 옛 결과 재사용&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    else props 다름&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        MemoChild-&gt;&gt;MemoChild: re-render&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;함정-function-prop&quot;&gt;함정: function prop&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Parent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;MemoChild&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; doX&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;()} /&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // 매 렌더 *새 함수* → MemoChild 매번 재렌더&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Parent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useCallback&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; doX&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(), []);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;!--&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mdx&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;component: MemoChild &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;--&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // ✅ stable reference&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;React.memo + 함수 prop&lt;/em&gt; 은 &lt;em&gt;useCallback 짝&lt;/em&gt; 이 거의 필수. &lt;em&gt;짝 없이&lt;/em&gt; memo 만 박으면 &lt;em&gt;효과 없음 + 비교 비용만 추가&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;진짜-효과가-있는가-렌더-횟수-직관&quot;&gt;진짜 효과가 있는가? (렌더 횟수 직관)&lt;/h2&gt;
&lt;p&gt;같은 컴포넌트 트리, &lt;em&gt;memoize 사용 여부&lt;/em&gt; 별 &lt;em&gt;재렌더 횟수&lt;/em&gt; 의 직관:&lt;/p&gt;
&lt;!-- mdx-component: ChartJs --&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
“&lt;em&gt;React.memo 만 박았는데 왜 효과 없냐&lt;/em&gt;” 의 가장 흔한 원인이 &lt;em&gt;함수 prop 의 매번 새 reference&lt;/em&gt;. memo + useCallback 짝이 &lt;em&gt;동작 조건&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;비용-분석&quot;&gt;비용 분석&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A[&quot;useMemo / useCallback&amp;#x3C;br/&gt;오버헤드&quot;] --&gt; B[&quot;dep 비교 + 캐시 슬롯&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    A --&gt; C[&quot;closure 유지&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D[&quot;효과&quot;] --&gt; E[&quot;재계산 skip&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    D --&gt; F[&quot;하위 컴포넌트&amp;#x3C;br/&gt;재렌더 skip&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    B &amp;#x26; C --&gt; Cost&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    E &amp;#x26; F --&gt; Benefit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Cost{비용 &amp;#x3C; 효과?} --&gt;|예| Use[유지]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Cost --&gt;|아니오| Remove[제거]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&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;매우 간단한 계산 (3 줄)&lt;/td&gt;&lt;td&gt;&lt;em&gt;오버헤드가 더 큼&lt;/em&gt;. 제거.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;가벼운 함수 + memo 없는 자식&lt;/td&gt;&lt;td&gt;&lt;em&gt;효과 없음&lt;/em&gt;. 제거.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;비싼 계산 + memo 있는 자식&lt;/td&gt;&lt;td&gt;&lt;em&gt;효과 큼&lt;/em&gt;. 유지.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;effect dep 의 stable 보장&lt;/td&gt;&lt;td&gt;&lt;em&gt;효과 명확&lt;/em&gt;. 유지.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;React Compiler on&lt;/td&gt;&lt;td&gt;거의 &lt;em&gt;모든 경우&lt;/em&gt; 자동. 새 코드에서 &lt;em&gt;생략&lt;/em&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;의존성-배열의-흔한-함정&quot;&gt;의존성 배열의 흔한 함정&lt;/h2&gt;
&lt;h3 id=&quot;1-객체--배열-리터럴을-deps-안에&quot;&gt;1. 객체 / 배열 리터럴을 &lt;em&gt;deps 안에&lt;/em&gt;&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ 매 렌더 새 객체 → useMemo 가 매 렌더 다시&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; result&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useMemo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; compute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(opts), [{ a, b }]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ 평탄화&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; result&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useMemo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; compute&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(opts), [a, b]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-함수-자체-를-dep-로&quot;&gt;2. &lt;em&gt;함수 자체&lt;/em&gt; 를 dep 로&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Parent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;onSelect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // onSelect 가 매번 새 reference 라면 무의미&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; handler&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useCallback&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onSelect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(id), [onSelect, id]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;→ &lt;em&gt;부모가 useCallback&lt;/em&gt; 으로 stable 보장 필요. 또는 &lt;em&gt;useEffectEvent&lt;/em&gt; 로 분리.&lt;/p&gt;
&lt;h3 id=&quot;3-모든-것을-dep-에-넣기&quot;&gt;3. &lt;em&gt;모든 것을 dep&lt;/em&gt; 에 넣기&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; handler&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useCallback&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  doX&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(a, b, c, d, e, f);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [a, b, c, d, e, f]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;→ &lt;em&gt;deps 가 매번 변하면&lt;/em&gt; useCallback &lt;em&gt;효과 없음&lt;/em&gt;. &lt;em&gt;그냥 일반 함수&lt;/em&gt; 와 동일. &lt;em&gt;진짜 stable 한 일부&lt;/em&gt; 만 dep 에 두거나, &lt;em&gt;useEffectEvent&lt;/em&gt; 로 분리.&lt;/p&gt;
&lt;h2 id=&quot;react-compiler-와의-협업&quot;&gt;React Compiler 와의 협업&lt;/h2&gt;
&lt;p&gt;Compiler 가 잡는 것:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모든 &lt;em&gt;값&lt;/em&gt; / &lt;em&gt;함수&lt;/em&gt; 의 자동 memoization&lt;/li&gt;
&lt;li&gt;&lt;em&gt;조건부 return 이후&lt;/em&gt; 까지 cover&lt;/li&gt;
&lt;li&gt;&lt;em&gt;조잡한 useMemo&lt;/em&gt; 보다 &lt;em&gt;정밀&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compiler 가 &lt;em&gt;못 잡는&lt;/em&gt; 것 (수동 도구 필요):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;외부 라이브러리&lt;/em&gt; 가 dep 로 받는 객체&lt;/li&gt;
&lt;li&gt;&lt;em&gt;effect 의 dep&lt;/em&gt; 명시 stability (자주 잡지만 &lt;em&gt;항상&lt;/em&gt; 은 아님)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;극도로 비싼 계산&lt;/em&gt; 의 &lt;em&gt;명시적 캐시&lt;/em&gt; 의도&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
Compiler 시대에 &lt;code&gt;useMemo&lt;/code&gt; / &lt;code&gt;useCallback&lt;/code&gt; 는 &lt;em&gt;없어지는 게 아니라 escape hatch&lt;/em&gt; 가 된다. &lt;em&gt;기본 도구가 컴파일러&lt;/em&gt;, &lt;em&gt;세밀 제어가 수동&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;새-코드--기존-코드-가이드&quot;&gt;새 코드 / 기존 코드 가이드&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;mermaid&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1{새 코드?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|예| Q2{Compiler 켜짐?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|예| New1[기본은 *수동 없이*&amp;#x3C;br/&gt;+ escape hatch 만]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q2 --&gt;|아니오| New2[수동 memo 필요시 사용]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q1 --&gt;|아니오| Q3{Compiler 도입?}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|예| Keep[기존 수동 memo *유지*&amp;#x3C;br/&gt;제거하지 말 것]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    Q3 --&gt;|아니오| Maintain[수동 memo 유지 + 개선]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;깊이-dep-비교-알고리즘&quot;&gt;깊이: dep 비교 알고리즘&lt;/h2&gt;
&lt;p&gt;React 의 useMemo / useCallback dep 비교:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 의사 코드&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; depsAreEqual&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;prev&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;next&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (prev.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; !==&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; next.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  for&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; i &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; i &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; prev.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; i&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Object.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;is&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(prev[i], next[i])) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;얕은 비교&lt;/em&gt;. 객체 안의 &lt;em&gt;내용 비교가 아니다&lt;/em&gt;. &lt;em&gt;reference 만&lt;/em&gt;. 그래서 &lt;em&gt;dep 에 객체&lt;/em&gt; 면 &lt;em&gt;매번 다르다고 인식&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;흔한-함정&quot;&gt;흔한 함정&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;&lt;strong&gt;항상 모든 곳에 useMemo&lt;/strong&gt;&lt;/em&gt; = &lt;em&gt;오버헤드만 추가, 이득 없음&lt;/em&gt;. 측정 후 결정.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useMemo&lt;/code&gt; 의 &lt;em&gt;사이드 이펙트&lt;/em&gt;&lt;/strong&gt; = 안에 setState, console.log 등 &lt;em&gt;순수 함수가 아니면&lt;/em&gt; StrictMode 에서 &lt;em&gt;두 번 호출&lt;/em&gt; 시 문제 발생.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;React.memo&lt;/code&gt; 만 박고 &lt;em&gt;함수 prop useCallback 없음&lt;/em&gt;&lt;/strong&gt; = 효과 없음.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useMemo&lt;/code&gt; 의 &lt;em&gt;추적&lt;/em&gt; 누락&lt;/strong&gt; = 컴파일러는 &lt;em&gt;eslint-plugin-react-hooks&lt;/em&gt; 로 dep 누락 잡는다. &lt;em&gt;eslint 출력 무시 금지&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;김신건의-현장-메모&quot;&gt;김신건의 현장 메모&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;기존 &lt;em&gt;수동 memoization 이 깔린 코드베이스&lt;/em&gt; 에 &lt;em&gt;React Compiler&lt;/em&gt; 도입 시: &lt;em&gt;기존 코드는 그대로 두고&lt;/em&gt; &lt;em&gt;새 코드에서만 수동 생략&lt;/em&gt;. 동작이 다를 수 있는 영역을 &lt;em&gt;최소화&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;useMemo 가 &lt;em&gt;정말 필요한지&lt;/em&gt; 측정&lt;/em&gt; 의 가장 빠른 방법: &lt;em&gt;React DevTools Profiler&lt;/em&gt; 에서 &lt;em&gt;해당 컴포넌트의 commit 횟수&lt;/em&gt; 를 보고 &lt;em&gt;memo 추가 전후 비교&lt;/em&gt;. &lt;em&gt;눈에 보이지 않는 효과&lt;/em&gt; 를 &lt;em&gt;수치로 결정&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;useCallback + 외부 라이브러리&lt;/em&gt; 조합에서 가장 자주 부딪힌 함정: &lt;em&gt;라이브러리가 &lt;em&gt;내부적으로 deps 비교&lt;/em&gt; 안 하면&lt;/em&gt; useCallback 의 의미가 &lt;em&gt;전혀 없다&lt;/em&gt;. 라이브러리 코드를 &lt;em&gt;확인&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;서버 컴포넌트 (RSC)&lt;/em&gt; 에서는 &lt;em&gt;useMemo / useCallback 자체가 불가&lt;/em&gt;. &lt;em&gt;클라이언트 컴포넌트&lt;/em&gt; (&lt;code&gt;&apos;use client&apos;&lt;/code&gt;) 안에서만 &lt;em&gt;유효&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[React]] (19.x 전반)&lt;/li&gt;
&lt;li&gt;[[React Compiler]] (자동 memoization 의 시대)&lt;/li&gt;
&lt;li&gt;[[React useEffect]] (effect dep stability 와의 관계)&lt;/li&gt;
&lt;li&gt;[[React Lifecycle]] (mount / re-render / unmount)&lt;/li&gt;
&lt;li&gt;[[React Component Composition]] (composition 으로 &lt;em&gt;memo 가 필요 없는&lt;/em&gt; 구조)&lt;/li&gt;
&lt;li&gt;[[JS Closure]] (useCallback 의 internal)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;공식: &lt;a href=&quot;https://react.dev/reference/react/useMemo&quot;&gt;useMemo&lt;/a&gt;, &lt;a href=&quot;https://react.dev/reference/react/useCallback&quot;&gt;useCallback&lt;/a&gt;, &lt;a href=&quot;https://react.dev/reference/react/memo&quot;&gt;memo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;React Compiler: &lt;a href=&quot;https://react.dev/blog/2025/10/07/react-compiler-1&quot;&gt;블로그&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>react</category><category>hook</category><category>memoization</category><category>performance</category><category>react19</category><category>react-compiler</category><author>koa (김신건)</author></item><item><title>[비교] JPA EAGER vs Rails eager_load</title><link>https://shinkeonkim.com/wiki/frameworks/jpa-rails-eager-comparison/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/jpa-rails-eager-comparison/</guid><pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;정의&quot;&gt;정의&lt;/h2&gt;
&lt;p&gt;JPA 와 Rails 는 둘 다 “eager” 라는 용어를 ORM 의 연관 로딩 전략에 사용한다. &lt;strong&gt;이름은 비슷하지만 의미는 정반대&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JPA &lt;code&gt;FetchType.EAGER&lt;/code&gt;&lt;/strong&gt;: 매핑 선언 시점에 영구 설정. 그 entity 를 조회할 때마다 항상 자동으로 함께 로드. 끄는 게 권장.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rails &lt;code&gt;eager_load&lt;/code&gt;&lt;/strong&gt;: 쿼리 시점에 옵트인. 그 쿼리에서만 JOIN 으로 함께 로드. 권장 패턴.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이름이 같아 혼동하기 쉬워 별도 페이지로 정리한다.&lt;/p&gt;
&lt;h2 id=&quot;1줄-비교&quot;&gt;1줄 비교&lt;/h2&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;측면&lt;/th&gt;&lt;th&gt;JPA &lt;code&gt;EAGER&lt;/code&gt;&lt;/th&gt;&lt;th&gt;Rails &lt;code&gt;eager_load&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;Entity 매핑 (&lt;code&gt;@ManyToOne(fetch=EAGER)&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;Query 호출 (&lt;code&gt;.eager_load(:assoc)&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;적용 범위&lt;/td&gt;&lt;td&gt;그 entity 의 모든 조회&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;strong&gt;거의 항상 비권장&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;권장 (또는 includes)&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;기본값&lt;/td&gt;&lt;td&gt;&lt;code&gt;@ManyToOne&lt;/code&gt; 은 EAGER (위험)&lt;/td&gt;&lt;td&gt;명시 안 함 (lazy)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;실행 방식&lt;/td&gt;&lt;td&gt;즉시 SELECT (별도 또는 JOIN)&lt;/td&gt;&lt;td&gt;LEFT OUTER JOIN 단일 쿼리&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;jpa-eager-의-의미&quot;&gt;JPA EAGER 의 의미&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;Entity&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ManyToOne&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;fetch&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; FetchType.EAGER)   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ← 기본값 (위험)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;JoinColumn&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;author_id&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    User author;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;List&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;Post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt; posts &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; em.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createQuery&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;SELECT p FROM Post p&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, Post.class).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;getResultList&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// → posts SELECT 1번 + 각 post 의 author SELECT N번 = N+1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 또는 Hibernate 의 batch fetch 가 켜있으면 IN 절로 일부 묶임&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;“Post 가 어디서 조회되든 author 도 함께 로드”&lt;/strong&gt;. 매핑이 영구. 끄려면 코드 수정.&lt;/p&gt;
&lt;p&gt;위험:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;N+1 폭탄&lt;/li&gt;
&lt;li&gt;깊은 그래프 (Post → Comment → User → Profile) 가 의도치 않게 전부 로드&lt;/li&gt;
&lt;li&gt;끌 수 없음 (LAZY 로 매핑 변경 후 재배포 필요)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;규칙: &lt;strong&gt;모든 연관 &lt;code&gt;LAZY&lt;/code&gt; 선언&lt;/strong&gt;, 쿼리마다 필요한 것만 fetch join / EntityGraph / @BatchSize 로 명시. [[spring-jpa-fetch-type]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;rails-eager_load-의-의미&quot;&gt;Rails eager_load 의 의미&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C; &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ApplicationRecord&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  belongs_to &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;:author&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;class_name:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;User&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 기본 (lazy)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;posts&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; Post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;all&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;posts.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;each&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { |p| &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;puts&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;author&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# N+1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# eager_load 옵트인&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;posts&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; Post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;eager_load&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;:author&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# → 1쿼리: SELECT p.*, u.* FROM posts LEFT OUTER JOIN users ON ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;posts.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;each&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { |p| &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;puts&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;author&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }   &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# 추가 쿼리 없음&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;“이 쿼리 한 번만 author 도 함께”&lt;/strong&gt;. 쿼리 시점 결정. 다음 쿼리는 lazy.&lt;/p&gt;
&lt;p&gt;권장 패턴: &lt;strong&gt;연관은 lazy by default, 필요할 때만 &lt;code&gt;includes&lt;/code&gt;/&lt;code&gt;preload&lt;/code&gt;/&lt;code&gt;eager_load&lt;/code&gt;&lt;/strong&gt; 명시. [[rails-query-optimization]] 참고.&lt;/p&gt;
&lt;h2 id=&quot;정확한-매핑&quot;&gt;정확한 매핑&lt;/h2&gt;
&lt;p&gt;JPA 와 Rails 의 개념적 대응:&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Rails 메서드&lt;/th&gt;&lt;th&gt;해당 JPA 패턴&lt;/th&gt;&lt;th&gt;발생 쿼리&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;preload(:assoc)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;@BatchSize&lt;/code&gt; 또는 &lt;code&gt;JOIN FETCH&lt;/code&gt; 안 쓰는 explicit 2-쿼리&lt;/td&gt;&lt;td&gt;2 쿼리 (부모 + 자식 IN)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;eager_load(:assoc)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;JOIN FETCH p.assoc&lt;/code&gt; JPQL&lt;/td&gt;&lt;td&gt;1 쿼리 (LEFT OUTER JOIN)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;includes(:assoc)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;(없음)&lt;/td&gt;&lt;td&gt;preload 또는 eager_load 자동&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;references(:assoc)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;(없음, JPQL 의 JOIN FETCH 와 유사)&lt;/td&gt;&lt;td&gt;eager_load 강제&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;JPA 의 &lt;code&gt;FetchType.EAGER&lt;/code&gt; 는 Rails 에 &lt;strong&gt;직접적 대응 없음&lt;/strong&gt;. 굳이 비유하면 association 정의에 &lt;code&gt;eager_load(:assoc)&lt;/code&gt; 를 default scope 로 박아두는 것 (안티패턴).&lt;/p&gt;
&lt;h2 id=&quot;rails-의-eager-라는-단어&quot;&gt;Rails 의 “EAGER” 라는 단어&lt;/h2&gt;
&lt;p&gt;Rails 문서 / 커뮤니티에서 “eager loading” 은 일반적으로 &lt;code&gt;includes&lt;/code&gt; / &lt;code&gt;preload&lt;/code&gt; / &lt;code&gt;eager_load&lt;/code&gt; 셋을 통칭. “eager 로 가져온다” = “lazy 안 하고 미리 가져온다” 의미.&lt;/p&gt;
&lt;p&gt;JPA 의 EAGER 는 fetch &lt;strong&gt;type&lt;/strong&gt; = 영구 매핑 속성. 다른 개념.&lt;/p&gt;
&lt;h2 id=&quot;권장-패턴-비교&quot;&gt;권장 패턴 비교&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;java&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// JPA: lazy + 쿼리마다 명시&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;Entity&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    @&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;ManyToOne&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;fetch&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; FetchType.LAZY)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    User author;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;Query&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;SELECT p FROM Post p JOIN FETCH p.author&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Post&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; findAllWithAuthor&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Rails: 기본 lazy + 쿼리마다 명시&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Post&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C; &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ApplicationRecord&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  belongs_to &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;:author&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;class_name:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;User&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;def&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; self.with_author&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  includes&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;:author&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;두 ORM 모두 &lt;strong&gt;lazy by default + 쿼리 시점에 fetch 선택&lt;/strong&gt; 이 best practice. JPA 가 기본을 EAGER 로 잘못 정한 게 함정.&lt;/p&gt;
&lt;h2 id=&quot;n1-디버깅-도구&quot;&gt;N+1 디버깅 도구&lt;/h2&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;항목&lt;/th&gt;&lt;th&gt;JPA / Hibernate&lt;/th&gt;&lt;th&gt;Rails&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;code&gt;org.hibernate.SQL=DEBUG&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;ActiveRecord::Base.logger&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;정적 분석&lt;/td&gt;&lt;td&gt;Hypersistence Utils&lt;/td&gt;&lt;td&gt;bullet gem&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;code&gt;strict_loading&lt;/code&gt; (Rails 6.1+)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Profiler&lt;/td&gt;&lt;td&gt;p6spy, datasource-proxy&lt;/td&gt;&lt;td&gt;rack-mini-profiler&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;인덱스 검사&lt;/td&gt;&lt;td&gt;Hibernate Statistics&lt;/td&gt;&lt;td&gt;EXPLAIN ANALYZE&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;code&gt;bullet&lt;/code&gt; gem 처럼 자동 N+1 알림 도구가 JPA 진영엔 약함. 직접 통합 테스트에서 Statement count 측정.&lt;/p&gt;
&lt;h2 id=&quot;함정-비교&quot;&gt;함정 비교&lt;/h2&gt;
&lt;h3 id=&quot;jpa-함정&quot;&gt;JPA 함정&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;@ManyToOne&lt;/code&gt; 기본 EAGER&lt;/strong&gt;: 명시적으로 LAZY 안 하면 위험&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;@OneToOne&lt;/code&gt; 양방향 LAZY 안 됨&lt;/strong&gt; (proxy 못 만듦): bytecode enhancement 또는 &lt;code&gt;optional=false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;컬렉션 fetch join + Pageable = in-memory paging&lt;/strong&gt;: OOM 위험&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OSIV&lt;/strong&gt; (Open Session In View) 가 LAZY 함정 가림: 운영에선 끄기 권장 &lt;code&gt;spring.jpa.open-in-view=false&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;rails-함정&quot;&gt;Rails 함정&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;find_by + includes&lt;/code&gt; 일부 버전에서 includes 무시&lt;/strong&gt;: &lt;code&gt;.where().first&lt;/code&gt; 패턴 권장&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;uniq&lt;/code&gt;/&lt;code&gt;distinct&lt;/code&gt; + includes&lt;/strong&gt; 의 row count 미묘함&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;select&lt;/code&gt; 시 FK 누락&lt;/strong&gt;: &lt;code&gt;select(:name).includes(:school)&lt;/code&gt; 깨짐&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;polymorphic&lt;/strong&gt; 은 JOIN 불가, preload 강제&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;작은-결과--큰-결과의-전략&quot;&gt;작은 결과 / 큰 결과의 전략&lt;/h2&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;결과 크기&lt;/th&gt;&lt;th&gt;JPA 권장&lt;/th&gt;&lt;th&gt;Rails 권장&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;부모 &amp;#x3C; 10&lt;/td&gt;&lt;td&gt;JOIN FETCH&lt;/td&gt;&lt;td&gt;eager_load&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;부모 &amp;#x3C; 1000, 자식 비대&lt;/td&gt;&lt;td&gt;2 쿼리 (&lt;code&gt;@BatchSize&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;preload&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pageable&lt;/td&gt;&lt;td&gt;EntityGraph + slice (collection 제외)&lt;/td&gt;&lt;td&gt;includes + paging&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;깊은 nested&lt;/td&gt;&lt;td&gt;단계별 explicit fetch&lt;/td&gt;&lt;td&gt;preload 단계 분리&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;정리&quot;&gt;정리&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;JPA EAGER 와 Rails eager_load 가 같은 뜻인가?&lt;/td&gt;&lt;td&gt;&lt;strong&gt;아니다.&lt;/strong&gt; 이름만 같다.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JPA EAGER 사용 권장하나?&lt;/td&gt;&lt;td&gt;&lt;strong&gt;아니다.&lt;/strong&gt; 모든 연관 LAZY.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rails eager_load 사용 권장하나?&lt;/td&gt;&lt;td&gt;&lt;strong&gt;그렇다.&lt;/strong&gt; lazy default + 쿼리마다 명시.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;둘 다 N+1 해결책인가?&lt;/td&gt;&lt;td&gt;Rails eager_load 는 명백한 해결책. JPA EAGER 는 종종 N+1 의 원인.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;이름이 미묘하게 같아서 “EAGER 면 빠른 것” 으로 단순화하지 말 것. 두 ORM 의 정반대 권장 사항을 기억.&lt;/p&gt;
&lt;h2 id=&quot;관련-위키&quot;&gt;관련 위키&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[[spring-jpa-fetch-type]]&lt;/li&gt;
&lt;li&gt;[[rails-query-optimization]]&lt;/li&gt;
&lt;li&gt;[[rails-activerecord-associations]]&lt;/li&gt;
&lt;li&gt;[[spring-data-jpa]]&lt;/li&gt;
&lt;li&gt;[[spring-jpa-persistence-context]]&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>jpa</category><category>rails</category><category>hibernate</category><category>active-record</category><category>n-plus-one</category><category>comparison</category><author>koa (김신건)</author></item><item><title>Astro</title><link>https://shinkeonkim.com/wiki/frameworks/astro/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/astro/</guid><pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;개요&quot;&gt;개요&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt; 는 콘텐츠 중심 사이트를 위한 정적 사이트 생성기다. Islands Architecture로 필요한 부분만 hydration 하기 때문에 기본 페이지 로드가 빠르다.&lt;/p&gt;
&lt;h2 id=&quot;이-블로그에서의-역할&quot;&gt;이 블로그에서의 역할&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;마크다운 / MDX 콘텐츠 렌더링&lt;/li&gt;
&lt;li&gt;자체 remark 플러그인으로 위키링크 자동 변환 ([[BrainDB]] 참고)&lt;/li&gt;
&lt;li&gt;[[D3]] 기반 그래프 페이지의 React Island 호스팅&lt;/li&gt;
&lt;li&gt;GitHub Actions를 통한 정적 빌드 → GitHub Pages 배포&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;참고&quot;&gt;참고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;공식 문서: &lt;a href=&quot;https://docs.astro.build&quot;&gt;https://docs.astro.build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Content Collections: &lt;a href=&quot;https://docs.astro.build/en/guides/content-collections/&quot;&gt;https://docs.astro.build/en/guides/content-collections/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>tool</category><category>web</category><author>koa (김신건)</author></item><item><title>BrainDB</title><link>https://shinkeonkim.com/wiki/frameworks/braindb/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/braindb/</guid><pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;개요&quot;&gt;개요&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/stereobooster/braindb&quot;&gt;BrainDB&lt;/a&gt; 는 마크다운 콘텐츠를 데이터베이스처럼 다룰 수 있게 해주는 [[Astro]] 통합 라이브러리다.&lt;/p&gt;
&lt;h2 id=&quot;제공하는-것&quot;&gt;제공하는 것&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;위키링크 자동 처리 (&lt;code&gt;@braindb/remark-wiki-link&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;backlink 추출&lt;/li&gt;
&lt;li&gt;콘텐츠 그래프 데이터 (이 블로그의 &lt;a href=&quot;https://shinkeonkim.com/graph/&quot;&gt;그래프&lt;/a&gt; 페이지 기반)&lt;/li&gt;
&lt;li&gt;깨진 링크 감지&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;이-블로그에서의-사용&quot;&gt;이 블로그에서의 사용&lt;/h2&gt;
&lt;p&gt;원래 BrainDB 를 &lt;code&gt;astro.config.mjs&lt;/code&gt; 의 integrations 에 추가해서 쓰려고 했으나, 의존 패키지인 &lt;code&gt;better-sqlite3&lt;/code&gt; 가 Node.js 26 과 호환되지 않아 빌드가 깨졌다. 그래서 결국 작은 커스텀 remark 플러그인(&lt;code&gt;src/plugins/remark-wikilink.mjs&lt;/code&gt;)으로 위키링크 처리, backlink, 그래프 데이터를 직접 구현했다.&lt;/p&gt;
&lt;p&gt;BrainDB 가 제공하던 핵심 기능만 추려 보면 그 양이 많지 않아서, 직접 작성하는 편이 의존성도 가볍고 디버깅도 쉬웠다.&lt;/p&gt;</content:encoded><category>tool</category><category>web</category><category>knowledge</category><author>koa (김신건)</author></item><item><title>D3</title><link>https://shinkeonkim.com/wiki/frameworks/d3/</link><guid isPermaLink="true">https://shinkeonkim.com/wiki/frameworks/d3/</guid><pubDate>Sat, 23 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;개요&quot;&gt;개요&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://d3js.org&quot;&gt;D3.js&lt;/a&gt; 는 데이터 기반의 SVG / Canvas 시각화 라이브러리다. 이 블로그의 &lt;a href=&quot;https://shinkeonkim.com/graph/&quot;&gt;그래프&lt;/a&gt; 페이지는 D3 의 &lt;code&gt;forceSimulation&lt;/code&gt; 으로 노드를 배치한다.&lt;/p&gt;
&lt;h2 id=&quot;사용한-force&quot;&gt;사용한 force&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;forceLink&lt;/code&gt;, 노드 사이의 연결 거리&lt;/li&gt;
&lt;li&gt;&lt;code&gt;forceManyBody&lt;/code&gt;, 노드 간 반발력 (음수 strength)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;forceCenter&lt;/code&gt;, 전체를 화면 중앙으로 끌어당김&lt;/li&gt;
&lt;li&gt;&lt;code&gt;forceCollide&lt;/code&gt;, 노드끼리 겹치지 않게&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>tool</category><category>visualization</category><author>koa (김신건)</author></item></channel></rss>