๐ก ํ์ต ๊ณต์ ์คํฐ๋์์ ๋ฐํ๋ฅผ ์ํด ์ ๋ฆฌํ ๊ธ์ ๋๋ค.
๐ค ๋น ๋ฅธ ์์ฑ์ ์ํด ์์ด์ฒด๋ก ์์ฑ๋์ด ์์ต๋๋ค.
โ ๏ธ ํ์ต ์ค์ ์์ฑํ ๊ธ์ด๋ฏ๋ก ๋ด์ฉ์ ์ค๋ฅ๊ฐ ์์ ์ ์์ต๋๋ค!
๐ฅฐ ์ค๋ฅ๋ฅผ ๋ฐ๊ฒฌํ์ ๋ค๋ฉด ๋๊ธ๋ก ์๋ ค์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค!
โ๏ธ useSyncExternalStore ์ ๋์ด?
1. React 18์ ํ์ , Concurrency
React ํ์ React 18์ ๋ฐํํ๋ฉด์, ์ด๋ฒ ๋ฆด๋ฆฌ์ฆ๋ ๋์์ฑ(Concurrency) ์ ๋์ ์ผ๋ก ๋ ๋๋ง ์์ง ๊ฐ์ (์๋๋ฐฐ์นญ)๊ณผ ์ฌ์ฉ์ ๊ฒฝํ ํฅ์(transition, suspense)์ ์ง์คํ๋ค๊ณ ํจ.
๋์์ฑ์ด ๋จผ๋?
์ด๋ฆ ๊ทธ๋๋ก ์์์ ์๊ด์์ด ๋์์ ์ํ ๋ ์ ์๋ ์ฑ์ง. ํ๋ก๊ทธ๋๋ฐ ์ ์ธ ๊ฐ๋ ์ผ๋ก ๋งํ๋ฉด ํ๋ก๊ทธ๋จ์ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ๋ ์ ์๋ ์ฌ๋ฌ ์กฐ๊ฐ์ผ๋ก ๋๋์ด ๊ตฌ์กฐํํ๋ ๋ฐฉ์์ด๋ผ๊ณ ํ ์ ์์. ์ฌ๋ฌ๊ฐ์ง ์์ ์ ์ฐ์ ์์๋ฅผ ์ค์ ํด์ ๋ง์น ๋์์ ์คํ๋๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ฒ ๋ง๋๋ ๊ฐ๋ ์ด๋ผ๊ณ ๋ ์๊ฐํ ์ ์์.

React 18 ์ด์ ์ ๋ ๋๋ง์ ๊ฐ์ ํ ์ ์๋ ํ๋์ ๋๊ธฐ์ ์ธ ์ฒ๋ฆฌ์๋ค. ๋ ๋๋ง์ด ์์๋๋ฉด ์ค๋จํ ์ ์์์. ๊ทธ๋ฐ๋ฐ React 18์์๋ ๋์์ฑ ๋ ๋๋ง์ ํตํด ๋ ๋๋ง ํ๋ก์ธ์ค์ ๊ฐ์ ํ์ฌ ์ด๋ฅผ ์ค๋จํ๊ฑฐ๋ ์ฌ๊ฐ, ๋๋ ์ทจ์ ํ ์ ์๊ฒ ๋จ.
๊ทธ๋์ ๋ฌด๊ฑฐ์ด ๋ ๋๋ง ์์ ์ ํ๋ ๋์ ๋ค๋ฅธ ๋ ๋๋ง์ด ๋ธ๋กํน ๋๋ ํ์์ ๊ฐ์ ํ์ฌ ๋ณด๋ค ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์ ์ํฌ ์ ์๊ฒ ๋๋ ๊ฒ.
์์ : startTransition
React 18์์๋ ์ ๋ฐ์ดํธ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ๋ถํ์ฌ ์๊ฐ ํจ.
- ๊ธด๊ธํ ์ ๋ฐ์ดํธ (urgent updates) : ์ ๋ ฅ, ํด๋ฆญ, ๋๋ฅด๊ธฐ ๊ฐ์ ๋ค์ด๋ ํธ ์ํธ์์ฉ์ ๋ฐ์
- ์ ํ ์ ๋ฐ์ดํธ (transition updates) : UI์ ์ ํ
ํ์ดํ, ํด๋ฆญ, ๋๋ฅด๊ธฐ ๊ฐ์ ๊ธด๊ธ ์ ๋ฐ์ดํธ๋ ๋น ๋ฅด๊ฒ ์ ๋ฐ์ดํธ ๋์ง ์์ผ๋ฉด ๋ฒ๋ฒ ๊ฑฐ๋ฆฌ๋ฉด์ ์ฑ์ด ์ด์ํ๋ค๋ ๋๋์ ์ค ์ ์๋ค. ํ์ง๋ง ํ๋ฉด์ ๊ณง๋ฐ๋ก ๊ฒฐ๊ณผ๊ฐ์ ๋ณผ๊ฑฐ๋ผ๊ณ ๊ธฐ๋ํ์ง ์๊ธฐ ๋๋ฌธ์ ์ ํ ์ ๋ฐ์ดํธ๋ ๋๋ฆฌ๊ฒ ์ ๋ฐ์ดํธ๊ฐ ๋์ด๋ ๊ด์ฐฎ๋ค.
import { startTransition } from 'react';
// ๊ธด๊ธํ ์
๋ฐ์ดํธ : ์
๋ ฅํ๊ณ ์๋ ๊ฐ
setInputValue(input);
// startTransition์ผ๋ก ๋ํ๋ ์
๋ฐ์ดํธ๋ ๊ธด๊ธํ์ง ์์ ๊ฒ์ผ๋ก ์ฒ๋ฆฌ๋๊ณ , ๋ ๊ธด๊ธํ ์
๋ฐ์ดํธ๊ฐ ๋ค์ด์ค๋ฉด ์ค๋จ๋๋ค.
startTransition(() => {
// ์ ํ ์
๋ฐ์ดํธ: ์
๋ ฅ๊ฐ์ ๋ฐ๋ฅธ ์ฟผ๋ฆฌ๊ฐ
setSearchQuery(input);
});
์์ ๊ฐ์ด startTransition ์ผ๋ก ๋ฉํ๋ ์ ๋ฐ์ดํธ๋ ์ ํ ์ ๋ฐ์ดํธ๋ก ์ฒ๋ฆฌ๋์ด ๊ธด๊ธํ ์ ๋ฐ์ดํธ๊ฐ ๋ค์ด์ค๋ฉด ์ค๋จ๋จ.
startTransition์ ์ฌ์ฉํ์ฌ ๋ธ๋กํน ๋ ๋๋ง ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ์์
2. ํ์ ๊ณผ ํจ๊ป ๋ ์ค๋ฅธ ๋ฌธ์ ?!
React System ๋ด์์๋ ํ๋ฅญํจ. ํ์ง๋ง ์ธ๋ถ ์คํ ์ด(external store) ๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ์์กดํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฝ์ฐ tearing ์ด๋ผ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
์ธ๋ถ ์คํ ์ด vs ๋ด๋ถ ์คํ ์ด

- Internal store(states) : React ์์ ์ ๊ณตํ๋ ์ํ ๊ด๋ฆฌ ๋๊ตฌ ๋๋ ์คํ ์ด, useState, useReducer, context, props ๋ฑ
- External store
- React์์ ์ ๊ณตํ๋ ์ํ๊ด๋ฆฌ api ์ธ์ ์์ฒด์ ์ผ๋ก ์ํ ๊ด๋ฆฌ ํด์ ๋ง๋ค์ด ๋ฆฌ์กํธ ํ ๊ณผ ์ฐ๋์ํจ ์ํ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(mobx, redux, recoil, zustand ๋ฑ)
- Dom ๊ฐ์ฒด, Web API(Date) ๋ฑ
- ref ๋ก ๊ด๋ฆฌํ๋ ๋ณ์ ๊ฐ
๋น๋์ ๋ ๋๋ง vs ๋์ ๋ ๋๋ง - What is tearing?

- React ์ ๋ ๋๋ง ์์ ์ด ์์๋จ. external store ์ ์ ๊ทผํด์ ์์ ๊ฐ์ ๊ฐ์ ธ์์ ๋ ๋๋งํจ.
- ๋ ๋๋ง์ ๊ณ์ ์งํํจ ์ฌ์ ํ external store ๋ ๋์ผํ ๊ฐ์ ๊ฐ์ง.
- ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ ํ๋์์ผ๋ก ๋ ๋๋ง ๋๊ณ ๋ชจ๋ ์ผ๊ด์ฑ ์๊ฒ ์ ๋ณด์.
- ๋ ๋๋ง ํ์ external store ๋ ์ ๋ฐ์ดํธ๊ฐ ๋ ์ ์์. React ์์ ๋ ๋๋ง์ด ๋ค ๋๋ ๋ค์ ๋ค๋ฅธ ์์ ์ด ์ผ์ด๋๋๋ก ํ์ฉํ๊ธฐ ๋๋ฌธ์ React ๊ฐ ๋ ๋๋ง ๋์ง ์์ ๋ ์คํ ์ด๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด ๋ค์์ React ๊ฐ ํธ๋ฆฌ๋ฅผ ๋ ๋๋ง ํ ๋ ์ฒซ๋ฒ์งธ ๊ณผ์ ๋ถํฐ ๋ค์ ์์ํด์ ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ ๋์ผํ ๊ฐ์ ๊ฐ์ง๊ฒ ๋จ

๋์ ๋ ๋๋ง์์๋ React ๊ฐ ์์ ์ด ์๋ฃ๋๊ธฐ ์ ์ ์์ ์ ์ค๋จํ๊ณ ๊ธํ ์์ ์ ์๋ณด ํ ์ ์์. ๋ฐ๋ผ์ ์ฌ์ฉ์๋ ๋ธ๋กํน ์์ด ํ์ด์ง์ ์ํธ์์ฉ ๊ฐ๋ฅํจ.
๊ทธ๋ ๋ค๋ฉด ๋ง์ฝ์ ๋๋ฒ์งธ ๋จ๊ณ์์ ์ฌ์ฉ์๊ฐ ์คํ ์ด๋ฅผ ํ๋์์์ ๋นจ๊ฐ์์ผ๋ก ๋ณ๊ฒฝ์ํค๋ ๋ฒํผ์ ๋๋ฅธ๋ค๋ฉด?
์ฒ์ ๋ ๋๋ง ๋๋ ์ปดํฌ๋ํธ๋ ํ๋์ ๊ฐ์ ๊ฐ์ ธ์ค์ง๋ง, ์ค๊ฐ๋ถํฐ๋ ์ฌ์ฉ์์ ๋น ๋ฅธ ์ํธ์์ฉ์ ๋ฐ์ํ๊ธฐ ์ํด ์ฐ์ ์ ์ผ๋ก ์ฒ๋ฆฌ๋์ด ๋ณ๊ฒฝ๋ ๋นจ๊ฐ์์ด ํ์๋๊ฒ ๋๊ณ ์ด๋ ์๊ฐ์ ์ธ ๋ถ์ผ์น๋ฅผ ๋ณด๊ฒ ๋จ. ์ด๋ฐ ํ์์ด Tearing(๊ณ์ธตํ).
Tearing Example - reference
์ด๋ฌํ Tearing ์ ํด๊ฒฐํ๊ณ ์์ ํ๊ฒ ์ธ๋ถ ์ ์ฅ์์ ๊ฐ์ ์ผ๊ด์ฑ ์๊ฒ ์ฌ์ฉํ๊ธฐ ์ํด ๋์จ ๊ฒ์ด ๋ฐ๋ก useSyncExternalStore ์ธ ๊ฒ!!!
โ๏ธ useSyncExternalStore ๊ฐ๋จ ์ฌ์ฉ๋ฒ

useSyncExternalStore ๋ Tearing ๋ฌธ์ ์์ด ์ธ๋ถ ์ ์ฅ์๋ฅผ ๊ตฌ๋ ํ ์ ์๋ React Hook.
2๊ฐ์ ํ์ ๋งค๊ฐ๋ณ์์ 1๊ฐ์ ์ต์ ๋ํ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ๊ณ getSnapshot ์ ๊ฒฐ๊ณผ๋ฌผ์ธ snapshot(์ํ๊ฐ) ์ ๋ฐํํ๋ค.
๋งค๊ฐ๋ณ์
- subscribe : ํ๋์ callback ์ธ์(onStoreChange)๋ฅผ ๋ฐ์์ ์คํ ์ด์ ๊ตฌ๋ ํ๋ ํจ์. ์คํ ์ด๊ฐ ๋ณ๊ฒฝ๋๋ฉด ์ ๊ณต๋ ์ฝ๋ฐฑ์ ํธ์ถ. ์ด๋ก ์ธํด ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง๋๋ค. ๊ตฌ๋ ์ ์ ๋ฆฌํ๋ ํจ์(clean up)๋ฅผ ๋ฐํํด์ผ ํ๋ค.
- getSnapshot : ์ปดํฌ๋ํธ์ ํ์ํ ์ ์ฅ์์ ๋ฐ์ดํฐ ์ค๋ ์ท์ ๋ฐํํ๋ ํจ์. ์ ์ฅ์๊ฐ ๋ณ๊ฒฝ๋์ง ์๋ ๋์ getSnapshot ์ ๋ํ ๋ฐ๋ณต ํธ์ถ์ ๋์ผํ ๊ฐ์ ๋ฐํํด์ผ ํ๋ค. ์ ์ฅ์๊ฐ ๋ณ๊ฒฝ๋๊ณ ๋ฐํ๋ ๊ฐ์ด ๋ค๋ฅธ ๊ฒฝ์ฐ(Object.is) ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ๋ค.
- getServerSnapshot : ์ ์ฅ์์ ์๋ ๋ฐ์ดํฐ์ ์ด๊ธฐ ์ค๋ ์ท์ ๋ฐํํ๋ ํจ์. ์๋ฒ ๋ ๋๋ง ์ ํด๋ผ์ด์ธํธ์์ ์๋ฒ ๋ ๋๋ง ์ฝํ ์ธ ์ hydration ์์ ๋์ค์ ์ฌ์ฉ๋จ. ์๋ฒ ์ค๋ ์ท์ ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์ ๋์ผํด์ผ ํ๋ฉฐ ์ผ๋ฐ์ ์ผ๋ก ์ง๋ ฌํ(serialized) ๋์ด ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌ๋จ. ์ด ํจ์๊ฐ ์ ๊ณต๋์ง ์์ผ๋ฉด ์๋ฒ์์ ๋ ๋๋ง ์ ์๋ฌ๊ฐ ๋ฐ์ํจ. โ SSR์ ์ํ ๊ธฐ๋ฅ
์์ : TodoList Store
โ๏ธ ๊ทผ๋ฐ ๋๋ ๋์์ฑ ๊ธฐ๋ฅ์ ํ์ฉ ์ํ๋๋ฐ?
๋ํ ์์ผ๋ก์ React ๊ฐ ๋์๊ฐ๋ ๋ฐฉํฅ์ ํฐ ์ถ์ด ๋์์ฑ ๋ ๋๋ง์ธ ๋งํผ ์ธ์ ์ด๋์ ์ด๋ป๊ฒ ๋์์ฑ ๊ธฐ๋ฅ์ ์ถ๊ฐ ํ๊ฒ ๋ ์ง ๋ชจ๋ฅด๊ณ , ๊ทธ๋ ๊ธฐ์ ๋ด๊ฐ ์์ฑํ ์ฝ๋๊ฐ ์ธ์ ๋ฌธ์ ๊ฐ ๋ ์ง ์์ธกํ ์ ์์.
๋ฐ๋ผ์ Jotai, Zustand ์ ๊ฐ๋ฐ์ ์นดํ ๋ค์ด์๋ ์ฝ๋ ๋ด์์ ์ธ๋ถ ์ ์ฅ์์ ๋ด๋ถ ์ ์ฅ์๋ฅผ ๋ช ํํ ๊ตฌ๋ถํ์ฌ ์ธ๋ถ ์ ์ฅ์์ ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํ๋ ์ฝ๋๋ useSyncExternalStore ๋ฅผ ์ ๊ทน ํ์ฉํ๋๊ฑธ ๊ถ์ฅํจ.
(ํนํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ๋ฐ์ ์ ์ฅ์์๋ ๋๋์ฑ ์ฌ์ฉ์๊ฐ ๋์์ฑ ์ง์ ์ฌ๋ถ๋ฅผ ์ ํํ ์ ์๋ ๋งํผ ๊ฑฐ์ ํ์.)
๋ ์์ ๋์์ฑ์ ์ ์ฉํ์ง ์๋๋ค๊ณ ํ๋๋ผ๋ ์ฃผ๊ด์ ์ธ ์๊ฐ์ผ๋ก useSyncExternalStore ํ ์ ๋ฐฐ๊ฒฝ์ ์น์ฐ๊ณ ๊ธฐ๋ฅ ์์ฒด๋ง ๋๊ณ ๋ณธ๋ค๋ฉด React ์์ ์ผ๋จ ํน์ ๋ฐ์ดํฐ์ ๊ตฌ๋ ์ ๊ฑธ์ด๋๊ณ ๋ฆฌ๋ ๋๋ง์ ํธ๋ฆฌ๊ฑฐ ํ ์ ์๋ ํ ์ธ๊ฑฐ์. ๊ทธ๋ฌ๋ฉด ๊ฐ๋ฐ์ ์ ์ฅ์์ ์ข ๋ ์์ ๋กญ๊ฒ ํน์ ์์ ์ ๋ ๋ฆฐ๋๋ง์ ํธ๋ฆฌ๊ฑฐ ํ ์ ์๋ ์๋ก์ด ๋ฐฉ๋ฒ์ธ๊ฒ ์๋๊น?
๋ํ ํด๋น ํ ์ ์ฌ์ฉํ๋ฉด Context API ๋ฑ์ผ๋ก ๋ณต์กํ ์ํ๊ฐ์ ํต์ฑ๋ก ์ฐธ์กฐํด์ ๋ฐ์ํ๋ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง ๋ฌธ์ ๋ ์ค๋ ์ท ํจ์๋ก ํน์ ๊ฐ๋ง ์ฐธ์กฐํ๋๋ก ๊ตฌํํ๋ฉด ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง๋ ์ค์ผ ์ ์์ ๊ฒ ๊ฐ์. โ ํ์ฌ cos-ui form ์ ์ ์ฉ ์๋ ์ค
๊ทธ๋ฌํ ๋ก์ง์ ๋ ์ด์ ์ useState์ useEffect ๋ฅผ ์จ์ ๋ค์ ๋ณต์กํ๊ฒ ๊ตฌํ์ ํด์ผ ํ๋๋ฐ ์ด์ subscribe ํจ์๋ง ์ ๊ตฌํํด๋์ผ๋ฉด useSyncExternalStore ํ๋๋ก ํด๊ฒฐ์ด ๊ฐ๋ฅํจ. ๊ทธ๋ผ ์ฝ๋๊ฐ ์ข ๋ ๊ฐ๋ ์ฑ์ด ์ข์์ง๋๊ฒ ์๋๊น?
โ๏ธ ๊ทธ๋์ ์ฐพ์๋ณธ useSyncExternalStore ๋ฅผ ํ์ฉํ ๋๋ค๋ฅธ ์์
- useSyncExternalStore - ๊ณผ์ํ๊ฐ ๋ React API
- Zustand ์ฒ๋ผ Global State Management ๊ด๋ฆฌ ๊ธฐ๋ฅ ๊ตฌํ
โ๏ธ ์ฐธ๊ณ ํ ์๋ฃ
โญ๏ธ React Docs : useSyncExternalStore : React Docs useSyncExternalStore ๊ณต์๋ฌธ์
โญ๏ธ React Conf 2021 : React 18 for External Store Libraries : Zustand ๊ฐ๋ฐ์ ์นดํ ๋ค์ด์ ๋ฐํ ์์ โญ๏ธ
Zustand ๋์ ์๋ฆฌ์ ExternalStore
https://velog.io/@jay/useSyncExternalStore
React 18์ useSyncExternalStore, Tearing ํ์์ ๋ฌด์์ธ๊ฐ?