diff --git a/July/article/How-Suspense-works-internally-in-Concurrent-Mode-1 - Reconciling-flow.md b/July/article/How-Suspense-works-internally-in-Concurrent-Mode-1 - Reconciling-flow.md new file mode 100644 index 0000000..706a6b8 --- /dev/null +++ b/July/article/How-Suspense-works-internally-in-Concurrent-Mode-1 - Reconciling-flow.md @@ -0,0 +1,739 @@ +## ๐Ÿ”— [How Suspense works internally in Concurrent Mode 1 - Reconciling flow](https://jser.dev/react/2022/04/02/suspense-in-concurrent-mode-1-reconciling) + +### ๐Ÿ—“๏ธ ๋ฒˆ์—ญ ๋‚ ์งœ: 2024.07.01 + +### ๐Ÿงš ๋ฒˆ์—ญํ•œ ํฌ๋ฃจ: ๋ฒ„๊ฑด๋””(์ „ํƒœํ—Œ) + +--- + +์ €๋Š” ํ•œ ๋ฒˆ Suspense๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ œ [์œ ํŠœ๋ธŒ ๋น„๋””์˜ค](https://www.youtube.com/watch?v=4Ippewm6AXk)์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ต‰์žฅํžˆ ๊ฐ„๋‹จํ•˜๋ฉฐ React 18์˜ ์ตœ์‹  ๋…ผ๋ฆฌ๋ฅผ ๋ฐ˜์˜ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. + +์ด์ œ **๋™์‹œ์„ฑ ๋ชจ๋“œ์—์„œ Suspense๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€** ๋” ๊นŠ์ด ์‚ดํŽด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋งค์šฐ ๋ณต์žกํ•˜๊ฒŒ ๋ฐํ˜€์ ธ์„œ ์—ฌ๋Ÿฌ ์—ํ”ผ์†Œ๋“œ์— ๊ฑธ์ณ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹จ๊ณ„๋กœ ์ง„ํ–‰ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. + +1. ์žฌ์กฐ์ •(reconciling) - Suspense๊ฐ€ ์–ด๋–ป๊ฒŒ ์žฌ์กฐ์ •ํ•˜๋Š”๊ฐ€ + +2. ์˜คํ”„์Šคํฌ๋ฆฐ ์ปดํฌ๋„ŒํŠธ(Offscreen component) - Suspense ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ + +3. Suspense ์ปจํ…์ŠคํŠธ(Suspense context) - ?? + +4. ํ•‘ & ์žฌ์‹œ๋„(Ping & Retry) - ํ”„๋ผ๋ฏธ์Šค๊ฐ€ ํ•ด๊ฒฐ๋œ ํ›„ ๊น”๋”ํ•˜๊ฒŒ ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜๋„๋ก ํ•˜๊ธฐ + +์ด๋ฒˆ ์—ํ”ผ์†Œ๋“œ๋Š” ์ฒซ ๋ฒˆ์งธ ์ฃผ์ œ์ธ ์žฌ์กฐ์ •(reconciling)์— ๊ด€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +## - Suspense Demo + +[๊ฐ„๋‹จํ•œ Suspense ๋ฐ๋ชจ](https://jser.dev/demos/react/suspense/basic)๋ฅผ ์‚ดํŽด๋ณด์„ธ์š”. + +![](https://jser.dev/static/basic-suspense.gif) + +์ฝ”๋“œ๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์•˜์„ ๋•Œ Promise๋ฅผ ๋˜์ง€๋Š” ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค. + +```tsx +const getResource = (data, delay = 1000) => ({ + _data: null, + _promise: null, + status: "pending", + get data() { + if (this.status === "ready") { + return this._data; + } else { + if (this._promise == null) { + this._promise = new Promise((resolve) => { + setTimeout(() => { + this._data = data; + this.status = "ready"; + resolve(); + }, delay); + }); + } + throw this._promise; + } + }, +}); +function App() { + const [resource, setResource] = React.useState(null); + return ( +
+ + loading...

}> + +
+
+ ); +} +``` + +๊ธฐ๋Œ€ํ–ˆ๋˜๋Œ€๋กœ Fallback์€ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋กœ๋”ฉ ๋ ๋•Œ ๋ณด์—ฌ์ง‘๋‹ˆ๋‹ค. + +## ์–ด๋–ป๊ฒŒ Suspense ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž๊ธฐ ์ž์‹ ์„ ๋ Œ๋”๋ง ํ•˜๋Š”์ง€ ์‚ดํŽด๋ด…์‹œ๋‹ค. + +`beginWork()`์—์„œ ์šฐ๋ฆฌ๋Š” ํ•ด๋‹น ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์ฐพ์•„๋ณผ์ˆ˜ ์žˆ์Šต๋‚˜๋‹ค. [์ฝ”๋“œ](https://github.com/facebook/react/blob/b8cfda15e1232554487c7285fb464f22705a23ce/packages/react-reconciler/src/ReactFiberBeginWork.new.js#L3925) + +```ts +case SuspenseComponent: + return updateSuspenseComponent(current, workInProgress, renderLanes); +``` + +์ดˆ๊ธฐ ๋ Œ๋”๋ง๊ณผ ์—…๋ฐ์ดํŠธ ๋ชจ๋‘ `updateSuspenseComponent`์—์„œ ์ด๋ฃจ์–ด์ง€๋ฉฐ, ์ด๋Š” ๋งค์šฐ ๋ฐฉ๋Œ€ํ•œ [์ฝ”๋“œ ์†Œ์Šค](https://github.com/facebook/react/blob/b8cfda15e1232554487c7285fb464f22705a23ce/packages/react-reconciler/src/ReactFiberBeginWork.new.js#L2343)์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ๋‚˜๋ˆ„์–ด์„œ ์„ค๋ช…ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +```ts +function updateSuspenseComponent(current, workInProgress, renderLanes) { + const nextProps = workInProgress.pendingProps; + let suspenseContext: SuspenseContext = suspenseStackCursor.current; + let showFallback = false; + const didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; + if ( + didSuspend || + shouldRemainOnFallback(suspenseContext, current, workInProgress, renderLanes) + ) { + // Something in this boundary's subtree already suspended. Switch to + // rendering the fallback children. + showFallback = true; + workInProgress.flags &= ~DidCapture; + } +``` + +๋จผ์ €, `SuspenseContext`๊ฐ€ ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +์ด๋Š” ๋‹ค์Œ ์—ํ”ผ์†Œ๋“œ์—์„œ ๋‹ค๋ฃฐ ๋‚ด์šฉ์ด๋ฏ€๋กœ ์ง€๊ธˆ์€ ๋„˜์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. + +`showFallback`๋Š” ๋น„๊ต์  ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. + +์ด๋Š” ํด๋ฐฑ์„ ํ‘œ์‹œํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ณ€์ˆ˜๋กœ, ๊ธฐ๋ณธ๊ฐ’์€ false์ž…๋‹ˆ๋‹ค. + +`showFallback`์ด `didSuspend`์— ์˜์กดํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. didSuspend๋Š” ๋‹ค์‹œ `DidCapture`์— ์˜์กดํ•˜๋Š”๋ฐ, ์ด๋Š” ์ค‘์š”ํ•œ ํ”Œ๋ž˜๊ทธ์ด๋ฏ€๋กœ ๊ธฐ์–ตํ•ด ๋‘์„ธ์š”. + +`shouldRemainOnFallback()`์€ SuspenseContext์™€ ๊ด€๋ จ๋œ ๊ฒƒ์œผ๋กœ, ์ด๋Š” ๋‹ค๋ฅธ ์—ํ”ผ์†Œ๋“œ์—์„œ ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. + +`DidCapture`๊ฐ€ ์ œ๊ฑฐ๋œ๋‹ค๋Š” ์ ์— ์ฃผ๋ชฉํ•˜์„ธ์š”. ์ด๋Š” ๋ฏธ๋ž˜์˜ ๋ฆฌ๋ Œ๋”๋ง ์‹œ ์˜ฌ๋ฐ”๋ฅธ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ์‹œ๋„ํ•  ๊ฒƒ์ด๋ฉฐ, ์ด๋Š” ๋‹ค์‹œ ๋งํ•ด ํ”„๋ผ๋ฏธ์Šค๊ฐ€ ๋‹ค์‹œ ๋˜์ ธ์งˆ ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. (์ด [๋ฐ๋ชจ๋ฅผ ์‹œ๋„](https://jser.dev/demos/react/suspense/rethrow)ํ•ด ๋ณด์„ธ์š”) + +## initial mount + +```ts +if (current === null) { + const nextPrimaryChildren = nextProps.children; + const nextFallbackChildren = nextProps.fallback; + if (showFallback) { + const fallbackFragment = mountSuspenseFallbackChildren( + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ); + const primaryChildFragment: Fiber = (workInProgress.child: any); + primaryChildFragment.memoizedState = + mountSuspenseOffscreenState(renderLanes); + workInProgress.memoizedState = SUSPENDED_MARKER; + return fallbackFragment; + } else { + return mountSuspensePrimaryChildren( + workInProgress, + nextPrimaryChildren, + renderLanes + ); + } +} +``` + +`current === null`์€ ์ดˆ๊ธฐ ๋ Œ๋”๋ง์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. + +`mountSuspenseFallbackChildren()`๋Š” ๊ธฐ๋ณธ ์ž์‹ ์ฝ˜ํ…์ธ ์™€ ํด๋ฐฑ(fallback) ๋ชจ๋‘๋ฅผ ๋งˆ์šดํŠธํ•˜์ง€๋งŒ, ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒƒ์€ Fallback์ž…๋‹ˆ๋‹ค. + +`memoizedState`๋„ ์ดˆ๊ธฐํ™”๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ด Suspense๊ฐ€ ํด๋ฐฑ์„ ๋ Œ๋”๋งํ•˜๊ณ  ์žˆ๋‹ค๋Š” ํ‘œ์‹œ์ž…๋‹ˆ๋‹ค. + +Fallback์„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, `mountSuspensePrimaryChildren()`๊ฐ€ ์ž์‹๋“ค์„ ๋งˆ์šดํŠธํ•ฉ๋‹ˆ๋‹ค. + +`mountSuspenseFallbackChildren()`์™€ `mountSuspensePrimaryChildren()`์— ๋Œ€ํ•ด์„œ๋Š” ์ด๋ฒˆ ์—ํ”ผ์†Œ๋“œ์—์„œ ๋‚˜์ค‘์— ๋‹ค์‹œ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. + +## - ์—…๋ฐ์ดํŠธ + +๊ทธ๋ฆฌ๊ณ  ์—…๋ฐ์ดํŠธ์˜ ๊ฒฝ์šฐ, ์‹ค์ œ๋กœ ๋กœ์ง์€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. + +ํ˜„์žฌ ์ƒํƒœ์™€ ๋  ์ƒํƒœ์— ๋”ฐ๋ผ ๋„ค ๊ฐ€์ง€ ๋ถ„๊ธฐ๋กœ ๋‚˜๋‰˜๊ฒŒ ๋˜๋ฉฐ, ์ด๋ฅผ ์ž์„ธํžˆ ๋‹ค๋ฃฐ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +```ts +} else { + // ์ด๊ฑด ์—…๋ฐ์ดํŠธ์ž…๋‹ˆ๋‹ค. + // ํ˜„์žฌ์˜ fiber์— SuspenseState๊ฐ€ ์žˆ๋‹ค๋ฉด, ์ด๋Š” ์ด๋ฏธ ํด๋ฐฑ(fallback)์„ ํ‘œ์‹œํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. + const prevState: null | SuspenseState = current.memoizedState; + if (prevState !== null) { + // current tree๋Š” ์ด๋ฏธ fallback์„ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + if (showFallback) { + // prev: fallback, now: fallback + ... + } else { + // prev: fallback, now: content + ... + } + } else { + if (showFallback) { + // prev: content, now: callback + ... + } else { + // prev: content, now: content + ... + } + } +} +``` + +prev: fallback, now: fallback + +```ts +const nextFallbackChildren = nextProps.fallback; +const nextPrimaryChildren = nextProps.children; +const fallbackChildFragment = updateSuspenseFallbackChildren( + current, + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes +); +const primaryChildFragment: Fiber = (workInProgress.child: any); +const prevOffscreenState: OffscreenState | null = (current.child: any) + .memoizedState; +primaryChildFragment.memoizedState = + prevOffscreenState === null + ? mountSuspenseOffscreenState(renderLanes) + : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); +primaryChildFragment.childLanes = getRemainingWorkInPrimaryTree( + current, + renderLanes +); +workInProgress.memoizedState = SUSPENDED_MARKER; +return fallbackChildFragment; + +``` + +๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ ํด๋ฐฑ(fallback)์„ ๋ Œ๋”๋งํ•˜์ง€๋งŒ, ํด๋ฐฑ ์ž์ฒด๋Š” ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. `updateSuspenseFallbackChildren()`๋Š” ์žฌ์กฐ์ •์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. + +OffscreenState ๋ถ€๋ถ„์€ ์•ฝ๊ฐ„ ํ˜ผ๋ž€์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋Š” Suspense Cache์™€ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ ์•ž์œผ๋กœ์˜ ์—ํ”ผ์†Œ๋“œ์—์„œ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. + +prev: fallback, now: content + +```ts +const nextPrimaryChildren = nextProps.children; +const primaryChildFragment = updateSuspensePrimaryChildren( + current, + workInProgress, + nextPrimaryChildren, + renderLanes +); +workInProgress.memoizedState = null; +return primaryChildFragment; +``` + +์ด๊ฒƒ์€ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์ž์‹ ๋ถ€๋ถ„์„ ์žฌ์กฐ์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. + +**์ด์ „: ์ฝ˜ํ…์ธ , ํ˜„์žฌ: ํด๋ฐฑ** + +์ฝ”๋“œ๋Š” `์ด์ „: ํด๋ฐฑ, ํ˜„์žฌ: ์ฝ˜ํ…์ธ `์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค. + +**์ด์ „: ์ฝ˜ํ…์ธ , ํ˜„์žฌ: ์ฝ˜ํ…์ธ ** + +์ฝ”๋“œ๋Š” `์ด์ „: ํด๋ฐฑ, ํ˜„์žฌ: ์ฝ˜ํ…์ธ `์™€ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. + +## Suspense ๋‚ด์˜ Wrapper๋“ค + +Suspense ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹จ์ˆœํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹ˆ๋ฉฐ, ์ž์‹๋“ค์„ Offscreen ์ปดํฌ๋„ŒํŠธ์™€ ๊ฐ™์€ ๊ฒƒ์œผ๋กœ ๊ฐ์‹ธ์„œ ๋ฉ‹์ง„ ํšจ๊ณผ๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค. + +Offscreen์— ๋Œ€ํ•ด ์ž ๊น ์‚ดํŽด๋ณด๊ณ , ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•ž์œผ๋กœ์˜ ์—ํ”ผ์†Œ๋“œ์—์„œ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. + +**mountSuspenseFallbackChildren()** + +์ด์ œ `mountSuspenseFallbackChildren()`์—์„œ ์‹ค์ œ๋กœ ์–ด๋–ค ์ผ์ด ๋ฐœ์ƒํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +```ts +function mountSuspenseFallbackChildren( + workInProgress, + primaryChildren, + fallbackChildren, + renderLanes +) { + const mode = workInProgress.mode; + const progressedPrimaryFragment: Fiber | null = workInProgress.child; + const primaryChildProps: OffscreenProps = { + mode: "hidden", + children: primaryChildren, + }; + let primaryChildFragment; + let fallbackChildFragment; + primaryChildFragment = mountWorkInProgressOffscreenFiber( + primaryChildProps, + mode, + NoLanes + ); + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); + primaryChildFragment.return = workInProgress; + fallbackChildFragment.return = workInProgress; + primaryChildFragment.sibling = fallbackChildFragment; + workInProgress.child = primaryChildFragment; + return fallbackChildFragment; +} +``` + +1. primary child๋Š” Offscreen Fiber๋กœ ๊ฐ์‹ธ๊ณ , mode๋Š” `hidden`์œผ๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. + +2. fallback์€ Fragment๋กœ ๊ฐ์‹ธ์ง‘๋‹ˆ๋‹ค. + +3. primary child์™€ fallback ๋ชจ๋‘ ์ž์‹์œผ๋กœ ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค. + +์™œ fallback์„ Fragment๋กœ ๊ฐ์Œ€๊นŒ์š”? + +fallback์€ `ReactNodeList`์˜ ์ผ์ข…์ด๊ธฐ ๋•Œ๋ฌธ์— ์ˆซ์ž๋‚˜ ๋ฌธ์ž์—ด์ผ ์ˆ˜ ์žˆ๊ณ , ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฌธ์ž์—ด์€ ํŠน๋ณ„ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ Fragment๋กœ ๊ฐ์‹ธ๋Š” ๊ฒƒ์ด ๋” ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. + +```ts +export type ReactNode = + | React$Element + | ReactPortal + | ReactText + | ReactFragment + | ReactProvider + | ReactConsumer; +export type ReactEmpty = null | void | boolean; +export type ReactFragment = ReactEmpty | Iterable; +export type ReactNodeList = ReactEmpty | React$Node; +export type ReactText = string | number; +``` + +์—ฌ๊ธฐ Suspense์˜ fiber ๊ตฌ์กฐ๋ฅผ ๋‹ด์€ ๋‹ค์ด์–ด๊ทธ๋žจ์ด ์žˆ์Šต๋‹ˆ๋‹ค. + +![](https://jser.dev/static/suspense-fiber-structure-hidden.png) + +`mountWorkInProgressOffscreenFiber`์˜ ํŠน๋ณ„ํ•œ ์ ์€ ๋ฌด์—‡์ผ๊นŒ์š” ? + +```ts +function mountWorkInProgressOffscreenFiber( + offscreenProps: OffscreenProps, + mode: TypeOfMode, + renderLanes: Lanes +) { + // `createFiberFromOffscreen` ํ•จ์ˆ˜์˜ props ์ธ์ˆ˜๋Š” `any` ํƒ€์ž…์œผ๋กœ ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, + // ์ด๋ฅผ ์ œํ•œํ•˜๊ธฐ ์œ„ํ•ด ์ด ๋ž˜ํผ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + return createFiberFromOffscreen(offscreenProps, mode, NoLanes, null); +} +export function createFiberFromOffscreen( + pendingProps: OffscreenProps, + mode: TypeOfMode, + lanes: Lanes, + key: null | string +) { + const fiber = createFiber(OffscreenComponent, pendingProps, key, mode); + fiber.elementType = REACT_OFFSCREEN_TYPE; + fiber.lanes = lanes; + const primaryChildInstance: OffscreenInstance = {}; + fiber.stateNode = primaryChildInstance; + return fiber; +} +``` + +ํŠน๋ณ„ํ•œ ์ ์€ ์—†์–ด๋ณด์ด์ง€๋งŒ, `hidden`์ด๋‚˜ `visible` ๊ฐ’์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” `mode` ํ”„๋กœํผํ‹ฐ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. + +**mountSuspensePrimaryChildren()** + +```ts +function mountSuspensePrimaryChildren( + workInProgress, + primaryChildren, + renderLanes +) { + const mode = workInProgress.mode; + const primaryChildProps: OffscreenProps = { + mode: "visible", + children: primaryChildren, + }; + const primaryChildFragment = mountWorkInProgressOffscreenFiber( + primaryChildProps, + mode, + renderLanes + ); + primaryChildFragment.return = workInProgress; + workInProgress.child = primaryChildFragment; + return primaryChildFragment; +} +``` + +์ด๋ฒˆ์—๋„ Offscreen fiber๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์ด๋ฒˆ์—๋Š” fallback ์—†์ด ๋ชจ๋“œ๋ฅผ "visible"๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. + +![](https://jser.dev/static/suspense-fiber-structure-visible.png) + +์ฐธ๊ณ ๋กœ, `workInProgress`๋„ `mode`๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ, ๋‹ค๋ฅธ ์œ ํ˜•์ธ `TypeOfMode`์ž…๋‹ˆ๋‹ค. + +```ts +export type TypeOfMode = number; +export const NoMode = /* */ 0b000000; +// TODO: ConcurrentMode๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๋ฃจํŠธ ํƒœ๊ทธ์—์„œ ์ฝ์–ด์˜ค๊ธฐ +export const ConcurrentMode = /* */ 0b000001; +export const ProfileMode = /* */ 0b000010; +export const DebugTracingMode = /* */ 0b000100; +export const StrictLegacyMode = /* */ 0b001000; +export const StrictEffectsMode = /* */ 0b010000; +export const ConcurrentUpdatesByDefaultMode = /* */ 0b100000; +``` + +์™œ ์šฐ๋ฆฌ๊ฐ€ primary children์„ fiber tree์— ์œ ์ง€ํ•˜๋Š”์ง€ ๊ถ๊ธˆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์™œ ๊ทธ๋ƒฅ ์ œ๊ฑฐํ•˜์ง€ ์•Š์„๊นŒ์š”? ์ข‹์€ ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด, ์ด๋Š” fiber์˜ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. + +fallback์—์„œ ๋‹ค์‹œ ์ „ํ™˜๋  ๋•Œ ๋ชจ๋“  ๊ฒƒ์ด ์ƒˆ๋กœ์›Œ์ง€๊ธฐ๋ฅผ ์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +์ž์„ธํ•œ ๋‚ด์šฉ์€ Offscreen์˜ ๋‹ค์Œ ์—ํ”ผ์†Œ๋“œ์—์„œ ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. + +์ด์ œ Promise๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ด…์‹œ๋‹ค. + +## ์–ด๋–ป๊ฒŒ Suspense ๋‚ด๋ถ€์—์„œ Promise ๊ฐ€ ๊ฐ์ง€ ๋˜๊ณ  ์—…๋ฐ์ดํŠธ๊ฐ€ ์œ ๋ฐœ ๋˜๋Š” ๊ฑธ๊นŒ์š” ? + +์šฐ๋ฆฌ๋Š” ์ด๋ฏธ suspense๊ฐ€ promise๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ฐ˜์‘ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์—๋Ÿฌ ์ฒ˜๋ฆฌ์˜ ์ผ๋ถ€์ด๋ฏ€๋กœ, ๋จผ์ € handleError๋กœ ๊ฐ€๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. [์ถœ์ฒ˜](https://github.com/facebook/react/blob/5a1e558df21bd3cafbaea01cc418fa69d14a8cab/packages/react-reconciler/src/ReactFiberWorkLoop.new.js#L1553) + +```ts +function handleError(root, thrownValue): void { + do { + let erroredWork = workInProgress; + try { + // ๋ Œ๋”๋ง ๋‹จ๊ณ„ ๋™์•ˆ ์„ค์ •๋œ ๋ชจ๋“ˆ ์ˆ˜์ค€ ์ƒํƒœ๋ฅผ ๋ฆฌ์…‹ํ•ฉ๋‹ˆ๋‹ค. + resetContextDependencies(); + resetHooksAfterThrow(); + // TODO: ๋ณ„๋„์˜ ๋ฌธ์ œ๋ฅผ ์กฐ์‚ฌํ•˜๋Š” ๋™์•ˆ ์ด ๋ˆ„๋ฝ๋œ ์ค„์„ ๋ฐœ๊ฒฌํ•˜๊ณ  ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. + // string refs๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”. + ReactCurrentOwner.current = null; + throwException( + root, + erroredWork.return, + erroredWork, + thrownValue, + workInProgressRootRenderLanes + ); + completeUnitOfWork(erroredWork); + } catch (yetAnotherThrownValue) { + // ๋ฐ˜ํ™˜ ๊ฒฝ๋กœ์—์„œ ๋ฌด์–ธ๊ฐ€๊ฐ€ ๋‹ค์‹œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. + thrownValue = yetAnotherThrownValue; + if (workInProgress === erroredWork && erroredWork !== null) { + // ์ด ๊ฒฝ๊ณ„(boundary)์—์„œ ์ด๋ฏธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ, ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. + // ์˜ค๋ฅ˜๋ฅผ ๋‹ค์Œ ๊ฒฝ๊ณ„๋กœ ์ „ํŒŒํ•ฉ๋‹ˆ๋‹ค. + } + + erroredWork = erroredWork.return; + workInProgress = erroredWork; + } else { + erroredWork = workInProgress; + } + continue; + } + // ์ •์ƒ์ ์ธ ์ž‘์—… ๋ฃจํ”„๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค. + return; + } while (true); +} +``` + +์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ ์ด ๋‘ ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ์ž…๋‹ˆ๋‹ค. + +1. throwException +2. completeUnitOfWork + +## - throwException + +[์†Œ์Šค ์ฝ”๋“œ](https://github.com/facebook/react/blob/b8cfda15e1232554487c7285fb464f22705a23ce/packages/react-reconciler/src/ReactFiberThrow.new.js#L430) + +์ด๊ฒƒ์€ ๋งค์šฐ ํฐ ์ฝ”๋“œ ์กฐ๊ฐ์ด๋ฏ€๋กœ, ๋‚˜๋ˆ„์–ด์„œ ์„ค๋ช…ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ €, throwํ•œ fiber๊ฐ€ Incomplete๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. + +```ts +// ์ด source Fiber๋Š” ํ•ด๊ฒฐ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. +sourceFiber.flags |= Incomplete; +``` + +```ts +if ( + value !== null && + typeof value === 'object' && + typeof value.then === 'function' +) { + // ์ด๊ฒƒ์€ ๊นจ์šธ ์ˆ˜ ์žˆ๋Š”(wakeable) ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ผ์‹œ ์ค‘๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + const wakeable: Wakeable = (value: any); + ... +} else { + // ์ผ๋ฐ˜์ ์ธ ์—๋Ÿฌ +} +``` + +์šฐ๋ฆฌ๋Š” `wakeable`์„ ๋‹จ์ˆœํžˆ throw๋œ Promise๋กœ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +Promise๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด, ์ด๋Š” ๋‹จ์ˆœํžˆ Error Boundary์—์„œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ์ผ๋ฐ˜์ ์ธ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค. + +([ErrorBoundary์— ๋Œ€ํ•œ ๋‚ด ์˜์ƒ](https://www.youtube.com/watch?v=0TnuJKLjMyg)์„ ์ฐธ๊ณ ํ•˜์„ธ์š”). + +์ด์ œ Suspense ๋ถ€๋ถ„์— ์ง‘์ค‘ํ•ด๋ด…์‹œ๋‹ค. + +```ts +// ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Suspense๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋œ ๋ทฐ๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•˜๋„๋ก ์Šค์ผ€์ค„ํ•ฉ๋‹ˆ๋‹ค. +const suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber); +``` + +๋จผ์ € ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Suspense๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ด๋ฅผ Suspense Boundary๋ผ๊ณ  ๋ถ€๋ฅด๋ฉฐ, ์ด๋Š” Error Boundary์™€ ๋งค์šฐ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. + +`getNearestSuspenseBoundaryToCapture`๋Š” ๊ฐ„๋‹จํ•˜๋ฏ€๋กœ ์ƒ๋žตํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +์ด๋Š” ๋‹จ์ˆœํžˆ `return`์„ ํ†ตํ•ด ์กฐ์ƒ fiber ๋…ธ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. [์ถœ์ฒ˜](https://github.com/facebook/react/blob/b8cfda15e1232554487c7285fb464f22705a23ce/packages/react-reconciler/src/ReactFiberThrow.new.js#L277) + +```ts +if (suspenseBoundary !== null) { + suspenseBoundary.flags &= ~ForceClientRender; + markSuspenseBoundaryShouldCapture( + suspenseBoundary, + returnFiber, + sourceFiber, + root, + rootRenderLanes + ); + // concurrent ๋ชจ๋“œ์—์„œ๋งŒ ping ๋ฆฌ์Šค๋„ˆ๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. Legacy Suspense๋Š” ํ•ญ์ƒ fallback์„ ๋™๊ธฐ์ ์œผ๋กœ ์ปค๋ฐ‹ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ping์ด ์—†์Šต๋‹ˆ๋‹ค. + + if (suspenseBoundary.mode & ConcurrentMode) { + attachPingListener(root, wakeable, rootRenderLanes); + } + attachRetryListener(suspenseBoundary, root, wakeable, rootRenderLanes); + return; +} +``` + +Suspense Boundary๋ฅผ ์ฐพ์€ ํ›„, ์—ฌ๊ธฐ์„œ 3๊ฐ€์ง€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค: + +1. markSuspenseBoundaryShouldCapture() +2. attachPingListener() +3. attachRetryListener() + +๋ช…๋ฐฑํžˆ, `markSuspenseBoundaryShouldCapture()`๋Š” Suspense๊ฐ€ fallbacks๋ฅผ ๋ Œ๋”๋งํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๊ณ , ๋‹ค๋ฅธ ๋‘ ๊ฐ€์ง€๋Š” ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ๋“  Promise์— ์ฝœ๋ฐฑ์„ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋“ค์ด ์™„๋ฃŒ๋˜๋ฉด ์ฝ˜ํ…์ธ ๋ฅผ ๋ Œ๋”๋งํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +2๋ฒˆ๊ณผ 3๋ฒˆ์€ Ping & Retry์˜ ๋‹ค์Œ ์—ํ”ผ์†Œ๋“œ์—์„œ ์ž์„ธํžˆ ์„ค๋ช…๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +### ๋งŒ์•ฝ ์šฐ๋ฆฌ๊ฐ€ Suspense๋ฅผ ์ฐพ์ง€ ๋ชปํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š” + +์ฝ”๋“œ๋ฅผ ๊ณ„์† ๋ณด๋ฉด, SyncLane์ด ์•„๋‹Œ ๊ฒฝ์šฐ, Suspense Boundary๊ฐ€ ์—†์–ด๋„ ๊ดœ์ฐฎ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```ts +else { + // ๊ฒฝ๊ณ„(boundary)๊ฐ€ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. sync ์—…๋ฐ์ดํŠธ๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ, ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. + // ์šฐ๋ฆฌ๋Š” ์ผ์‹œ ์ค‘๋‹จํ•˜๊ณ  ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋„์ฐฉํ•˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + if (!includesSyncLane(rootRenderLanes)) { + // ์ด๊ฒƒ์€ sync ์—…๋ฐ์ดํŠธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์ผ์‹œ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค. Suspense ๊ฒฝ๊ณ„๋ฅผ ํ™œ์„ฑํ™”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, + // fallback์„ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด ๋‘ ๋ฒˆ์งธ ํŒจ์Šค๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ  ๋ฃจํŠธ๊นŒ์ง€ ๋ชจ๋‘ ๋˜๊ฐ๊น๋‹ˆ๋‹ค. + // (์ด๋Š” refresh ์ „ํ™˜์ด ์ž‘๋™ํ•ด์•ผ ํ•˜๋Š” ๋ฐฉ์‹์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์–ด์ฐจํ”ผ fallback์„ ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.) + // + // ์ด ๊ฒฝ์šฐ๋Š” ์ดˆ๊ธฐ ํ•˜์ด๋“œ๋ ˆ์ด์…˜์—๋„ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. + attachPingListener(root, wakeable, rootRenderLanes); + renderDidSuspendDelayIfPossible(); + return; + } + // ์ด๊ฒƒ์€ sync/๊ฐœ๋ณ„ ์—…๋ฐ์ดํŠธ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด ๊ฒฝ์šฐ๋ฅผ ์˜ค๋ฅ˜์ฒ˜๋Ÿผ ์ทจ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค. + // ๊ฐœ๋ณ„ ๋ Œ๋”๋ง์€ ์™ธ๋ถ€ ์ƒํƒœ์™€์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋™๊ธฐ์ ์œผ๋กœ ์™„์ „ํ•œ ํŠธ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + const uncaughtSuspenseError = new Error( + "๋™๊ธฐ ์ž…๋ ฅ์— ์‘๋‹ตํ•˜๋Š” ๋™์•ˆ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ผ์‹œ ์ค‘๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. " + + "์ด๋กœ ์ธํ•ด UI๊ฐ€ ๋กœ๋”ฉ ํ‘œ์‹œ๊ธฐ๋กœ ๋Œ€์ฒด๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์ˆ˜์ •ํ•˜๋ ค๋ฉด, " + + "์ผ์‹œ ์ค‘๋‹จ๋˜๋Š” ์—…๋ฐ์ดํŠธ๋ฅผ startTransition์œผ๋กœ ๊ฐ์‹ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค." + ); + // ์ „ํ™˜ ์™ธ๋ถ€์— ์žˆ๋Š” ๊ฒฝ์šฐ, ์ผ๋ฐ˜์ ์ธ ์˜ค๋ฅ˜ ๊ฒฝ๋กœ๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค. + // ์˜ค๋ฅ˜๋Š” ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Suspense ๊ฒฝ๊ณ„์—์„œ ํฌ์ฐฉ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + value = uncaughtSuspenseError; +} +``` + +๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด, suspense๊ฐ€ ์‚ฌ์šฉ์ž ์•ก์…˜์— ์˜ํ•ด ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ, Suspense ๊ฒฝ๊ณ„๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. + +์‚ฌ์šฉ์ž ์•ก์…˜์ด ์•„๋‹ˆ๊ฑฐ๋‚˜ ์ „ํ™˜ ์ค‘์ธ ๊ฒฝ์šฐ, `attachPingListener()`์™€ `renderDidSuspendDelayIfPossible()`๊ฐ€ ๋ณต๊ตฌ๋ฅผ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. + +๋‹ค์Œ์€ [Suspense ๊ฒฝ๊ณ„ ์—†์ด ์ „ํ™˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ๋ชจ](https://jser.dev/demos/react/suspense/transition)์ž…๋‹ˆ๋‹ค. ์ด ๋ฐ๋ชจ์—์„œ Suspense ๊ฒฝ๊ณ„๊ฐ€ ์—†์–ด๋„ ์—ฌ์ „ํžˆ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +**markSuspenseBoundaryShouldCapture()** + +[์†Œ์Šค ์ฝ”๋“œ](https://github.com/facebook/react/blob/b8cfda15e1232554487c7285fb464f22705a23ce/packages/react-reconciler/src/ReactFiberThrow.new.js#L297) + +`markSuspenseBoundaryShouldCapture()` ํ•จ์ˆ˜๋Š” Concurrent Mode์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋กœ์ง์—๋งŒ ์ง‘์ค‘ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. Legacy Suspense๋Š” Concurrent Mode ์ด์ „์— ์‚ฌ์šฉ๋œ ๊ฒƒ์ด๋ฏ€๋กœ, ์ด๋ฅผ ๋ฌด์‹œํ•˜๊ณ  Concurrent Mode์—๋งŒ ์ดˆ์ ์„ ๋งž์ถ”์„ธ์š”. + +``` +suspenseBoundary.flags |= ShouldCapture; +``` + +์—ฌ๊ธฐ์„œ `ShouldCapture`๊ฐ€ ์„ค์ •๋˜๋Š”๋ฐ, ์ด๊ฒƒ์ด `DidCapture`๋กœ ๋ณ€ํ™˜๋˜๋Š” ๋‹จ๊ณ„๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ๋ถ€๋ถ„์€ ๋‚˜์ค‘์— ๋‹ค๋ฃฐ ํ…Œ๋‹ˆ ์ž ์‹œ ๊ธฐ๋‹ค๋ ค ์ฃผ์„ธ์š”. + +```ts +sourceFiber.flags |= ForceUpdateForLegacySuspense; +// ์ด fiber๊ฐ€ ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ปค๋ฐ‹ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. +// ํ•˜์ง€๋งŒ ์–ด๋–ค ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฉ”์„œ๋“œ๋‚˜ ์ฝœ๋ฐฑ๋„ ํ˜ธ์ถœํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ผ์ดํ”„์‚ฌ์ดํด ํšจ๊ณผ ํƒœ๊ทธ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. +sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); +``` + +source fiber๋Š” ์ด๋ฏธ Incomplete๋กœ ํ‘œ์‹œํ–ˆ์ง€๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” ๊ทธ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. + +```ts +export const LifecycleEffectMask = + Passive | Update | Callback | Ref | Snapshot | StoreConsistency; +``` + +LifecycleEffectMask๋Š” ๋ชจ๋“  ๋ถ€์ˆ˜ ํšจ๊ณผ๋ฅผ ํฌํ•จํ•˜๋ฏ€๋กœ, ์ด๋Š” ์‹ค์ œ๋กœ ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์ง€๋งŒ ๋งˆ์น˜ ์™„๋ฃŒ๋œ ๊ฒƒ์ฒ˜๋Ÿผ ์ฒ˜๋ฆฌํ•œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. + +```ts +// source fiber๊ฐ€ ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์•„์ง ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ์ž‘์—…์ด ๋‚จ์•„ ์žˆ์Œ์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด Sync ์šฐ์„ ์ˆœ์œ„๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. +sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); +``` + +์ด๋Š” Suspense๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ DidCapture๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ๊ณผ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋ Œ๋”๋งํ•  ๋•Œ, ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด `lanes`๊ฐ€ ์„ค์ •๋˜์–ด [bail-out](https://jser.dev/react/2022/01/07/how-does-bailout-work)์„ ํ”ผํ•ฉ๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฐ ๋‹ค์Œ `completeUnitOfWork(erroredWork)`๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. + +## completeUnitOfWork + +throwException()์ด ์™„๋ฃŒ๋œ ํ›„, `completeUnitOfWork()`๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. [source](https://github.com/facebook/react/blob/b8cfda15e1232554487c7285fb464f22705a23ce/packages/react-reconciler/src/ReactFiberWorkLoop.new.js#L1858) + +Suspense์—์„œ๋Š” ์ž‘์—…์ด Incomplete ์ƒํƒœ์ด๋ฏ€๋กœ, ์šฐ๋ฆฌ๋Š” Incomplete ๋ธŒ๋žœ์น˜๋งŒ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +```ts +function completeUnitOfWork(unitOfWork: Fiber): void { + // ํ˜„์žฌ ์ž‘์—… ๋‹จ์œ„๋ฅผ ์™„๋ฃŒํ•œ ํ›„ ๋‹ค์Œ ํ˜•์ œ ์ž‘์—…์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. + // ํ˜•์ œ ์ž‘์—…์ด ์—†์œผ๋ฉด ๋ถ€๋ชจ fiber๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค. + let completedWork = unitOfWork; + do { + // ํ˜„์žฌ์˜ ํ”Œ๋Ÿฌ์‹œ๋œ ์ƒํƒœ๋Š” alternate์ž…๋‹ˆ๋‹ค. + // ์ด์ƒ์ ์œผ๋กœ๋Š” ์ด ์ƒํƒœ์— ์˜์กดํ•˜์ง€ ์•Š์•„์•ผ ํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ ์˜์กดํ•˜๋ฉด ์ง„ํ–‰ ์ค‘์ธ ์ž‘์—…์— ์ถ”๊ฐ€ ํ•„๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + const current = completedWork.alternate; + const returnFiber = completedWork.return; + + // ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ๋Š”์ง€ ์•„๋‹ˆ๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + if ((completedWork.flags & Incomplete) === NoFlags) { + // ์ž‘์—…์ด ์ •์ƒ์ ์œผ๋กœ ์™„๋ฃŒ๋œ ๊ฒฝ์šฐ์˜ ์ฒ˜๋ฆฌ์ž…๋‹ˆ๋‹ค. + // ์ด ๋ถ€๋ถ„์€ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค. + } else { + // ์ด fiber๋Š” ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. + // complete ๋‹จ๊ณ„์— ๋“ค์–ด๊ฐ€์ง€ ์•Š๊ณ  ์Šคํƒ์—์„œ ๊ฐ’์„ pop ํ•ฉ๋‹ˆ๋‹ค. + // ๋งŒ์•ฝ ์ด๊ฒƒ์ด boundary๋ผ๋ฉด, ๊ฐ€๋Šฅํ•œ ๊ฐ’์„ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค. + const next = unwindWork(current, completedWork, subtreeRenderLanes); + + // ์ด fiber๊ฐ€ ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, lanes๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + if (next !== null) { + // ์ด ์ž‘์—…์„ ์™„๋ฃŒํ•˜๋Š” ๊ณผ์ •์—์„œ ์ƒˆ๋กœ์šด ์ž‘์—…์ด ์ƒ์„ฑ๋œ ๊ฒฝ์šฐ, ๊ทธ ์ž‘์—…์„ ๋‹ค์Œ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. + // ๋‹ค์‹œ ์—ฌ๊ธฐ์— ๋Œ์•„์˜ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + // ์žฌ์‹œ์ž‘ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ํ˜ธ์ŠคํŠธ ํšจ๊ณผ๊ฐ€ ์•„๋‹Œ ๊ฒƒ์€ effect ํƒœ๊ทธ์—์„œ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. + next.flags &= HostEffectMask; + workInProgress = next; + return; + } + + if (returnFiber !== null) { + // ๋ถ€๋ชจ fiber๋ฅผ ๋ฏธ์™„๋ฃŒ ์ƒํƒœ๋กœ ํ‘œ์‹œํ•˜๊ณ , ์„œ๋ธŒํŠธ๋ฆฌ ํ”Œ๋ž˜๊ทธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. + returnFiber.flags |= Incomplete; + returnFiber.subtreeFlags = NoFlags; + returnFiber.deletions = null; + } else { + // ๋ฃจํŠธ๊นŒ์ง€ ๋ชจ๋‘ ๋˜๋Œ์•„์™”์Šต๋‹ˆ๋‹ค. + workInProgressRootExitStatus = RootDidNotComplete; + workInProgress = null; + return; + } + } + + const siblingFiber = completedWork.sibling; + if (siblingFiber !== null) { + // ์ด returnFiber์— ๋” ํ•  ์ผ์ด ์žˆ์œผ๋ฉด, ๋‹ค์Œ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. + workInProgress = siblingFiber; + return; + } + + // ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด, ๋ถ€๋ชจ๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค. + completedWork = returnFiber; + // ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ ๋‹ค์Œ ์ž‘์—… ํ•ญ๋ชฉ์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. + workInProgress = completedWork; + } while (completedWork !== null); + + // ๋ฃจํŠธ์— ๋„๋‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค. + if (workInProgressRootExitStatus === RootInProgress) { + workInProgressRootExitStatus = RootCompleted; + } +} +``` + +completeUnitOfWork๋Š” fiber ๋…ธ๋“œ๋ฅผ ์กฐ์ •ํ•˜๋Š” ๊ณผ์ •์—์„œ ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. + +[Traversal ์•Œ๊ณ ๋ฆฌ์ฆ˜](https://jser.dev/react/2022/01/16/fiber-traversal-in-react)์˜ ์„ค๋ช…์— ๋”ฐ๋ผ ์ด ํ•จ์ˆ˜๋Š” ์ž‘์—… ๋‹จ์œ„๋ฅผ ์™„๋ฃŒํ•˜๊ณ , ํ˜•์ œ ๋…ธ๋“œ๋‚˜ ๋ถ€๋ชจ ๋…ธ๋“œ๋กœ ์ด๋™ํ•˜๋ฉด์„œ ๋ชจ๋“  ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. + +Incomplete ์ƒํƒœ์˜ fiber ๋…ธ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ข€ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +```ts +const next = unwindWork(current, completedWork, subtreeRenderLanes); + +// ์ด fiber๊ฐ€ ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, lanes๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. +if (next !== null) { + // ์ด ์ž‘์—…์„ ์™„๋ฃŒํ•˜๋Š” ๊ณผ์ •์—์„œ ์ƒˆ๋กœ์šด ์ž‘์—…์ด ์ƒ์„ฑ๋œ ๊ฒฝ์šฐ, ๊ทธ ์ž‘์—…์„ ๋‹ค์Œ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. + // ๋‹ค์‹œ ์—ฌ๊ธฐ์— ๋Œ์•„์˜ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + // ์žฌ์‹œ์ž‘ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ํ˜ธ์ŠคํŠธ ํšจ๊ณผ๊ฐ€ ์•„๋‹Œ ๊ฒƒ์€ effect ํƒœ๊ทธ์—์„œ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. + next.flags &= HostEffectMask; + workInProgress = next; + return; +} +``` + +unwindWork๊ฐ€ ๋ฐ˜ํ™˜๋˜๋ฉด ์ผ๋ถ€ ์ž‘์—…์„ ๊ณ„์†ํ•  ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +๋˜ํ•œ ์กฐ์ƒ ๋…ธ๋“œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ๋ฏธ์™„๋ฃŒ ์ƒํƒœ๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. + +์ด๋ฆ„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด `unwindWork`๋Š” ์ปจํ…์ŠคํŠธ ๋“ฑ์˜ ์ •๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.[์†Œ์Šค์ฝ”๋“œ](https://github.com/facebook/react/blob/b8cfda15e1232554487c7285fb464f22705a23ce/packages/react-reconciler/src/ReactFiberUnwindWork.new.js#L52) + +```ts +case SuspenseComponent: { + popSuspenseContext(workInProgress); + const flags = workInProgress.flags; + if (flags & ShouldCapture) { + workInProgress.flags = (flags & ~ShouldCapture) | DidCapture; + // ์„œ์ŠคํŽœ์Šค ํšจ๊ณผ๋ฅผ ์บก์ฒ˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ๊ณ„๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. + if ( + enableProfilerTimer && + (workInProgress.mode & ProfileMode) !== NoMode + ) { + transferActualDuration(workInProgress); + } + return workInProgress; + } + return null; +} +``` + +Suspense๋กœ ๋˜๋Œ์•„๊ฐˆ ๋•Œ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ผ์ด ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค: + +1. ์„œ์ŠคํŽœ์Šค ์ปจํ…์ŠคํŠธ๋ฅผ pop ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ดํ›„ ์—ํ”ผ์†Œ๋“œ์—์„œ ๋‹ค๋ฃฐ ๊ฒƒ์ž…๋‹ˆ๋‹ค. +2. ShouldCapture๋ฅผ ์ฐพ์œผ๋ฉด, ์ด๋ฅผ DidCapture๋กœ ์„ค์ •ํ•˜๊ณ  ์ž์‹ ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. + +ShouldCapture๋Š” complete ๋‹จ๊ณ„์—์„œ DidCapture๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. + +## ์š”์•ฝ + +์˜ค๋žœ ์—ฌ์ •์„ ๊ฑฐ์ณ, ๋‹ค์Œ์€ ์š”์•ฝ์ž…๋‹ˆ๋‹ค. + +Suspense๋Š” DidCapture ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉ: + +1. `DidCapture` ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ fallback์„ ๋ Œ๋”๋งํ• ์ง€, ๋‚ด์šฉ์„ ๋ Œ๋”๋งํ• ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค (๊ธฐ๋ณธ ์ž์‹๋“ค). + +2. Suspense๋Š” ๋‚ด์šฉ์„ Offscreen ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐ์Œ‰๋‹ˆ๋‹ค: + ์ด๋กœ ์ธํ•ด fallback์ด ๋ Œ๋”๋ง๋˜๋”๋ผ๋„, ๋‚ด์šฉ์ด fiber ํŠธ๋ฆฌ์—์„œ ์ œ๊ฑฐ๋˜์ง€ ์•Š์•„ ์ƒํƒœ๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. + +3. ์กฐ์ • ์ค‘ Suspense์˜ ๊ฒฐ์ •: + `DidCapture` ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ธฐ์ค€์œผ๋กœ Offscreen์„ ๊ฑด๋„ˆ๋›ธ์ง€ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.์ด๋Š” "์ผ๋ถ€ fibers๋ฅผ ์ˆจ๊ธฐ๋Š”" ํšจ๊ณผ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. + +4. Promise๊ฐ€ throw๋  ๋•Œ: + ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Suspense ๊ฒฝ๊ณ„๋ฅผ ์ฐพ์•„ `ShouldCapture` ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•˜๊ณ , promise๋Š” ping ๋ฐ retry ๋ฆฌ์Šค๋„ˆ์™€ ํ•จ๊ป˜ ์ฒด์ธ๋ฉ๋‹ˆ๋‹ค. + ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ, errored ์ปดํฌ๋„ŒํŠธ์—์„œ Suspense๊นŒ์ง€์˜ ๋ชจ๋“  fiber๊ฐ€ Incomplete๋กœ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค. + ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Suspense๋ฅผ ์™„๋ฃŒํ•˜๋ ค๊ณ  ํ•  ๋•Œ, `ShouldCapture`๋Š” DidCapture๋กœ ํ‘œ์‹œ๋˜๊ณ  Suspense ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. + ์ž‘์—… ๋ฃจํ”„๊ฐ€ Suspense๋ฅผ ์กฐ์ •: ์ด๋ฒˆ์—๋Š” fallback ๋ธŒ๋žœ์น˜๋ฅผ ๋ Œ๋”๋งํ•˜๋ฉด์„œ ๊ณ„์† ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. + +5. Promise๊ฐ€ ํ•ด๊ฒฐ๋  ๋•Œ: + ping ๋ฐ retry ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. (์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ดํ›„ ์—ํ”ผ์†Œ๋“œ์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค) diff --git a/June/article/CSS vs. CSS-in-JS: How-and-why-to-use-each.md b/June/article/CSS vs. CSS-in-JS: How-and-why-to-use-each.md new file mode 100644 index 0000000..9a89b26 --- /dev/null +++ b/June/article/CSS vs. CSS-in-JS: How-and-why-to-use-each.md @@ -0,0 +1,475 @@ +## [CSS vs. CSS-in-JS: How and why to use each](https://blog.logrocket.com/css-vs-css-in-js/) + +### ๐Ÿ—“๏ธ ๋ฒˆ์—ญ ๋‚ ์งœ: 2024.06.10 + +### ๐Ÿงš ๋ฒˆ์—ญํ•œ ํฌ๋ฃจ: ๋ฒ„๊ฑด๋””(์ „ํƒœํ—Œ) + +--- + +# CSS vs. CSS-in-JS: ๊ฐ๊ฐ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ์ด์œ  + +JavaScript ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ฐœ๋ฐœ์ž๋“ค์ด ํ”ํžˆ ์ง๋ฉดํ•˜๋Š” ๋”œ๋ ˆ๋งˆ ์ค‘ ํ•˜๋‚˜๋Š” CSS-in-JS๋ฅผ ์‚ฌ์šฉํ• ์ง€ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค. + +React ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ์•„๋งˆ CSS-in-JS๋ฅผ ์‚ฌ์šฉํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +![](https://blog.logrocket.com/wp-content/uploads/2022/11/How-why-use-modern-CSS-vs-CSS-in-JS.png) + +์š”์ฆ˜ CSS vs CSS-in-JS๋Š” ๋œจ๊ฑฐ์šด ์ฃผ์ œ์ž…๋‹ˆ๋‹ค. + +์ฃผ๋กœ CSS-in-JS๊ฐ€ ์„ฑ๋Šฅ ๋ฌธ์ œ๋กœ ์ง€์ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์ƒˆ๋กœ์šด CSS ๊ธฐ๋Šฅ๋“ค๋„ ๊ฐœ๋ฐœ ์ค‘์— ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ธ€์˜ ๋ชฉ์ ์€ ํ˜„๋Œ€ CSS์˜ ํ˜„์žฌ ์ƒํƒœ์™€ ๋ฏธ๋ž˜์— ์–ด๋–ป๊ฒŒ ๋ณ€ํ™”ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š”์ง€๋ฅผ ๊ณ ๋ คํ•˜์—ฌ, ๋‹ค๊ฐ€์˜ค๋Š” ํ”„๋กœ์ ํŠธ์—์„œ CSS์™€ CSS-in-JS ์ค‘์—์„œ ์„ ํƒํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ฃผ๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. + +1. [Render-blocking and CSS](https://blog.logrocket.com/css-vs-css-in-js/#render-blocking-css) +2. [What CSS-in-JS offers](https://blog.logrocket.com/css-vs-css-in-js/#what-css-in-js-offers) +3. [Pros of CSS-in-JS](https://blog.logrocket.com/css-vs-css-in-js/#pros-css-in-js) +4. [Cons of CSS-in-JS](https://blog.logrocket.com/css-vs-css-in-js/#cons-css-in-js) +5. [Recommendations for where to use CSS-in-JS](https://blog.logrocket.com/css-vs-css-in-js/#recommendations-where-use-css-in-js) +6. [Overview of CSS Module](https://blog.logrocket.com/css-vs-css-in-js/#overview-css-module) +7. [Pros of CSS Module](https://blog.logrocket.com/css-vs-css-in-js/#pros-css-module) +8. [Cons of CSS Module](https://blog.logrocket.com/css-vs-css-in-js/#cons-css-module) +9. [Recommendations for where to use CSS Module](https://blog.logrocket.com/css-vs-css-in-js/#recommendations-where-use-css-module) +10. [Modern CSS features to watch](https://blog.logrocket.com/css-vs-css-in-js/#modern-css-features-watch) + +์ด ๊ธ€์—์„œ ์ œ์‹œํ•˜๋Š” ๋ชจ๋“  ์ฝ”๋“œ ์˜ˆ์‹œ์™€ ๋ฐ๋ชจ๋Š” React์™€ CSS๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ ๊ณ„์† ์ง„ํ–‰ํ•˜๊ธฐ ์ „์— ์ด ๋‘ ๊ธฐ์ˆ ์— ์ต์ˆ™ํ•ด์ง€์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. + +๋ชจ๋“  JavaScript ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” CSS-in-JS์˜ ์•„์ด๋””์–ด๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ธ€์—์„œ๋Š” CSS-in-JS์˜ ์ ์šฉ๊ณผ ๊ทธ ์žฅ๋‹จ์ ์„ ๋…ผ์˜ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” JavaScript ํ”„๋ก ํŠธ์—”๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ React๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + +## - ๋ Œ๋”๋ง ์ฐจ๋‹จ ๋ฐ CSS + +๋ณธ๊ฒฉ์ ์œผ๋กœ ์–ด๋–ค ๊ฒƒ์ด ์ข‹๊ณ  ์ข‹์ง€ ์•Š์€์ง€ ๋…ผํ•˜๊ธฐ ์ „์—, CSS๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜๋Š” ๋ Œ๋”๋ง ๋ฌธ์ œ์— ๋Œ€ํ•ด ์กฐ๊ธˆ ์ด์•ผ๊ธฐํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +์ „ํ†ต์ ์œผ๋กœ, ๋ธŒ๋ผ์šฐ์ €๋Š” ๋จผ์ € HTML์„ ๋กœ๋“œํ•˜๊ณ , ๊ทธ ๋‹ค์Œ์— ๋ชจ๋“  ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๋กœ๋ถ€ํ„ฐ CSS๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. + +์ดํ›„ ๋ธŒ๋ผ์šฐ์ €๋Š” ์™ธ๋ถ€ ๋ฐ ๋‚ด๋ถ€ CSS ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CSSOM(CSS Object Model)์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. + +์ด์ œ ๋ธŒ๋ผ์šฐ์ €๋Š” CSS ๊ณ„์ธต ๊ทœ์น™์— ๋”ฐ๋ผ ๋ Œ๋”๋œ HTML์— ์Šคํƒ€์ผ์„ ์ ์šฉํ•  ์ค€๋น„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. + +์ด ๊ณผ์ •์€ CSS๊ฐ€ [ํŽ˜์ด์ง€ ๋ Œ๋”๋ง์„ ์ฐจ๋‹จ](https://blog.logrocket.com/11-best-practices-eliminate-render-blocking-resources/)ํ•˜๊ณ  ์š”์ฒญ๋œ ํŽ˜์ด์ง€์˜ ์ฒซ ํŽ˜์ธํŠธ(first paint)๋ฅผ ์ง€์—ฐ์‹œํ‚ต๋‹ˆ๋‹ค. + +์ฒซ ํŽ˜์ธํŠธ๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์š”์ฒญ๋œ ํŽ˜์ด์ง€์˜ ์ฒซ ๋ฒˆ์งธ ํ”ฝ์…€์„ ํ™”๋ฉด์— ๊ทธ๋ฆฌ๋Š” ์ด๋ฒคํŠธ์ž…๋‹ˆ๋‹ค. + +์ฒซ ํŽ˜์ธํŠธ๊ฐ€ 0.5์ดˆ ์ด์ƒ ์ง€์—ฐ๋˜๋ฉด ์‚ฌ์šฉ์ž ๋ถˆ๋งŒ์กฑ์˜ ์œ„ํ—˜์ด ์ปค์ง€๊ณ , ์ด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ชฉํ‘œ์— ๋ถ€์ •์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +CSS๋ฅผ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋” ๋นจ๋ฆฌ ์ œ๊ณตํ•  ์ˆ˜๋ก ํŽ˜์ด์ง€์˜ ์ฒซ ํŽ˜์ธํŠธ ์‹œ๊ฐ„์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## - ๋ Œ๋”๋ง ์ฐจ๋‹จ์— ๋Œ€์‘ํ•˜๋Š” ๋ฒ• + +HTTP/2๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์—ฌ๋Ÿฌ HTML, CSS, ๋ฐ JS ํŒŒ์ผ์„ ๋ณ‘๋ ฌ๋กœ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์€ HTTP/1.1์—์„œ๋Š” ์ œํ•œ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค. + +๋Œ€๋ถ€๋ถ„์˜ ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €์™€ ์›น์‚ฌ์ดํŠธ๋Š” ์ด์ œ HTTP/2๋ฅผ ์ง€์›ํ•˜๋ฉฐ, ์ด๋Š” ๋‹ค๋ฅธ ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๋ฐœ์ƒํ•˜๋Š” ๋ Œ๋”๋ง ์ฐจ๋‹จ์„ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค. + +![](https://blog.logrocket.com/wp-content/uploads/2022/11/img1-Graphic-showing-difference-file-loading-HTTP1-HTTP2.png) + +๊ทธ๋Ÿฌ๋‚˜ ๋ Œ๋”๋ง ์ฐจ๋‹จ์—๋Š” ํŒŒ์ผ ๋กœ๋”ฉ ์†๋„ ์™ธ์—๋„ ๋‹ค๋ฅธ ์š”์ธ์ด ์žˆ์Šต๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด, ์šฐ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•œ ํŽ˜์ด์ง€์— ๋งŽ์€ CSS๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ด…์‹œ๋‹ค. + +ํŽ˜์ด์ง€๋งˆ๋‹ค ํ•˜๋‚˜์˜ ๋งˆ์Šคํ„ฐ CSS ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์…€๋ ‰ํ„ฐ๋“ค๋„ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์œ„์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ CSS UI ํ”„๋ ˆ์ž„์›Œํฌ๋‚˜ ๋น ๋ฅด๊ฒŒ ๋””์ž์ธ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“  UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ง์ ‘ ์†Œ๋น„ํ•˜๋Š” ๋ฐฉ์‹์— ์ต์ˆ™ํ•ด์ ธ ์žˆ๋Š” ์ƒํ™ฉ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. + +๊ทธ ํ”„๋ ˆ์ž„์›Œํฌ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ฐธ์กฐ๋œ ๋ชจ๋“  ์Šคํƒ€์ผ์ด ๋ชจ๋“  ํŽ˜์ด์ง€์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. + +๊ฒฐ๊ณผ์ ์œผ๋กœ, ์ตœ์ข… ํŽ˜์ด์ง€์˜ CSS ์Šคํƒ€์ผ์—๋Š” ๋” ๋งŽ์€ ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. + +CSS๊ฐ€ ๋งŽ์„์ˆ˜๋ก ๋ธŒ๋ผ์šฐ์ €๊ฐ€ CSSOM์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋ฉฐ, ์ด๋Š” ์™„์ „ํžˆ ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง ์ฐจ๋‹จ์„ ์ดˆ๋ž˜ํ•ฉ๋‹ˆ๋‹ค. + +์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด CSS๋ฅผ ์ž‘์€ ์ฒญํฌ๋กœ ๋ถ„ํ• ํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. + +๋‹ค์‹œ ๋งํ•ด, ์ „์—ญ ์Šคํƒ€์ผ๊ณผ ์ค‘์š”ํ•œ CSS๋Š” ํ•˜๋‚˜์˜ ๋ฒ”์šฉ CSS ํŒŒ์ผ์— ์œ ์ง€ํ•˜๊ณ , ๋‚˜๋จธ์ง€๋Š” ๋ชจ๋‘ ์ปดํฌ๋„ŒํŠธํ™”ํ•ฉ๋‹ˆ๋‹ค. + +์ด ์ „๋žต์€ ํ›จ์”ฌ ๋” ํ•ฉ๋ฆฌ์ ์ด๋ฉฐ ๋ถˆํ•„์š”ํ•œ ์ฐจ๋‹จ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. + +![](https://blog.logrocket.com/wp-content/uploads/2022/11/img2-Project-structure-componentized-CSS.png) + +์œ„ ๊ทธ๋ฆผ์€ React์—์„œ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด ๊ฐœ๋ณ„ CSS ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ์ „ํ†ต์ ์ธ ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. + +๊ฐ CSS ํŒŒ์ผ์ด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์— ์ง์ ‘ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์–ด, ๊ด€๋ จ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž„ํฌํŠธ๋  ๋•Œ๋งŒ ๋กœ๋“œ๋˜๊ณ  ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ œ๊ฑฐ๋˜๋ฉด ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. + +์ด ๋ฐฉ๋ฒ•์—๋Š” ํ•œ ๊ฐ€์ง€ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด, ์šฐ๋ฆฌ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— 100๊ฐœ์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๊ณ , ๊ฐ™์€ ํ”„๋กœ์ ํŠธ๋ฅผ ์ž‘์—…ํ•˜๋Š” ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค์ด ์ผ๋ถ€ CSS ํŒŒ์ผ์—์„œ ๊ฐ™์€ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์‹ค์ˆ˜๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค. + +์—ฌ๊ธฐ์„œ ๊ฐ ์ปดํฌ๋„ŒํŠธ์˜ CSS ํŒŒ์ผ์˜ ๋ฒ”์œ„๋Š” ๊ธ€๋กœ๋ฒŒ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Ÿฌํ•œ ์‹ค์ˆ˜๋กœ ์ค‘๋ณต๋œ ์Šคํƒ€์ผ์€ ์„œ๋กœ๋ฅผ ๊ณ„์† ๋ฎ์–ด์“ฐ๊ณ  ์ „์—ญ์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. + +์ด๋Ÿฌํ•œ ์ƒํ™ฉ์€ ์‹ฌ๊ฐํ•œ ๋ ˆ์ด์•„์›ƒ ๋ฐ ๋””์ž์ธ ๋ถˆ์ผ์น˜๋ฅผ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +CSS-in-JS๋Š” ์ด ์Šค์ฝ”ํ•‘ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค๊ณ  ์•Œ๋ ค์ ธ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋‹ค์Œ ์„ธ๊ทธ๋จผํŠธ์—์„œ๋Š” CSS-in-JS๋ฅผ ๋†’์€ ์ˆ˜์ค€์—์„œ ๊ฒ€ํ† ํ•˜๊ณ , ๊ทธ๊ฒƒ์ด ์ด ์Šค์ฝ”ํ•‘ ๋ฌธ์ œ๋ฅผ ํ•œ ๋ฒˆ์— ํšจ๊ณผ์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋…ผ์˜ํ•ฉ๋‹ˆ๋‹ค. + +## - CSS-in-JS๋Š” ๋ฌด์—‡์„ ์ œ์•ˆํ•˜๋Š”๊ฐ€ + +๊ฐ„๋‹จํžˆ ๋งํ•ด, CSS-in-JS๋Š” JavaScript๋ฅผ ํ†ตํ•ด ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ CSS ์†์„ฑ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์™ธ๋ถ€ ๊ธฐ๋Šฅ ๊ณ„์ธต์ž…๋‹ˆ๋‹ค. + +์ด ๋ชจ๋“  ๊ฒƒ์€ 2015๋…„์— [JSS๋ผ๋Š” JavaScript ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ](https://cssinjs.org/)์—์„œ ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ ํ™œ๋ฐœํ•˜๊ฒŒ ์œ ์ง€๋ณด์ˆ˜๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. JavaScript ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ์…€๋ ‰ํ„ฐ์— CSS ์†์„ฑ์„ ์ œ๊ณตํ•˜๋ฉด, ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋  ๋•Œ ์ž๋™์œผ๋กœ ํ•ด๋‹น ์…€๋ ‰ํ„ฐ์— ์†์„ฑ์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. + +React์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด JavaScript๊ฐ€ ํ”„๋ก ํŠธ์—”๋“œ ๋ Œ๋”๋ง ๋ฐ ๊ด€๋ฆฌ๋ฅผ ์žฅ์•…ํ•˜๋ฉด์„œ, [styled-components](https://blog.logrocket.com/how-style-react-router-links-styled-components/)๋ผ๋Š” CSS-in-JS ์†”๋ฃจ์…˜์ด ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ ๋‹ค๋ฅธ ์ ์  ์ธ๊ธฐ๋ฅผ ์–ป๊ณ  ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ [Emotion ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ](https://blog.logrocket.com/styled-components-vs-emotion-for-handling-css/)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +CSS-in-JS์˜ ์˜ˆ์ œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ๋ฐฉ์‹์ธ ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹œ์—ฐํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +## - ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•œ CSS-in-JS์˜ ์‚ฌ์šฉ ์˜ˆ์‹œ + +React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์•„๋ž˜ Yarn ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ styled-components ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•˜์„ธ์š”. [๋‹ค๋ฅธ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €](https://blog.logrocket.com/javascript-package-managers-compared/)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, [styled-components ์„ค์น˜ ๋ฌธ์„œ](https://styled-components.com/docs/basics#installation)๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ์ ์ ˆํ•œ ์„ค์น˜ ๋ช…๋ น์–ด๋ฅผ ์ฐพ์œผ์„ธ์š”: + +``` +yarn add styled-components +``` + +styled-components ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•œ ํ›„, styled ํ•จ์ˆ˜๋ฅผ ์ž„ํฌํŠธํ•˜์—ฌ ์•„๋ž˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜์„ธ์š”: + +```ts +import styled from "styled-components"; + +const StyledButton = styled.a` + padding: 0.75em 1em; + background-color: ${({ primary }) => (primary ? "#07c" : "#333")}; + color: white; + + &:hover { + background-color: #111; + } +`; + +export default StyledButton; +``` + +React ํ™˜๊ฒฝ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ, ์œ„ ์ฝ”๋“œ๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก [CodePen ๋ฐ๋ชจ](https://codepen.io/_rahul/pen/oNywWXR)๋ฅผ ์ œ๊ณตํ•ด๋“œ๋ฆฝ๋‹ˆ๋‹ค: + +> ์›๊ธ€์— ์ฝ”๋“œํŽœ์ด ์•„์˜ˆ ์‚ฝ์ž…๋˜์–ด์žˆ๋Š”๋ฐ, mdํŒŒ์ผ์— ์ฝ”๋“œํŽœ์„ ์‚ฝ์ž…ํ•˜๋Š” ๋ฒ•์„ ๋ชฐ๋ผ์„œ ์ผ๋‹จ ๋งํฌ๋งŒ ๋„ฃ์–ด๋†“์•˜์Šต๋‹ˆ๋‹ค.. ๐Ÿฅฒ + +์œ„ ์ฝ”๋“œ๋Š” React์—์„œ ๋ฒ„ํŠผ-๋งํฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์Šคํƒ€์ผ๋งํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. + +์ด์ œ ์ด ์Šคํƒ€์ผ๋œ ์ปดํฌ๋„ŒํŠธ๋Š” ์–ด๋””์„œ๋“  ์ž„ํฌํŠธํ•˜์—ฌ ์ง์ ‘ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํƒ€์ผ์— ๋Œ€ํ•ด ๊ฑฑ์ •ํ•˜์ง€ ์•Š๊ณ  ๊ธฐ๋Šฅ์ ์ธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```ts +import StyledButton from './components/styles/Button.styled'; + +function App() { + return ( +
+ ... + Default Call-to-action + Primary Call-to-action +
+ ); +} + +export default App; +``` + +์Šคํƒ€์ผ๋œ ์ปดํฌ๋„ŒํŠธ์— ์ ์šฉ๋œ ์Šคํƒ€์ผ์€ ๋กœ์ปฌ ๋ฒ”์œ„๋กœ ํ•œ์ •๋˜์–ด ์žˆ์–ด CSS ํด๋ž˜์Šค ๋ช…๋ช…๊ณผ ๊ธ€๋กœ๋ฒŒ ๋ฒ”์œ„์— ๋Œ€ํ•ด ์‹ ๊ฒฝ ์“ธ ํ•„์š”๊ฐ€ ์—†์–ด์ง‘๋‹ˆ๋‹ค. + +๋˜ํ•œ, ์ปดํฌ๋„ŒํŠธ์— ์ œ๊ณต๋œ props๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋Šฅ์ด ์š”๊ตฌํ•˜๋Š” ๋‹ค๋ฅธ ๋…ผ๋ฆฌ์— ๋”ฐ๋ผ CSS๋ฅผ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## - CSS-in-JS์˜ ์žฅ์  + +JavaScript ๊ฐœ๋ฐœ์ž๋Š” CSS ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  CSS-in-JS๋กœ ์Šคํƒ€์ผ์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +CSS-in-JS ์ ‘๊ทผ ๋ฐฉ์‹์ด ํ•ด๊ฒฐํ•˜๋Š” ๊ฐ€์žฅ ํฐ ๋ฌธ์ œ๋Š” ๊ธ€๋กœ๋ฒŒ ์Šค์ฝ”ํ”„ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. + +๋˜ํ•œ, JavaScript ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋งค์šฐ ์œ ์šฉํ•œ ๋ช‡ ๊ฐ€์ง€ ๋‹ค๋ฅธ ์žฅ์ ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด์ œ ์ด๋Ÿฌํ•œ ์žฅ์  ์ค‘ ์ผ๋ถ€๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +## - ์Šค์ฝ”ํ•‘ ๋ฐ ์šฐ์„ ์ˆœ์œ„ ๋ฌธ์ œ ์—†์Œ + +์Šคํƒ€์ผ์ด ๋กœ์ปฌ ๋ฒ”์œ„ ๋‚ด์—์„œ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ๊ณผ ์ถฉ๋Œํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค. + +์Šคํƒ€์ผ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ์—„๊ฒฉํ•˜๊ฒŒ ์ด๋ฆ„์„ ์ •ํ•  ํ•„์š”๋„ ์—†์Šต๋‹ˆ๋‹ค. + +์Šคํƒ€์ผ์€ ์ž์‹ ์…€๋ ‰ํ„ฐ๋ฅผ ์•ž์— ๋ถ™์ด์ง€ ์•Š๊ณ  ํ•œ ์ปดํฌ๋„ŒํŠธ์—๋งŒ ๋…์ ์ ์œผ๋กœ ์ž‘์„ฑ๋˜๋ฏ€๋กœ, ์šฐ์„ ์ˆœ์œ„ ๋ฌธ์ œ๋„ ๊ฑฐ์˜ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +## - ๋™์  ์Šคํƒ€์ผ๋ง + +์กฐ๊ฑด๋ถ€ CSS๋Š” CSS-in-JS์˜ ๋˜ ๋‹ค๋ฅธ ์ค‘์š”ํ•œ ํŠน์ง•์ž…๋‹ˆ๋‹ค. + +์œ„์˜ ๋ฒ„ํŠผ ์˜ˆ์ œ์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด, prop ๊ฐ’์„ ํ™•์ธํ•˜๊ณ  ์ ์ ˆํ•œ ์Šคํƒ€์ผ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ ๋ณ€ํ˜•๋งˆ๋‹ค ๋ณ„๋„์˜ CSS ์Šคํƒ€์ผ์„ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๋” ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค. + +## CSS ์šฐ์„ ์ˆœ์œ„ ๊ฐ์†Œ + +CSS-in-JS๋Š” CSS ์„ ์–ธ์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. + +์Šคํƒ€์ผ๋งํ•˜๋Š” ์œ ์ผํ•œ ๋Œ€์ƒ์ด ์š”์†Œ ์ž์ฒด์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋™์ผํ•œ ์›์น™์ด ์ปดํฌ๋„ŒํŠธ ๋ณ€ํ˜•์„ ๋งŒ๋“ค ๋•Œ๋„ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. + +prop ๊ฐ์ฒด ๊ฐ’์„ ํ™•์ธํ•˜๊ณ  ํ•„์š”ํ•  ๋•Œ ๋™์  ์Šคํƒ€์ผ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## ์‰ฌ์šด ํ…Œ๋งˆ ์ ์šฉ + +[์‚ฌ์šฉ์ž ์ •์˜ CSS ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํ…Œ๋งˆ๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ](https://blog.logrocket.com/a-guide-to-theming-in-css/)์€ ํ•ฉ๋ฆฌ์ ์ž…๋‹ˆ๋‹ค. + +๊ฒฐ๊ตญ, ์‚ฌ์šฉ์ž ์ž…๋ ฅ์— ๋”ฐ๋ผ ํ…Œ๋งˆ๋ฅผ ์ „ํ™˜ํ•˜๊ณ  ๊ธฐ์–ตํ•˜๋Š” ๋กœ์ง์„ ์ž‘์„ฑํ•˜๋ ค๋ฉด JavaScript ์ชฝ์œผ๋กœ ์ด๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +CSS-in-JS๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ…Œ๋งˆ ๋กœ์ง์„ ์™„์ „ํžˆ JavaScript๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +styled-components์˜ ThemeProvider ๋ž˜ํผ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ ํ…Œ๋งˆ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ƒ‰์ƒ ์ฝ”๋“œ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +styled-components๋ฅผ ์‚ฌ์šฉํ•œ ์ปดํฌ๋„ŒํŠธ ํ…Œ๋งˆ ์ ์šฉ์„ ์ง์ ‘ ํ™•์ธํ•˜๋ ค๋ฉด ์ด [CodePen ์˜ˆ์ œ](https://codepen.io/_rahul/pen/qBKXevo)๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”: + +> ์ด๊ฒƒ๋„ ์ฝ”๋“œํŽœ ์ฒจ๋ถ€ ์•ˆ๋˜๋ฉด ๊ทธ๋ƒฅ ๋งํฌ๋งŒ ์‚ฝ์ž…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค~ + +## ๊ฐ„ํŽธํ•œ ์œ ์ง€๋ณด์ˆ˜ + +CSS-in-JS๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ๊ณผ ์žฅ์ ์„ ๊ณ ๋ คํ•˜๋ฉด, JavaScript ๊ฐœ๋ฐœ์ž๋Š” ์ˆ˜๋ฐฑ ๊ฐœ์˜ CSS ํŒŒ์ผ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค CSS-in-JS๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ํŽธ๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ ์—ฌ์ „ํžˆ CSS-in-JS๋กœ ๊ตฌ๋™๋˜๋Š” ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์œ ์ง€ํ•˜๋ ค๋ฉด JavaScript์™€ CSS ๋ชจ๋‘์— ๋Œ€ํ•œ ์ถฉ๋ถ„ํ•œ ์ดํ•ด๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ์‚ฌ์‹ค์€ ๋ณ€ํ•จ์—†์Šต๋‹ˆ๋‹ค. + +## CSS-in-JS์˜ ๋‹จ์  + +CSS-in-JS๋Š” ์Šค์ฝ”ํ•‘ ๋ฌธ์ œ๋ฅผ ๋งค์šฐ ์ž˜ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ ์ดˆ๊ธฐ ๋…ผ์˜์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์— ์ง์ ‘์ ์œผ๋กœ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๋ Œ๋”๋ง ์ฐจ๋‹จ๊ณผ ๊ฐ™์€ ๋” ํฐ ๋„์ „ ๊ณผ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด์™€ ํ•จ๊ป˜, CSS-in-JS ๊ฐœ๋…์ด ์—ฌ์ „ํžˆ ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๋ช‡ ๊ฐ€์ง€ ๋‹ค๋ฅธ ๋ฌธ์ œ๋“ค๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +## ๋ Œ๋”๋ง ์ง€์—ฐ + +CSS-in-JS๋Š” JavaScript๋ฅผ ์‹คํ–‰ํ•˜์—ฌ JavaScript ์ปดํฌ๋„ŒํŠธ์—์„œ CSS๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•œ ๋‹ค์Œ, ์ด ๊ตฌ๋ฌธ ๋ถ„์„๋œ ์Šคํƒ€์ผ์„ DOM์— ์‚ฝ์ž…ํ•ฉ๋‹ˆ๋‹ค. + +์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งŽ์„์ˆ˜๋ก ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ฒซ ํŽ˜์ธํŠธ(first paint)๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ๋” ๋งŽ์€ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค + +## ์บ์‹ฑ ๋ฌธ์ œ + +CSS ์บ์‹ฑ์€ ์—ฐ์†์ ์ธ ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ๊ฐ„์„ ๊ฐœ์„ ํ•˜๋Š” ๋ฐ ์ž์ฃผ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +CSS-in-JS๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” CSS ํŒŒ์ผ์ด ํฌํ•จ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์บ์‹ฑ์ด ํฐ ๋ฌธ์ œ๋กœ ์ž‘์šฉํ•ฉ๋‹ˆ๋‹ค. + +๋˜ํ•œ, ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ CSS ํด๋ž˜์Šค ์ด๋ฆ„์€ ์ด ๋ฌธ์ œ๋ฅผ ๋”์šฑ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. + +## CSS ์ „์ฒ˜๋ฆฌ๊ธฐ ์ง€์› ๋ถ€์กฑ + +์ผ๋ฐ˜์ ์ธ ์ปดํฌ๋„ŒํŠธํ™”๋œ CSS ์ ‘๊ทผ ๋ฐฉ์‹์—์„œ๋Š” SASS, Less, PostCSS ๋“ฑ๊ณผ ๊ฐ™์€ ์ „์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ CSS-in-JS์—์„œ๋Š” ๋™์ผํ•œ ์ง€์›์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + +## ์ง€์ €๋ถ„ํ•œ DOM + +CSS-in-JS๋Š” ๋ชจ๋“  ์Šคํƒ€์ผ ์ •์˜๋ฅผ JavaScript์—์„œ ์ผ๋ฐ˜ CSS๋กœ ๊ตฌ๋ฌธ ๋ถ„์„ํ•œ ๋‹ค์Œ, ์Šคํƒ€์ผ ๋ธ”๋ก์„ ์‚ฌ์šฉํ•˜์—ฌ DOM์— ์‚ฝ์ž…ํ•˜๋Š” ์•„์ด๋””์–ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. + +CSS-in-JS๋กœ ์Šคํƒ€์ผ๋ง๋œ ๊ฐ ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค 100๊ฐœ์˜ ์Šคํƒ€์ผ ๋ธ”๋ก์ด ๊ตฌ๋ฌธ ๋ถ„์„๋˜๊ณ  ์‚ฝ์ž…๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ฐ„๋‹จํžˆ ๋งํ•ด, ์ถ”๊ฐ€์ ์ธ ์˜ค๋ฒ„ํ—ค๋“œ ๋น„์šฉ์ด ๋ฐœ์ƒํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +## ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด์„ฑ + +CSS-in-JS ๊ธฐ๋Šฅ์„ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ JavaScript์—์„œ CSS ์Šคํƒ€์ผ๋กœ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•ด styled-components์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜์กดํ•˜๋ฏ€๋กœ, ์‹ค์ œ CSS ๊ตฌ๋ฌธ ๋ถ„์„ ์ „์— ๋งŽ์€ JavaScript๊ฐ€ ํฌํ•จ๋˜๊ณ  ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. + +## ํ•™์Šต ๊ณก์„  + +CSS-in-JS์—๋Š” ๋งŽ์€ ๊ธฐ๋ณธ CSS ๋ฐ SCSS ๊ธฐ๋Šฅ์ด ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค. + +CSS์™€ SCSS์— ์ต์ˆ™ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์ด CSS-in-JS์— ์ ์‘ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## ๊ด‘๋ฒ”์œ„ํ•œ ์ง€์› ๋ถ€์กฑ + +๋Œ€๋ถ€๋ถ„์˜ UI ๋ฐ ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ˜„์žฌ CSS-in-JS ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +์ด๋Š” ์—ฌ์ „ํžˆ ํ•ด๊ฒฐํ•ด์•ผ ํ•  ๋งŽ์€ ๋ฌธ์ œ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +์œ„์—์„œ ๋…ผ์˜ํ•œ ๋ฌธ์ œ๋“ค์€ ์ข…ํ•ฉ์ ์œผ๋กœ ์ €์„ฑ๋Šฅ, ์œ ์ง€๋ณด์ˆ˜ ์–ด๋ ค์›€, ์—ฌ๋Ÿฌ UI ๋ฐ UX ๋ถˆ์ผ์น˜๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## CSS-in-JS ์‚ฌ์šฉ ์ถ”์ฒœ ์‚ฌํ•ญ + +CSS-in-JS ์†”๋ฃจ์…˜์€ ์„ฑ๋Šฅ์ด ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง€๋Š” ์ž‘์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค๋ฃฐ ๋•Œ ์ด์ƒ์ ์ž…๋‹ˆ๋‹ค. + +์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ๋Œ€๊ทœ๋ชจ ๋””์ž์ธ ์‹œ์Šคํ…œ์„ ๊ฐ€์ง„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์ด์ƒ์ ์ด์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ปค์งˆ์ˆ˜๋ก, CSS-in-JS์˜ ๋ชจ๋“  ๋‹จ์ ์„ ๊ณ ๋ คํ•  ๋•Œ ๋ณต์žกํ•ด์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค. + +๋””์ž์ธ ์‹œ์Šคํ…œ์„ CSS-in-JS๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐ ๋งŽ์€ ์ž‘์—…์ด ํ•„์š”ํ•˜๋ฉฐ, ์ œ ์ƒ๊ฐ์—๋Š” ์–ด๋–ค JavaScript ๊ฐœ๋ฐœ์ž๋„ ๊ทธ๊ฒƒ์„ ๋‹ค๋ฃจ๊ณ  ์‹ถ์–ดํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +## CSS Module ์— ๊ด€ํ•˜์—ฌ + +CSS Module์€ ๋ Œ๋”๋œ CSS์—์„œ ๋ชจ๋“  ์†์„ฑ์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋กœ์ปฌ ๋ฒ”์œ„๋กœ ์ง€์ •๋˜๋Š” [CSS ํŒŒ์ผ](https://blog.logrocket.com/a-deep-dive-into-css-modules/)์ž…๋‹ˆ๋‹ค. + +JavaScript๋Š” CSS Module ํŒŒ์ผ์„ ์ถ”๊ฐ€๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ์Šคํƒ€์ผ ์„ ์–ธ์„ ์บก์Šํ™”ํ•˜๊ณ  ์Šค์ฝ”ํ•‘ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. + +CSS Module์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด CSS ํŒŒ์ผ ์ด๋ฆ„์„ .module.css ํ™•์žฅ์ž๋กœ ์ง€์ •ํ•˜๊ณ  ์ด๋ฅผ JavaScript ํŒŒ์ผ์— ์ž„ํฌํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ ์˜ˆ์‹œ๋Š” CSS Module์„ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ณธ ์˜ˆ์ œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค: + +```ts +import styles from './Button.module.css'; + +export default function Button(props) { + return ( + + {props.name} + + ); +} +``` + +์ด [StackBlitz ์˜ˆ์ œ](https://stackblitz.com/edit/react-hbivvp?file=src%2Fcomponents%2FButton%2FButton.module.css)๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ React์—์„œ CSS Modules์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•ด๋ณด์„ธ์š”. + +์ด ์˜ˆ์ œ๋Š” CSS Modules์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šค์ฝ”ํ•‘ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. + +StackBlitz ์˜ˆ์ œ์—์„œ Button.module.css์™€ AnotherButton.module.css์—์„œ ๋™์ผํ•œ ํด๋ž˜์Šค ์ด๋ฆ„์ด ์ฒ˜๋ฆฌ๋˜๊ณ  ์ตœ์ ํ™”๋˜์–ด ๋ช…๋ช… ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•ด๋ณด์„ธ์š”. + +## CSS Module์˜ ์žฅ์  + +CSS Module์ด ์ œ๊ณตํ•˜๋Š” ๊ฐ€์žฅ ํฐ ์ด์ ์€ ์Šค์ฝ”ํ•‘๊ณผ ์šฐ์„ ์ˆœ์œ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด CSS-in-JS์— ์˜์กดํ•  ํ•„์š”๋ฅผ ์—†์• ์ค€๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +CSS๋ฅผ ๊ฐ€๋Šฅํ•œ ์ „ํ†ต์ ์ธ ๋ฐฉ์‹์œผ๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ์Šค์ฝ”ํ•‘๊ณผ ์šฐ์„ ์ˆœ์œ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, CSS-in-JS๋Š” ๋ถˆํ•„์š”ํ•œ ์ž‘์—…์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +## ์Šค์ฝ”ํ•‘ ๋ฐ ์šฐ์„ ์ˆœ์œ„ ๋ฌธ์ œ ์—†์Œ + +์œ„์˜ ์˜ˆ์ œ์—์„œ ๋ณด๋“ฏ์ด, CSS Module์€ ์ „ํ†ต์ ์ธ CSS์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์Šค์ฝ”ํ•‘ ๋ฌธ์ œ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. + +CSS Module ํŒŒ์ผ์— ๊ทœ์น™์ด ๋Š์Šจํ•˜๊ฒŒ ์ž‘์„ฑ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ์„ ์ˆœ์œ„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ๋„ ๋“œ๋ญ…๋‹ˆ๋‹ค. + +## ์กฐ์ง๋œ ์ฝ”๋“œ + +๋ณ„๋„์˜ CSS ํŒŒ์ผ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์€ ์ œํ•œ์ฒ˜๋Ÿผ ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ ์ด ๋ฐฉ๋ฒ•์€ ์‹ค์ œ๋กœ ๋” ๋‚˜์€ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด, ์ €๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ๊ฐ์˜ ํด๋”๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค: + +``` +-Project - + src - + components - + Button - + Button.jsx - + Button.modules.css - + Carousel - + Carousel.jsx - + Carousel.modules.css; +``` + +## ์บ์‹ฑ ๊ฐ€๋Šฅ์„ฑ + +์ตœ์ข… ๋นŒ๋“œ์™€ ํ•จ๊ป˜ ์ƒ์„ฑ๋œ ์ตœ์†Œํ™”๋œ CSS ํŒŒ์ผ์€ ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ์บ์‹ฑ๋˜์–ด ์—ฐ์†์ ์ธ ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ๊ฐ„์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## CSS ์ „์ฒ˜๋ฆฌ + +PostCSS, SASS, Less ๋“ฑ๊ณผ ๊ฐ™์€ CSS ์ „์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ถ”๊ฐ€๋กœ ์ง€์›ํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ์ถ”๊ฐ€ ํŒจํ‚ค์ง€์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ํ•™์Šต ๊ณก์„  ์—†์Œ + +CSS๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์•Œ๊ณ  ์žˆ๋‹ค๋ฉด, ์•ž์„œ ์†Œ๊ฐœํ•œ ๋ช‡ ๊ฐ€์ง€ ์‚ฌํ•ญ์„ ์ œ์™ธํ•˜๊ณ ๋Š” ์ƒˆ๋กœ์šด ๊ฒƒ์„ ๋ฐฐ์šฐ์ง€ ์•Š๊ณ ๋„ CSS Module์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## ๋›ฐ์–ด๋‚œ ์ง€์› + +CSS Modules๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. + +๋ชจ๋“  ์ฃผ์š” ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ CSS Modules๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. + +## CSS Module์˜ ๋‹จ์  + +CSS Module์€ ๋งŽ์€ ์ด์ ์„ ์ œ๊ณตํ•˜์ง€๋งŒ ์™„๋ฒฝํ•œ ์†”๋ฃจ์…˜์€ ์•„๋‹™๋‹ˆ๋‹ค. + +์•„๋ž˜๋Š” ๊ณ ๋ คํ•ด์•ผ ํ•  ๋ช‡ ๊ฐ€์ง€ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. + +## ๋น„ํ‘œ์ค€์†์„ฑ + +๊ธ€๋กœ๋ฒŒ ๋ฒ”์œ„์˜ ์…€๋ ‰ํ„ฐ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•  ๋•Œ๋Š” +๊ทœ์น™์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +์ด๋Š” CSS ์‚ฌ์–‘์˜ ์ผ๋ถ€๊ฐ€ ์•„๋‹ˆ๋ฉฐ, ๊ธ€๋กœ๋ฒŒ ์Šคํƒ€์ผ์„ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด JavaScript์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. + +## ๋™์  ์Šคํƒ€์ผ ๋ถˆ๊ฐ€๋Šฅ + +CSS Module์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ชจ๋“  ์„ ์–ธ์ด ๋ณ„๋„์˜ CSS ํŒŒ์ผ๋กœ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ CSS ํŒŒ์ผ์—์„œ JavaScript๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ CSS-in-JS์ฒ˜๋Ÿผ ๋™์  ์Šคํƒ€์ผ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + +## ์™ธ๋ถ€ CSS ํŒŒ์ผ + +CSS Module์„ ์‚ฌ์šฉํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ์—์„œ CSS ํŒŒ์ผ ์‚ฌ์šฉ์„ ์ƒ๋žตํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. + +CSS Module์„ ์‚ฌ์šฉํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ ์™ธ๋ถ€ CSS ํŒŒ์ผ์„ ์œ ์ง€ํ•˜๊ณ  ์ž„ํฌํŠธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +## TypeScript ์ œํ•œ ์‚ฌํ•ญ + +TypeScript์—์„œ CSS Modules์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, index.d.ts ํŒŒ์ผ์— ๋ชจ๋“ˆ ์ •์˜๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์›นํŒฉ ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค: + +```ts +/** index.d.ts **/ +declare module "*.module.css"; // CSS Module ํŒŒ์ผ์šฉ TS ๋ชจ๋“ˆ +declare module "*.module.scss"; // SCSS ํ˜•์‹์˜ CSS Module ํŒŒ์ผ์šฉ TS ๋ชจ๋“ˆ +``` + +## CSS Module ์ถ”์ฒœ ๊ฒฝ์šฐ + +CSS Module์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋Œ€๊ทœ๋ชจ UI๋ฅผ ๊ฐ–์ถ˜ ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ข‹์€ ์„ ํƒ์ž…๋‹ˆ๋‹ค. + +CSS Module์ด ์ œ๊ณตํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ์ด ๊ถ๊ทน์ ์œผ๋กœ ์ „ํ†ต์ ์ด๊ณ  ์‹คํ—˜์ ์ด์ง€ ์•Š์€ ์‚ฌ์šฉ๋ฒ•์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ด ๋ฐฉ๋ฒ•์€ ์„ฑ๋Šฅ์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ์ˆ˜์ •ํ•˜๊ธฐ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค. + +CSS Module ํŒŒ์ผ์€ CSS๋งŒ ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์—, ์›ํ•˜๋Š” CSS ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์•ž์„œ ๋…ผ์˜ํ–ˆ๋“ฏ์ด, ๊ธฐ๋ณธ์ ์ธ CSS ์ง€์‹๋งŒ ์žˆ์œผ๋ฉด ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ์— ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. + +## ์ฃผ๋ชฉํ•  ํ˜„๋Œ€ CSS ๊ธฐ๋Šฅ + +์„œ๋ก ์—์„œ, CSS Module, CSS-in-JS ๋˜๋Š” ๋‹ค๋ฅธ JavaScript ์†”๋ฃจ์…˜์— ์˜์กดํ•˜์ง€ ์•Š๊ณ ๋„ ์Šค์ฝ”ํ•‘ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ํ˜„๋Œ€์ ์ธ CSS ๊ธฐ๋Šฅ์ด ๋ฏธ๋ž˜์— ๋„์›€์ด ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์–ธ๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ƒˆ๋กœ์šด ๋ฐ ๊ณ„ํš๋œ ๊ธฐ๋Šฅ๋“ค - ์˜ˆ๋ฅผ ๋“ค์–ด, ์Šค์ฝ”ํ•‘ ์ง€์‹œ์ž ๋ฐ @scope ๊ฐ€์ƒ ์š”์†Œ์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์€ ์ „ํ†ต์ ์ธ CSS์˜ ์˜ค๋ž˜๋œ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. + +์ด๋Š” ๊ฐœ๋ฐœ์ž๋“ค์ด ์ด๋Ÿฌํ•œ ๋ฌธ์ œ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ CSS-in-JS์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์— ์˜์กดํ•  ํ•„์š”๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +ํ˜„์žฌ ์ดˆ์•ˆ์— ์žˆ๋Š” ์Šค์ฝ”ํ”„๋œ CSS๊ฐ€ CSS-in-JS์™€ ์‹ฌ์ง€์–ด CSS Module์˜ ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +๋‹ค๋ฅธ ํ˜„๋Œ€ CSS ๊ธฐ๋Šฅ์˜ ์ „์ฒด ๋ชฉ๋ก์€ [State of CSS 2022](https://web.dev/blog/state-of-css-2022?hl=ko)๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”. + +## CSS ์Šค์ฝ”ํ•‘์˜ ์ž ์žฌ์  ๋ฏธ๋ž˜ + +[ +``` + +์ด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์€ ์Šค์ฝ”ํ•‘ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด CSS Module์ด๋‚˜ CSS-in-JS์˜ ํ•„์š”์„ฑ์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ธฐ๋Šฅ์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ด์งˆ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ๊ฒฐ๋ก  + +์œ„์—์„œ ์šฐ๋ฆฌ๋Š” CSS ๋ Œ๋”๋ง ์ฐจ๋‹จ์ด ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ฃผ์š” ์„ฑ๋Šฅ ๋ฌธ์ œ์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ๋…ผ์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฐ ๋‹ค์Œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ช‡ ๊ฐ€์ง€ ์†”๋ฃจ์…˜์„ ๋…ผ์˜ํ•˜๋ฉด์„œ CSS-in-JS, CSS Module, ๊ทธ๋ฆฌ๊ณ  ์ง„ํ–‰ ์ค‘์ธ ์ƒˆ๋กœ์šด ์Šค์ฝ”ํ”„๋œ CSS ๊ธฐ๋Šฅ์˜ ๊ณต์‹ ์ดˆ์•ˆ ์ƒํƒœ๋ฅผ ํƒ๊ตฌํ–ˆ์Šต๋‹ˆ๋‹ค. + +JavaScript๋ฅผ ์ข‹์•„ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋“ค์€ CSS-in-JS๋ฅผ ์„ ํ˜ธํ•˜๋Š”๋ฐ, ์ด๋Š” JavaScript๋กœ ๊ฑฐ์˜ ๋ชจ๋“  ์Šคํƒ€์ผ๋ง ์ธก๋ฉด์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +๋ฐ˜๋ฉด์— CSS๋ฅผ ์ข‹์•„ํ•˜๊ณ  ํ˜„์žฌ ๊ธฐ์ˆ ์ด ๊ฐœ๋ฐœ์ž์™€ ์ตœ์ข… ์‚ฌ์šฉ์ž ๋ชจ๋‘๋ฅผ ์ง€์›ํ•˜๊ธฐ๋ฅผ ์›ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์€ CSS Module์„ ์„ ํ˜ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ธฐ์‚ฌ๋ฅผ ์ฆ๊ฒ๊ฒŒ ์ฝ์œผ์…จ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๋Œ“๊ธ€๋กœ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ, ์งˆ๋ฌธ, ์ œ์•ˆ์„ ์•Œ๋ ค์ฃผ์„ธ์š”. diff --git a/June/article/How-useSyncExternalStore()-works-internally-in-React?.md b/June/article/How-useSyncExternalStore()-works-internally-in-React?.md new file mode 100644 index 0000000..d02c537 --- /dev/null +++ b/June/article/How-useSyncExternalStore()-works-internally-in-React?.md @@ -0,0 +1,880 @@ +## ๐Ÿ”— [How useSyncExternalStore() works internally in React?](https://jser.dev/2023-08-02-usesyncexternalstore/) + +### ๐Ÿ—“๏ธ ๋ฒˆ์—ญ ๋‚ ์งœ: 2024.06.10 + +### ๐Ÿงš ๋ฒˆ์—ญํ•œ ํฌ๋ฃจ: ๋ฒ„๊ฑด๋””(์ „ํƒœํ—Œ) + +--- + +# ๋ฆฌ์•กํŠธ์—์„œ useSyncExternalStore๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€ ? + +`useSyncExternalStore()`๋Š” ์™ธ๋ถ€ ์ €์žฅ์†Œ์— ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” React ํ›…์ž…๋‹ˆ๋‹ค. + +์ €๋Š” ์ด ํ›…์„ ์‚ฌ์šฉํ•ด๋ณธ ์ ์ด ์—†์ง€๋งŒ, ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์™€ ๊ฐ™์€ ์ž์ฒด ์ƒํƒœ๋ฅผ ๊ฐ€์ง„ ์›น API์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฝค ์œ ์šฉํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. + +## 1. ์™œ useSyncExternalStore๋Š” ํ•„์š”ํ•œ๊ฐ€ ? + +์•„๋ž˜๋Š” React.dev์—์„œ ๊ฐ€์ ธ์˜จ [๋ฐ๋ชจ ์ฝ”๋“œ](https://react.dev/reference/react/useSyncExternalStore)์ž…๋‹ˆ๋‹ค. + +์ด ์ฝ”๋“œ๋Š” ๋ธŒ๋ผ์šฐ์ €์˜ ํ˜„์žฌ ์˜จ๋ผ์ธ ์ƒํƒœ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. + +๋„คํŠธ์›Œํฌ๋ฅผ ๋„๋ฉด ์ƒํƒœ๊ฐ€ ๊ทธ์— ๋”ฐ๋ผ ์—…๋ฐ์ดํŠธ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ผ๋ถ€ ์ฝ”๋“œ๋Š” ์ˆ˜์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +## - useOnlineStatus.js + +```jsx +import { useEffect, useCallback, useState } from "react"; + +export function useOnlineStatus() { + const [isOnline, setIsOnline] = useState(navigator.onLine); + + const update = useCallback(() => { + setIsOnline(navigator.onLine); + }, []); + + useEffect(() => { + window.addEventListener("online", update); + window.addEventListener("offline", update); + return () => { + window.removeEventListener("online", update); + window.removeEventListener("offline", update); + }; + }, [update]); + + return isOnline; +} +``` + +## - App.js + +```jsx +import { useOnlineStatus } from "./useOnlineStatus.js"; + +function StatusBar() { + const isOnline = useOnlineStatus(); + return

{isOnline ? "โœ… Online" : "โŒ Disconnected"}

; +} + +function SaveButton() { + const isOnline = useOnlineStatus(); + + function handleSaveClick() { + console.log("โœ… Progress saved"); + } + + return ( + + ); +} + +export default function App() { + return ( + <> +

Turn on & off your network to see the status changing

+ + + ); +} +``` + +์ €๋งŒ์˜ `useOnlineStatus`๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. + +```jsx +import { useEffect, useCallback, useState } from "react"; +export function useOnlineStatus() { + const [isOnline, setIsOnline] = useState(navigator.onLine); + const update = useCallback(() => { + setIsOnline(navigator.onLine); + }, []); + useEffect(() => { + window.addEventListener("online", update); + window.addEventListener("offline", update); + return () => { + window.removeEventListener("online", update); + window.removeEventListener("offline", update); + }; + }, [update]); + return isOnline; +} +``` + +์ฝ”๋“œ๋Š” ๊ดœ์ฐฎ์•„ ๋ณด์ด์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๊ฒฌ๊ณ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +ํ•ต์‹ฌ ๋ฌธ์ œ๋Š” ์™ธ๋ถ€ ์ €์žฅ์†Œ๊ฐ€ ์–ธ์ œ๋“ ์ง€ ์—…๋ฐ์ดํŠธ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ `useState()`์™€ `useEffect()` ์‚ฌ์ด์— ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๊ฒฝ์šฐ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋‚˜์ค‘์— ๋“ฑ๋ก๋˜๋ฏ€๋กœ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ์ง€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. + +์ข€ ๋” ์ผ์ฐ ์‹คํ–‰๋˜๋Š” `useLayoutEffect()`๋‚˜ `useInsertionEffect()`๋กœ ์ „ํ™˜ํ•ด๋„ ๋ฌธ์ œ๋Š” ํ•ด๊ฒฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +์™ธ๋ถ€ ์ €์žฅ์†Œ์— ๋Œ€ํ•ด ์•„๋ฌด๊ฒƒ๋„ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด **์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์™ธ๋ถ€ ์ €์žฅ์†Œ๋ฅผ ์ฒ˜์Œ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋ณด๋‹ค ๋Šฆ์ง€ ์•Š๊ฒŒ ๋“ฑ๋ก๋˜๋„๋ก ํ•˜๊ฑฐ๋‚˜**, ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋“ฑ๋ก๋œ ํ›„ ํ•œ ๋ฒˆ ๋” ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +์•„๋ž˜์™€ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```ts +import { useEffect, useCallback, useState } from "react"; +export function useOnlineStatus() { + const [isOnline, setIsOnline] = useState(navigator.onLine); + const update = useCallback(() => { + setIsOnline(navigator.onLine); + }, []); + useEffect(() => { + window.addEventListener("online", update); + window.addEventListener("offline", update); + update(); + // ์™ธ๋ถ€ ์ €์žฅ์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํ•œ ๋ฒˆ ๋” ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. + + return () => { + window.removeEventListener("online", update); + window.removeEventListener("offline", update); + }; + }, [update]); + return isOnline; +} +``` + +์ค‘๋ณต๋œ ์ฝ”๋“œ๋ฅผ ์ง€์›€์œผ๋กœ์จ ์กฐ๊ธˆ ๋” ๋‚ซ๋„๋ก ์ˆ˜์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +```ts +import { useEffect, useCallback, useState } from "react"; +function getIsOnLine() { + return navigator.onLine; +} +function subscribe(callback) { + window.addEventListener("online", callback); + window.addEventListener("offline", callback); + callback(); + // ์ด๋Š” ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋Š” ๋ฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. + + return () => { + window.removeEventListener("online", callback); + window.removeEventListener("offline", callback); + }; +} +export function useOnlineStatus() { + const [isOnline, setIsOnline] = useState(getIsOnLine()); + const update = useCallback(() => { + setIsOnline(getIsOnLine()); + }, []); + useEffect(() => { + return subscribe(update); + }, [update]); + return isOnline; +} +``` + +์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์„ `useSyncExternalStore()`์ฒ˜๋Ÿผ ๋” ๋น„์Šทํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```tsx +import { useEffect, useCallback, useState } from "react"; + +function getIsOnLine() { + return navigator.onLine; +} + +function subscribe(callback) { + window.addEventListener("online", callback); + window.addEventListener("offline", callback); + return () => { + window.removeEventListener("online", callback); + window.removeEventListener("offline", callback); + }; +} +function useSyncExternalStore(subscribe, getSnapshot) { + const [data, setData] = useState(getSnapshot()); + const update = useCallback(() => { + setData(getSnapshot()); + }, []); + useEffect(() => { + update(); + return subscribe(update); + }, [update]); + return data; +} + +export function useOnlineStatus() { + const isOnline = useSyncExternalStore(subscribe, getIsOnLine); + return isOnline; +} +``` + +## - App.js + +```tsx +import { useOnlineStatus } from "./useOnlineStatus.js"; + +function StatusBar() { + const isOnline = useOnlineStatus(); + return

{isOnline ? "โœ… Online" : "โŒ Disconnected"}

; +} + +export default function App() { + return ( + <> +

Turn on & off your network to see the status changing

+ + + ); +} +``` + +์šฐ๋ฆฌ๊ฐ€ ์™ธ๋ถ€ ์ €์žฅ์†Œ๋ฅผ ๋™๊ธฐํ™”ํ•  ๋•Œ ์–ด๋ ค์šด ๋ถ€๋ถ„์€ ์™ธ๋ถ€์˜ ๋ชจ๋“  ์—…๋ฐ์ดํŠธ๊ฐ€ ๊ฐ์ง€๋˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +์šฐ๋ฆฌ์˜ ํ•ด๊ฒฐ์ฑ…์€ **์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ๋“ฑ๋ก๋œ ํ›„ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋„๋ก ๋ณด์žฅํ•˜๋Š” ๊ฒƒ**์ž…๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ์šฐ๋ฆฌ์˜ ๊ตฌํ˜„์—๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ '์ฐข๊น€(tear)' ํ˜„์ƒ์ž…๋‹ˆ๋‹ค. + +## - ์ฐข๊น€(Tearing) ๋ฌธ์ œ + +๋ฆฌ์•กํŠธ ํŒ€์ด [์ฐข๊น€(tearing)](https://github.com/reactwg/react-18/discussions/69) ํ˜„์ƒ์— ๋Œ€ํ•ด ํ›Œ๋ฅญํ•˜๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์—ˆ๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ๋Š” ๊ฐ„๋‹จํžˆ ์š”์•ฝํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +๋ฆฌ์•กํŠธ ํŒ€์—์„œ [useTransition()์ด ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์„ค๋ช…](https://jser.dev/2023-05-19-how-does-usetransition-work/)ํ•  ๋•Œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด, ๋™์‹œ ๋ชจ๋“œ(concurrent mode)์—์„œ๋Š” ๋ Œ๋”๋ง ๋‹จ๊ณ„๊ฐ€ ์ž‘์—…์˜ ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ ์ค‘๋‹จ๋˜๊ณ  ์žฌ๊ฐœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋ž˜์„œ ๋ฆฌ์•กํŠธ๊ฐ€ React ํŠธ๋ฆฌ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๋™์•ˆ, ์ด ๊ณผ์ •์ด ์ค‘๋‹จ๋˜๊ณ  ๋น„๋™๊ธฐ ์Šค์ผ€์ค„๋ง์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฒˆ ์‹œ๋„๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ์Šค์ผ€์ค„๋ง์ด ๋™๊ธฐ์ ์ด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, [React ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™](https://jser.dev/react/2022/03/16/how-react-scheduler-works)ํ•˜๋Š”์ง€ ์„ค๋ช…ํ•  ๋•Œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด, ๊ธฐ๋ณธ์ ์œผ๋กœ ์ตœ์†Œ ์ง€์—ฐ์ด ์—†๋Š” ๋” ๋‚˜์€ `setTimeout()`์ด๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ ๋ Œ๋”๋ง ๋„์ค‘ ์™ธ๋ถ€ ์ €์žฅ์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด UI์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๊ฐ€ ์ปค๋ฐ‹๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด๊ฒƒ์ด ์ฐข๊น€(tear) ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ์ด๋ฉฐ, ์•„๋ž˜๋Š” ๊ทธ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. + +## - App.js + +```tsx +import { useEffect, startTransition, useCallback, useState } from "react"; + +let data = 1; +function getData() { + return data; +} + +setTimeout(() => (data = 2), 100); + +function Cell() { + let start = Date.now(); + while (Date.now() - start < 50) { + // ๋™์‹œ์„ฑ ๋ชจ๋“œ์—์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์— ์ž‘์—…์„ ์–‘๋ณดํ•ฉ๋‹ˆ๋‹ค. + // 50๋ฐ€๋ฆฌ์ดˆ ๋™์•ˆ ์‹คํ–‰ ๋˜์–ด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•˜๊ณ , ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ + // ์ด ์‹œ๊ธด ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. + } + const data = getData(); + return
{data}
; +} + +export default function App() { + const [showCells, setShowCells] = useState(false); + + useEffect(() => { + startTransition(() => setShowCells(true)); + }, []); + return ( + <> +

+ Example of tearing.
+ below are multiple cells rendering same external data which changes during + rendering. +

+ {showCells ? ( +
+ + + + +
+ ) : ( +

preparing..

+ )} + + ); +} +``` + +์ฐข๊น€(tear) ํ˜„์ƒ์€ ๋™์‹œ ๋ชจ๋“œ(concurrent mode)์—์„œ ๋ Œ๋”๋ง์ด ์ค‘๋‹จ๋  ๊ฐ€๋Šฅ์„ฑ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. + +(์œ„ ์ฝ”๋“œ์—์„œ `startTransition()`์„ ์ œ๊ฑฐํ•˜๋ฉด ์ฐข๊น€ ํ˜„์ƒ์ด ์žฌํ˜„๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.) + +React๋Š” ๋‚ด๋ถ€ Fiber ์•„ํ‚คํ…์ฒ˜์— ์šฐ๋ฆฌ๊ฐ€ ๊ฐœ์ž…ํ•˜๋Š” ๊ฒƒ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. + +๊ทธ๋ž˜์„œ React๋Š” ์šฐ๋ฆฌ์—๊ฒŒ `useSyncExternalStore()`๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +์•„๋ž˜๋Š” ์œ„ ์˜ˆ์ œ์—์„œ ์•ฝ๊ฐ„ ์ˆ˜์ •ํ•œ ํ›„ `useSyncExternalStore()`๋ฅผ ์‚ฌ์šฉํ•œ ๋ฐ๋ชจ์ž…๋‹ˆ๋‹ค. + +## - App.js + +```ts +import { useEffect, useSyncExternalStore, startTransition, useCallback, useState } from 'react'; + +let data = 1 +function getData() { + return data +} + +setTimeout(() => data = 2, 100) + +function Cell() { + let start = Date.now() + while (Date.now() - start < 50) { + // ๋™์‹œ์„ฑ ๋ชจ๋“œ์—์„œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์— ์ž‘์—…์„ ์–‘๋ณดํ•ฉ๋‹ˆ๋‹ค. + // 50๋ฐ€๋ฆฌ์ดˆ ๋™์•ˆ ์‹คํ–‰ ๋˜์–ด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•˜๊ณ , ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ + // ์ด ์‹œ๊ธด ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. + } + const data = useSyncExternalStore(() => {return () => {}},getData); + return
{data}
+} + +export default function App() { + const [showCells, setShowCells] = useState(false) + + useEffect(() => { + startTransition(() => setShowCells(true)) + }, []) + return ( + <> +

Example of tearing.
below are multiple cells rendering same external data which changes during rendering.

+ {showCells ?
+ +
:

preparing..

} + + ); +} +``` + +์ฐข๊น€(tearing) ํ˜„์ƒ์ด ๊ณ ์ณ์ง„๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +## 2. ์–ด๋–ป๊ฒŒ useSyncExternalStore()๊ฐ€ ๋ฆฌ์•กํŠธ ๋‚ด๋ถ€์—์„œ ๋™์ž‘ํ•˜๋‚˜์š” ? + +์š”์•ฝํ•˜์ž๋ฉด, `useSyncExternalStore()`๋Š” ์šฐ๋ฆฌ์—๊ฒŒ ๋‘ ๊ฐ€์ง€๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +1. ์™ธ๋ถ€ ์ €์žฅ์†Œ์˜ ๋ชจ๋“  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๊ฐ์ง€๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. + +2. ๋™์‹œ ๋ชจ๋“œ(concurrent mode)์—์„œ๋„ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ UI์—์„œ ๋™์ผํ•œ ์ €์žฅ์†Œ๋กœ ๋ Œ๋”๋ง๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. + +์ด์ œ React ๋‚ด๋ถ€ ๊ตฌ์กฐ๋ฅผ ์‚ดํŽด๋ณด๋ฉด์„œ ์ด๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„๋˜๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +### 2.1 ์ดˆ๊ธฐ ๋งˆ์šดํŠธ ์‹œ์— mountSyncExternalStore() + +```ts +function mountSyncExternalStore( + subscribe: (() => void) => () => void, + getSnapshot: () => T, + getServerSnapshot?: () => T, +): T { + const fiber = currentlyRenderingFiber; + const hook = mountWorkInProgressHook(); + // ์ƒˆ๋กœ์šด ํ›…์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. + + let nextSnapshot; + const isHydrating = getIsHydrating(); + if (isHydrating) { + ... + } else { + nextSnapshot = getSnapshot(); +retrieve the data once, just like we did in initializer of useState() + +// ๋ธ”๋กœํ‚น ๋ ˆ์ธ์„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š” ํ•œ, ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์Šค์ผ€์ค„๋งํ•ฉ๋‹ˆ๋‹ค. +// ์ปค๋ฐ‹ํ•˜๊ธฐ ์ง์ „์— ํŠธ๋ฆฌ๋ฅผ ํƒ์ƒ‰ํ•˜์—ฌ +// ์–ด๋–ค ์ €์žฅ์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. +// +// ์„œ๋ฒ„ ๋ Œ๋”๋ง๋œ ์ฝ˜ํ…์ธ ๋ฅผ ์ˆ˜๋ถ„ ๊ณต๊ธ‰(hydrating)ํ•˜๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” ์ด๊ฒƒ์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. +// ์ฝ˜ํ…์ธ ๊ฐ€ ์˜ค๋ž˜๋˜์—ˆ์„ ๊ฒฝ์šฐ, ์–ด์ฐจํ”ผ ์ด๋ฏธ ๋ณด์ด๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. +// ๋Œ€์‹ , ์šฐ๋ฆฌ๋Š” ์ด๋ฅผ ์ˆ˜๋™ ํšจ๊ณผ(passive effect)์—์„œ ์ˆ˜์ •ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + + const root: FiberRoot | null = getWorkInProgressRoot(); + if (!includesBlockingLane(root, renderLanes)) { + pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot); + } + +// ์ด ๋ถ€๋ถ„์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. +// ์ปค๋ฐ‹ํ•˜๊ธฐ ์ง์ „์— ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒ€์‚ฌ๋ฅผ ์Šค์ผ€์ค„๋งํ•ฉ๋‹ˆ๋‹ค. +// ์ด๋Š” ๋น„์ฐจ๋‹จ ๋ ˆ์ธ(non-blocking lane)์—๋งŒ ์ ์šฉ๋˜๋ฉฐ, ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด๋Š” ๋™์‹œ ๋ชจ๋“œ(concurrent mode)์—์„œ ์ž‘๋™ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. +// ์ด๋Š” ์•ž์„œ ์–ธ๊ธ‰ํ•œ ์ฐข๊น€(tear) ํ˜„์ƒ์„ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + + } + +// ๋งค ๋ Œ๋”๋ง ์‹œ๋งˆ๋‹ค ์ €์žฅ์†Œ์—์„œ ํ˜„์žฌ ์Šค๋ƒ…์ƒท์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค. +// ์ด๊ฒƒ์€ React์˜ ์ผ๋ฐ˜์ ์ธ ๊ทœ์น™์„ ๊นจ์ง€๋งŒ, ์ €์žฅ์†Œ ์—…๋ฐ์ดํŠธ๋Š” ํ•ญ์ƒ ๋™๊ธฐ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. + hook.memoizedState = nextSnapshot; +// ์ด hoo.memoizedState๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. +// ์šฐ๋ฆฌ์˜ ๊ตฌํ˜„์—์„œ useState()์™€ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ํ•ฉ๋‹ˆ๋‹ค. + + const inst: StoreInstance = { + value: nextSnapshot, + getSnapshot, + }; + hook.queue = inst; + +//์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ "React์—์„œ useState()๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”๊ฐ€"์—์„œ ์–ธ๊ธ‰ํ•œ ์—…๋ฐ์ดํŠธ ํ์™€ ์œ ์‚ฌํ•ด ๋ณด์ด์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ํ•„๋“œ ์ด๋ฆ„๋งŒ ๋นŒ๋ ค์˜จ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + + //์ €์žฅ์†Œ์— ๊ตฌ๋…ํ•˜๊ธฐ ์œ„ํ•œ ํšจ๊ณผ๋ฅผ ์Šค์ผ€์ค„๋งํ•ฉ๋‹ˆ๋‹ค. + mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); +Great, mountEffect() is the internal of useEffect(), so + +it is similar to what we do, scheduling a (passive) effect to init subscription + +// ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์ธ์Šคํ„ด์Šค ํ•„๋“œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•œ ํšจ๊ณผ๋ฅผ ์Šค์ผ€์ค„๋งํ•ฉ๋‹ˆ๋‹ค. +// subscribe, getSnapshot ๋˜๋Š” ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ด๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. +// ์ •๋ฆฌ(clean-up) ํ•จ์ˆ˜๊ฐ€ ์—†๊ณ , ์ข…์†์„ฑ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ถ”์ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— +// ์ถ”๊ฐ€ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๊ณ ๋„ ์ง์ ‘ pushEffect๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +// ๊ฐ™์€ ์ด์œ ๋กœ, ์ •์  ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•  ํ•„์š”๋„ ์—†์Šต๋‹ˆ๋‹ค. +// TODO: ์ปค๋ฐ‹ ์ „ ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ์ด๋ฅผ ์ˆ˜๋™ ํšจ๊ณผ(passive phase)๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +// ๋‹ค์Œ ์ฃผ์„์„ ์ฐธ์กฐํ•˜์„ธ์š”. + fiber.flags |= PassiveEffect; + + // pushEffect : React์—์„œ useEffect()๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์„ค๋ช…ํ•œ ๊ฒƒ๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. + // ์ด๊ฒƒ์€ ํšจ๊ณผ๋ฅผ Fiber์— ํ‘ธ์‹œํ•ฉ๋‹ˆ๋‹ค. + pushEffect( + + + HookHasEffect | HookPassive, +// HookPassive : ์ˆ˜๋™ ํšจ๊ณผ(passive effect)๋Š” useEffect()์—์„œ ํ•œ ๊ฒƒ๊ณผ ์œ ์‚ฌํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. + + updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), +// updateStoreInstance : +// ์ดˆ๊ธฐ ๋งˆ์šดํŠธ ์‹œ, nextSnapshot์ด ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฒ€์‚ฌ์˜ ์ด์ ์„ ์ทจํ•ฉ๋‹ˆ๋‹ค. ์ด๋ก ์ ์œผ๋กœ 314๋ฒˆ ์ค„์˜ getSnapshot()๊ณผ 359๋ฒˆ ์ค„์˜ mountEffect() ์‚ฌ์ด์— ์ž‘์€ ์ฐฝ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ฐข๊น€(tear) ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ์™€๋Š” ๋‹ฌ๋ฆฌ, ์ตœ์‹  ๋ฐ์ดํ„ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์‹œ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์šฐ๋ฆฌ์˜ ๊ตฌํ˜„์—์„œ update()๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๋Š” ๋ชฉ์ ๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. + + undefined, + null, + ); + return nextSnapshot; +} +function subscribeToStore(fiber, inst, subscribe) { + const handleStoreChange = () => { +// ์ €์žฅ์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. +// ๋งˆ์ง€๋ง‰์œผ๋กœ ์ €์žฅ์†Œ์—์„œ ์ฝ์€ ์ดํ›„ ์Šค๋ƒ…์ƒท์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + if (checkIfSnapshotChanged(inst)) { + // ๋ฆฌ๋ Œ๋”๋ง์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. + forceStoreRerender(fiber); + } + }; +// ์ €์žฅ์†Œ์— ๊ตฌ๋…ํ•˜๊ณ  ์ •๋ฆฌ(clean-up) ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. + return subscribe(handleStoreChange); +} +function updateStoreInstance( + fiber: Fiber, + inst: StoreInstance, + nextSnapshot: T, + getSnapshot: () => T, +) { +// ์ด๊ฒƒ๋“ค์€ ์ˆ˜๋™ ๋‹จ๊ณ„์—์„œ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค. + inst.value = nextSnapshot; + inst.getSnapshot = getSnapshot; + +// ๋ Œ๋”์™€ ์ปค๋ฐ‹ ์‚ฌ์ด์— ๋ฌด์–ธ๊ฐ€๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +// ์ด๊ฒƒ์€ ์ˆ˜๋™ ํšจ๊ณผ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ์—์„œ ๋ฐœ์ƒํ–ˆ์„ ์ˆ˜๋„ ์žˆ๊ณ , +// ๋ ˆ์ด์•„์›ƒ ํšจ๊ณผ์—์„œ ๋ฐœ์ƒํ–ˆ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. +// ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ์ด์ „ ์Šค๋ƒ…์ƒท๊ณผ getSnapshot ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ๋น ์ ธ๋‚˜๊ฐ”์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. +// ํ•œ ๋ฒˆ ๋” ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + + if (checkIfSnapshotChanged(inst)) { + // ๋ฆฌ๋ Œ๋”๋ง์„ ๋‹ค์‹œ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. + forceStoreRerender(fiber); + } +} +function checkIfSnapshotChanged(inst) { + const latestGetSnapshot = inst.getSnapshot; + const prevValue = inst.value; + try { + const nextValue = latestGetSnapshot(); + return !is(prevValue, nextValue); + // subscribeToStore()์™€ updateStoreInstance() ๋‘˜ ๋‹ค์—์„œ, +// ์ด ๊ฒ€์‚ฌ๋Š” ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๊ฒฝ์šฐ ๋ฆฌ๋ Œ๋”๋ง์ด ์Šค์ผ€์ค„๋ง๋˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. + + } catch (error) { + return true; + } +} +function forceStoreRerender(fiber) { + const root = enqueueConcurrentRenderForLane(fiber, SyncLane); + if (root !== null) { + scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp); + +// SyncLane : ์—ฌ๊ธฐ์„œ ๋ฆฌ๋ Œ๋”๋ง์„ ์œ„ํ•ด SyncLane์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. React๊ฐ€ ์ดˆ๊ธฐ ๋งˆ์šดํŠธ๋ฅผ ์–ด๋–ป๊ฒŒ ์ˆ˜ํ–‰ํ•˜๋Š”์ง€ ์„ค๋ช…ํ–ˆ๋“ฏ์ด, SyncLane์€ ๋ธ”๋กœํ‚น ๋ ˆ์ธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ์ƒˆ๋กœ์šด ๋ Œ๋”๋ง์€ ๋™์‹œ ๋ชจ๋“œ์—์„œ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฐข๊น€(tear) ํ˜„์ƒ์€ ๋™์‹œ ๋ชจ๋“œ์—์„œ๋งŒ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Š” ๋‹ค์Œ ๋ Œ๋”๋ง์—์„œ ์ฐข๊น€ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. + + } +} +``` + +์ฝ”๋“œ๋Š” ๋‹ค์†Œ ๋ณต์žกํ•˜์ง€๋งŒ, ์•„์ด๋””์–ด๋Š” ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. + +์ค‘์š”ํ•˜๊ฒŒ ๋ณด์•„์•ผํ•  ์ ์€ ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€์ž…๋‹ˆ๋‹ค. + +๊ณ„์†ํ•ด์„œ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. + +### 2.2 ๋ฆฌ๋ Œ๋”๋ง ์•ˆ์—์„œ updateSyncExternalStore()๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€ + +```ts + +function updateSyncExternalStore( + subscribe: (() => void) => () => void, + getSnapshot: () => T, + getServerSnapshot?: () => T, +): T { + const fiber = currentlyRenderingFiber; + const hook = updateWorkInProgressHook(); +// ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ์ €์žฅ์†Œ์—์„œ ํ˜„์žฌ ์Šค๋ƒ…์ƒท์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค. +// ์ด๋Š” React์˜ ์ผ๋ฐ˜ ๊ทœ์น™์„ ๊นจ๋œจ๋ฆฌ์ง€๋งŒ, ์ €์žฅ์†Œ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•ญ์ƒ ๋™๊ธฐ์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. + const nextSnapshot = getSnapshot(); + + const prevSnapshot = hook.memoizedState; + const snapshotChanged = !is(prevSnapshot, nextSnapshot); + if (snapshotChanged) { + hook.memoizedState = nextSnapshot; + // ๋”ฐ๋ผ์„œ ์ƒˆ๋กœ์šด ์ƒํƒœ๊ฐ€ ๋ฐœ๊ฒฌ๋˜๋ฉด, ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. + + markWorkInProgressReceivedUpdate(); + } + const inst = hook.queue; + updateEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [ + subscribe, + ]); +// ์ด๋Š” ์ข…์†์„ฑ(deps)์— ๋”ฐ๋ผ ์—…๋ฐ์ดํŠธ์™€ ์ •๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด useEffect()๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. + +// getSnapshot ๋˜๋Š” subscribe๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค, +// ์ปค๋ฐ‹ ๋‹จ๊ณ„์—์„œ ์ค‘์ฒฉ๋œ ๋ณ€๊ฒฝ์ด ์žˆ์—ˆ๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. +// ๋™์‹œ ๋ชจ๋“œ์—์„œ๋Š” ์ด๋Ÿฌํ•œ ์ผ์ด ์ž์ฃผ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, +// ์‹ฌ์ง€์–ด ๋™๊ธฐ ๋ชจ๋“œ์—์„œ๋„ ์ด์ „ ํšจ๊ณผ๊ฐ€ ์ €์žฅ์†Œ๋ฅผ ๋ณ€๊ฒฝํ–ˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + if ( + inst.getSnapshot !== getSnapshot || + snapshotChanged || +// subscribe ํ•จ์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. +// ์œ„์—์„œ ๊ตฌ๋… ํšจ๊ณผ๋ฅผ ์Šค์ผ€์ค„๋งํ–ˆ๋Š”์ง€ ํ™•์ธํ•จ์œผ๋กœ์จ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + (workInProgressHook !== null && + workInProgressHook.memoizedState.tag & HookHasEffect) + ) { + fiber.flags |= PassiveEffect; + pushEffect( + HookHasEffect | HookPassive, + updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), + undefined, + null, + ); +// getSnapshot()์ด ๋ณ€๊ฒฝ๋˜๋ฉด, ์˜์กด์„ฑ ๋ฐฐ์—ด์ด ๋ณ€๊ฒฝ๋  ๋•Œ useEffect()๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ํšจ๊ณผ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +// ๋ธ”๋กœํ‚น ๋ ˆ์ธ์„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š” ํ•œ, ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์Šค์ผ€์ค„๋งํ•ฉ๋‹ˆ๋‹ค. +// ์ปค๋ฐ‹ ์ง์ „์— ํŠธ๋ฆฌ๋ฅผ ํƒ์ƒ‰ํ•˜์—ฌ ์ €์žฅ์†Œ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + const root: FiberRoot | null = getWorkInProgressRoot(); + if (root === null) { + throw new Error( + 'Expected a work-in-progress root. This is a bug in React. Please file an issue.', + ); + } + if (!includesBlockingLane(root, renderLanes)) { + pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot); + } +// ์—ฌ๊ธฐ์„œ ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ๋‹ค์‹œ ์Šค์ผ€์ค„๋ง๋ฉ๋‹ˆ๋‹ค. +// ์†”์งํžˆ ๋งํ•ด, ์™œ ์—ฌ๊ธฐ์— ๋ฐฐ์น˜๋˜์—ˆ๋Š”์ง€ ์ž˜ ์ดํ•ด๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค... + +// ์Šค๋ƒ…์ƒท ๋ณ€ํ™”์™€ getSnapshot() ๋ณ€ํ™”๊ฐ€ ์—†๋”๋ผ๋„ ์™ธ๋ถ€ ์ €์žฅ์†Œ๊ฐ€ ์šฐ๋ฆฌ์—๊ฒŒ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ณ€๊ฒฝ๋  ๊ฐ€๋Šฅ์„ฑ์€ ์—ฌ์ „ํžˆ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ๋ฅผ ๋“ค์–ด, ์“ฐ๋กœํ‹€๋œ ์ด๋ฒคํŠธ๋ฅผ ์ƒ๊ฐํ•ด๋ณด์„ธ์š”). ๋”ฐ๋ผ์„œ ์ด ์กฐ๊ฑด์—์„œ ๊ฒ€์‚ฌ๋ฅผ ์—ฌ๊ธฐ์— ๋‘๋Š” ๊ฒƒ์€ ๋ฒ„๊ทธ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. + +// 2023-08-11 ์—…๋ฐ์ดํŠธ: + +// ์ด๊ฒƒ์€ ๋ฒ„๊ทธ๊ฐ€ ์•„๋‹ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์ฃผ๋กœ subscribe()์™€ getSnapshot()์˜ ๋ณ€๊ฒฝ์— ๋Œ€ํ•ด ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์Šค์ผ€์ค„๋งํ•ฉ๋‹ˆ๋‹ค. PR์„ ํ™•์ธํ•ด๋ณด์„ธ์š”. (https://github.com/facebook/react/pull/27180) + + } + return nextSnapshot; +} +``` + +์ฝ”๋“œ๋Š” ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์Šค์ผ€์ค„๋งํ•˜๋Š” ๋งˆ์ง€๋ง‰ ๋ถ€๋ถ„์„ ์ œ์™ธํ•˜๊ณ ๋Š” ํ•ฉ๋ฆฌ์ ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. + +ํŠน์ • ์กฐ๊ฑด ํ•˜์—์„œ ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ฃผ๋Š” ๋ฐ๋ชจ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. + +## - App.js + +```ts + +import { + useEffect, + useSyncExternalStore, + startTransition, + useState, + useLayoutEffect, + useMemo, +} from 'react'; + +let data = 1; +let callbacks = []; + +function getSnapShot() { + return data; +} + +setTimeout(() => (data = 2), 800); + +function subscribe(callback) { + callbacks.push(callback); + return () => { + callbacks = callbacks.filter((item) => item != callback); + }; +} + +function Cell() { + let start = Date.now(); + while (Date.now() - start < 50) { + // force yielding to main thread in concurrent mode + } + const data = useSyncExternalStore(subscribe, getSnapShot); + + return
{data}
; +} + +export default function App() { + const [count, setCount] = useState(0); + + useEffect(() => { + startTransition(() => { + setCount((count) => count + 1); + }); + }, []); + + return ( +
+

+ Example of tearing.
+ below are multiple cells rendering same external data which changes + during rendering. +

+
+ + + + + + +
+
+ ); +} +``` + +์—ฌ๊ธฐ์„œ useSyncExternalStore()๋ฅผ ์‚ฌ์šฉํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ฐข๊น€(tear) ํ˜„์ƒ์ด ๋‹ค์‹œ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Š” ๋ฒ„๊ทธ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด [ํ’€ ๋ฆฌํ€˜์ŠคํŠธ](https://github.com/facebook/react/pull/27180)๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. + +> ๋งŒ์•ฝ ์ฐข๊น€(tear) ํ˜„์ƒ์ด ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์ฝ”๋“œ์—์„œ ํƒ€์ž„์•„์›ƒ์„ ๋” ์ž‘์€ ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•ด๋ณด์„ธ์š”. + +> 2023-08-11 ์—…๋ฐ์ดํŠธ: ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ํ•˜๋Š” ์—ญํ• ์„ ์˜คํ•ดํ•œ ๊ฒƒ์œผ๋กœ ๋“œ๋Ÿฌ๋‚ฌ์Šต๋‹ˆ๋‹ค. useSyncExternalStore()๋Š” ๋ชจ๋“  ์™ธ๋ถ€ ์ €์žฅ์†Œ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์ด ๋ฐ˜๋“œ์‹œ ๋ฐœ์ƒํ•˜๋„๋ก ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ subscribe() ๋‚ด๋ถ€์˜ forceStoreRerender()๊ฐ€ ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•œ ์ฐข๊น€ ํ˜„์ƒ์„ ์‹ค์ œ๋กœ ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๋Š” subscribe() ๋˜๋Š” getSnapshot()์˜ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•œ ์ฐข๊น€์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. + +### 2.3 ๋™์‹œ์„ฑ ๋ชจ๋“œ์—์„œ ์ผ๊ด€์„ฑ ํ™•์ธ + +์–ด๋–ป๊ฒŒ ์ผ๊ด€์„ฑ ์ฒดํฌ๊ฐ€ ์ด๋ฃจ์–ด์ง€๋Š”์ง€ ํ•œ๋ฒˆ ์ฐพ์•„๋ด…์‹œ๋‹ค. + +```ts +function pushStoreConsistencyCheck( + fiber: Fiber, + getSnapshot: () => T, + renderedSnapshot: T, +) { + fiber.flags |= StoreConsistency; + const check: StoreConsistencyCheck = { + getSnapshot, + value: renderedSnapshot, + }; + let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any); + if (componentUpdateQueue === null) { + componentUpdateQueue = createFunctionComponentUpdateQueue(); + currentlyRenderingFiber.updateQueue = (componentUpdateQueue: any); + +// currentlyRenderingFiber.updateQueue : useEffect๋ฅผ ์ƒ๊ธฐํ•ด๋ณด๋ฉด, updateQueue๋Š” ํšจ๊ณผ๋“ค์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด lastEffect๋„ ๋ณด์œ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  renderWithHooks()์—์„œ ์ด๊ฒƒ์ด ์ง€์›Œ์ง€๋ฏ€๋กœ, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กญ๊ฒŒ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. + + componentUpdateQueue.stores = [check]; +// componentUpdateQueue.stores : ์™ธ๋ถ€ ์ €์žฅ์†Œ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ ๋ณ„๋„์˜ ํ•„๋“œ์ž…๋‹ˆ๋‹ค. + + } else { + const stores = componentUpdateQueue.stores; + if (stores === null) { + componentUpdateQueue.stores = [check]; + } else { + stores.push(check); + } + } +} +``` + +์ด์ œ ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์–ธ์ œ ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ป๊ฒŒ ํŠธ๋ฆฌ๊ฑฐ๋˜๋Š”์ง€ ๋ณผ ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค. + +์ด๋Š” ์‹ค์ œ๋กœ ์ปค๋ฐ‹ ์ง์ „์—, ๋ Œ๋” ๋‹จ๊ณ„์˜ ๋์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. + +```ts +// ์ด ํ•จ์ˆ˜๋Š” ๋ชจ๋“  ๋™์‹œ ์ž‘์—…์˜ ์ง„์ž…์ ์ž…๋‹ˆ๋‹ค. ์ฆ‰, Scheduler๋ฅผ ํ†ตํ•ด +// ์ง„ํ–‰๋˜๋Š” ๋ชจ๋“  ์ž‘์—…์€ ์—ฌ๊ธฐ๋กœ ๋“ค์–ด์˜ต๋‹ˆ๋‹ค. +export function performConcurrentWorkOnRoot( + root: FiberRoot, + didTimeout: boolean, +): RenderTaskFn | null { + ... + // ๋ฃจํŠธ์— ์ €์žฅ๋œ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ ์ž‘์—…ํ•  ๋ ˆ์ธ์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. + // TODO: ํ˜ธ์ถœ์ž์—์„œ ์ด๋ฏธ ๊ณ„์‚ฐํ•œ ๊ฐ’์ž…๋‹ˆ๋‹ค. ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•˜๋„๋ก ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + let lanes = getNextLanes( + root, + root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes, + ); + if (lanes === NoLanes) { + // ๋ฐฉ์–ด์  ์ฝ”๋”ฉ. ์ด ๊ฒฝ์šฐ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. + return null; + } + // ํŠน์ • ๊ฒฝ์šฐ์—๋Š” ์‹œ๊ฐ„ ๋ถ„ํ• ์„ ๋น„ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค: ์ž‘์—…์ด ๋„ˆ๋ฌด ์˜ค๋ž˜ CPU์— ๋ฌถ์—ฌ ์žˆ์—ˆ์„ ๊ฒฝ์šฐ("๋งŒ๋ฃŒ๋œ" ์ž‘์—…์œผ๋กœ, ๊ธฐ์•„๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•จ) ๋˜๋Š” ๋™๊ธฐ ์—…๋ฐ์ดํŠธ ๊ธฐ๋ณธ ๋ชจ๋“œ์ผ ๊ฒฝ์šฐ. + // TODO: Scheduler ๋ฒ„๊ทธ๋ฅผ ์กฐ์‚ฌ ์ค‘์ด๋ผ, ๋ฐฉ์–ด์ ์œผ๋กœ `didTimeout`์„ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค. + // Scheduler์˜ ๋ฒ„๊ทธ๊ฐ€ ์ˆ˜์ •๋˜๋ฉด ์ด๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋งŒ๋ฃŒ๋ฅผ ์ž์ฒด์ ์œผ๋กœ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. + const shouldTimeSlice = + !includesBlockingLane(root, lanes) && + !includesExpiredLane(root, lanes) && + (disableSchedulerTimeoutInWorkLoop || !didTimeout); + let exitStatus = shouldTimeSlice + ? renderRootConcurrent(root, lanes) + : renderRootSync(root, lanes); + + if (exitStatus !== RootInProgress) { + if (exitStatus === RootErrored) { + // ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๊ฒฝ์šฐ, ํ•œ ๋ฒˆ ๋” ๋ Œ๋”๋ง์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. + // ๋™์‹œ ๋ฐ์ดํ„ฐ ๋ณ€์กฐ๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ๋™๊ธฐ์ ์œผ๋กœ ๋ Œ๋”๋งํ•˜๊ณ , ๋ชจ๋“  ๋ณด๋ฅ˜ ์ค‘์ธ ์—…๋ฐ์ดํŠธ๊ฐ€ ํฌํ•จ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. + // ๋‘ ๋ฒˆ์งธ ์‹œ๋„ ํ›„์—๋„ ์‹คํŒจํ•˜๋ฉด ๊ฒฐ๊ณผ ํŠธ๋ฆฌ๋ฅผ ์ปค๋ฐ‹ํ•ฉ๋‹ˆ๋‹ค. + const originallyAttemptedLanes = lanes; + const errorRetryLanes = getLanesToRetrySynchronouslyOnError( + root, + originallyAttemptedLanes, + ); + if (errorRetryLanes !== NoLanes) { + lanes = errorRetryLanes; + exitStatus = recoverFromConcurrentError( + root, + originallyAttemptedLanes, + errorRetryLanes, + ); + } + } + if (exitStatus === RootFatalErrored) { + const fatalError = workInProgressRootFatalError; + prepareFreshStack(root, NoLanes); + markRootSuspended(root, lanes); + ensureRootIsScheduled(root); + throw fatalError; + } + if (exitStatus === RootDidNotComplete) { + // ํŠธ๋ฆฌ๋ฅผ ์™„์„ฑํ•˜์ง€ ๋ชปํ•˜๊ณ  ๋ Œ๋”๋ง์ด ์ข…๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ผ๊ด€๋œ ํŠธ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์ปค๋ฐ‹ํ•˜์ง€ ์•Š๊ณ  + // ํ˜„์žฌ ๋ Œ๋”๋ง์„ ์ข…๋ฃŒํ•ด์•ผ ํ•˜๋Š” ํŠน์ˆ˜ํ•œ ๊ฒฝ์šฐ์— ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. + markRootSuspended(root, lanes); + } else { + // ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + // ์ด ๋ Œ๋”๋ง์ด ๋™์‹œ ์ด๋ฒคํŠธ์— ์–‘๋ณดํ–ˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ + // ์ƒˆ๋กœ ๋ Œ๋”๋ง๋œ ์Šคํ† ์–ด๊ฐ€ ์ผ๊ด€๋œ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + // TODO: ๋ Œ๋”๋ง์ด ์ถฉ๋ถ„ํžˆ ๋น ๋ฅด๊ฑฐ๋‚˜ ๋งŒ๋ฃŒ๋œ ๊ฒฝ์šฐ ์ฃผ ์Šค๋ ˆ๋“œ์— ์–‘๋ณดํ•˜์ง€ ์•Š์•˜์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + const renderWasConcurrent = !includesBlockingLane(root, lanes); + const finishedWork: Fiber = (root.current.alternate: any); + if ( + renderWasConcurrent && + !isRenderConsistentWithExternalStores(finishedWork) + // ์—ฌ๊ธฐ์„œ ์ผ๊ด€์„ฑ ์ฒดํฌ๊ฐ€ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค. + ) { + // ์Šคํ† ์–ด๊ฐ€ ๊ต์ฐจ ์ด๋ฒคํŠธ์—์„œ ๋ณ€์กฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋™์‹œ ๋ณ€์กฐ๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ๋™๊ธฐ์ ์œผ๋กœ ๋‹ค์‹œ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. + exitStatus = renderRootSync(root, lanes); + // exitStatus = renderRootSync(root, lanes) : ๋™๊ธฐ ๋ชจ๋“œ์—์„œ ๋‹ค์‹œ ๋ Œ๋”๋ง์ด ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. + // ์ปค๋ฐ‹์ด ๋ฐœ์ƒํ•˜๊ธฐ ์ „์—, ์‚ฌ์šฉ์ž๋“ค์€ UI์—์„œ ์ฐข์–ด์ง(ํ™”๋ฉด ๊นœ๋นก์ž„ ๋“ฑ)์„ ๋ณด์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. + + // ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ๋‹ค์‹œ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + if (exitStatus === RootErrored) { + const originallyAttemptedLanes = lanes; + const errorRetryLanes = getLanesToRetrySynchronouslyOnError( + root, + originallyAttemptedLanes, + ); + if (errorRetryLanes !== NoLanes) { + lanes = errorRetryLanes; + exitStatus = recoverFromConcurrentError( + root, + originallyAttemptedLanes, + errorRetryLanes, + ); + // ๋™์‹œ ์ด๋ฒคํŠธ์— ์–‘๋ณดํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์ด์ œ ํŠธ๋ฆฌ๊ฐ€ ์ผ๊ด€๋˜๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. + } + } + if (exitStatus === RootFatalErrored) { + const fatalError = workInProgressRootFatalError; + prepareFreshStack(root, NoLanes); + markRootSuspended(root, lanes); + ensureRootIsScheduled(root); + throw fatalError; + } + // FIXME: RootDidNotComplete๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ตฌ์กฐ๊ฐ€ ์ด์ƒ์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + } + // ์ด์ œ ํŠธ๋ฆฌ๊ฐ€ ์ผ๊ด€๋œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ๋‹จ๊ณ„๋Š” ์ด๋ฅผ ์ปค๋ฐ‹ํ•˜๊ฑฐ๋‚˜, + // ๋ฌด์–ธ๊ฐ€๊ฐ€ ๋Œ€๊ธฐ ์ค‘์ด๋ผ๋ฉด ํƒ€์ž„์•„์›ƒ ํ›„ ์ปค๋ฐ‹์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. + root.finishedWork = finishedWork; + root.finishedLanes = lanes; + finishConcurrentRender(root, exitStatus, + finishedWork, lanes); + // commitRoot()๋Š” ์—ฌ๊ธฐ์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. + } + } + ensureRootIsScheduled(root); + return getContinuationForRoot(root, originalCallbackNode); +} +``` + +```ts +function isRenderConsistentWithExternalStores(finishedWork: Fiber): boolean { + // ๋ Œ๋”๋œ ํŠธ๋ฆฌ์—์„œ ์™ธ๋ถ€ ์Šคํ† ์–ด ์ฝ๊ธฐ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ , ๋™์‹œ ์ด๋ฒคํŠธ์—์„œ ์Šคํ† ์–ด๊ฐ€ ๋ณ€์กฐ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. + // ์žฌ๊ท€ ๋Œ€์‹  ๋ฐ˜๋ณต ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ผ์ฐ ์ข…๋ฃŒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. + let node: Fiber = finishedWork; + while (true) { + if (node.flags & StoreConsistency) { + const updateQueue: FunctionComponentUpdateQueue | null = + (node.updateQueue: any); + if (updateQueue !== null) { + const checks = updateQueue.stores; + if (checks !== null) { + for (let i = 0; i < checks.length; i++) { + const check = checks[i]; + const getSnapshot = check.getSnapshot; + const renderedValue = check.value; + try { + if (!is(getSnapshot(), renderedValue)) { + // ๋ถˆ์ผ์น˜ํ•˜๋Š” ์Šคํ† ์–ด๋ฅผ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. + return false; + } + } catch (error) { + // `getSnapshot`์ด ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด `false`๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. + // ์ด๋กœ ์ธํ•ด ๋‹ค์‹œ ๋ Œ๋”๋ง์ด ์˜ˆ์•ฝ๋˜๊ณ , ๋ Œ๋”๋ง ์ค‘์— ์˜ค๋ฅ˜๊ฐ€ ๋‹ค์‹œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. + return false; + } + } + } + } + } + const child = node.child; + if (node.subtreeFlags & StoreConsistency && child !== null) { + child.return = node; + node = child; + continue; + } + if (node === finishedWork) { + return true; + } + while (node.sibling === null) { + if (node.return === null || node.return === finishedWork) { + return true; + } + node = node.return; + } + node.sibling.return = node.return; + node = node.sibling; + } + // Flow๋Š” ์ด๊ฒƒ์ด ๋„๋‹ฌํ•  ์ˆ˜ ์—†๋Š” ์ฝ”๋“œ๋ผ๋Š” ๊ฒƒ์„ ์•Œ์ง€ ๋ชปํ•˜์ง€๋งŒ, eslint๋Š” ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + // eslint-disable-next-line no-unreachable + return true; +} + +``` + +### 3. ์š”์•ฝ + +์ด์ œ `useSyncExternalStore()`๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์ดํ•ดํ–ˆ์œผ๋ฏ€๋กœ, ์ฃผ๋กœ ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +1. **๋™์‹œ ๋ชจ๋“œ์—์„œ์˜ ์ฐข์–ด์ง(Tearing):** `useSyncExternalStore()`๋Š” ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋œ ํ›„ ์ปค๋ฐ‹์ด ์‹œ์ž‘๋˜๊ธฐ ์ „์— ์ผ๊ด€์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์˜ˆ์•ฝํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๋™๊ธฐ ๋ชจ๋“œ์—์„œ ๋‹ค์‹œ ๋ Œ๋”๋ง์ด ๊ฐ•์ œ๋˜์–ด ๋ถˆ์ผ์น˜ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ UI์— ํ‘œ์‹œ๋˜์ง€ ์•Š๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. + +2. **๊ฐ์ง€๋˜์ง€ ์•Š์€ ์™ธ๋ถ€ ์Šคํ† ์–ด ๋ณ€๊ฒฝ:** `useSyncExternalStore()`๋Š” ์ˆ˜๋™ ํšจ๊ณผ๋ฅผ ํ†ตํ•ด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ์œผ๋ฉด ๋™๊ธฐ ๋ชจ๋“œ์—์„œ ๋‹ค์‹œ ๋ Œ๋”๋ง์„ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ปค๋ฐ‹ ํ›„์— ๋ฐœ์ƒํ•˜๋ฏ€๋กœ ์‚ฌ์šฉ์ž๋Š” UI ๊นœ๋นก์ž„์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. diff --git a/June/article/React-Compiler.md b/June/article/React-Compiler.md new file mode 100644 index 0000000..6526c82 --- /dev/null +++ b/June/article/React-Compiler.md @@ -0,0 +1,523 @@ +## ๐Ÿ”— [React-Compiler](https://react.dev/learn/react-compiler) + +### ๐Ÿ—“๏ธ ๋ฒˆ์—ญ ๋‚ ์งœ: 2024.06.01 + +### ๐Ÿงš ๋ฒˆ์—ญํ•œ ํฌ๋ฃจ: ๋ฒ„๊ฑด๋””(์ „ํƒœํ—Œ) + +--- + +# ๋ฆฌ์•กํŠธ ์ปดํŒŒ์ผ๋Ÿฌ + +This page will give you an introduction to the new experimental React Compiler and how to try it out successfully. + +์ด ํŽ˜์ด์ง€๋Š” ์ƒˆ๋กญ์ง€๋งŒ ์•„์ง์€ ์‹คํ—˜์ ์ธ ๋ฆฌ์•กํŠธ ์ปดํŒŒ์ผ๋Ÿฌ์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์‹œ๋„ํ•ด๋ณผ์ˆ˜ ์žˆ์„์ง€ ์„ค๋ช… ํ•ฉ๋‹ˆ๋‹ค. + +> ์ด ๋ฌธ์„œ๋Š” ์•„์ง ์ž‘์—… ์ค‘์ž…๋‹ˆ๋‹ค. ๋” ๋งŽ์€ ๋ฌธ์„œ๋Š” [React Compiler Working Group](https://github.com/reactwg/react-compiler/discussions) ์ €์žฅ์†Œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฌธ์„œ๊ฐ€ ๋” ์•ˆ์ •ํ™”๋˜๋ฉด ์ด ๋ฌธ์„œ๋กœ ์—…์ŠคํŠธ๋ฆผ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. + +## ์ด๋Ÿฐ๊ฑธ ๋ฐฐ์šฐ์‹œ๊ฒŒ ๋ ๊ฑฐ์—์š” + +1. ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์–ด๋–ป๊ฒŒ ์‹œ์ž‘ํ•˜๋Š”์ง€ +2. ์ปดํŒŒ์ผ๋Ÿฌ์™€ eslint ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•˜๋Š” ๋ฒ• +3. ๋ฌธ์ œ ํ•ด๊ฒฐ + +### ์ฃผ์˜ ์‚ฌํ•ญ + +React Compiler๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ๋กœ๋ถ€ํ„ฐ ์ดˆ๊ธฐ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›๊ธฐ ์œ„ํ•ด ์˜คํ”ˆ ์†Œ์Šคํ™”๋œ ์ƒˆ๋กœ์šด ์‹คํ—˜์  ์ปดํŒŒ์ผ๋Ÿฌ์ž…๋‹ˆ๋‹ค. ์•„์ง ์™„๋ฒฝํ•˜์ง€ ์•Š์œผ๋ฉฐ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์‚ฌ์šฉํ•˜๊ธฐ์— ์ค€๋น„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. + +React Compiler๋Š” React 19 RC๋ฅผ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค. + +๋งŒ์•ฝ React 19๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ˆ˜ ์—†๋‹ค๋ฉด, [Working Group](<(https://github.com/reactwg/react-compiler/discussions/6)>)์—์„œ ์„ค๋ช…๋œ ์บ์‹œ ํ•จ์ˆ˜์˜ ์‚ฌ์šฉ์ž ๊ณต๊ฐ„ ๊ตฌํ˜„์„ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +React Compiler๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ๋กœ๋ถ€ํ„ฐ ์ดˆ๊ธฐ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›๊ธฐ ์œ„ํ•ด ์˜คํ”ˆ ์†Œ์Šคํ™”๋œ ์ƒˆ๋กœ์šด ์‹คํ—˜์  ์ปดํŒŒ์ผ๋Ÿฌ์ž…๋‹ˆ๋‹ค. + +์ด๋Š” ๋นŒ๋“œ ํƒ€์ž„ ์ „์šฉ ๋„๊ตฌ๋กœ, React ์•ฑ์„ ์ž๋™์œผ๋กœ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ˆœ์ˆ˜ JavaScript์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋ฉฐ, [React์˜ ๊ทœ์น™](https://react.dev/reference/rules)์„ ์ดํ•ดํ•˜๋ฏ€๋กœ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. + +์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋˜ํ•œ ์ปดํŒŒ์ผ๋Ÿฌ์˜ ๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ์—๋””ํ„ฐ ๋‚ด์—์„œ ๋ฐ”๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” [eslint ํ”Œ๋Ÿฌ๊ทธ์ธ](https://react.dev/learn/react-compiler#installing-eslint-plugin-react-compiler)์„ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ปดํŒŒ์ผ๋Ÿฌ์™€ ๋…๋ฆฝ์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฉฐ, ์•ฑ์—์„œ ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋ชจ๋“  React ๊ฐœ๋ฐœ์ž๋“ค์ด ์ด eslint ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฒ ์ด์Šค์˜ ํ’ˆ์งˆ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ ์ด๋Š” ๊ถŒ์žฅ๋˜์ง€ ์•Š์œผ๋ฉฐ ๊ฐ€๋Šฅํ•  ๋•Œ React 19๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. + +## ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ฌด์—‡์„ ํ•˜๋‚˜์š”? + +์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด React Compiler๋Š” ์ž๋™์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜(memoization)ํ•ฉ๋‹ˆ๋‹ค. + +์˜ค๋Š˜๋‚  `useMemo`, `useCallback`, `React.memo`์™€ ๊ฐ™์€ API๋ฅผ ํ†ตํ•ด ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ์ ‘ํ•ด๋ณด์…จ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +์ด๋Ÿฌํ•œ API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž…๋ ฅ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜์„ ๋•Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์ • ๋ถ€๋ถ„์„ ๋‹ค์‹œ ๊ณ„์‚ฐํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  React์— ์•Œ๋ฆด ์ˆ˜ ์žˆ์–ด ์—…๋ฐ์ดํŠธ ์ž‘์—…์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ž„์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์„ ์žŠ๊ฑฐ๋‚˜ ์ž˜๋ชป ์ ์šฉํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. + +์ด๋กœ ์ธํ•ด React๊ฐ€ ์˜๋ฏธ ์žˆ๋Š” ๋ณ€๊ฒฝ์ด ์—†๋Š” UI ๋ถ€๋ถ„์„ ํ™•์ธํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋น„ํšจ์œจ์ ์ธ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +React Compiler๋Š” JavaScript์™€ React์˜ ๊ทœ์น™์— ๋Œ€ํ•œ ์ง€์‹์„ ํ™œ์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ์™€ ํ›… ๋‚ด์˜ ๊ฐ’์ด๋‚˜ ๊ฐ’ ๊ทธ๋ฃน์„ ์ž๋™์œผ๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•ฉ๋‹ˆ๋‹ค. + +๊ทœ์น™ ์œ„๋ฐ˜์ด ๊ฐ์ง€๋˜๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›…๋งŒ ๊ฑด๋„ˆ๋›ฐ๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ค๋ฅธ ์ฝ”๋“œ๋ฅผ ๊ณ„์† ์ปดํŒŒ์ผํ•ฉ๋‹ˆ๋‹ค. + +์ด๋ฏธ ์ฝ”๋“œ๋ฒ ์ด์Šค๊ฐ€ ์ž˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋˜์–ด ์žˆ๋‹ค๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•ด๋„ ํฐ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +ํ•˜์ง€๋งŒ ์‹ค์งˆ์ ์œผ๋กœ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š” ์ •ํ™•ํ•œ ์ข…์†์„ฑ์„ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•˜๋Š” ๊ฒƒ์€ ์†์œผ๋กœ ํ•˜๊ธฐ์—๋Š” ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. + +## - ๋ฆฌ์•กํŠธ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ฌด์Šจ ์ข…๋ฅ˜์˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ํ•˜๋‚˜์š” ? + +React ์ปดํŒŒ์ผ๋Ÿฌ์˜ ์ดˆ๊ธฐ ๋ฒ„์ „์€ ์—…๋ฐ์ดํŠธ ์„ฑ๋Šฅ(๊ธฐ์กด ์ปดํฌ๋„ŒํŠธ์˜ ๋ฆฌ๋ Œ๋”๋ง)์„ ๊ฐœ์„ ํ•˜๋Š” ๋ฐ ์ค‘์ ์„ ๋‘๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋ž˜์„œ ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ์ง‘์ค‘ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค: + +1. **๊ณ„๋‹จ์‹์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๋‹ค์‹œ ๋ Œ๋”๋ง ๊ฑด๋„ˆ๋›ฐ๊ธฐ** + +`` ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌ๋ Œ๋”๋งํ•  ๋•Œ, ์‹ค์ œ๋กœ๋Š” ``๋งŒ ๋ณ€๊ฒฝ๋˜์—ˆ์ง€๋งŒ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์˜ ๋งŽ์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜๋Š” ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. + +2. **React ์™ธ๋ถ€์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ๊ณ„์‚ฐ ๊ฑด๋„ˆ๋›ฐ๊ธฐ** + +์˜ˆ๋ฅผ ๋“ค์–ด, ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›… ๋‚ด๋ถ€์—์„œ `expensivelyProcessAReallyLargeArrayOfObjects()`์™€ ๊ฐ™์€ ๋Œ€์šฉ๋Ÿ‰ ๋ฐฐ์—ด ๊ฐ์ฒด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. + +### ๋ฆฌ๋ Œ๋”๋ง ์ตœ์ ํ™” + +React๋Š” ํ˜„์žฌ ์ƒํƒœ(๊ตฌ์ฒด์ ์œผ๋กœ๋Š” props, state, ๊ทธ๋ฆฌ๊ณ  context)์— ๋”ฐ๋ผ UI๋ฅผ ํ•จ์ˆ˜๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. + +ํ˜„์žฌ ๊ตฌํ˜„ ๋ฐฉ์‹์—์„œ๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ React๋Š” ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์™€ ๊ทธ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ชจ๋‘ ๋ฆฌ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. + +๋‹จ, useMemo(), useCallback(), ๋˜๋Š” React.memo()์™€ ๊ฐ™์€ ์ˆ˜๋™ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ์ ์šฉํ•œ ๊ฒฝ์šฐ๋Š” ์˜ˆ์™ธ์ž…๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹ค์Œ ์˜ˆ์ œ์—์„œ ``์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ``์ด ๋ฆฌ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. + +```tsx +function FriendList({ friends }) { + const onlineCount = useFriendOnlineCount(); + if (friends.length === 0) { + return ; + } + return ( +
+ {onlineCount} online + {friends.map((friend) => ( + + ))} + +
+ ); +} +``` + +[๋ฆฌ์•กํŠธ ์ปดํŒŒ์ผ๋Ÿฌ ์˜ˆ์ œ ์‚ฌ์ดํŠธ์—์„œ ํ™•์ธํ•ด๋ณด์„ธ์š”](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAYjHgpgCYAyeYOAFMEWuZVWEQL4CURwADrEicQgyKEANnkwIAwtEw4iAXiJQwCMhWoB5TDLmKsTXgG5hRInjRFGbXZwB0UygHMcACzWr1ABn4hEWsYBBxYYgAeADkIHQ4uAHoAPksRbisiMIiYYkYs6yiqPAA3FMLrIiiwAAcAQ0wU4GlZBSUcbklDNqikusaKkKrgR0TnAFt62sYHdmp+VRT7SqrqhOo6Bnl6mCoiAGsEAE9VUfmqZzwqLrHqM7ubolTVol5eTOGigFkEMDB6u4EAAhKA4HCEZ5DNZ9ErlLIWYTcEDcIA) + +React ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ˆ˜๋™ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๊ณผ ๋™๋“ฑํ•œ ์ž‘์—…์„ ์ž๋™์œผ๋กœ ์ ์šฉํ•˜์—ฌ ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ ์•ฑ์˜ ๊ด€๋ จ ๋ถ€๋ถ„๋งŒ ๋ฆฌ๋ Œ๋”๋ง๋˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. + +์ด๋ฅผ ๋•Œ๋กœ๋Š”`์„ธ๋ฐ€ํ•œ ๋ฐ˜์‘์„ฑ(fine-grained reactivity)`์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. + +์œ„์˜ ์˜ˆ์—์„œ React ์ปดํŒŒ์ผ๋Ÿฌ๋Š” friends๊ฐ€ ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ ``์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ์„ ํŒ๋‹จํ•˜๊ณ , ์ด JSX๋ฅผ ๋‹ค์‹œ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ count๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ``์„ ๋‹ค์‹œ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. + +### ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ๊ณ„์‚ฐ๋„ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋ฉ๋‹ˆ๋‹ค. + +์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ Œ๋”๋ง ์ค‘์— ์‚ฌ์šฉ๋˜๋Š” ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ๊ณ„์‚ฐ์— ๋Œ€ํ•ด์„œ๋„ ์ž๋™์œผ๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```tsx +// ์ด๊ฒƒ์€ ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›…์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ์•กํŠธ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. +function expensivelyProcessAReallyLargeArrayOfObjects() { + /* ... */ +} + +// ์ด๊ฒƒ์€ ์ปดํฌ๋„ŒํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ์•กํŠธ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํ•ฉ๋‹ˆ๋‹ค. +function TableContainer({ items }) { + // ์ด ํ•จ์ˆ˜๋Š” ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ๋ฉ๋‹ˆ๋‹ค. + const data = expensivelyProcessAReallyLargeArrayOfObjects(items); + // ... +} +``` + +[๋ฆฌ์•กํŠธ ์ปดํŒŒ์ผ๋Ÿฌ ์˜ˆ์ œ ์‚ฌ์ดํŠธ์—์„œ ํ™•์ธํ•ด๋ณด์„ธ์š”](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAejQAgFTYHIQAuumAtgqRAJYBeCAJpgEYCemASggIZyGYDCEUgAcqAGwQwANJjBUAdokyEAFlTCZ1meUUxdMcIcIjyE8vhBiYVECAGsAOvIBmURYSonMCAB7CzcgBuCGIsAAowEIhgYACCnFxioQAyXDAA5gixMDBcLADyzvlMAFYIvGAAFACUmMCYaNiYAHStOFgAvk5OGJgAshTUdIysHNy8AkbikrIKSqpaWvqGIiZmhE6u7p7ymAAqXEwSguZcCpKV9VSEFBodtcBOmAYmYHz0XIT6ALzefgFUYKhCJRBAxeLcJIsVIZLI5PKFYplCqVa63aoAbm6u0wMAQhFguwAPPRAQA+YAfL4dIloUmBMlODogDpAA) + +๊ทธ๋Ÿฌ๋‚˜ `expensivelyProcessAReallyLargeArrayOfObjects`๊ฐ€ ์ •๋ง๋กœ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ํ•จ์ˆ˜๋ผ๋ฉด, React ์™ธ๋ถ€์—์„œ ์ž์ฒด์ ์œผ๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š”: + +- React ์ปดํŒŒ์ผ๋Ÿฌ๋Š” React ์ปดํฌ๋„ŒํŠธ์™€ ํ›…๋งŒ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•˜๋ฉฐ, ๋ชจ๋“  ํ•จ์ˆ˜๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +- React ์ปดํŒŒ์ผ๋Ÿฌ์˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์€ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›…์—์„œ ๊ณต์œ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +๋”ฐ๋ผ์„œ `expensivelyProcessAReallyLargeArrayOfObjects`๊ฐ€ ์—ฌ๋Ÿฌ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉ๋œ๋‹ค๋ฉด, ๋™์ผํ•œ ํ•ญ๋ชฉ์ด ์ „๋‹ฌ๋˜๋”๋ผ๋„ ๊ทธ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ๊ณ„์‚ฐ์ด ๋ฐ˜๋ณต์ ์œผ๋กœ ์‹คํ–‰๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. + +์ฝ”๋“œ๊ฐ€ ๋” ๋ณต์žกํ•ด์ง€๊ธฐ ์ „์— ๋จผ์ € [ํ”„๋กœํŒŒ์ผ๋ง](https://react.dev/reference/react/useMemo#how-to-tell-if-a-calculation-is-expensive)์„ ํ†ตํ•ด ์‹ค์ œ๋กœ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. + +## ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋ฌด์—‡์„ ๊ฐ€์ •ํ• ๊นŒ์š” ? + +React ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‚ฌํ•ญ์„ ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค: + +1. ์œ ํšจํ•˜๊ณ  ์˜๋ฏธ๋ก ์ ์ธ JavaScript ์ฝ”๋“œ: + +2. ์ฝ”๋“œ๊ฐ€ ์œ ํšจํ•˜๊ณ  ์˜๋ฏธ๋ก ์ ์ธ JavaScript๋กœ ์ž‘์„ฑ๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + ๋„ ๊ฐ€๋Šฅ/์˜ต์…˜ ๊ฐ’ ๋ฐ ์†์„ฑ์˜ ์ •์˜ ์—ฌ๋ถ€๋ฅผ ํ…Œ์ŠคํŠธ: + +๋„ ๊ฐ€๋Šฅ(nullable)ํ•˜๊ฑฐ๋‚˜ ์˜ต์…˜(optional)์ธ ๊ฐ’๊ณผ ์†์„ฑ์„ ์ ‘๊ทผํ•˜๊ธฐ ์ „์— ์ •์˜๋˜์–ด ์žˆ๋Š”์ง€ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค. + +์˜ˆ๋ฅผ ๋“ค์–ด, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ [strictNullChecks](https://www.typescriptlang.org/tsconfig/#strictNullChecks)๋ฅผ ํ™œ์„ฑํ™”ํ•˜์—ฌ if `(object.nullableProperty) { object.nullableProperty.foo }` ๋˜๋Š” ์˜ต์…”๋„ ์ฒด์ด๋‹์„ ์‚ฌ์šฉํ•˜์—ฌ `object.nullableProperty?.foo`์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +3. [React์˜ ๊ทœ์น™](https://react.dev/reference/rules)์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. + +React ์ปดํŒŒ์ผ๋Ÿฌ๋Š” React์˜ ๊ทœ์น™์„ ์ •์ ์œผ๋กœ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์˜ค๋ฅ˜๊ฐ€ ๊ฐ์ง€๋˜๋ฉด ์•ˆ์ „ํ•˜๊ฒŒ ์ปดํŒŒ์ผ์„ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค. + +์˜ค๋ฅ˜๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด [eslint-plugin-react-compiler](https://www.npmjs.com/package/eslint-plugin-react-compiler)๋ฅผ ์„ค์น˜ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. + +## ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‹œ๋„ํ•ด๋ด์•ผ ํ• ๊นŒ์š”? + +์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์•„์ง ์‹คํ—˜์ ์ธ ๋‹จ๊ณ„์— ์žˆ์œผ๋ฉฐ, ๋งŽ์€ ๋ถ€๋ถ„์ด ๋ฏธ์™„์„ฑ ์ƒํƒœ์ž„์„ ์œ ์˜ํ•˜์„ธ์š”. + +Meta์™€ ๊ฐ™์€ ํšŒ์‚ฌ์—์„œ ํ”„๋กœ๋•์…˜์— ์‚ฌ์šฉ๋˜์—ˆ์ง€๋งŒ, ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์—ฌ๋Ÿฌ๋ถ„์˜ ์•ฑ ํ”„๋กœ๋•์…˜์— ๋„์ž…ํ• ์ง€๋Š” ์ฝ”๋“œ๋ฒ ์ด์Šค์˜ ์ƒํƒœ์™€ [React์˜ ๊ทœ์น™](https://react.dev/reference/rules)์„ ์–ผ๋งˆ๋‚˜ ์ž˜ ์ค€์ˆ˜ํ–ˆ๋Š”์ง€์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. + +**์ง€๊ธˆ ๋‹น์žฅ ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค.** + +**์•ˆ์ •๋œ ๋ฆด๋ฆฌ์Šค๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๋„์ž…ํ•ด๋„ ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค.** + +ํ•˜์ง€๋งŒ, ์•ฑ์—์„œ ์ž‘์€ ์‹คํ—˜์„ ํ†ตํ•ด ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‹œ๋„ํ•ด๋ณด๊ณ  ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•ด ์ฃผ์‹œ๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ๋”์šฑ ๊ฐœ์„ ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. + +## ์‹œ์ž‘ํ•˜๊ธฐ + +์ด ๋ฌธ์„œ๋“ค ์™ธ์—๋„, React ์ปดํŒŒ์ผ๋Ÿฌ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด์™€ ๋…ผ์˜๋ฅผ ์œ„ํ•ด [React Compiler Working Group](https://github.com/reactwg/react-compiler)์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค. + +## ํ˜ธํ™˜์„ฑ ํ™•์ธ + +์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์„ค์น˜ํ•˜๊ธฐ ์ „์—, ๋จผ์ € ์ฝ”๋“œ๋ฒ ์ด์Šค๊ฐ€ ํ˜ธํ™˜๋˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +``` +npx react-compiler-healthcheck@latest +``` + +์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค: + +- ์ตœ์ ํ™”์— ์„ฑ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ์ˆ˜๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค: ์ˆซ์ž๊ฐ€ ํด์ˆ˜๋ก ์ข‹์Šต๋‹ˆ๋‹ค. + +- `` ์‚ฌ์šฉ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค: ์ด๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ  ๋”ฐ๋ฅด๋Š” ๊ฒฝ์šฐ [React์˜ ๊ทœ์น™](https://react.dev/reference/rules)์„ ๋”ฐ๋ฅผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค. + +- ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค: ์ปดํŒŒ์ผ๋Ÿฌ์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” ๊ฒƒ์œผ๋กœ ์•Œ๋ ค์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค. + +``` +Successfully compiled 8 out of 9 components. +StrictMode usage not found. +Found no usage of incompatible libraries. +``` + +## eslint-plugin-react-compiler ์„ค์น˜ + +React ์ปดํŒŒ์ผ๋Ÿฌ๋Š” eslint ํ”Œ๋Ÿฌ๊ทธ์ธ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. + +์ด eslint ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ปดํŒŒ์ผ๋Ÿฌ์™€ ๋…๋ฆฝ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„ eslint ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +``` +npm install eslint-plugin-react-compiler +``` + +๊ทธ๋ฆฌ๊ณ , eslint config์— ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”. + +```ts +module.exports = { + plugins: ["eslint-plugin-react-compiler"], + rules: { + "react-compiler/react-compiler": "error", + }, +}; +``` + +eslint ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์—๋””ํ„ฐ์—์„œ React์˜ ๊ทœ์น™ ์œ„๋ฐ˜ ์‚ฌํ•ญ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. + +์ด ๊ฒฝ์šฐ, ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›…์˜ ์ตœ์ ํ™”๋ฅผ ๊ฑด๋„ˆ๋›ด๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. + +์ด๋Š” ์ „ํ˜€ ๋ฌธ์ œ๊ฐ€ ์—†์œผ๋ฉฐ, ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ด๋ฅผ ๋ณต๊ตฌํ•˜๊ณ  ์ฝ”๋“œ๋ฒ ์ด์Šค์˜ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ณ„์†ํ•ด์„œ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +**๋ชจ๋“  eslint ์œ„๋ฐ˜ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ˆ˜์ •ํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค.** + +์ตœ์ ํ™”๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์™€ ํ›…์˜ ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๊ธฐ ์œ„ํ•ด ์ž์‹ ์˜ ์†๋„์— ๋งž์ถฐ ์ด๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ๋ชจ๋“  ๊ฒƒ์„ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. + +## ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๋„์ž…ํ•˜๊ธฐ + +### ๊ธฐ์กด ํ”„๋กœ์ ํŠธ + +์ปดํŒŒ์ผ๋Ÿฌ๋Š” [React์˜ ๊ทœ์น™](https://react.dev/reference/rules)์„ ๋”ฐ๋ฅด๋Š” ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์™€ ํ›…์„ ์ปดํŒŒ์ผํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +๋˜ํ•œ, ํ•ด๋‹น ๊ทœ์น™์„ ์œ„๋ฐ˜ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ๋Š” ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›…์„ ๊ฑด๋„ˆ๋›ฐ๋„๋ก ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ JavaScript์˜ ์œ ์—ฐํ•œ ํŠน์„ฑ ๋•Œ๋ฌธ์—, ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ชจ๋“  ์œ„๋ฐ˜์„ ์žก์•„๋‚ด์ง€๋Š” ๋ชปํ•˜๋ฉฐ, ์ž˜๋ชป๋œ ๊ธ์ •(false negative)์œผ๋กœ ์ธํ•ด ๊ทœ์น™์„ ์œ„๋ฐ˜ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›…์„ ์ปดํŒŒ์ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Š” ์ •์˜๋˜์ง€ ์•Š์€ ๋™์ž‘์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฌํ•œ ์ด์œ ๋กœ, ๊ธฐ์กด ํ”„๋กœ์ ํŠธ์—์„œ ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๋„์ž…ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ œํ’ˆ ์ฝ”๋“œ์˜ ์ž‘์€ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ๋จผ์ € ์‹คํ–‰ํ•ด๋ณด๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. + +์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ํŠน์ • ๋””๋ ‰ํ† ๋ฆฌ ์ง‘ํ•ฉ์—์„œ๋งŒ ์‹คํ–‰ํ•˜๋„๋ก ์„ค์ •ํ•˜์—ฌ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +```ts +const ReactCompilerConfig = { + sources: (filename) => { + return filename.indexOf("src/path/to/dir") !== -1; + }, +}; +``` + +๋“œ๋ฌธ ๊ฒฝ์šฐ์ด์ง€๋งŒ, `compilationMode: "annotation"` ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ "opt-in" ๋ชจ๋“œ๋กœ ์‹คํ–‰ํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด `"use memo"` ์ง€์‹œ์–ด๊ฐ€ ์ฃผ์„๋œ ์ปดํฌ๋„ŒํŠธ์™€ ํ›…๋งŒ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ปดํŒŒ์ผํ•ฉ๋‹ˆ๋‹ค. + +์ฃผ์„ ๋ชจ๋“œ๋Š” ์ดˆ๊ธฐ ๋„์ž…์ž๋ฅผ ๋•๊ธฐ ์œ„ํ•œ ์ž„์‹œ์ ์ธ ๊ฒƒ์ด๋ฉฐ, "use memo" ์ง€์‹œ์–ด๊ฐ€ ์žฅ๊ธฐ์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๊ธฐ๋ฅผ ์˜๋„ํ•œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. + +```ts +const ReactCompilerConfig = { + compilationMode: "annotation", +}; + +// src/app.jsx +export default function App() { + "use memo"; + // ... +} +``` + +์ปดํŒŒ์ผ๋Ÿฌ ๋„์ž…์— ๋Œ€ํ•œ ํ™•์‹ ์ด ์ƒ๊ธฐ๋ฉด, ๋‹ค๋ฅธ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ์ ์šฉ ๋ฒ”์œ„๋ฅผ ํ™•์žฅํ•˜์—ฌ ์ ์ฐจ ์ „์ฒด ์•ฑ์œผ๋กœ ๋ฒ”์œ„๋ฅผ ๋„“ํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ + +์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ, ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์ „์ฒด ์ฝ”๋“œ๋ฒ ์ด์Šค์— ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๊ธฐ๋ณธ ๋™์ž‘์ž…๋‹ˆ๋‹ค. + +### ์‚ฌ์šฉ๋ฒ• + +### Babel + +``` +npm install babel-plugin-react-compiler +``` + +์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +์„ค์น˜ ํ›„, Babel ์„ค์ •์— ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ **๊ฐ€์žฅ ๋จผ์ €** ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค: + +```ts +// babel.config.js +const ReactCompilerConfig = { + /* ... */ +}; + +module.exports = function () { + return { + plugins: [ + ["babel-plugin-react-compiler", ReactCompilerConfig], // ๊ฐ€์žฅ ๋จผ์ € ์‹คํ–‰ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค! + // ... + ], + }; +}; +``` + +`babel-plugin-react-compiler`๋Š” ๋‹ค๋ฅธ Babel ํ”Œ๋Ÿฌ๊ทธ์ธ๋“ค๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ •ํ™•ํ•œ ๋ถ„์„์„ ์œ„ํ•ด ์ž…๋ ฅ ์†Œ์Šค ์ •๋ณด๋ฅผ ํ•„์š”๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +## Vite + +๋งŒ์•ฝ Vite๋ฅผ ์‚ฌ์šฉํ•˜์‹ ๋‹ค๋ฉด, ์„ค์ • ํŒŒ์ผ์— ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋„ฃ์œผ์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```ts +// vite.config.js +const ReactCompilerConfig = { + /* ... */ +}; + +export default defineConfig(() => { + return { + plugins: [ + react({ + babel: { + plugins: [["babel-plugin-react-compiler", ReactCompilerConfig]], + }, + }), + ], + // ... + }; +}); +``` + +## Next.js + +Next.js๋Š” React ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ธฐ ์œ„ํ•œ ์‹คํ—˜์  ์„ค์ •์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ `babel-plugin-react-compiler`๊ฐ€ ์ž๋™์œผ๋กœ ์„ค์ •๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. + +- Next.js canary๋ฅผ ์„ค์น˜ํ•˜์„ธ์š”. ์ด๋Š” React 19 Release Candidate๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. + +- `babel-plugin-react-compiler`๋ฅผ ์„ค์น˜ํ•˜์„ธ์š”. + +``` +npm install next@canary babel-plugin-react-compiler +``` + +๊ทธ ํ›„์—, `next.config.js`์—์„œ ์‹คํ—˜์ ์ธ ์˜ต์…˜์„ ์„ค์ •ํ•ด์ฃผ์„ธ์š”. + +```ts +// next.config.js +/** @type {import('next').NextConfig} */ +const nextConfig = { + experimental: { + reactCompiler: true, + }, +}; + +module.exports = nextConfig; +``` + +์‹คํ—˜์  ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ์—์„œ React ์ปดํŒŒ์ผ๋Ÿฌ์— ๋Œ€ํ•œ ์ง€์›์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค: + +- ์•ฑ ๋ผ์šฐํ„ฐ(App Router) + +- ํŽ˜์ด์ง€ ๋ผ์šฐํ„ฐ(Pages Router) + +- ์›นํŒฉ(Webpack) (๊ธฐ๋ณธ ์„ค์ •) + +- ํ„ฐ๋ณดํŒฉ(Turbopack) (์˜ต์…˜์œผ๋กœ --turbo๋ฅผ ํ†ตํ•ด ํ™œ์„ฑํ™”) + +## - Remix + +`vite-plugin-babel`์„ ์„ค์น˜ํ•˜์‹œ๊ณ , ํ•ด๋‹น ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”. + +``` +npm install vite-plugin-babel +``` + +```ts +// vite.config.js +import babel from "vite-plugin-babel"; + +const ReactCompilerConfig = { + /* ... */ +}; + +export default defineConfig({ + plugins: [ + remix({ + /* ... */ + }), + babel({ + filter: /\.[jt]sx?$/, + babelConfig: { + presets: ["@babel/preset-typescript"], // if you use TypeScript + plugins: [["babel-plugin-react-compiler", ReactCompilerConfig]], + }, + }), + ], +}); +``` + +## Webpack + +์ด ์ฒ˜๋Ÿผ ๋ณธ์ธ๋งŒ์˜ React Compiler๋ฅผ ์œ„ํ•œ ๋กœ๋”๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```ts +const ReactCompilerConfig = { /* ... */ }; +const BabelPluginReactCompiler = require('babel-plugin-react-compiler'); + +function reactCompilerLoader(sourceCode, sourceMap) { + // ... + const result = transformSync(sourceCode, { + // ... + plugins: [ + [BabelPluginReactCompiler, ReactCompilerConfig], + ], + // ... + }); + + if (result === null) { + this.callback( + Error( + `Failed to transform "${options.filename}"` + ) + ); + return; + } + + this.callback( + null, + result.code + result.map === null ? undefined : result.map + ); +} + +module.exports = reactCompilerLoader; +``` + +## Expo + +Expo๋Š” Metro๋ฅผ ํ†ตํ•ด Babel์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ์„ค์น˜ ์ง€์นจ์€ [Babel ์‚ฌ์šฉ ์„น์…˜](https://react.dev/learn/react-compiler#usage-with-babel)์„ ์ฐธ์กฐํ•˜์„ธ์š”. + +## Metro (React Native) + +React Native๋Š” Metro๋ฅผ ํ†ตํ•ด Babel์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ์„ค์น˜ ์ง€์นจ์€ [Babel ์‚ฌ์šฉ ์„น์…˜](https://react.dev/learn/react-compiler#usage-with-babel)์„ ์ฐธ์กฐํ•˜์„ธ์š”. + +## ๋ฌธ์ œ ํ•ด๊ฒฐ + +์ด์Šˆ๋ฅผ ๋ณด๊ณ ํ•˜๋ ค๋ฉด ๋จผ์ € [React Compiler Playground](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAejQAgFTYHIQAuumAtgqRAJYBeCAJpgEYCemASggIZyGYDCEUgAcqAGwQwANJjBUAdokyEAFlTCZ1meUUxdMcIcIjyE8vhBiYVECAGsAOvIBmURYSonMCAB7CzcgBuCGIsAAowEIhgYACCnFxioQAyXDAA5gixMDBcLADyzvlMAFYIvGAAFACUmMCYaNiYAHStOFgAvk5OGJgAshTUdIysHNy8AkbikrIKSqpaWvqGIiZmhE6u7p7ymAAqXEwSguZcCpKV9VSEFBodtcBOmAYmYHz0XIT6ALzefgFUYKhCJRBAxeLcJIsVIZLI5PKFYplCqVa63aoAbm6u0wMAQhFguwAPPRAQA+YAfL4dIloUmBMlODogDpAA)์—์„œ ์ตœ์†Œ ์žฌํ˜„ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด ๋ฒ„๊ทธ ๋ณด๊ณ ์„œ์— ํฌํ•จํ•˜์„ธ์š”. + +์ด์Šˆ๋Š” [facebook/react ์ €์žฅ์†Œ](https://github.com/facebook/react/issues)์— ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +React Compiler Working Group์— ๊ฐ€์ž…ํ•˜์—ฌ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +[๊ฐ€์ž…์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ README๋ฅผ ์ฐธ์กฐ](https://github.com/reactwg/react-compiler)ํ•˜์„ธ์š”. + +## (0 , \_c) is not a function ์˜ค๋ฅ˜ + +์ด ์˜ค๋ฅ˜๋Š” React 19 RC ์ด์ƒ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. + +์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์•ฑ์„ [React 19 RC๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ](https://react.dev/blog/2024/04/25/react-19-upgrade-guide)ํ•˜์„ธ์š”. + +React 19๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ, [Working Group](https://github.com/reactwg/react-compiler/discussions/6)์—์„œ ์„ค๋ช…ํ•œ ์บ์‹œ ํ•จ์ˆ˜์˜ ์‚ฌ์šฉ์ž ๊ณต๊ฐ„ ๊ตฌํ˜„์„ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ ์ด๋Š” ๊ถŒ์žฅ๋˜์ง€ ์•Š์œผ๋ฉฐ ๊ฐ€๋Šฅํ•œ ํ•œ ๋นจ๋ฆฌ React 19๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. + +## ๋‚ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ตœ์ ํ™”๋˜์—ˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ๋‚˜์š”? + +[React Devtools(v5.0+)](https://react.dev/learn/react-developer-tools)๋Š” React ์ปดํŒŒ์ผ๋Ÿฌ์— ๋Œ€ํ•œ ๋‚ด์žฅ ์ง€์›์„ ์ œ๊ณตํ•˜๋ฉฐ, ์ปดํŒŒ์ผ๋Ÿฌ์— ์˜ํ•ด ์ตœ์ ํ™”๋œ ์ปดํฌ๋„ŒํŠธ ์˜†์— "Memo โœจ" ๋ฐฐ์ง€๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. + +## ์ปดํŒŒ์ผ ํ›„ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ + +eslint-plugin-react-compiler๋ฅผ ์„ค์น˜ํ•œ ๊ฒฝ์šฐ, ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์—๋””ํ„ฐ์—์„œ React ๊ทœ์น™ ์œ„๋ฐ˜ ์‚ฌํ•ญ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. + +์ด ๊ฒฝ์šฐ, ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›…์˜ ์ตœ์ ํ™”๋ฅผ ๊ฑด๋„ˆ๋›ฐ์—ˆ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. + +์ด๋Š” ์ „ํ˜€ ๋ฌธ์ œ๊ฐ€ ์—†์œผ๋ฉฐ, ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ด๋ฅผ ๋ณต๊ตฌํ•˜๊ณ  ์ฝ”๋“œ๋ฒ ์ด์Šค์˜ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ณ„์†ํ•ด์„œ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋ชจ๋“  eslint ์œ„๋ฐ˜ ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ์ˆ˜์ •ํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. + +์ตœ์ ํ™”๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์™€ ํ›…์˜ ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๊ธฐ ์œ„ํ•ด ์ž์‹ ์˜ ์†๋„์— ๋งž์ถฐ ์ด๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋Ÿฌ๋‚˜ JavaScript์˜ ์œ ์—ฐํ•˜๊ณ  ๋™์ ์ธ ํŠน์„ฑ ๋•Œ๋ฌธ์— ๋ชจ๋“  ๊ฒฝ์šฐ๋ฅผ ํฌ๊ด„์ ์œผ๋กœ ๊ฐ์ง€ํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + +์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์— ๋ฌดํ•œ ๋ฃจํ”„์™€ ๊ฐ™์€ ๋ฒ„๊ทธ๋‚˜ ์ •์˜๋˜์ง€ ์•Š์€ ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ปดํŒŒ์ผ ํ›„ ์•ฑ์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š๊ณ  eslint ์˜ค๋ฅ˜๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ž˜๋ชป ์ปดํŒŒ์ผํ–ˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด ๊ด€๋ จ์ด ์žˆ์„ ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›…์„ `"use no memo"` ์ง€์‹œ์–ด๋ฅผ ํ†ตํ•ด ๊ฐ•์ œ๋กœ opt-out ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด๋ณด์„ธ์š”. + +```ts +function SuspiciousComponent() { + "use no memo"; // ์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ React ์ปดํŒŒ์ผ๋Ÿฌ์— ์˜ํ•ด ์ปดํŒŒ์ผ๋˜์ง€ ์•Š๋„๋ก ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค. + // ... +} +``` + +## ์•Œ์•„๋‘์–ด์•ผ ํ•  ์  + +> "use no memo" + +`"use no memo"`๋Š” React ์ปดํŒŒ์ผ๋Ÿฌ์— ์˜ํ•ด ์ปดํŒŒ์ผ๋˜์ง€ ์•Š๋„๋ก ์ปดํฌ๋„ŒํŠธ์™€ ํ›…์„ ๋ฐฐ์ œํ•  ์ˆ˜ ์žˆ๋Š” ์ž„์‹œ ํƒˆ์ถœ๊ตฌ์ž…๋‹ˆ๋‹ค. + +์ด ์ง€์‹œ์–ด๋Š” "use client"์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์žฅ๊ธฐ์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. + +์ด ์ง€์‹œ์–ด๋Š” ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. + +์ผ๋‹จ ์ปดํฌ๋„ŒํŠธ๋‚˜ ํ›…์„ ๋ฐฐ์ œํ•˜๋ฉด, ์ง€์‹œ์–ด๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์˜๊ตฌ์ ์œผ๋กœ ๋ฐฐ์ œ๋œ ์ƒํƒœ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. + +์ฆ‰, ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๋”๋ผ๋„ ์ง€์‹œ์–ด๋ฅผ ์ œ๊ฑฐํ•˜์ง€ ์•Š์œผ๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์—ฌ์ „ํžˆ ์ปดํŒŒ์ผ์„ ๊ฑด๋„ˆ๋›ฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. + +์˜ค๋ฅ˜๊ฐ€ ์‚ฌ๋ผ์ง€๋ฉด, opt-out ์ง€์‹œ์–ด๋ฅผ ์ œ๊ฑฐํ–ˆ์„ ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋‹ค์‹œ ๋ฐœ์ƒํ•˜๋Š”์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. + +๊ทธ๋Ÿฐ ๋‹ค์Œ [React Compiler Playground](https://playground.react.dev/#N4Igzg9grgTgxgUxALhAejQAgFTYHIQAuumAtgqRAJYBeCAJpgEYCemASggIZyGYDCEUgAcqAGwQwANJjBUAdokyEAFlTCZ1meUUxdMcIcIjyE8vhBiYVECAGsAOvIBmURYSonMCAB7CzcgBuCGIsAAowEIhgYACCnFxioQAyXDAA5gixMDBcLADyzvlMAFYIvGAAFACUmMCYaNiYAHStOFgAvk5OGJgAshTUdIysHNy8AkbikrIKSqpaWvqGIiZmhE6u7p7ymAAqXEwSguZcCpKV9VSEFBodtcBOmAYmYHz0XIT6ALzefgFUYKhCJRBAxeLcJIsVIZLI5PKFYplCqVa63aoAbm6u0wMAQhFguwAPPRAQA+YAfL4dIloUmBMlODogDpAA)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฒ„๊ทธ ๋ณด๊ณ ์„œ๋ฅผ ์ž‘์„ฑํ•ด ์ฃผ์„ธ์š”. + +๋ฌธ์ œ๋ฅผ ์ž‘์€ ์žฌํ˜„ ์˜ˆ์ œ๋กœ ์ค„์ด๊ฑฐ๋‚˜, ์˜คํ”ˆ ์†Œ์Šค ์ฝ”๋“œ์ธ ๊ฒฝ์šฐ ์ „์ฒด ์†Œ์Šค๋ฅผ ๋ถ™์—ฌ๋„ฃ์–ด๋„ ๋ฉ๋‹ˆ๋‹ค. + +์ด๋ฅผ ํ†ตํ•ด ๋ฌธ์ œ๋ฅผ ์‹๋ณ„ํ•˜๊ณ  ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. + +## ๋‹ค๋ฅธ ์ด์Šˆ๋“ค + +[์ด ๊ณณ](https://github.com/reactwg/react-compiler/discussions/7)์— ์ ‘์†ํ•ด์ฃผ์„ธ์š”.