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/4-useState-Mistakes-You-Should-Avoid-in-React.md b/June/article/4-useState-Mistakes-You-Should-Avoid-in-React.md
new file mode 100644
index 0000000..b68e3d9
--- /dev/null
+++ b/June/article/4-useState-Mistakes-You-Should-Avoid-in-React.md
@@ -0,0 +1,252 @@
+## ๐ [4 useState Mistakes You Should Avoid in React๐ซ](https://medium.com/gitconnected/4-usestate-mistakes-you-should-avoid-in-react-0d9d676869e2)
+
+### ๐๏ธ ๋ฒ์ญ ๋ ์ง: 2024.06.10
+
+### ๐ง ๋ฒ์ญํ ํฌ๋ฃจ: ์ํ(์ต์์ฐ)
+
+---
+
+## React์์ ํผํด์ผ ํ 4๊ฐ์ง `useState` ์ค์๐ซ
+
+#### ์๊ฐ
+
+๋ฆฌ์กํธ(React.js)๋ ์ปดํฌ๋ํธ ๋ด ์ํ ๊ด๋ฆฌ์ ๋ํ ๋
ํนํ ์ ๊ทผ ๋ฐฉ์์ผ๋ก ํ๋ ์น ๊ฐ๋ฐ์ ์ด์์ด ๋์์ต๋๋ค. ์ผ๋ฐ์ ์ธ ํ
์ค ํ๋์ธ useState๋ ๊ธฐ๋ณธ์ ์ด์ง๋ง ์ข
์ข
์๋ชป ์ฌ์ฉ๋ฉ๋๋ค. ํจ์จ์ ์ด๊ณ ๋ฒ๊ทธ ์๋ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค๊ณ ์ ํ๋ ์ด๋ณด์์ ๊ฒฝํ์ด ํ๋ถํ ๊ฐ๋ฐ์ ๋ชจ๋์๊ฒ ์ด๋ฌํ ์ผ๋ฐ์ ์ธ ์ค์๋ฅผ ์ดํดํ๊ณ ํผํ๋ ๊ฒ์ ๋งค์ฐ ์ค์ํฉ๋๋ค.
+
+์ด ๋ธ๋ก๊ทธ์์๋ ๋ฆฌ์กํธ์์ useState๋ฅผ ์ฌ์ฉํ ๋ ํผํด์ผ ํ ๋ค ๊ฐ์ง ์ค์ํ ์ค์๋ฅผ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ํจ๊ป ๋ฆฌ์กํธ ๊ธฐ์ ์ ํฅ์์์ผ ๋ด
์๋ค!
+
+๋ณธ๊ฒฉ์ ์ผ๋ก ์ดํด๋ณด๊ธฐ ์ ์ [์ ๊ฐ์ธ ์น์ฌ์ดํธ](https://programwithjayanth.com/?source=post_page-----0d9d676869e2--------------------------------)์์ ์น ๊ฐ๋ฐ์ ๋ํ ๋ณด๋ค ์ฌ์ธต์ ์ธ ๊ธฐ์ฌ๋ฅผ ์ดํด๋ณด์ธ์:
+
+#### ์ค์ 1: ์ด์ ์ํ๋ฅผ ๊ณ ๋ คํ์ง ์์ ๐จ
+
+React์ useState ํ
์ ์ฌ์ฉํ ๋ ๊ฐ์ฅ ์ต๊ทผ์ ์ํ๋ฅผ ์
๋ฐ์ดํธํ ๋ ์ด์ ์ํ๋ฅผ ๊ณ ๋ คํ์ง ์๋ ๊ฒ์ ํํ ์ค์์
๋๋ค. ์ด๋ฌํ ์ค์๋ ํนํ ๋น ๋ฅธ ๋๋ ์ฌ๋ฌ ์ํ ์
๋ฐ์ดํธ๋ฅผ ์ฒ๋ฆฌํ ๋ ์์์น ๋ชปํ ๋์์ ์ด๋ํ ์ ์์ต๋๋ค.
+
+#### โ ๋ฌธ์ ์ดํด
+
+React์์ ์นด์ดํฐ๋ฅผ ๋ง๋ ๋ค๊ณ ๊ฐ์ ํด ๋ด
์๋ค. ๋ฒํผ์ ํด๋ฆญํ ๋๋ง๋ค ์นด์ดํธ๋ฅผ ์ฆ๊ฐ์ํค๋ ๊ฒ์ด ๋ชฉํ์
๋๋ค. ๊ฐ๋จํ ์ ๊ทผ ๋ฐฉ๋ฒ์ ํ์ฌ ์ํ ๊ฐ์ 1์ ๋ํ๋ ๊ฒ์ผ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๊ฒ์ ๋ฌธ์ ๊ฐ ๋ ์ ์์ต๋๋ค.
+
+```jsx
+import React, { useState } from 'react';
+
+const CounterComponent = () => {
+ const [counter, setCounter] = useState(0);
+
+ const incrementCounter = () => {
+ setCounter(counter + 1); // ํญ์ ์์๋๋ก ์๋ํ์ง ์์ ์ ์์ต๋๋ค.
+ };
+
+ return (
+
+
Counter: {counter}
+
+
+ );
+};
+
+export default CounterComponent;
+```
+
+์์ ์ฝ๋์์ incrementCounter๋ ํ์ฌ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ์นด์ดํฐ๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค. ์ด๊ฒ์ ๊ฐ๋จํด ๋ณด์ด์ง๋ง ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค. React๋ ์ฌ๋ฌ setCounter ํธ์ถ์ ํจ๊ป ๋ฌถ๊ฑฐ๋ ๋ค๋ฅธ ์ํ ์
๋ฐ์ดํธ๊ฐ ๊ฐ์ญํ์ฌ ์นด์ดํฐ๊ฐ ๋งค๋ฒ ์ฌ๋ฐ๋ฅด๊ฒ ์
๋ฐ์ดํธ๋์ง ์์ ์ ์์ต๋๋ค.
+
+#### โ
์์ :
+
+์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํผํ๊ธฐ ์ํด setCounter ๋ฉ์๋์ ํจ์ํ์ ์ฌ์ฉํ์ธ์. ์ด ๋ฒ์ ์ React๊ฐ ๊ฐ์ฅ ์ต๊ทผ์ ์ํ ๊ฐ์ผ๋ก ํธ์ถํ๋ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ํญ์ ์ต์ ์ํ ๊ฐ์ผ๋ก ์์
ํ ์ ์์ต๋๋ค.
+
+```jsx
+import React, { useState } from 'react';
+
+const CounterComponent = () => {
+ const [counter, setCounter] = useState(0);
+
+ const incrementCounter = () => {
+ setCounter((prevCounter) => prevCounter + 1); // ๊ฐ์ฅ ์ต๊ทผ์ ์ํ์ ๋ฐ๋ผ ์ฌ๋ฐ๋ฅด๊ฒ ์
๋ฐ์ดํธ๋ฉ๋๋ค.
+ };
+
+ return (
+
+
Counter: {counter}
+
+
+ );
+};
+
+export default CounterComponent;
+```
+
+์์ ๋ ์ฝ๋์์๋ incrementCounter๊ฐ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ ๋ฐ ํจ์๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ด ํจ์๋ ๊ฐ์ฅ ์ต๊ทผ์ ์ํ(prevCounter)๋ฅผ ๋ฐ์ ์
๋ฐ์ดํธ๋ ์ํ๋ฅผ ๋ฐํํฉ๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ํนํ ์
๋ฐ์ดํธ๊ฐ ๋น ๋ฅด๊ฒ ์ผ์ด๋๊ฑฐ๋ ์ฐ์์ ์ผ๋ก ์ฌ๋ฌ ๋ฒ ๋ฐ์ํ ๋ ํจ์ฌ ๋ ์ ๋ขฐ์ฑ์ด ๋์ต๋๋ค.
+
+React JS ์ค์๊ฐ ๊ต์ก์ ๊ด์ฌ์ด ์๋ค๋ฉด ์์ธํ ๋ด์ฉ์ ์ ์๊ฒ ๋ฌธ์ํด ์ฃผ์ธ์.
+
+#### ์ค์ 2: ์ํ ๋ถ๋ณ์ฑ ๋ฌด์ ๐ง
+
+#### โ ๋ฌธ์ ์ดํด
+
+React์์๋ ์ํ๋ฅผ ๋ถ๋ณ์ผ๋ก ์ทจ๊ธํด์ผ ํฉ๋๋ค. ํํ ์ค์๋ ๊ฐ์ฒด์ ๋ฐฐ์ด๊ณผ ๊ฐ์ ๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ก ์ํ๋ฅผ ์ง์ ์์ ํ๋ ๊ฒ์
๋๋ค.
+
+์ํ ๊ฐ์ฒด์ ๋ํ ์๋ชป๋ ์ ๊ทผ ๋ฐฉ์์ ๊ณ ๋ คํด ๋ด
์๋ค:
+
+```jsx
+import React, { useState } from 'react';
+
+const ProfileComponent = () => {
+ const [profile, setProfile] = useState({ name: 'John', age: 30 });
+
+ const updateAge = () => {
+ profile.age = 31; // ์ง์ ์ํ ์์
+ setProfile(profile);
+ };
+
+ return (
+
+
Name: {profile.name}
+
Age: {profile.age}
+
+
+ );
+};
+
+export default ProfileComponent;
+```
+
+์ด ์ฝ๋๋ profile ๊ฐ์ฒด๋ฅผ ์๋ชป ์์ ํฉ๋๋ค. ์ด๋ฌํ ์์ ์ ์ฌ๋ ๋๋ง์ ํธ๋ฆฌ๊ฑฐํ์ง ์์ผ๋ฉฐ ์์ธกํ ์ ์๋ ๋์์ ์ด๋ํฉ๋๋ค.
+
+#### โ
์์ :
+
+์ํ๋ฅผ ์
๋ฐ์ดํธํ ๋๋ ํญ์ ๋ถ๋ณ์ฑ์ ์ ์งํ๊ธฐ ์ํด ์ ๊ฐ์ฒด ๋๋ ๋ฐฐ์ด์ ์์ฑํ์ธ์. ์ด๋ฅผ ์ํด ์คํ๋ ๋ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์ธ์.
+
+```jsx
+import React, { useState } from 'react';
+
+const ProfileComponent = () => {
+ const [profile, setProfile] = useState({ name: 'John', age: 30 });
+
+ const updateAge = () => {
+ setProfile({ ...profile, age: 31 }); // ์ฌ๋ฐ๋ฅด๊ฒ ์ํ ์
๋ฐ์ดํธ
+ };
+
+ return (
+
+
Name: {profile.name}
+
Age: {profile.age}
+
+
+ );
+};
+
+export default ProfileComponent;
+```
+
+์์ ๋ ์ฝ๋์์ updateAge๋ ์ํ ๋ถ๋ณ์ฑ์ ์ ์งํ๋ฉด์ ์
๋ฐ์ดํธ๋ ๋์ด๋ก ์ profile ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํด ์คํ๋ ๋ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+#### ์ค์ 3: ๋น๋๊ธฐ ์
๋ฐ์ดํธ ์ดํด ๋ถ์กฑ โณ
+
+#### โ ๋ฌธ์ ์ดํด
+
+useState๋ฅผ ํตํ React์ ์ํ ์
๋ฐ์ดํธ๋ ๋น๋๊ธฐ์ ์
๋๋ค. ํนํ ์ฌ๋ฌ ์ํ ์
๋ฐ์ดํธ๊ฐ ๋น ๋ฅธ ์ฐ์์ผ๋ก ์ด๋ฃจ์ด์ง ๋, ์ด๋ ์ข
์ข
ํผ๋์ ์ด๋ํฉ๋๋ค. ๊ฐ๋ฐ์๋ setState ํธ์ถ ์งํ ์ํ๊ฐ ์ฆ์ ๋ณ๊ฒฝ๋ ๊ฒ์ผ๋ก ์์ํ ์ ์์ง๋ง, ์ค์ ๋ก๋ React๊ฐ ์ฑ๋ฅ์์ ์ด์ ๋ก ์ด๋ฌํ ์
๋ฐ์ดํธ๋ฅผ ์ผ๊ด ์ฒ๋ฆฌํฉ๋๋ค.
+
+์ด๋ฌํ ์คํด๊ฐ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์๋ ์ผ๋ฐ์ ์ธ ์๋๋ฆฌ์ค๋ฅผ ์ดํด๋ด
์๋ค.
+
+```jsx
+import React, { useState } from 'react';
+
+const AsyncCounterComponent = () => {
+ const [count, setCount] = useState(0);
+
+ const incrementCount = () => {
+ setCount(count + 1);
+ setCount(count + 1);
+ // ๊ฐ๋ฐ์๋ count๊ฐ ๋ ๋ฒ ์ฆ๊ฐํ ๊ฒ์ ์์
+ };
+
+ return (
+
+
Count: {count}
+
+
+ );
+};
+
+export default AsyncCounterComponent;
+```
+
+์ด ์์ ์์ ๊ฐ๋ฐ์๋ count๋ฅผ ๋ ๋ฒ ์ฆ๊ฐ์ํค๋ ค๊ณ ํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ํ ์
๋ฐ์ดํธ์ ๋น๋๊ธฐ์ ํน์ฑ์ผ๋ก ์ธํด ๋ setCount ํธ์ถ์ ๋ชจ๋ ๋์ผํ ์ด๊ธฐ ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฏ๋ก count๊ฐ ํ ๋ฒ๋ง ์ฆ๊ฐํฉ๋๋ค.
+
+#### โ
์์ :
+
+๋น๋๊ธฐ ์
๋ฐ์ดํธ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌํ๋ ค๋ฉด setCount์ ํจ์ํ ์
๋ฐ์ดํธ ํ์์ ์ฌ์ฉํ์ธ์. ์ด๋ ๊ฒ ํ๋ฉด ๊ฐ ์
๋ฐ์ดํธ๊ฐ ๊ฐ์ฅ ์ต๊ทผ์ ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ํ๋ฉ๋๋ค.
+
+```jsx
+import React, { useState } from 'react';
+
+const AsyncCounterComponent = () => {
+ const [count, setCount] = useState(0);
+
+ const incrementCount = () => {
+ setCount((prevCount) => prevCount + 1);
+ setCount((prevCount) => prevCount + 1);
+ // ์ด์ ๊ฐ ์
๋ฐ์ดํธ๋ ๊ฐ์ฅ ์ต๊ทผ ์ํ์ ์ฌ๋ฐ๋ฅด๊ฒ ์์กด
+ };
+ // ์ ํ์ฌํญ: useEffect๋ฅผ ์ฌ์ฉํ์ฌ ์
๋ฐ์ดํธ ๋ ์ํ ํ์ธ
+ useEffect(() => {
+ console.log(count); // 2
+ }, [count]);
+
+ return (
+
+
Count: {count}
+
+
+ );
+};
+
+export default AsyncCounterComponent;
+```
+
+์์ ์ฝ๋์์ ๊ฐ setCount ํธ์ถ์ ์ํ์ ๊ฐ์ฅ ์ต๊ทผ ๊ฐ์ ์ฌ์ฉํ์ฌ ์ ํํ๊ณ ์์ฐจ์ ์ธ ์
๋ฐ์ดํธ๋ฅผ ๋ณด์ฅํฉ๋๋ค. ํนํ ์ฌ๋ฌ ์ํ ์
๋ฐ์ดํธ๊ฐ ์ฐ์์ ์ผ๋ก ๋น ๋ฅด๊ฒ ๋ฐ์ํ ๋ ํ์ฌ ์ํ์ ์์กดํ๋ ์์
์๋ ์ด ์ ๊ทผ ๋ฐฉ์์ด ๋งค์ฐ ์ค์ํฉ๋๋ค.
+
+#### ์ค์ 4: ํ์ ๋ฐ์ดํฐ์ ๋ํ ์ํ ์ค์ฉ ๐
+
+#### โ ๋ฌธ์ ์ดํด
+
+๊ธฐ์กด ์ํ ๋๋ props์์ ํ์ ๋ ์ ์๋ ๋ฐ์ดํฐ์ ์ํ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋น๋ฒํ ์ค๋ฅ์
๋๋ค. ์ด ์ค๋ณต ์ํ๋ ๋ณต์กํ๊ณ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ์ด๋ํ ์ ์์ต๋๋ค.
+
+์๋ฅผ ๋ค์ด:
+
+```jsx
+import React, { useState } from 'react';
+
+const GreetingComponent = ({ name }) => {
+ const [greeting, setGreeting] = useState(`Hello, ${name}`);
+
+ return {greeting}
;
+};
+
+export default GreetingComponent;
+```
+
+์ฌ๊ธฐ์ greeting ์ํ๋ name์์ ์ง์ ํ์ ๋ ์ ์์ผ๋ฏ๋ก ๋ถํ์ํฉ๋๋ค.
+
+#### โ
์์ :
+
+์ํ๋ฅผ ์ฌ์ฉํ๋ ๋์ , ๊ธฐ์กด ์ํ ๋๋ props์์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ํ์์ํค์ธ์.
+
+```jsx
+import React from 'react';
+
+const GreetingComponent = ({ name }) => {
+ const greeting = `Hello, ${name}`; // props์์ ์ง์ ํ์
+
+ return {greeting}
;
+};
+
+export default GreetingComponent;
+```
+
+์์ ๋ ์ฝ๋์์๋ greeting์ name prop์์ ์ง์ ๊ณ์ฐ๋๋ฏ๋ก ์ปดํฌ๋ํธ๊ฐ ๋จ์ํ๋๊ณ ๋ถํ์ํ ์ํ ๊ด๋ฆฌ๋ฅผ ํผํ ์ ์์ต๋๋ค.
+
+#### ๊ฒฐ๋ก ๐
+
+React์์ useState ํ
์ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์ ์ ๋ขฐ์ฑ ์๊ณ ํจ์จ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ์ถํ๋ ๋ฐ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์ด์ ์ํ ๋ฌด์, ์ํ ๋ถ๋ณ์ฑ ๊ด๋ฆฌ ์ค๋ฅ, ๋น๋๊ธฐ ์
๋ฐ์ดํธ ๊ฐ๊ณผ, ํ์ ๋ฐ์ดํฐ์ ๋ํ ์ค๋ณต ์ํ ๋ฐฉ์ง์ ๊ฐ์ ์ผ๋ฐ์ ์ธ ์ค์๋ฅผ ์ดํดํ๊ณ ํผํจ์ผ๋ก์จ ๋ณด๋ค ๋งค๋๋ฝ๊ณ ์์ธก ๊ฐ๋ฅํ ์ปดํฌ๋ํธ ๋์์ ๋ณด์ฅํ ์ ์์ต๋๋ค. ์ด๋ฌํ ์ธ์ฌ์ดํธ๋ฅผ ์ผ๋์ ๋๊ณ React ๊ฐ๋ฐ ์ฌ์ ์ ํฅ์์ํค๊ณ ๋ณด๋ค ๊ฒฌ๊ณ ํ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง๋ค์ด๋ณด์ธ์.
+
+์ด ๊ธ์ด ๋ง์์ ๋์
จ๋์? ์น ๊ฐ๋ฐ์ ๋ํ ์ฌ์ธต์ ์ธ ํ ๋ก ๊ณผ ์ธ์ฌ์ดํธ๋ฅผ ๋ณด๋ ค๋ฉด ์ ๊ฐ์ธ ๋ธ๋ก๊ทธ์ธ Program With Jayanth ๋ฅผ ๋ฐฉ๋ฌธํด์ฃผ์ธ์.
+
+Happy Coding!!
diff --git a/June/article/5-Best-Practices-for-the-Sign-up-Flow-with-examples.md b/June/article/5-Best-Practices-for-the-Sign-up-Flow-with-examples.md
new file mode 100644
index 0000000..0eda7ab
--- /dev/null
+++ b/June/article/5-Best-Practices-for-the-Sign-up-Flow-with-examples.md
@@ -0,0 +1,116 @@
+## ๐ [5 Best Practices for the Sign-up Flow (with examples!)](https://medium.com/prototypr/5-best-practices-for-the-sign-up-flow-with-examples-f55832edc8a3)
+### ๐๏ธ ๋ฒ์ญ ๋ ์ง: 2024.06.11
+### ๐ง ๋ฒ์ญํ ํฌ๋ฃจ: ๋ ์(๊น๋ค์)
+
+
+
+---
+
+# ํ์ ๊ฐ์
ํ๋ก์ฐ๋ฅผ ์ํ 5๊ฐ์ง ๋ชจ๋ฒ ์ฌ๋ก(์์์ ํจ๊ป!)
+
+์ด๋ป๊ฒ ํ๋ฉด ๋ ๋์ ํ์ ๊ฐ์
ํ๋ฆ์ ์ค๊ณํ ์ ์์๊น์? ๋ชจ๋ฒ ์ฌ๋ก๋ก ๋ค์ด๊ฐ ๋ณด๊ฒ ์ต๋๋ค.
+
+![์ธ๋ค์ผ](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*i-mVZoEC3KdwaW9iqO5yJw.png)
+
+
+
+## 1. ๋จ์ํ๊ฒ ๋ง๋ ๋ค
+๊ฐ์
์์์๋ ํ์ ์ ๋ณด๋ง ์
๋ ฅํด์ผ ํฉ๋๋ค. ์ ํ์ ์ด๋ฉ์ผ๊ณผ ๋น๋ฐ๋ฒํธ ์ธ์ ์ถ๊ฐ ์ ๋ณด๊ฐ ํ์ํ ๊ฒฝ์ฐ ๊ฐ์
ํ๋ฆ์ ์ฌ๋ฌ ๋จ๊ณ๋ก ๋๋๋๋ค.
+
+![๋์ ์์ - ํ ๋ฒ์ ๋๋ฌด ๋ง์ ์ ๋ณด๋ฅผ ์๊ตฌํ๋ค](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*ybmI5wJnAEZZ8w0l32wHyA.png)
+
+
+
+์์ ์์์ ํ ๋ฒ์ ๋๋ฌด ๋ง์ ์ ๋ณด๋ฅผ ์๊ตฌํฉ๋๋ค. ์ ์ฌ์ ์ธ ์ฌ์ฉ์๋ ๋ณต์กํ ๋ฑ๋ก ์์์ ์์ฑํ๋ ๋ฐ ์๊ฐ์ ํ ์ ํ ๋๊ธฐ๊ฐ ์์ต๋๋ค.
+
+
+
+![๋ชจ๋ฒ ์์ - ๋ ๋จ๊ณ๋ก ๋๋์์ต๋๋ค](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*BKUOfAzTAOL2GolCWmk_GQ.jpeg)
+
+
+
+์ด ์์์์ ๊ณ์ ์ ๋ง๋ค๋ ค๋ฉด ์ด๋ฉ์ผ๊ณผ ์ํธ ์ธ์ ์ถ๊ฐ ์ ๋ณด๊ฐ ํ์ํฉ๋๋ค. ๋ฐ๋ผ์ ๊ฐ์
ํ๋ฆ์ ๋ ๋จ๊ณ๋ก ๋๋ฉ๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ์ฒซ ๋ฒ์งธ ๋จ๊ณ ์ดํ์ ์ฌ์ฉ์์๊ฒ ์์ฑ๋๋ฅผ ์ ๊ณตํ๊ณ , ์ด๋ ์ฌ์ฉ์๊ฐ ๋ค์ ๋จ๊ณ๋ฅผ ์๋ฃํ๋๋ก ๋๊ธฐ๋ฅผ ๋ถ์ฌํฉ๋๋ค.
+
+
+
+![๊ฐ์ ๋ ํ์๊ฐ์
ํผ๊ณผ ์ ์ ํ๋ก์ฐ](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*6x5k0R62sQaqHbkYlX8u_w.png)
+
+
+
+์ด ๊ฐ์
์์์ ๊ฐ๋จํฉ๋๋ค. ์ด๋ฉ์ผ๊ณผ ๋น๋ฐ๋ฒํธ๋ง ์๊ตฌํฉ๋๋ค. ๊ณ์ ์์ฑ์ ํ์ํ์ง ์์ ๊ธฐํ ์ ๋ณด๋ ๊ฐ์
์๋ฃ ํ ํฅํ ์จ๋ณด๋ฉ ๋จ๊ณ์์ ์์ฒญํ ์ ์์ต๋๋ค.
+
+
+
+### ์์
+
+![์์ด๋น์๋น ํ์๊ฐ์
์ ์ฐจ](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*rFLcR_qdc7szYxWILlwNRA.png)
+
+![Jira ํ์๊ฐ์
์ ์ฐจ](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*FHwyDTQdq5_ksAQ9BkIueQ.png)
+
+
+
+## 2. ์์
๋ก๊ทธ์ธ ์ ๊ณต
+
+์์
๋ก๊ทธ์ธ์ ๊ฐ์
์ ์ฐจ๋ฅผ ๋ ๋น ๋ฅด๊ณ ์ฝ๊ฒ ๋ง๋ญ๋๋ค. ์์
๋ก๊ทธ์ธ์ ์ฌ์ฉํ์ฌ ๊ฐ์
ํ๋ฉด ์ด๋ฆ๊ณผ ์ด๋ฉ์ผ๊ณผ ๊ฐ์ ๋ค๋ฅธ ํ์ ์ ๋ณด๋ฅผ ์๋์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. ๋ํ ์ฌ์ฉ์๋ ์ํธ๋ฅผ ๋ง๋ค ํ์๊ฐ ์์ต๋๋ค.
+
+![์์
๋ก๊ทธ์ธ์ ์ ๊ณตํ์ง ์๋ ํ์๊ฐ์
ํผ](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*qec4QUkeVGAyaCcum4xkBA.png)
+
+
+
+### ์์
+
+![ํ์๊ฐ์
์ ์์
๋ก๊ทธ์ธ์ ์ ๊ณตํ๋ ์ ํ๋ค](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*huPpQx8wieO5sFiHQP9HGg.png)
+
+
+
+## 3. ๋น๋ฐ๋ฒํธ ํ๋์ ๋ํ ๊ฐ์ด๋ ์ ๊ณต
+
+๋น๋ฐ๋ฒํธ ์
๋ ฅ ํ๋ ๊ทผ์ฒ์ ์ฌ์ฉ์๊ฐ ๋น๋ฐ๋ฒํธ๋ฅผ ์์ฑํ ์ ์๋๋ก ๊ฐ์ด๋๋ฅผ ํ์ํ๊ณ , ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๋ ์ฆ๊ฐ์ ์ธ ํผ๋๋ฐฑ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋น๋ฐ๋ฒํธ๋ฅผ ์์ฑํ๊ณ ์ ์ฒด์ ์ธ ๊ฐ์
์ ์ฐจ๋ฅผ ์ฝ๊ฒ ์งํํ ์ ์์ต๋๋ค.
+
+
+
+![์
๋ ฅ ๊ฐ์ด๋๊ฐ ์๋ ๋น๋ฐ๋ฒํธ ํ๋](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Bb-RFo_S9JOPL_C3XT9cFQ.png)
+
+
+
+![๊ฐ์ด๋์ ์ ์ ์ ์
๋ ฅ์ ๋ฐ๋ฅธ ์ฆ๊ฐ์ ์ธ ํผ๋๋ฐฑ์ ์ ๊ณตํ๋ ๋น๋ฐ๋ฒํธ ํ๋](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Okp3bEY1wn7qaAOUYyfgxw.png)
+
+
+
+### ์์
+
+![์์ด๋น์ค๋น์ ๋น๋ฐ๋ฒํธ ํ๋](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*LDv7-D0zuvOS7dGgWPN8gA.png)
+
+
+
+![์คํฌํฐํ์ด์ ๋น๋ฐ๋ฒํธ ํ๋](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*W6snLrx-ssp2lyDPc26D0A.png)
+
+
+
+## 4. ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํ ์ ์๋๋ก ํ๊ธฐ
+
+์
๋ ฅํ ์ํธ๋ฅผ ๋ณด์ฌ์ฃผ๋ ์ต์
์ ์ ๊ณตํ์ฌ ์ฌ์ฉ์๋ ์
๋ ฅ์ ํ์ธํ ์ ์์ผ๋ฏ๋ก ์ค๋ฅ๋ฅผ ์
๋ ฅํ ๊ฐ๋ฅ์ฑ์ด ์ค์ด๋ญ๋๋ค.
+
+![๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํ ์ ์๋ ํ๋](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*pAZ7clw1rOHwa-nDTEDhYQ.png)
+
+
+
+![๊ตฌ๊ธ๊ณผ ๋งํฌ๋์ธ์ ๋น๋ฐ๋ฒํธ ํ๋](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*8zLwyB75qiJXRwOYxYkg1Q.png)
+
+
+
+## 5. ๋ช
๋ฃํ๊ณ ์์ธํ ์๋ฌ ๋ฉ์ธ์ง ์ ๊ณต
+
+์ฌ์ฉ์๊ฐ ์
๋ ฅ ํ๋์ ์๋ชป ์
๋ ฅํ๊ฑฐ๋ ํ์ ํ๋๋ฅผ ๋์ณค์ ๋ ์ค๋ฅ ๋ฉ์์ง์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์์น์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ด ํ์๋์ด์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ฌ์ฉ์๋ ์ด๋ค ์ ๋ณด๊ฐ ์๋ชป ์
๋ ฅ๋์๋์ง, ์ ์๋ชป ์
๋ ฅ๋์๋์ง ์ถ์ธกํด์ผ ํฉ๋๋ค.
+
+![์๋ฌ ์ํ๋ฅผ ํ์ํ๋ ๋์ ์์์ ๋ชจ๋ฒ ์์](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*c3nc7ECsEOvqNH1CLEw0kw.png)
+
+
+
+![Dropbox์ ์คํฌํฐํ์ด์ ํ์๊ฐ์
ํผ์ ์๋ฌ ๋ฉ์ธ์ง](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*c3nc7ECsEOvqNH1CLEw0kw.png)
+
+
+
+---
+
+
diff --git a/June/article/API-Layer-&-Fetch-Functions.md b/June/article/API-Layer-&-Fetch-Functions.md
new file mode 100644
index 0000000..4a25e17
--- /dev/null
+++ b/June/article/API-Layer-&-Fetch-Functions.md
@@ -0,0 +1,199 @@
+## ๐ [API Layer & Fetch Functions](https://profy.dev/article/react-architecture-api-layer-and-fetch-functions)
+
+### ๐๏ธ ๋ฒ์ญ ๋ ์ง: 2024.06.10
+
+### ๐ง ๋ฒ์ญํ ํฌ๋ฃจ: ๋ฌ๊ธฐ(๋ฐ์ ์ฐ)
+
+---
+
+## ๋ฒ์ญ ์ ๋ชฉ
+
+# API๊ณ์ธต & Fetch ํจ์
+
+๋ฆฌ์กํธ์ ์๊ฒฌ์ด ์๋ ํน์ฑ์ ์๋ ์ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค:
+
+- ํํธ์ผ๋ก๋ ์ ํ์ ์์ ๊ฐ ์์ต๋๋ค.
+- ๋ค๋ฅธ ํํธ์ผ๋ก ๋ง์ ํ๋ก์ ํธ๋ค์ด ๋ง์ถคํ์ด๊ณ ์ข
์ข
์ง์ ๋ถํ ๊ตฌ์กฐ๋ก ๋๋๊ณค ํฉ๋๋ค.
+
+์ด ๊ธ์ ์ํํธ์จ์ด ์ํคํ
์ฒ์ ๋ฆฌ์กํธ ์ฑ์ ๊ดํ ์ฐ์ฌ์ ๋ ๋ฒ์งธ ๋ถ๋ถ์ผ๋ก, ์ฐ๋ฆฌ๋ ๋์ ๊ดํ์ด ๋ง์ ์ฝ๋ ๋ฒ ์ด์ค๋ฅผ ๋จ๊ณ๋ณ๋ก ๋ฆฌํฉํ ๋งํด ๋๊ฐ ๊ฒ์
๋๋ค.
+
+[์ด์ ์๋ ์ ํ๋ฆฌ์ผ์ด์
๋ด์ ๋ชจ๋ ์์ฒญ ์ฌ์ด์์ API ๊ธฐ๋ณธ URL๊ณผ ๊ฐ์ ๊ณตํต ๊ตฌ์ฑ์ ๊ณต์ ํ๊ธฐ ์ํด API ํด๋ผ์ด์ธํธ๋ฅผ ์ถ์ถํ์ต๋๋ค.](https://profy.dev/article/react-architecture-api-client)
+
+์ด๋ฒ ๊ธ์์๋ API ๊ด๋ จ ์ฝ๋๋ฅผ UI ์ปดํฌ๋ํธ์์ ๋ถ๋ฆฌํ๋ ๋ฐ ์ด์ ์ ๋ง์ถ๊ณ ์ถ์ต๋๋ค.
+
+![alt text](https://ik.imagekit.io/87wct6jq4ql/tr:w-1280/https://media.graphassets.com/RUDy4wgkRquKJoBUs6Pn)
+
+## ๋์ ์ฝ๋์ ์์: API์ UI ์ฝ๋๊ฐ ์์ฌ ์์
+
+๋์ ์ฝ๋ ์์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ์ฌ๊ธฐ ์ด์ ๊ธ์ ์ฒซ ๋ฒ์งธ ๋ฆฌํฉํ ๋ง ๋จ๊ณ์ ๊ฒฐ๊ณผ๊ฐ ์์ต๋๋ค:
+
+๋ API ์๋ํฌ์ธํธ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํ๋ ์ปดํฌ๋ํธ์
๋๋ค.
+
+```tsx
+import { useEffect, useState } from "react";
+import { useParams } from "react-router";
+import { apiClient } from "@/api/client";
+import { LoadingSpinner } from "@/components/loading";
+import { ShoutList } from "@/components/shout-list";
+import { UserResponse, UserShoutsResponse } from "@/types";
+import { UserInfo } from "./user-info";
+
+export function UserProfile() {
+ const { handle } = useParams<{ handle: string }>();
+
+ const [user, setUser] = useState();
+ const [userShouts, setUserShouts] = useState();
+ const [hasError, setHasError] = useState(false);
+
+ useEffect(() => {
+ apiClient
+ .get(`/user/${handle}`)
+ .then((response) => setUser(response.data))
+ .catch(() => setHasError(true));
+
+ apiClient
+ .get(`/user/${handle}/shouts`)
+ .then((response) => setUserShouts(response.data))
+ .catch(() => setHasError(true));
+ }, [handle]);
+
+ if (hasError) {
+ return An error occurred
;
+ }
+
+ if (!user || !userShouts) {
+ return ;
+ }
+
+ return (
+
+
+
+
+ );
+}
+```
+
+## ์ด๊ฒ ์ ๋์ ์ฝ๋์ธ๊ฐ์?
+
+๊ฐ๋จํฉ๋๋ค. API ์์ฒญ๊ณผ UI ์ฝ๋๊ฐ ์์ฌ์์ต๋๋ค. API์ฝ๋์ UI์ฝ๋๊ฐ ์ฌ๊ธฐ์ ๊ธฐ ์กฐ๊ธ์ฉ ์กด์ฌํฉ๋๋ค.
+
+![alt text](https://ik.imagekit.io/87wct6jq4ql/tr:w-1280/https://media.graphassets.com/Il7QrSIMRFuFq782XMOg)
+
+์ฌ์ค์, UI๋ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ๊ฐ์ ธ์ค๋์ง๋ ์ ๊ฒฝ์ฐ์ง ์์์ผ ํฉ๋๋ค.
+
+- GET, POST, ๋๋ PATCH ์์ฒญ์ด ๋ณด๋ด์ง๋์ง ์ ๊ฒฝ ์ฐ์ง ์์์ผ ํฉ๋๋ค.
+- ์๋ํฌ์ธํธ์ ์ ํํ ๊ฒฝ๋ก๊ฐ ๋ฌด์์ธ์ง ์ ๊ฒฝ ์ฐ์ง ์์์ผ ํฉ๋๋ค.
+- ์์ฒญ ๋งค๊ฐ๋ณ์๊ฐ API์ ์ด๋ป๊ฒ ์ ๋ฌ๋๋์ง ์ ๊ฒฝ ์ฐ์ง ์์์ผ ํฉ๋๋ค.
+- ์ฌ์ง์ด REST API์ ์ฐ๊ฒฐ๋๋์ง ์น์์ผ์ ์ฐ๊ฒฐ๋๋์ง์กฐ์ฐจ ์ ๋ง๋ก ์ ๊ฒฝ ์ฐ์ง ์์์ผ ํฉ๋๋ค.
+
+์ด ๋ชจ๋ ๊ฒ์ UI์ ๊ด์ ์์ ๊ตฌํ ์ธ๋ถ์ฌํญ์ด์ด์ผ ํฉ๋๋ค.
+
+## ํด๊ฒฐ์ฑ
: API ์ฐ๊ฒฐ ํจ์๋ฅผ ์ถ์ถํ๋ค.
+
+API์ ์ฐ๊ฒฐํ๋ ํจ์๋ฅผ ๋ณ๋์ ์ฅ์๋ก ์ถ์ถํ๋ฉด API ๊ด๋ จ ์ฝ๋์ UI ์ฝ๋์ ๊ฒฐํฉ์ ํฌ๊ฒ ์ค์ผ ์ ์์ต๋๋ค.
+
+์ด ํจ์๋ค์ ๋ค์๊ณผ ๊ฐ์ ๊ตฌํ ์ธ๋ถ์ฌํญ์ ์จ๊น๋๋ค:
+
+- ์์ฒญ ๋ฐฉ์
+- ์๋ํฌ์ธํธ ๊ฒฝ๋ก
+- ๋ฐ์ดํฐ ์ ํ
+
+๋ถ๋ช
ํ ๋ถ๋ฆฌ๋ฅผ ์ํด ์ด๋ฌํ ํจ์๋ค์ ์ ์ญ API ํด๋์ ์์น์ํฌ ๊ฒ์
๋๋ค.
+
+```tsx
+// src/api/user.ts
+
+import { User, UserShoutsResponse } from "@/types";
+import { apiClient } from "./client";
+
+async function getUser(handle: string) {
+ const response = await apiClient.get<{ data: User }>(`/user/${handle}`);
+ return response.data;
+}
+
+async function getUserShouts(handle: string) {
+ const response = await apiClient.get(`/user/${handle}/shouts`);
+ return response.data;
+}
+
+export default { getUser, getUserShouts };
+```
+
+์ด์ ์ปดํฌ๋ํธ ์์์ fetch ํจ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
+
+```tsx
+import UserApi from "@/api/user";
+
+...
+
+export function UserProfile() {
+ const { handle } = useParams<{ handle: string }>();
+
+ const [user, setUser] = useState();
+ const [userShouts, setUserShouts] = useState();
+ const [hasError, setHasError] = useState(false);
+
+ useEffect(() => {
+ if (!handle) {
+ return;
+ }
+
+ UserApi.getUser(handle)
+ .then((response) => setUser(response.data))
+ .catch(() => setHasError(true));
+
+ UserApi.getUserShouts(handle)
+ .then((response) => setUserShouts(response))
+ .catch(() => setHasError(true));
+ }, [handle]);
+
+ ...
+```
+
+## UI์ฝ๋์ API์ฝ๋๋ฅผ ๋ถ๋ฆฌํ๋๊ฒ์ด ์ ๋ ์ข์ ์ฝ๋์ฃ ?
+
+๋ง์ต๋๋ค. ์ฝ๋์ ๋ณํ๊ฐ ํฌ์ง ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฝ๊ฐ์ ์ฝ๋๋ง ์ฎ๊ฒผ์ ๋ฟ์
๋๋ค.
+
+๊ฒฐ๊ณผ๋ฌผ์ด ์ฒ์์๋ง ์กฐ๊ธ ๊น๋ํ๊ฒ ๋ณด์ผ ์ ์์ต๋๋ค.
+
+```tsx
+// before
+apiClient.get(`/user/${handle}`);
+
+// after
+UserApi.getUser(handle);
+```
+
+๊ทธ๋ฌ๋ ์ฌ์ค์, ์ฐ๋ฆฌ๋ UI์ฝ๋์ API ๊ด๋ จ ํจ์๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๋ถ๋ฆฌํ๊ธฐ ์์ํ์ต๋๋ค.
+
+๋ฐ๋ณตํด์ ๋งํ๋ ๊ฒ์ด ์ง๊ฒน์ง ์์ต๋๋ค: ์ด์ ์ปดํฌ๋ํธ๋ ๋ค์๊ณผ ๊ฐ์ ๋ง์ API ๊ด๋ จ ์ธ๋ถ์ฌํญ์ ์์ง ๋ชปํฉ๋๋ค.
+
+- ์์ฒญ ๋ฐฉ์ GET
+- ๋ฐ์ดํฐ ์ ํ UserResponse์ ์ ์
+- ์๋ํฌ์ธํธ ๊ฒฝ๋ก /user/some-handle
+- ๋๋ ํธ๋ค์ด URL ๋งค๊ฐ๋ณ์๋ก API์ ์ ๋ฌ๋๋ค๋ ์ฌ์ค
+
+๋์ , ํ์
์ด ์ง์ ๋ ๊ฒฐ๊ณผ๋ฅผ ํ๋ก๋ฏธ์ค๋ก ๊ฐ์ผ ๊ฐ๋จํ ํจ์ `UserApi.getUser(handle)`๋ฅผ ํธ์ถํฉ๋๋ค.
+
+๋ํ, ์ด๋ฌํ ๊ฐ์ ธ์ค๊ธฐ ํจ์๋ค์ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์ฌ์ฌ์ฉํ๊ฑฐ๋ ํด๋ผ์ด์ธํธ ์ธก ๋๋ ์๋ฒ ์ธก ๋ ๋๋ง๊ณผ ๊ฐ์ ๋ค๋ฅธ ๋ ๋๋ง ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
+
+## ๋ค์ ๋ฆฌํฉํฐ๋ง ๋จ๊ณ
+
+๋ค, ์ฐ๋ฆฌ๋ API๋ก ๋ถํฐ UI์ฝ๋๋ฅผ ๋ถ๋ฆฌํ๋๋ฐ ์ฒซ ๋ฒ์งธ ํฐ ์ง์ ์ ์ด๋ฃจ์์ต๋๋ค. API๊ณ์ธต์ ๋์
ํ๊ณ , UI์ปดํฌ๋ํธ์ ๊ตฌํ๋ ๋ง์ ์ธ๋ถ์ฌํญ์ ์ ๊ฑฐํ์์ต๋๋ค.
+
+๊ทธ๋ฌ๋ ์ฌ์ ํ API ๊ณ์ธต๊ณผ ์ฐ๋ฆฌ ์ปดํฌ๋ํธ ์ฌ์ด์ ์๋นํ ๊ฒฐํฉ์ด ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ฐ๋ฆฌ๋ ์ปดํฌ๋ํธ ๋ด์์ ์๋ต ๋ฐ์ดํฐ๋ฅผ ๋ณํํฉ๋๋ค:
+
+![alt text](https://ik.imagekit.io/87wct6jq4ql/tr:w-1280/https://media.graphassets.com/lJjVG80BRVi817s4vOwF)
+
+์ด๊ฒ ์ธ์์ ์ธ ์์๊ฐ ์๋ ์ ์์ต๋๋ค. ๊ฐ์ ์ฝ๋์ ๋ค๋ฅธ ์์๋ฅผ ๋ณด๊ฒ ์ต๋๋ค.
+
+![alt text](https://ik.imagekit.io/87wct6jq4ql/tr:w-1280/https://media.graphassets.com/6FUzRWpTSbmufwlBQQy5)
+
+์ฌ๊ธฐ์์ ์ฐ๋ฆฌ๋ ํผ๋์ ์๋ต์ ์ฌ์ฉ์์ ์ด๋ฏธ์ง๋ฅผ ํฌํจํ๋ ํ๋๊ฐ ์์์ ๋ณผ ์ ์์ต๋๋ค.
+
+์์์ง๋ ์์ง๋ง, ๋๋๋ก ์ฐ๋ฆฌ๋ ์ด๋ฌํ API๋ฅผ ๋ค๋ฃจ์ด์ผ ํฉ๋๋ค.
+
+๊ทธ๋ฐ๋ฐ ์ ์ปดํฌ๋ํธ๊ฐ ์ด๊ฒ์ ์์์ผ ํ ๊น์?
+
+์ด์จ๋ , ์ด ๋ฌธ์ ๋ ๋ค์ ์ํฐํด์์ ๋ค๋ฃจ๊ฒ ์ต๋๋ค.
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-to-handle-multiple-modals-in-a-React-application.md b/June/article/How-to-handle-multiple-modals-in-a-React-application.md
new file mode 100644
index 0000000..abf4912
--- /dev/null
+++ b/June/article/How-to-handle-multiple-modals-in-a-React-application.md
@@ -0,0 +1,278 @@
+## ๐ [How to handle multiple modals in a React application](https://dev.to/zettadam/how-to-handle-multiple-modals-in-a-react-application-2pei)
+
+### ๐๏ธ ๋ฒ์ญ ๋ ์ง: 2024.06.29
+
+### ๐ง ๋ฒ์ญํ ํฌ๋ฃจ: ๋ฌ๊ธฐ(๋ฐ์ ์ฐ)
+
+---
+
+## ๋ฆฌ์กํธ ์ดํ๋ฆฌ์ผ์ด์
์์ ์ฌ๋ฌ๊ฐ์ ๋ชจ๋ฌ๋ค์ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ
+
+์ฐธ๊ณ : ์ ์ฒด ์์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฌ๊ธฐ์์ ํ์ธํ ์ ์์ต๋๋ค: https://stackblitz.com/edit/react-modals
+
+React ์ ํ๋ฆฌ์ผ์ด์
์์ ๋ชจ๋ฌ์ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ํ๋๋ง ์๋ ๊ฒ์ด ์๋์ง๋ง, ๋ช๋ช ๋ฐฉ์์ด ๋ค๋ฅธ ๋ฐฉ์๋ณด๋ค ๋์ ์ ์์ต๋๋ค. ์ด ๊ธ์์๋ Redux ์คํ ์ด ๊ฐ์ ๊ธ๋ก๋ฒ ์คํ ์ด๋ฅผ ์ฌ์ฉํ์ฌ ๋ชจ๋ฌ์ ๊ด๋ฆฌํ๋ ๊ฒ๋ณด๋ค ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ์๊ฐํ๊ณ ์ ํฉ๋๋ค. ์ด ์์ ์์๋ ์ปดํฌ๋ํธ ์ํ์ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ ์ฌ์ฉํ ๊ฒ์ด๋ฉฐ, ์ด๋ React์ Portals๋ ์ธ๊ธ๋์ด ์์ต๋๋ค.
+
+๋ชจ๋ฌ์ ๋ณดํต ๋ผ์ฐํฐ์ ์ํด ๊ด๋ฆฌ๋๋ ๋ณ๋์ ํ๋ฉด๊ณผ ๋น์ทํ ๋ถ๋ถ์ด ์์ต๋๋ค.
+
+## AppShell
+
+์๋ฅผ ๋ค์ด ๋ค์ src/AppShell.jsx ์์ ์ ๊ฐ์ด, ๋ ์ข
๋ฅ์ ์ปดํฌ๋ํธ๋ฅผ ์ค์ ์ปดํฌ๋ํธ์์ ์๋ก ๊ฐ๊น๊ฒ ๋ ๋๋งํ๋ ๊ฒ์ด ํฉ๋ฆฌ์ ์ผ ์ ์์ต๋๋ค.
+
+```tsx
+import React, { useState } from 'react'
+import { BrowserRouter, NavLink, Route, Switch } from 'react-router-dom'
+
+import ScreenOne from './components/screen-one/ScreenOne'
+import ScreenTwo from './components/screen-two/ScreenTwo'
+import ScreenThree from './components/screen-three/ScreenThree'
+
+import ModalOne from './components/common/modal-one/ModalOne'
+import ModalTwo from './components/common/modal-two/ModalTwo'
+import ModalThree from './components/common/modal-three/ModalThree'
+
+import './app-shell.css'
+
+const AppShell = () => {
+ const [modalOpen, setModal] = useState(false)
+
+ const openModal = event => {
+ event.preventDefault()
+ const { target: { dataset: { modal }}} = event
+ if (modal) setModal(modal)
+ }
+
+ const closeModal = () => {
+ setModal('')
+ }
+
+ return (
+
+
+
+ {/* Application header and navigation */}
+
+ React Modal Windows
+
+
+
+ {/* Application screens */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Modals */}
+
+
+
+
+
+
+ {/* Application footer */}
+
+
+
+
+```
+
+## ๋จ์ผ ์ฑ
์ ์ปดํฌ๋ํธ๋ก ๋ฆฌํฉํฐ๋ง ํ๊ธฐ
+
+๋ง์ฝ ์ฌ๋ฌ๋ถ์ ์ ํ๋ฆฌ์ผ์ด์
์ด ๋ง์ ํ๋ฉด ํน์ ๋ง์ ๋ชจ๋ฌ์ ํฌํจํ๊ณ ์๋ค๋ฉด, ์๋ฅผ ๋ค์ด ScreenSwitchboard.jsx์ ModalManager.jsx์ ๊ฐ์ด ๋ผ์ฐํธ์ ๋ชจ๋ฌ์ ๋ณ๋์ ์ปดํฌ๋ํธ๋ก ์ถ์ถํจ์ผ๋ก์จ AppShell.jsx ์ปดํฌ๋ํธ๋ฅผ ์ข ๋ ๊น๋ํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
+
+```tsx
+import React, { useState } from "react";
+import { BrowserRouter } from "react-router-dom";
+
+import AppHeader from "./AppHeader";
+import AppFooter from "./AppFooter";
+
+import ScreenSwitchboard from "./ScreenSwitchboard";
+import ModalManager from "./ModalManager";
+
+import "./app-shell.css";
+
+const AppShell = () => {
+ const [modalOpen, setModal] = useState(false);
+
+ const openModal = (event) => {
+ event.preventDefault();
+ const {
+ target: {
+ dataset: { modal },
+ },
+ } = event;
+ if (modal) setModal(modal);
+ };
+
+ const closeModal = () => {
+ setModal("");
+ };
+
+ return (
+
+
+
+ );
+};
+
+export default AppShell;
+```
+
+## ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ ์ฌ์ฉํ์ฌ ํน์ ๋ชจ๋ฌ ์ด๊ธฐ
+
+์ฐ๋ฆฌ๋ #app--shell ์์์์ ๋ฒ๋ธ๋ง๋ ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ์บก์ฒํฉ๋๋ค. ํน์ ๋ชจ๋ฌ์ ์ด๋๋ก ํธ๋ฆฌ๊ฑฐํ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ openModal์ ์ฐ๋ฆฌ ์ ํ๋ฆฌ์ผ์ด์
์ ์ผ๋ถ ์์(๋ฒํผ, ๋งํฌ ๋ฑ)์ ์ค์ ํ ์ ์๋ data-modal ์์ฑ์ ์ฐพ์ต๋๋ค.
+
+์๋๋ ํด๋ฆญํ์ ๋ ๋ชจ๋ฌ์ ์ด๋๋ก ํธ๋ฆฌ๊ฑฐํ๋ ๋ฒํผ์ด ์๋ ํ๋ฉด ์ปดํฌ๋ํธ์ ์์์
๋๋ค.
+
+```tsx
+import React from "react";
+
+const ScreenOne = ({}) => {
+ return (
+
+ Screen One
+
+
+
+
+
+
+
+ );
+};
+
+export default ScreenOne;
+```
+
+๋ณด์๋ค์ํผ, ์ฐ๋ฆฌ๋ ์ ํ๋ฆฌ์ผ์ด์
์ ๊ณ์ธต ๊ตฌ์กฐ props ๋ด๋ฆฌ๊ธฐ๋ฅผ ํตํด ํจ์๋ ๊ฐ์ ์ ๋ฌํ๊ณ ์์ง ์์ต๋๋ค. ๋์ , data-modal ์์ฑ๊ณผ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ ์์กดํ์ฌ ํน์ ๋ชจ๋ฌ์ ์ฌ๋ ๊ฒ์ ์ฒ๋ฆฌํฉ๋๋ค.
+
+## ModalManager
+
+์ฐ๋ฆฌ์ ์ปดํฌ๋ํธ๋ ๋ ๊ฐ์ง props๋ฅผ ์๊ตฌํฉ๋๋ค: ์ด๋ค ๋ชจ๋ฌ์ด ์ด๋ ค์ผ ํ๋์ง๋ฅผ ์ค๋ช
ํ๋ modal prop์ผ๋ก์์ ์ํ ๊ฐ๊ณผ, ์ด๋ ค ์๋ ๋ชจ๋ฌ์ ๋ซ๋๋ก ์ ํ๋ฆฌ์ผ์ด์
์ ํจ๊ณผ์ ์ผ๋ก ์ง์ํ๋ closeFn prop์
๋๋ค.
+
+์ฐธ๊ณ : ๋ชจ๋ฌ์ ๊ฐ๋จํ ์ฝํ
์ธ ๋ฅผ ํฌํจํ ์๋ ์๊ณ , ํผ ์ฒ๋ฆฌ์ ๊ฐ์ ๋ ๋ณต์กํ ๊ฒฝ์ฐ๋ฅผ ๋ค๋ฃฐ ์๋ ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ๊ทธ๋ค์ ๋ซํ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ํด๋ฆญ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ ์์กดํ๊ณ ์ถ์ง ์์ต๋๋ค. ์ฌ๊ธฐ์์ prop์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ๊ฐ๋จํ๊ณ ์ ์ฐํฉ๋๋ค.
+
+๋ค์์ ์ปดํฌ๋ํธ์
๋๋ค.
+
+```tsx
+import React from "react";
+
+import ModalOne from "./components/common/modal-one/ModalOne";
+import ModalTwo from "./components/common/modal-two/ModalTwo";
+import ModalThree from "./components/common/modal-three/ModalThree";
+
+const ModalManager = ({ closeFn = () => null, modal = "" }) => (
+ <>
+
+
+
+
+
+ >
+);
+
+export default ModalManager;
+```
+
+## React Portal์ ์ฌ์ฉํ์ฌ ๋ชจ๋ฌ ๋ ๋๋งํ๊ธฐ
+
+ํ ๋ฒ์ ํ๋์ Modal๋ง ํ์ํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ํจํด์ด๋ฏ๋ก, ์์ ์์๋ฅผ React ํฌํธ๋ก ๋ ๋๋งํ ๋ํผ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๊ฒ์ด ํ๋นํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
+
+๋ค์์ src/components/common/modal/Modal.jsx ์ปดํฌ๋ํธ์ ์ฝ๋์
๋๋ค.
+
+```tsx
+import React, { useEffect } from "react";
+import ReactDOM from "react-dom";
+
+const modalRootEl = document.getElementById("modal-root");
+
+const Modal = ({ children, open = false }) => {
+ if (!open) return null;
+
+ return ReactDOM.createPortal(children, modalRootEl);
+};
+
+export default Modal;
+```
+
+์ฐ๋ฆฌ๋ #modal-root ์์๊ฐ document ์ด๋๊ฐ์, ํนํ ์ ํ๋ฆฌ์ผ์ด์
์ด ๋ง์ดํธ๋ #app-root ์์์ ํ์ ์์๋ก์ ์กด์ฌํ๊ธฐ๋ฅผ ๊ธฐ๋ํฉ๋๋ค.
+
+์๋ฅผ ๋ค์ด, index.html์ ๋ ๋ค์๊ณผ ๊ฐ์ด ๋ณด์ผ ์ ์์ต๋๋ค:
+
+```html
+
+
+
+
+```
+
+๋ง์ง๋ง์ผ๋ก, ํน์ ๋ชจ๋ฌ ์ปดํฌ๋ํธ์ ์์์
๋๋ค.
+
+```tsx
+import React from "react";
+
+import Modal from "../modal/Modal";
+
+const ModalOne = ({ closeFn = () => null, open = false }) => {
+ return (
+
+
+
+
+
+
Modal One content will be rendered here.
+
+
+
+
+
+ );
+};
+
+export default ModalOne;
+```
+
+์ด ๊ธ์์๋ ๊ตฌ์ฒด์ ์ธ ์์๋ฅผ ๋ค๋ฉด์ ์๋์ ์ผ๋ก ์งง๊ณ ๊ฐ๋จํ๊ฒ ๋ง๋ค๊ณ ์ ๋ชจ๋ ๋ด์ฉ์ ๋ค๋ฃจ์ง ์์์ต๋๋ค. ์คํ์ผ๋ง, ์ ๊ทผ์ฑ ๊ทธ๋ฆฌ๊ณ ์๋ง๋ ๋ค๋ฅธ ์์๋ค์ ๊ณ ๋ คํด์ผ ํ ๊ฒ์
๋๋ค.
+
+์ด ๊ธ์ ๋งจ ์์ ๊ฒ์๋ ๋งํฌ์์ ์ด ์์ค ์ฝ๋๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
+
+๋๊ธ๋ก ์ด ๊ธ์ ๋ํด ์ด๋ป๊ฒ ์๊ฐํ๋์ง, ํน์ ์ฌ๋ฌ๋ถ์ ์ ํ๋ฆฌ์ผ์ด์
์์ ๋ชจ๋ฌ์ ์ด๋ป๊ฒ ๊ด๋ฆฌํ๊ณ ์๋์ง ์๋ ค์ฃผ์ธ์.
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)์ ์ ์ํด์ฃผ์ธ์.
diff --git a/June/study/5-Best-Practices-for-the-Sign-up-Flow-with-examples.md b/June/study/5-Best-Practices-for-the-Sign-up-Flow-with-examples.md
new file mode 100644
index 0000000..f89b28b
--- /dev/null
+++ b/June/study/5-Best-Practices-for-the-Sign-up-Flow-with-examples.md
@@ -0,0 +1,6 @@
+# ํ์ ๊ฐ์
ํ๋ก์ฐ๋ฅผ ์ํ 5๊ฐ์ง ๋ชจ๋ฒ ์ฌ๋ก(์์์ ํจ๊ป!)
+1. ๋จ์ํ๊ฒ ๋ง๋ ๋ค.(์ฌ๋ฌ ์ ๋ณด๋ฅผ ๋ฐ์์ผ ํ ๊ฒฝ์ฐ ๊ฐ์
ํ๋ฆ์ ์ฌ๋ฌ ๋จ๊ณ๋ก ๋๋๋ค)
+2. ์์
๋ก๊ทธ์ธ ์ ๊ณต
+3. ๋น๋ฐ๋ฒํธ ํ๋์ ๋ํ ๊ฐ์ด๋ ์ ๊ณต
+4. ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํ ์ ์๋๋ก ํ๊ธฐ
+5. ๋ช
๋ฃํ๊ณ ์์ธํ ์๋ฌ ๋ฉ์ธ์ง ์ ๊ณต(์ฌ์ฉ์๊ฐ ์ค๋ฅ์ ์์ธ์ ์ถ์ธกํ๊ฒ ๋ง๋ค์ง ์๋๋ค)