05. ๐ช ๋ฆฌ์กํธ ํ ๊ณผ ๊ด์ฌ์ฌ ๋ถ๋ฆฌ ์ค๊ณ
๐ ๊ฐ์
์ปค์คํ ํ ์ ํตํ ๋น์ฆ๋์ค ๋ก์ง ๋ถ๋ฆฌ์, ์ธ๋ถ ์ํ ๊ตฌ๋ ์ ๋ฐ์ํ๋ ํ ์ด๋ง ํ์์ ๋ฐฉ์งํ๋ ๊ณ ๊ธ ํ ์ค๊ณ ๊ธฐ์ ์ ์ ๋ณตํฉ๋๋ค.
๐ ์ด ๋ฉด์ ํญ๋ชฉ์ ๋ชฉํ
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 22๋ถ (ํต์ฌ ์์ฝ: 11๋ถ)
๐บ๏ธ ์ด ์ฑํฐ์ ํ๋ฆ
[๊ฐ๋
์ฌ์ ] โ [์ง๋ฌธ 1: ๋ก์ง ๋ถ๋ฆฌ ๊ธฐ์ค] โ [์ง๋ฌธ 2: useSyncExternalStore] โ [์ค์ ๋ณํ ์ง๋ฌธ]
๐ฏ ์ด ์ฑํฐ๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- ๋ทฐ(View)์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ถ๋ฆฌํ๋ ๋ช ํํ ๊ธฐ์ค์ ์ธ์ธ ์ ์์ต๋๋ค.
useSyncExternalStore๋ฅผ ์ฌ์ฉํด ์ธ๋ถ ์ํ๋ฅผ ์์ ํ๊ฒ ๊ตฌ๋ ํ๋ ๋ฒ์ ์ดํดํฉ๋๋ค.- HOC์ ์ปค์คํ ํ ํจํด์ ์ ์ฌ์ ์ ํ์ฉ๋ฒ์ ๋ ผ๋ฆฌ์ ์ผ๋ก ๋น๊ตํฉ๋๋ค.
๐ ํต์ฌ ๊ฐ๋ ์ฌ์ (Concept Glossary)
1. ๊ด์ฌ์ฌ ๋ถ๋ฆฌ (Separation of Concerns, SoC)
ํ๋์ ์ปดํฌ๋ํธ๋ ํจ์๊ฐ ํ๋์ ๋ชฉ์ ์๋ง ์ง์คํ๋๋ก ๋ง๋๋ ์ค๊ณ ์์น์ ๋๋ค. ๋ฆฌ์กํธ์์๋ ์ฃผ๋ก UI๋ฅผ ๋ด๋นํ๋ ๋ทฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ก์ง์ ๋ถ๋ฆฌํ๋ ๋ฐ ์ง์คํฉ๋๋ค.
2. ํ ์ด๋ง (Tearing)
๋์์ฑ ๋ ๋๋ง ํ๊ฒฝ์์ ํ๋ฉด์ ์ฌ๋ฌ ๋ถ๋ถ์ด ์๋ก ๋ค๋ฅธ ์์ ์ ์ํ๋ฅผ ๋ณด์ฌ์ฃผ๋ ์๊ฐ์ ๋ถ์ผ์น ํ์์ ๋๋ค. ๋ ๋๋ง ๋์ค ์ธ๋ถ ์ํ(Store ๋ฑ)๊ฐ ๋ฐ๋์ด ๋ฐ์ํฉ๋๋ค.
3. ๊ณ ์ฐจ ์ปดํฌ๋ํธ (Higher-Order Component, HOC)
์ปดํฌ๋ํธ๋ฅผ ์ธ์๋ก ๋ฐ์ ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์ปดํฌ๋ํธ๋ฅผ ๋ฐํํ๋ ํจํด์ ๋๋ค. ํ ์ด ๋ฑ์ฅํ๊ธฐ ์ ๋ก์ง ์ฌ์ฌ์ฉ์ ํต์ฌ์ด์์ผ๋ฉฐ, ํ์ฌ๋ ๋ก๊น ์ด๋ ๊ถํ ์ฒดํฌ ๋ฑ UI๋ฅผ ๊ฐ์ธ๋ ๋ก์ง์ ์ฃผ๋ก ์ฐ์ ๋๋ค.
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ๐ฃ ์์ฒ (์ค๋ฐ): "์ํธ ๋! '์์๋ค ๋์๋ณด๋' ์ปดํฌ๋ํธ๊ฐ 500์ค์ด ๋์ด๊ฐ์ ๊ณ ์น๊ธฐ ์ด๋ ต์ต๋๋ค. API ํธ์ถ, ์ ๋ ฌ ๋ก์ง, ํํฐ ๋ก์ง์ด ์์ฌ ์์ด์ ํ๋๋ฅผ ๋ฐ๊พธ๋ฉด ์์ ๋ฐ์ ํ๋ฉด๊น์ง ์ํฅ์ ๋ฐ์์."
- ๐ฆ ์ํธ (๋ฆฌ๋): "์์ฒ ๋, ์ด์ ๋ ์ฝ๋ ๊ธธ์ด๋ณด๋ค ๋ณํ ์ด์ ๋ฅผ ๋ด์ผ ํฉ๋๋ค. ์ปดํฌ๋ํธ๋ ํ๋ฉด ํํ์ ์ง์คํ๊ณ , ๋ฐ์ดํฐ ํ๋ฆ๊ณผ ๋ถ์ํจ๊ณผ๋ ์ด๋ฆ ์๋ ํ ์ผ๋ก ๋ถ๋ฆฌํด ๋ด ์๋ค."
๋ฉด์ ์ง๋ฌธ 1. ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ๋ฅผ ์ํด ๋น์ฆ๋์ค ๋ก์ง์ ์ปค์คํ ํ ์ผ๋ก ์ถ์ถํ ๋, ๋ทฐ(View)์ ๋ก์ง์ ๋ถ๋ฆฌํ๋ ๊ธฐ์ค์ ๋ฌด์์ธ๊ฐ์?
๐ฏ ์ถ์ ์๋
์ฝ๋๋ฅผ ๋จ์ํ ์ฎ๊ธฐ๋ ํ์๋ฅผ ๋์ด, ์ปดํฌ๋ํธ์ ์ฑ ์์ ์ด๋ป๊ฒ ์ ์ํ๋์ง ๊ทธ๋ฆฌ๊ณ ์ฌ์ฌ์ฉ์ฑ๊ณผ ํ ์คํธ ์ฉ์ด์ฑ์ ๊ณ ๋ คํ ์ค๊ณ ์ฒ ํ์ด ์๋์ง ํ์ธํฉ๋๋ค.
๐ฃ ์์ฒ ์ด์ Naive ๊ตฌํ (Bad Case)
์์ฒ ์ด๋ ์ปดํฌ๋ํธ ์์ ๋ฐ์ดํฐ ์กฐํ, ๊ฐ๊ณต, ๋ ๋๋ง ์ฑ ์์ ๋ชจ๋ ๋ฃ์์ต๋๋ค.
// ๐ฃ ์์ฒ : "์ฐพ์๋ณด๊ธฐ ํธํ๊ฒ ํ ํ์ผ์ ๋ค ๋ชจ์๋จ์ด์!"
function UserDashboard() {
const [data, setData] = useState([]);
useEffect(() => {
// โ ๏ธ API ํธ์ถ ๋ก์ง ์ง์ ๋
ธ์ถ
fetch('/api/users').then(res => res.json()).then(setData);
}, []);
// โ ๏ธ ๋ณต์กํ ํํฐ/์ ๋ ฌ ๋ก์ง ์ง์ ๋
ธ์ถ
const sortedData = [...data].sort((a, b) => b.score - a.score);
const activeUsers = sortedData.filter(u => u.isActive);
return (
<div>
{activeUsers.map(user => <UserItem key={user.id} {...user} />)}
</div>
);
}๐ฆ ์ํธ์ ๋ฆฌ๋ทฐ ํฌ์ธํธ
"์์ฒ ๋, ์ด๋ ๊ฒ ์ง๋ฉด '๋ฐ์ดํฐ ํํฐ๋ง ๋ก์ง'๋ง ๋ฐ๋ก ํ ์คํธํ๊ณ ์ถ์ ๋ ์ปดํฌ๋ํธ ์ ์ฒด๋ฅผ ๋ ๋๋งํด์ผ ํด์. ์ปดํฌ๋ํธ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ ํ๋ฉด์ ๊ทธ๋ฆฌ๋ ๊ฒ์ ์ง์คํ๊ณ , ์กฐํ์ ๊ฐ๊ณต์ ํ ์คํธ ๊ฐ๋ฅํ ํ ์ผ๋ก ๋ถ๋ฆฌํ๋ ํธ์ด ์ข์ต๋๋ค."
๐ฆ ์ํธ์ ์ํคํ ์ฒ ๊ฐ์ด๋ (Good Case)
์ํธ ๋ฆฌ๋๋ ๋น์ฆ๋์ค ๋ก์ง์ useUserBoard๋ผ๋ ์ปค์คํ
ํ
์ผ๋ก ๋ถ๋ฆฌํฉ๋๋ค.
// ๐ฆ ์ํธ: "์ปดํฌ๋ํธ๋ UI ๋ช
์ธ์์ฒ๋ผ ์ฝํ๊ฒ ํ๊ณ , ๋ณํํ๋ ๋ก์ง์ ํ
์ ์ด๋ฆ ๋ถ์ด์ธ์."
// ๐ง Business Logic Hook
function useUserBoard() {
const [users, setUsers] = useState([]);
// ๋คํธ์ํฌ ์์ฒญ๊ณผ ์๋ฌ/๋ก๋ฉ ์ฒ๋ฆฌ๋ ์ด ํ
์์์ ํ ๊ณณ์ ๋ชจ์๋ค.
// ๊ทธ๋ฌ๋ฉด ํ๋ฉด ์ปดํฌ๋ํธ๋ ๋ฐ์ดํฐ๊ฐ ์ด๋์ ์ค๋์ง ๋ชฐ๋ผ๋ ๋๋ค.
const processUsers = (data) => {
return data
.toSorted((a, b) => b.score - a.score)
.filter(u => u.isActive);
};
return { users: processUsers(users) };
}
// ๐ผ๏ธ View Component
function UserDashboard() {
const { users } = useUserBoard(); // ํ๋ฉด์ ๊ฐ๊ณต๋ ์ฌ์ฉ์ ๋ชฉ๋ก๋ง ๋ฐ๋๋ค.
return (
<div>
{users.map(user => <UserItem key={user.id} {...user} />)}
</div>
);
}sort๋ ์๋ณธ ๋ฐฐ์ด์ ๋ณ๊ฒฝํ๋ฏ๋ก ์๋ฒ ์๋ต์ด๋ ์บ์ ๋ฐ์ดํฐ๋ฅผ ๊ทธ๋๋ก ๋ค๋ฃฐ ๋ ๋ถ์์ฉ์ ๋ง๋ค ์ ์์ต๋๋ค. ์์์ฒ๋ผ toSorted ๋๋ ๋ณต์ฌ ํ ์ ๋ ฌ์ ์ฌ์ฉํ๋ฉด ํ
๋ด๋ถ์์๋ ๋ถ๋ณ์ฑ์ ์งํฌ ์ ์์ต๋๋ค.
๐ ๋ ๋ฒจ๋ณ ๋ต๋ณ ๊ฐ์ด๋ (Self-Check)
- Level 1 (Junior): "์ฌ์ฌ์ฉํ๊ณ ์ถ์ ๋ก์ง์ด ์๊ธฐ๋ฉด ์ปค์คํ ํ ์ผ๋ก ๋ง๋ญ๋๋ค. ์ปดํฌ๋ํธ ์ฝ๋๊ฐ ๋๋ฌด ๊ธธ์ด์ง ๋๋ ๋ถ๋ฆฌํฉ๋๋ค."
- Level 2 (Senior): "๋ฐ์ดํฐ ํ์นญ, ์ํ ๊ด๋ฆฌ, ๋ณต์กํ ์ฐ์ฐ ๋ฑ '๋๋ฉ์ธ ๋ก์ง'๊ณผ 'UI ๋ ๋๋ง'์ ๋ถ๋ฆฌํ๋ ๊ธฐ์ค์ ์ค๋ช ํฉ๋๋ค. ์ด๋ฅผ ํตํด UI ๋ณ๊ฒฝ ์์ด ๋ก์ง๋ง ๋ ๋ฆฝ์ ์ผ๋ก ๋จ์ ํ ์คํธ(Unit Test)๋ฅผ ๊ฐ๋ฅํ๊ฒ ํจ์ ๊ฐ์กฐํฉ๋๋ค."
- Level 3 (Specialist): "'Headless Component' ํจํด์ด๋ 'Hook ๊ธฐ๋ฐ ์ํคํ ์ฒ'๋ฅผ ์ธ๊ธํฉ๋๋ค. UI ํ๋ฆฌ์ ํ ์ด์ ๊ณผ ์ํ ๊ด๋ฆฌ ์์ง์ ์์ ํ ๊ฒฉ๋ฆฌํ์ฌ, ์น๋ฟ๋ง ์๋๋ผ ์ฑ(React Native) ๋ฑ ๋ค๋ฅธ ํ๋ซํผ์์๋ ๋์ผํ ๋น์ฆ๋์ค ๋ก์ง ํ ์ ๊ณต์ ํ ์ ์๋ ํ์ฅ์ฑ ์๋ ๊ตฌ์กฐ๋ฅผ ์ ์ํฉ๋๋ค."
๋ฉด์ ์ง๋ฌธ 2. useSyncExternalStore ํ ์ด ๋์ ๋ ๋ฐฐ๊ฒฝ๊ณผ, ํ ์ด๋ง(Tearing) ํ์์ ๋ฐฉ์งํ๋ ์๋ฆฌ๋ ๋ฌด์์ธ๊ฐ์?
๐ฏ ์ถ์ ์๋
React 18 ๋์์ฑ ๋ชจ๋์์ ๋ฐ์ํ ์ ์๋ ๋ฏธ๋ฌํ ์๊ฐ์ ๋ฒ๊ทธ๋ฅผ ์ดํดํ๊ณ , ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(Redux, Zustand ๋ฑ)๊ฐ ๋ฆฌ์กํธ ์ค์ผ์ค๋ฌ์ ์ด๋ป๊ฒ ํ์ ํ๋์ง ๊น์ด ์๊ฒ ์๋์ง ํ์ธํฉ๋๋ค.
๐ฃ ์์ฒ ์ด์ Naive ๊ตฌํ (Bad Case)
์์ฒ ์ด๋ window.innerWidth๋ฅผ ์ ์ญ ์ํ์ฒ๋ผ ์ฐ๊ธฐ ์ํด ๋จ์ํ useEffect๋ฅผ ์ผ๋ค๊ฐ ๋ ์ด์์์ด ํ๋ค๋ฆฌ๋ ํ์์ ๋ฐ๊ฒฌํ์ต๋๋ค.
// ๐ฃ ์์ฒ : "๊ทธ๋ฅ useEffect๋ก ๊ฐ ๊ฐ์ํ๋ฉด ๋๋ ๊ฑฐ ์๋๊ฐ์?"
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width; // โ ๏ธ ๋์์ฑ ๋ชจ๋์์ ์ฌ๋ฌ ์ปดํฌ๋ํธ๊ฐ ์๋ก ๋ค๋ฅธ width๋ฅผ ์ฝ์ ์ํ(Tearing) ์์
}๐ฆ ์ํธ์ ๋ฆฌ๋ทฐ ํฌ์ธํธ
"์์ฒ ๋, ๋ฆฌ์กํธ 18์ ๋์์ฑ ๋ ๋๋ง์ ๋์ค์ ๋ฉ์ถ ์ ์์ด์. ๋ ๋๋ง 1๋จ๊ณ์์ A ์ปดํฌ๋ํธ๊ฐ width๋ฅผ ์ฝ๊ณ , ๊ทธ ์ฌ์ด์ ๋ฆฌ์ฌ์ด์ฆ๊ฐ ๋ฐ์ํ ๋ค 2๋จ๊ณ์์ B ์ปดํฌ๋ํธ๊ฐ ์ฝ์ผ๋ฉด ํ๋ฉด์ด ์ง์ง์ด๊ฐ ๋ฉ๋๋ค. ์ด๊ฑธ ๋ง์ผ๋ผ๊ณ ๋ง๋ ๊ฒuseSyncExternalStore์ ๋๋ค."
๐ฆ ์ํธ์ ์ํคํ ์ฒ ๊ฐ์ด๋ (Good Case)
์ํธ ๋ฆฌ๋๊ฐ ์์ ํ ์ธ๋ถ ์ํ ๊ตฌ๋ ๋ฐฉ์์ ์ ์ํฉ๋๋ค.
// ๐ฆ ์ํธ: "์ธ๋ถ ์ํ๋ ๋ฆฌ์กํธ์ ๋ ๋๋ง ์๋์ ๋ง์ง ์์ ์ ์์ต๋๋ค. ๊ฐ์ ๋ก ๋๊ธฐํํ์ธ์."
const widthStore = {
subscribe: (cb) => {
window.addEventListener('resize', cb);
return () => window.removeEventListener('resize', cb);
},
getSnapshot: () => window.innerWidth,
getServerSnapshot: () => 0,
};
function useWindowWidth() {
// โ
๋ฆฌ์กํธ์๊ฒ '์ด ๊ฐ์ ์ธ๋ถ์์ ์ค๋๊น ๋ ๋๋ง ์ค์ ๋ฐ๋๋ฉด ์ค๋จํด!'๋ผ๊ณ ์๋ ค์ค
return useSyncExternalStore(
widthStore.subscribe,
widthStore.getSnapshot,
widthStore.getServerSnapshot
);
}SSR ํ๊ฒฝ์์๋ window๊ฐ ์์ผ๋ฏ๋ก ์๋ฒ์ฉ snapshot์ ๋ฐ๋ก ์ ๊ณตํด์ผ ํฉ๋๋ค. ์ค์ ์๋น์ค์์๋ 0 ๋์ ๋ ์ด์์์ด ํ๋ค๋ฆฌ์ง ์๋ ๊ธฐ๋ณธ๊ฐ์ ์ ํ๊ณ , ํด๋ผ์ด์ธํธ์์ ์ฒซ ๊ตฌ๋
์ดํ ์ค์ ๋๋น๋ก ๋๊ธฐํํ๋ ์ ๋ต์ ์ธ์๋๋ค.
๐ ๋ ๋ฒจ๋ณ ๋ต๋ณ ๊ฐ์ด๋ (Self-Check)
- Level 1 (Junior): "์ธ๋ถ ์ํ(Redux ๋ฑ)๋ฅผ ๋ฆฌ์กํธ์ ์ฐ๊ฒฐํด ์ฃผ๋ ํ ์ ๋๋ค. ๊ฐ์ด ๋ฐ๋๋ฉด ๋ฆฌ๋ ๋๋งํด ์ค๋๋ค."
- Level 2 (Senior): "๋์์ฑ ๋ ๋๋ง์ ์์
์ ์ชผ๊ฐ์ด ์ฒ๋ฆฌํ๋๋ฐ, ๊ทธ ์ฌ์ด ์ธ๋ถ ์ํ๊ฐ ๋ณํ๋ฉด ์๊ฐ์ ๋ถ์ผ์น(Tearing)๊ฐ ๋ฐ์ํจ์ ์ค๋ช
ํฉ๋๋ค.
useSyncExternalStore๋ ์ฝ๊ธฐ ์์ ์ ์ผ๊ด์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด ๋์ ๋์์์ ์ธ๊ธํฉ๋๋ค." - Level 3 (Specialist): "์ด ํ ์ด ์์์ (Atomic) ์ ๋ฐ์ดํธ๋ฅผ ์ด๋ป๊ฒ ๋ณด์ฅํ๋์ง ๋ด๋ถ ๋ฉ์ปค๋์ฆ์ ์ค๋ช ํฉ๋๋ค. ๋ ๋๋ง ์ค ์ธ๋ถ ๊ฐ์ด ๋ณํ๋ฉด ๋ค์ ๋ ๋๋ง์ ์๋ํ๊ฑฐ๋ ๋๊ธฐ์ ์ ๋ฐ์ดํธ๋ก ์ ํํ์ฌ ์ฌ์ฉ์์๊ฒ ํญ์ ์ผ๊ด๋ ์ค๋ ์ท์ ๋ณด์ฌ์ฃผ๋ '์ผ๊ด์ฑ ๋ณด์ฅ' ์ ๋ต์ ๋ถ์ํฉ๋๋ค."
๐ ์ค์ ๋ณํ ์ง๋ฌธ (Related Variations)
๋ฉด์ ์ง๋ฌธ 182. ๊ณ ์ฐจ ์ปดํฌ๋ํธ(HOC) ํจํด๊ณผ ์ปค์คํ ํ ํจํด์ ์ฅ๋จ์ ์ ๋น๊ตํด ๋ณด์ธ์.
- ๐ฏ ์ถ์ ์๋: ๋ฆฌ์กํธ์ ๋ก์ง ์ฌ์ฌ์ฉ ํจํด ๋ณ์ฒ์ฌ๋ฅผ ์ดํดํ๊ณ , ์ํฉ์ ๋ง๋ ๊ฐ์ฅ ์ ์ ํ ๋๊ตฌ๋ฅผ ์ ํํ ์ ์๋์ง ํ์ธํฉ๋๋ค.
- ๐ก ํต์ฌ ์๋ฆฌ & ๋ต๋ณ: ์ปค์คํ ํ ์ ํจ์ํ ์ปดํฌ๋ํธ์ ํ๋ฆ ์์์ ์ํ์ ๋ก์ง์ ๋งค์ฐ ์ ์ฐํ๊ฒ ์กฐํฉํ ์ ์์ด ํ์ฌ ๋ฆฌ์กํธ์ ํ์ค์ ๋๋ค. ๋ฐ๋ฉด, HOC๋ ์๋ณธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ๋ ๊ตฌ์กฐ ํน์ฑ์ Props๊ฐ ์ด๋์ ์ค๋์ง ๋ถ๋ช ํํด์ง๋ 'Props Drilling'์ด๋ '์ด๋ฆ ์ถฉ๋' ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ธฐ ์ฝ์ต๋๋ค. ํ์ง๋ง ์ธ์ฆ ์ฒดํฌ๋ ๋ก๊น ์ฒ๋ผ UI ์์ฒด๋ฅผ ๊ฐ์ธ๊ฑฐ๋ ์ฃผ์ ํด์ผ ํ๋ ์๊ฐ์ ์ธ ํก๋จ ๊ด์ฌ์ฌ(Cross-cutting Concerns)์๋ ์ฌ์ ํ HOC๊ฐ ์ ์ธ์ ์ด๊ณ ๊น๋ํ ๋์์ด ๋ ์ ์์ต๋๋ค.
๋ฉด์ ์ง๋ฌธ 249. ๋ฆฌ์กํธ์์ ๋๋ฐ์ด์ค(Debounce)์ ์ฐ๋กํ(Throttle)์ ์ ์ฉํ ๋ ์ฃผ์ํ ์ ์ ๋ฌด์์ธ๊ฐ์?
- ๐ฏ ์ถ์ ์๋: ๋ฆฌ๋ ๋๋ง ์๋ง๋ค ํจ์๊ฐ ์๋ก ์์ฑ๋๋ ๋ฆฌ์กํธ์ ํน์ฑ๊ณผ ํ์ด๋จธ ๋ฉ์ปค๋์ฆ ๊ฐ์ ์ถฉ๋์ ๋ฐฉ์ดํ ์ ์๋์ง ํ์ธํฉ๋๋ค.
- ๐ก ํต์ฌ ์๋ฆฌ & ๋ต๋ณ: ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋ ๋๋ง๋ค ์ ์๋ ํจ์๋ ์๋ก ์์ฑ๋ฉ๋๋ค. ๋จ์ํ๊ฒ
lodash.debounce๋ฅผ ํจ์ ์์ ์ ์ธํ๋ฉด ๋ฆฌ๋ ๋๋ง๋ง๋ค ์๋ก์ด ํ์ด๋จธ๊ฐ ์๊ฒจ ์ ๊ธฐ๋ฅ์ ๋ชป ํ๊ฒ ๋ฉ๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๋ ค๋ฉดuseMemo๋useCallback์ผ๋ก ๋๋ฐ์ด์ค๋ ํจ์ ์ธ์คํด์ค๋ฅผ ์ ์งํ๊ฑฐ๋,useRef๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๋๋ง ์ฌ์ดํด๊ณผ ๋ฌด๊ดํ๊ฒ ํ์ด๋จธ ์ํ๋ฅผ ๋ณด์กดํด์ผ ํฉ๋๋ค. ๋ํ ์ปดํฌ๋ํธ ์ธ๋ง์ดํธ ์ ๋ฐ๋์cancel์ ํธ์ถํ์ฌ ๋ฉ๋ชจ๋ฆฌ ๋์์ ์์์น ๋ชปํ ์ํ ์ ๋ฐ์ดํธ๋ฅผ ๋ฐฉ์งํด์ผ ํฉ๋๋ค.
๋ฉด์ ์ง๋ฌธ 258. ๋ก์ปฌ ์คํ ๋ฆฌ์ง ๋๊ธฐํ ํ ์ ์ค๊ณํ ๋ SSR ํ๊ฒฝ์ 'window is not defined' ์๋ฌ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ๋์?
- ๐ฏ ์ถ์ ์๋: ์ ๋๋ฒ์ค(Universal) ์๋ฐ์คํฌ๋ฆฝํธ ์คํ ํ๊ฒฝ์ ๋ํ ์ดํด์ Next.js ๋ฑ SSR ํ๋ ์์ํฌ์์์ ์์ธ ์ฒ๋ฆฌ๋ฅผ ํ์ธํฉ๋๋ค.
- ๐ก ํต์ฌ ์๋ฆฌ & ๋ต๋ณ: ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง ์์๋ ๋ธ๋ผ์ฐ์ ๊ฐ์ฒด์ธ
window๊ฐ ์กด์ฌํ์ง ์์ต๋๋ค. ๋ฐ๋ผ์useState์ ์ด๊ธฐ๊ฐ์ผ๋ก ์คํ ๋ฆฌ์ง๋ฅผ ์ง์ ์ฝ์ผ๋ ค ํ๋ฉด ์๋ฌ๊ฐ ๋ฉ๋๋ค. ํด๊ฒฐ์ฑ ์ผ๋ก๋useEffect๋ด์์๋งwindow์ ์ ๊ทผํ์ฌ ๋ฐํ์์ ๊ฐ์ ์ค์ ํ๊ฑฐ๋,typeof window !== 'undefined'์กฐ๊ฑด๋ฌธ์ผ๋ก ๊ฐ๋ ๋ก์ง์ ์์ฑํด์ผ ํฉ๋๋ค. ๋ํ ์ต์ ๋ฆฌ์กํธ์์๋useSyncExternalStore์ ์ธ ๋ฒ์งธ ์ธ์์ธgetServerSnapshot์ ํ์ฉํ์ฌ ์๋ฒ์ฉ ๊ธฐ๋ณธ๊ฐ์ ๋ฐ๋ก ์ ๊ณตํจ์ผ๋ก์จ ํ์ด๋๋ ์ด์ ๋ถ์ผ์น(Hydration Mismatch)๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ์ปค์คํ ํ ์ผ๋ก ๋ก์ง์ ๋ถ๋ฆฌํ ๋ ๊ฐ์ฅ ๋จผ์ ํ์ธํด์ผ ํ ๊ธฐ์ค์ ๋ฌด์์ธ๊ฐ์?
โ
์ ๋ต: UI ๋ ๋๋ง ์ฑ
์๊ณผ ๋ฐ์ดํฐ/์ํ/์ด๋ฒคํธ ํ๋ฆ ์ฑ
์์ด ๋ช
ํํ ๋ถ๋ฆฌ๋๋์ง ํ์ธํ๋ ๊ฒ
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช : ์ปค์คํ ํ ์ ์ฝ๋ ์ค ์๋ฅผ ์ค์ด๋ ๋๊ตฌ๊ฐ ์๋๋ผ, ์ปดํฌ๋ํธ๊ฐ ํ๋ฉด ํํ์ ์ง์คํ๋๋ก ๊ฒฝ๊ณ๋ฅผ ๋๋๋ ์ค๊ณ ๋๊ตฌ์ ๋๋ค.
- ์ค๋ต ํผ๋๋ฐฑ: ๋จ์ํ ๋ชจ๋ ํจ์๋ฅผ
useSomething์ผ๋ก ๋นผ๋ฉด ์คํ๋ ค ์์กด์ฑ๊ณผ ๋ถ์ํจ๊ณผ๊ฐ ์จ์ด ํ ์คํธํ๊ธฐ ์ด๋ ค์์ง ์ ์์ต๋๋ค. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ํ ์ ์จ๊ธฐ๋ ๊ณณ์ด ์๋๋ผ ์ฑ ์์ ์ด๋ฆ ๋ถ์ด๋ ๊ณณ์ ๋๋ค.
Q2. useSyncExternalStore๊ฐ ํ์ํ ๋ํ์ ์ธ ์ด์ ๋ ๋ฌด์์ธ๊ฐ์?
โ
์ ๋ต: ๋์์ฑ ๋ ๋๋ง ์ค ์ธ๋ถ ์คํ ์ด ๊ฐ์ด ๋ฐ๋์ด ํ๋ฉด ์ผ๋ถ๊ฐ ์๋ก ๋ค๋ฅธ ์ํ๋ฅผ ๋ณด์ฌ์ฃผ๋ ํ
์ด๋ง์ ๋ง๊ธฐ ์ํด์
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
: React ์ธ๋ถ ์คํ ์ด๋ฅผ ์ง์ ๊ตฌ๋
ํ๋ฉด ๋ ๋๋ง ์ค๊ฐ์ ๊ฐ์ด ๋ฐ๋์์ ๋ ์ผ๊ด์ฑ์ด ๊นจ์ง ์ ์์ต๋๋ค.
useSyncExternalStore๋ ๊ตฌ๋ ๊ณผ snapshot ์ฝ๊ธฐ ๊ณ์ฝ์ React์ ๋ง์ถฅ๋๋ค. - ์ค๋ต ํผ๋๋ฐฑ: ๋จ์ํ
useEffect๋ก subscribe/unsubscribe๋ฅผ ๊ตฌํํ๋ฉด ๋์์ฑ ํ๊ฒฝ์ snapshot ์ผ๊ด์ฑ์ ๋ณด์ฅํ์ง ๋ชปํฉ๋๋ค. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ์ธ๋ถ ์ํ๋ React์ ์ฝ๋ ์ฝ์์ ๋ง์ถฐ์ผ ํฉ๋๋ค.
Q3. ์์ฒ ์ด์ ํ ์คํธ ํ์: ์ปค์คํ ํ ์ ๋ง๋ค๊ธฐ ์ ์ํธ๊ฐ ๋ฌผ์ด๋ณผ ์ง๋ฌธ์ ๋ฌด์์ผ๊น์?
โ
์ ๋ต: ์ด ํ
์ด ํ๋ฉด ํํ์ ์จ๊ธฐ๋์ง, ์๋๋ฉด ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ํ ์ ์ด์ ๋ถ์ํจ๊ณผ ๊ฒฝ๊ณ๋ฅผ ๋ถ๋ฆฌํ๋์ง ํ์ธํ๋ค
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช : ์ข์ ํ ์ ํธ์ถํ๋ ์ปดํฌ๋ํธ๊ฐ "๋ฌด์์ ๋ณด์ฌ์ค์ง"์ ์ง์คํ๊ฒ ๋ง๋ค๊ณ , ๋คํธ์ํฌ/์คํ ์ด/์ด๋ฒคํธ ๊ฐ์ ๋ณํ์ ๊ฒฝ๊ณ๋ฅผ ํ ์คํธ ๊ฐ๋ฅํ ๋จ์๋ก ๋ฌถ์ต๋๋ค.
- ์ค๋ต ํผ๋๋ฐฑ: ๋น์ทํ ์ฝ๋๊ฐ ๋ ๋ฒ ๋์๋ค๊ณ ๋ฐ๋ก ํ ์ผ๋ก ๋นผ๋ฉด, ์คํ๋ ค ๋ณํ ์ด์ ๊ฐ ๋ค๋ฅธ ๋ก์ง์ ๋ฌด๋ฆฌํ๊ฒ ๊ฒฐํฉํ ์ ์์ต๋๋ค.
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ์ฌ์ฌ์ฉ๋ณด๋ค ๋จผ์ ์ฑ ์์ ๊ฒฝ๊ณ๋ฅผ ๋ด ๋๋ค.
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ ์ปค์คํ ํ ์ "์ค๋ณต ์ ๊ฑฐ์ฉ ํด๋"์ฒ๋ผ ์ฐ๋ ์๊ฐ์ ๊ณ ์ณค๋ค. ์ํธ ๋์ด ๊ฐ์ ์ฝ๋๋ผ๋ ๋ณํ ์ด์ ๊ฐ ๋ค๋ฅด๋ฉด ๋ถ๋ฆฌํ์ง ๋ง๋ผ๊ณ ํ์ ๋๋ ์กฐ๊ธ ๋นํฉํ์ง๋ง, ์ด์ ๋ ํ ์ด ์ฑ ์์ ์ด๋ฆ์ด๋ผ๋ ๋ง์ด ์ดํด๋๋ค.
๐ก "์ข์ ํ ์ ์ฝ๋๋ฅผ ์จ๊ธฐ๋ ๊ฒ ์๋๋ผ, ๋ณํ๊ฐ ์ผ์ด๋๋ ๊ฒฝ๊ณ๋ฅผ ๋๋ฌ๋ธ๋ค."
๋ด์ผ๋ถํฐ ์ธ๋ถ ์คํ ์ด๋ฅผ ์ง์ ๊ตฌ๋
ํ๋ ์ฝ๋๋ฅผ ๋ณด๋ฉด useEffect๊ฐ ์๋๋ผ snapshot ์ผ๊ด์ฑ๋ถํฐ ๋ ์ฌ๋ ค์ผ๊ฒ ๋ค. ์ด์ ๋๋ ๋จ์ํ ํ
์ ๋ง์ด ์๋ ์ฌ๋์ด ์๋๋ผ, ํ
์ด ํ ์ฝ๋์ ๊ฒฝ๊ณ๋ฅผ ์ด๋ป๊ฒ ๋ฐ๊พธ๋์ง ์ค๋ช
ํ ์ ์๋ ์ฌ๋์ด ๋์ด๊ฐ๋ ์ค์ด๋ค.