๐ฃ 05. ์ค๋ฌด 100% ํ์ฉ๋ฒ: ์ปค์คํ ํ ๊ณผ ์ํฐํจํด
๐ ๊ฐ์
Zustand ์คํ ์ด๋ฅผ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์ ๊ฒฐํฉํ ๋ ์ง์ผ์ผ ํ ์ปค์คํ ํ ์ถ์ํ ํจํด๊ณผ ์น๋ช ์ ์ธ ๋๊ธฐํ ์ํฐํจํด
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์คํ ์ด ํ (
useStore)์ ์ปดํฌ๋ํธ์์ ์ง์ ํธ์ถํ์ง ์๊ณ ์ปค์คํ ํ ์ผ๋ก ์บก์ํ(Encapsulation)ํ๋ ์ด์ ๋ฅผ ์ ์ ์๋ค.- ์ํ(State) ๊ตฌ๋ ์์ด ์ก์ (Action) ํจ์๋ง ์ถ์ถํ์ฌ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ์์ ํ ์ฐจ๋จํ๋ ํจํด์ ์ ์ฉํ ์ ์๋ค.
useEffect๋ฅผ ์ฌ์ฉํด React ์ปดํฌ๋ํธ์ ์ํ๋ Props๋ฅผ Zustand์ ๊ฐ์ ๋๊ธฐํํ๋ ค๋ ํ์๊ฐ ์ ์ต์ ์ ์ํฐํจํด์ธ์ง ์ดํดํ๋ค.
๐ ๋ชฉ์ฐจ
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ 1. ์คํ ์ด ์บก์ํ: ์ปค์คํ ํ (Custom Hooks)
- ๐ 2. ํ์ ์ํ(Derived State) ๋ก์ง ๊ฐ์ถ๊ธฐ
- ๐ 3. ๊ตฌ๋ ๋๊ธฐ: Action ์ ์ฉ Hook
- โ ๏ธ 4. ์ต์
์ ์ํฐํจํด:
useEffect๋ก ๋๊ธฐํํ๊ธฐ - ๐๏ธโโ๏ธ ์์ฒ ์ ํ ์คํธ ํ์ (Q&A)
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 10๋ถ / ํต์ฌ ํํธ: 6๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
์ธ๋ผ์ธ ์
๋ ํฐ์ ๋ฌธ์ ์ โ ์ปค์คํ
ํ
์ผ๋ก ์ถ์ํ โ ํ์ ๋ก์ง ์๋ โ ์ก์
๋ถ๋ฆฌ โ useEffect ๋๊ธฐํ ์ํฐํจํด ๊ฒฝ๊ณ
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ๐ฃ ์์ฒ ( ์ ์
): "๋ฆฌ๋ ๋! Zustand ์ ๋ง ํธํด์. ์์ฆ์ ์ ๊ฐ ์ปดํฌ๋ํธ ์งค ๋๋ง๋ค ๋ฌด์กฐ๊ฑด ํ์ผ ๋งจ ์์
const user = useAppStore(state => state.user.profile)์ด๋ ๊ฒ ์ ๋ ํฐ๋ฅผ ์ง๋ฃ๊ณ ์์ํด์. ๊ทผ๋ฐ ์ ์ ์คํ ์ด ๊ตฌ์กฐ๊ฐ ํ ๋ฒ ๋ฐ๋๋๊น ํ 50๊ฐ ํ์ผ ์ฐพ์๋ค๋๋ฉด์ ์ ๋ ํฐ๋ฅผ ๋ค ์์ ํด์ผ ํ๋๋ฐ, ์ด๊ฑฐ ๋ง๋์?" - ๐ฆ ์ํธ ( ๋ฆฌ๋ ): "์์ฒ ๋, Zustand๊ฐ ๋ณด์ผ๋ฌํ๋ ์ดํธ๊ฐ ์๋ค๊ณ ํด์ ์ถ์ํ(Abstraction) ๊น์ง ์๋ตํ๋ผ๋ ๋ป์ ์๋๋๋ค. ์ปดํฌ๋ํธ๊ฐ ์คํ ์ด์ ๋ด๋ถ ๊น์ ๊ตฌ์กฐ(Depth)๋ฅผ ๋๋ฌด ํคํ ์๊ณ ์์ผ๋ฉด ๊ฒฐํฉ๋๊ฐ ๋์์ง๋๋ค. ๊ธ๋ก๋ฒ ์คํ ์ด๋ฅผ ์ปค์คํ ํ ์ผ๋ก ์์๊ฒ ํฌ์ฅํ๋ ์ค๋ฌด ํ ํฌ๋์ ์๋ ค๋๋ฆด๊ฒ์."
๐ค ์ ์์์ผ ํ๋๊ฐ
Zustand ๋ฌธ์๋ฅผ ์ด๋ฉด ๊ฐ์ฅ ๋จผ์ ๋์ค๋ ์ฝ๋๋ const bears = useStore((state) => state.bears) ์
๋๋ค. ์ด์ฒ๋ผ ์ปดํฌ๋ํธ๊ฐ ์ง์ ์คํ ์ด ์ ์ฒด ์ํ ํธ๋ฆฌ์ ์ ๊ทผํ์ฌ ์ํ๋ ์กฐ๊ฐ์ ๋ผ์ด์ค๋(Selecting) ๋ฐฉ์์ ๋น ๋ฅด๊ณ ์ง๊ด์ ์
๋๋ค.
ํ์ง๋ง ์ค๋ฌด ํ๊ฒฝ์์ ํ๋ก๋ํธ ๊ท๋ชจ๊ฐ ์ปค์ง๋ฉด ๋ฌธ์ ์ ๋ค์ด ์์ถํฉ๋๋ค.
- ์ค๋ณต๊ณผ ํํธํ: ์๋ง์ ์ปดํฌ๋ํธ์์ ๋์ผํ Selector ์ฝ๋ฐฑ์ด ๋ณต์ฌ-๋ถ์ฌ๋ฃ๊ธฐ ๋ฉ๋๋ค.
- ๋ฆฌํฉํ ๋ง ์ง์ฅ: ์คํ ์ด ๋ด๋ถ ๊ตฌ์กฐ๋ฅผ ํ ๋ฒ ๋ณ๊ฒฝ(
state.user->state.auth.user)ํ๋ฉด, ํด๋น ์คํ ์ด๋ฅผ ์ฌ์ฉํ๋ ์์ญ ๊ฐ์ ํ์ผ์์ ์ ๋ถ ์๋ฌ๊ฐ ํฐ์ง๋๋ค. - ๋ ๋๋ง ์ง๋ขฐ๋ฐญ: ์ก์ (ํจ์) ํ๋๋ง ํ์ํ๋ฐ ์คํ ์ด ๊ป๋ฐ๊ธฐ๋ฅผ ์๋ชป ๊ตฌ๋ ํ์ฌ ๋ฌดํ ๋ฆฌ๋ ๋๋ง์ด ๋ฉ๋๋ค (์ด์ ๋ ๋๋ง ์ต์ ํ ์ฑํฐ ์ฐธ๊ณ ).
์ ์ง์ธ ์ํคํ ์ฒ๋ "์ด๋ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐ๋์ง"๋ฅผ ์ปดํฌ๋ํธ๊ฐ ๋ชจ๋ฅด๊ฒ ํฉ๋๋ค. Zustand ์คํ ์ด๋ฅผ ์ปค์คํ ํ (Custom Hook) ์ด๋ผ๋ ์ธํฌ๋ก ์ ํ์ ์ ๊ณตํด์ผ ํฉ๋๋ค.
๐๏ธ 1. ์คํ ์ด ์บก์ํ: ์ปค์คํ ํ (Custom Hooks)
์์ฒ ์ด๊ฐ ์ง๋์ ๊ธฐ์กด ์ฝ๋๋ฅผ ๋ณด๊ฒ ์ต๋๋ค.
โ ์ธ๋ผ์ธ ์ ๋ ํฐ ๋จ์ฉ
// components/UserProfile.tsx
import { useAuthStore } from '@/store/authStore';
export function UserProfile() {
// ๐จ ์์ฒ : "์ปดํฌ๋ํธ๋ง๋ค ์คํ ์ด ์์ชฝ์ ์ง์ ๋ค ๋ค์ ธ์ ๊บผ๋ด์์ผ์ง!"
const userName = useAuthStore(state => state.user.profile.name);
const avatar = useAuthStore(state => state.user.profile.avatarUrl);
// ...
}โ ์ปค์คํ ํ ์ผ๋ก ์๋ (Encapsulation)
์คํ ์ด๋ฅผ ์ ์ํ ํ์ผ(authStore.ts) ๋งจ ๋จ์, ์ปดํฌ๋ํธ๋ค์ด ๊ฐ์ ธ๋ค ์ธ ์ ์๋ ์์ ์ปค์คํ
ํ
๋ค์ ๋ฏธ๋ฆฌ ์ ์ํด์ export ํฉ๋๋ค.
// store/authStore.ts
import { create } from 'zustand';
const useAuthStore = create(...)
// ๐ฆ ์ํธ: "์ปดํฌ๋ํธํํ
๋ ์คํ ์ด ์ฉ์ผ(useAuthStore)์ ๋ณด์ฌ์ฃผ์ง ๋ง์ธ์. ์์๊ฒ ํฌ์ฅํด์ ๋ด๋ณด๋
๋๋ค."
export const useUserName = () => useAuthStore(state => state.user.profile.name);
export const useUserAvatar = () => useAuthStore(state => state.user.profile.avatarUrl);
export const useIsLoggedIn = () => useAuthStore(state => !!state.accessToken);์ด์ ์ปดํฌ๋ํธ์ ์ฝ๋๊ฐ ์๋์ ์ผ๋ก ์ฐ์ํด์ง๋๋ค.
// components/UserProfile.tsx
import { useUserName, useUserAvatar, useIsLoggedIn } from '@/store/authStore';
export function UserProfile() {
const isLoggedIn = useIsLoggedIn();
const name = useUserName();
const avatar = useUserAvatar();
// ๐ก ์ปดํฌ๋ํธ๋ ์ด๊ฒ Zustand์์ ์ค๋์ง, Context์์ ์ค๋์ง ์ ํ์๋ ์์ต๋๋ค!
}์ ์ง๋ณด์์ ๋ง๋ฒ: ํ๋ ์คํ ์ด ๊ตฌ์กฐ๊ฐ ๋ฐ๋๋ฉด ์ปดํฌ๋ํธ๋ ๋จ ํ ์ค๋ ์์ ํ ํ์๊ฐ ์์ต๋๋ค. ์ค์ง authStore.ts ํ์ผ ๋ด๋ถ์ ์ ์ธ๋ ์ปค์คํ
ํ
์ Selector ๋ฆฌํด๊ฐ๋ง ์์ ํด์ฃผ๋ฉด ์ฑ ์ ์ฒด์ ๋ฐฉ์ด์ ์ด ์ณ์ง๋๋ค.
๐ 2. ํ์ ์ํ(Derived State) ๋ก์ง ๊ฐ์ถ๊ธฐ
์ฅ๋ฐ๊ตฌ๋ ์ด์ก์ด๋ ํํฐ๋ง๋ ๊ฒ์๊ธ์ฒ๋ผ ๊ณ์ฐ์ด ํ์ํ ๊ฐ(ํ์ ์ํ)์ ๊ฐ์ ธ์ฌ ๋๋ ์ปค์คํ ํ ์ด ๋น์ ๋ฐํฉ๋๋ค.
// โ ์ปดํฌ๋ํธ ๋จ์์ ์ฐ์ฐ (๋ฌด๊ฑฐ์ด ํ
)
function CartSummary() {
const items = useCartStore(state => state.items);
const total = items.reduce((sum, i) => sum + i.price, 0); // ๋ ๋๋ง๋ง๋ค ๊ณ์ฐ
}
// โ
์คํ ์ด ๋จ์์ Selector๋ก ์ฐ์ฐ ์๋
export const useCartTotal = () => useCartStore(state =>
state.items.reduce((sum, item) => sum + item.price, 0)
);
function CartSummaryPro() {
// ๐ฆ ์ํธ: "์ปดํฌ๋ํธ๋ ๋ทฐ(View)๋ง ๊ทธ๋ฆฝ๋๋ค. ๊ณ์ฐ์์ ์คํ ์ด๋ก ๋ฐ์ด ๋ฃ์ผ์ธ์."
const total = useCartTotal();
}๐ 3. ๊ตฌ๋ ๋๊ธฐ: Action ์ ์ฉ Hook
๊ฐ์ฅ ๋น๋ฒํ๊ฒ ์ผ์ด๋๋ ๋ ๋๋ง ์ต์ ํ ์คํจ ์ฌ๋ก์ ๋๋ค. ๊ทธ์ ๋ฒํผ์ ๋๋ฅผ ๋ ์๋ํ '๋ฉ์๋(ํจ์)' ํ๋๋ง ํ์ํ๋ฐ ๋ฌด์ฌ๊ฒฐ์ ์คํ ์ด๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ์ ๋๋ค.
// โ ์์ฒ ์ด์ ๋ฌดํ ๋ ๋๋ฒํผ
function LogoutButton() {
// ๐จ Zustand์ ๋ชจ๋ ์ํ ์ค ํ๋๋ผ๋ ๋ฐ๋๋ฉด ์ด ๋ก๊ทธ์์ ๋ฒํผ ์ปดํฌ๋ํธ๊ฐ ์ฌ๋ ๋๋ง๋จ
const { logout } = useAuthStore();
return <button onClick={logout}>๋ก๊ทธ์์</button>
}ํจ์๋ ์ญ์ Selector๋ก ํ๋์ฉ ์ง์ด์์ผ ํ์ง๋ง, ์ก์ ์ด ๋ง์์ง๋ฉด ๋ฐ๋ณต ๋ ธ๊ฐ๋ค๊ฐ ๋ฉ๋๋ค. ์ด๋ 5๋ ์ฐจ๋ค์ Actions ๊ฐ์ฒด๋ฅผ ๋ฌถ์ด์ ๋นผ๋ ๋๋ค.
// store/authStore.ts
const useAuthStore = create((set) => ({
user: null,
// ๐ก ์ํ(State)์ ์ก์
(Action) ํจ์ ๊ฐ์ฒด๋ฅผ ๋ด๋ถ์ ์ผ๋ก ๋์ค๋ฅผ ๋๋ ๋ถ๋ฆฌํฉ๋๋ค
actions: {
login: () => set(...),
logout: () => set(...),
}
}));
// Action๋ง ์ ๋นผ๊ฐ๋ ์ ์ฉ ํ
export const useAuthActions = () => useAuthStore(state => state.actions);// components/LogoutButton.tsx
import { useAuthActions } from '@/store/authStore';
function LogoutButtonPro() {
// โ
์๋ฒฝ ์ฐจ๋จ! ์ํ๊ฐ์ด ์ฒ ๋ฒ ๋ณํด๋ ์ด ๋ฒํผ์ ์ด๊ธฐ 1ํ๋ง ๋ ๋๋ง๋๊ณ ๋๋ฉ๋๋ค.
const { logout } = useAuthActions();
return <button onClick={logout}>๋ก๊ทธ์์</button>
}โ ๏ธ 4. ์ต์
์ ์ํฐํจํด: useEffect๋ก ๋๊ธฐํํ๊ธฐ
Zustand ์ปค๋ฎค๋ํฐ์์ ๊ฐ์ฅ ๊ฒฝ๊ณ ํ๋ ์น๋ช
์ ์ธ ์ํฐํจํด์
๋๋ค. ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ๋ฐ์ props๋ ๋ก์ปฌ useState ๊ฐ์ Zustand ๊ธ๋ก๋ฒ ์คํ ์ด์ ์ค์
๋ฃ๊ธฐ ์ํด useEffect๋ฅผ ๋จ์ฉํ๋ ํ์์
๋๋ค.
โ ์์ฒ ์ด์ ํญ๋ฐํ๋ ํ์ด๋จธ
// ๐ฃ ์์ฒ : "๊ฒ์๊ธ ID Props๋ฅผ ๋ฐ์์ผ๋ Zustand ์คํ ์ด์ ๋ฃ์ด๋ฌ์ผ ๋ค๋ฅธ ์ ๋ค๋ ์ด ID๋ฅผ ์์ง!"
function PostDetail({ postId }: { postId: string }) {
const setPostId = useAppStore(state => state.setPostId);
// ๐จ ์ต์
์ ์ํฐํจํด
useEffect(() => {
setPostId(postId); // ๋ ๋๋ง -> Effect ๋ฐ์ -> ์ํ ๋ณ๊ฒฝ -> ์ฌ๋ ๋๋ง ๋ฌดํ ๋ฃจํ ์ํ!
}, [postId, setPostId]);
return <div>...</div>
}์ด ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๋ฅผ ๋ณ์ต๋๋ค.
- React์ ๋ฐ์ดํฐ ๋จ๋ฐฉํฅ ํ๋ฆ(Top-Down)์ ์ ๋ฉด์ผ๋ก ์๋ฐฐํฉ๋๋ค.
- ์ปดํฌ๋ํธ ๋ ๋๋ง์ด ์๋ฃ๋ ์งํ(Effect) ์ ์ญ ์ํ๊ฐ ๋ฐ๋๊ธฐ ๋๋ฌธ์ ๋ถํ์ํ ์ฐ์ ๋ฆฌ๋ ๋๋ง ํญํ(Waterfall)์ ์ผ์ผํต๋๋ค.
- ์ด ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธ(Unmount) ๋ ๋ ์คํ ์ด์
postId๋ฅผ ๋๊ฐ ์ฒญ์ํ ๊ฑด๊ฐ์? ์ฐ๋ ๊ธฐ ๋ฐ์ดํฐ๊ฐ ๋จ์ต๋๋ค.
๐ฆ ์ํธ์ ์ง์ฅ ํ์ถ ์๋ฃจ์
:
"Props๋ก ๋ด๋ ค์จ ๋ฐ์ดํฐ๋ ๊ทธ๋ฅ Props๋ก ์ฐ๊ฑฐ๋, ํ์ ํธ๋ฆฌ์์๋ง ๊ณต์ ํด์ผ ํ๋ค๋ฉด ๊ฑฐ์ฐฝํ Zustand ๋์ React Context API๋ฅผ ๋ง๋ค์ด ๋ด๋ ค์ฃผ์ธ์. ์ ์ ์ญ ์คํ ์ด๋ฅผ ์ฐ๋ฅด๊ณ ์ถ๋ค๋ฉด ๋ ๋๋ง ์ค(useEffect)์ด ์๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ(๋ฒํผ ํด๋ฆญ, ํ์ด์ง ์ด๋ ์ก์
๋ด๋ถ) ์์ ์คํ ์ด๋ฅผ ์
๋ฐ์ดํธํด์ผ ํฉ๋๋ค."
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. Zustand ์คํ ์ด๋ฅผ ํ๋ก์ ํธ ๋ด ์ปดํฌ๋ํธ์์ ๊ฐ์ ธ๋ค ์ธ ๋ ๊ฐ์ฅ ๊ถ์ฅ๋๋ ์ถ์ํ ํจํด์ ๋ฌด์์ธ๊ฐ์?
- A) ๋ชจ๋ ์ปดํฌ๋ํธ์์
useStore๋ฅผ ํต์งธ๋ก ์ํฌํธํ์ฌ ์ธ๋ผ์ธ ์ฝ๋ฐฑ์ผ๋ก ์ํ๋ฅผ ๊บผ๋ด ์ด๋ค. - B) ์คํ ์ด ์ ์ ํ์ผ ํ๋จ์ ์ ์ฉ ์ปค์คํ
ํ
(
useUser,useAuthActions)์ ๋ง๋ค์ด ๊ทธ๊ฒ๋งexportํ๋ค. - C) Context API๋ก ํ ๋ฒ ๋ ๊ฐ์ธ์ ๋ด๋ ค ๋ณด๋ธ๋ค.
- D) ์ ์ญ ๊ฐ์ฒด์ธ Window์ ์คํ ์ด๋ฅผ ๋ง์ดํธํ์ฌ ์ง์ ์ฐธ์กฐํ๋ค.
โ
์ ๋ต: B
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช : ์คํ ์ด ํ ์ ์๋(Encapsulation)ํ์ฌ ์ปค์คํ ํ ์ผ๋ก ๋ฐฐํฌํ๋ฉด, ์ปดํฌ๋ํธ๋ ์ ์ญ ์ํ์ ๋ด๋ถ ๋์ค์ ๊ตฌ์กฐ์ ์์กดํ์ง ์๊ฒ ๋ฉ๋๋ค. ์คํ ์ด ํํ๊ฐ ๋ณ๊ฒฝ๋๋๋ผ๋ ์ปค์คํ ํ ๋จ 1๊ณณ๋ง ์์ ํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ์ ์ง๋ณด์์ฑ์ด ๊ทน๋ํ๋ฉ๋๋ค.
- ์ค๋ต ํผ๋๋ฐฑ: "์์ฒ ๋, ๋งค๋ฒ ์ปดํฌ๋ํธ์์ ๋ณด๋ฐ๋ฆฌ(์คํ ์ด)๋ฅผ ์ง์ ํ์ด์ ํค์ง์ด ๋์ง ๋ง์ธ์. ํ์ํ ๊ฒ๋ง ๊ฑด๋ค์ฃผ๋ ์ง์(์ปค์คํ ํ )์ ๊ณ ์ฉํฉ์๋ค."
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ๐ฆ ์ํธ: "์คํ ์ด๋ ์จ๊ธฐ๊ณ , ์ปค์คํ ํ ์ผ๋ก๋ง ์ํตํ๋ผ."
Q2. ํจ์(Action) ํ๋๋ง ์ปดํฌ๋ํธ์์ ํธ์ถํ๋ ค๊ณ ์คํ ์ด๋ฅผ ์ฐธ์กฐํ์ ๋ ๋ถํ์ํ๊ฒ ๋ฆฌ๋ ๋๋ง์ด ์์ฃผ ๋ฐ์ํ๋ค๋ฉด, ๊ฐ์ฅ ์์ฌํด์ผ ํ๋ ํจํด์ ๋ฌด์์ธ๊ฐ์?
- A)
useStore(state => state)์ฒ๋ผ ์คํ ์ด ์ ์ฒด๋ฅผ ๋ฐํ๋ฐ์ ๋น๊ตฌ์กฐํ ํ ๋น(Destructuring)์ผ๋ก ํจ์๋ฅผ ๊บผ๋๋ค. - B) ๋ฒํผ ํด๋ฆญ ์ด๋ฒคํธ ๋ด๋ถ์์
get()ํจ์๋ฅผ ์ฌ์ฉํ๋ค. - C) ์ปดํฌ๋ํธ์
React.memo๋ฅผ ์์ฐ์ง ์์๋ค. - D)
useStore(state => state.logout)๊ณผ ๊ฐ์ด ํจ์ ํ๋๋ง ์ฐ์ด์ ๊ฐ์ ธ์๋ค.
โ
์ ๋ต: A
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
:
const { logout } = useStore()์ฒ๋ผ ์คํ ์ด๋ฅผ ํต์งธ๋ก ๊ตฌ๋ ํด๋ฒ๋ฆฌ๋ฉด, ์คํ ์ด ๋ด๋ถ์count๋user๋ฑ ์ด๋ค ์ํ๊ฐ ๋ฐ๋์ด๋ ๊ฐ์ฒด ์ฐธ์กฐ๊ฐ ๋ณ๊ฒฝ๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋์ด ์ปดํฌ๋ํธ ์ ์ฒด๊ฐ ๋ค์ ๊ทธ๋ ค์ง๋๋ค. ์ฑ๋ฅ ์ธก๋ฉด์์ ์ต์ ์ ์ํฐํจํด ์ค ํ๋์ ๋๋ค. - ์ค๋ต ํผ๋๋ฐฑ: "์์ฒ ๋, ๊ตฌ์กฐ ๋ถํด ํ ๋น ํธํ๋ค๊ณ ๋ง ์ฐ๋ฉด ๋ฆฌ๋ ๋๋ง ํญํ ๋ง์ต๋๋ค. ํ์ํ ํ๊ฒ๋ฌผ๋ง ์กฐ์ค๊ฒฝ์ผ๋ก ์ ํํ๊ฒ ์ฐ์ผ์ธ์!"
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ๐ฆ ์ํธ: "ํจ์ ํ๋ ์ฐ๋ ค๋ค ์คํ ์ด ์ ์ฒด๋ฅผ ๊ตฌ๋ ํ๋ ๊ฒ์ ๋ฐฐ๊ฐ ๊ณ ํ๋ค๊ณ ์๋ฃํ ์ฐฝ๊ณ ์งธ ์ง์ด์ง๋ ๊ผด์ ๋๋ค."
Q3. ๐๏ธโโ๏ธ ์์ฒ ์ ํ ์คํธ ํ์ (Q&A)
์์ฒ ์ด๊ฐ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์
useAppStore(state => state.theme === 'dark')๋ฅผ ๋ณต๋ถํด์ ์ฐ๊ณ ์์์ต๋๋ค. ๋ฆฌ๋ทฐ์์ ์ด๋ฅผ ์ปค์คํ ํ ์ผ๋ก ๋นผ๋ผ๋ ์ง์๋ฅผ ๋ฐ์๋๋ฐ, ์ด์ฐจํผ ํ ์ค์ง๋ฆฌ ์ฝ๋์ธ๋ฐ ๊ตณ์ด ๋นผ์ผ ํ ๊น์?
โ
์ ๋ต: ๋ค, ๋ฐ๋์ ๋นผ์ผ ํฉ๋๋ค. export const useIsDarkMode = () => useAppStore(state => state.theme === 'dark'); ์ฒ๋ผ ๋ชจ๋ํํด์ผ ํฉ๋๋ค.
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
: ์ง๊ธ ๋น์ฅ์ ํ ์ค์ด์ง๋ง, ์ถํ์ ๋คํฌ๋ชจ๋ ํ๋ณ ๋ก์ง์ด
state.theme === 'dark' || state.systemTheme === 'dark'์ฒ๋ผ ๋ณต์กํด์ง๋ฉด ํ์ผ 100๊ฐ๋ฅผ ์ผ์ผ์ด ์์ ํด์ผ ํฉ๋๋ค. ์ปค์คํ ํ ์ ๋ณ๊ฒฝ์ ๋ํ ์ ์ผ๋ฌด์ดํ ๋จ์ผ ์ง์ (Single Point of Truth)์ ๋ง๋ค์ด ๋ฐฉ์ด๋ฒฝ์ ํ์ฑํฉ๋๋ค. - ์ค๋ต ํผ๋๋ฐฑ: "์์ฒ ๋, ๋ณต๋ถ(Copy&Paste)์ ์ฃ์ ์ ๋๋ค! ์๊ฐ๋ฝ์ด ์กฐ๊ธ ํธํ์๊ณ ๋ฏธ๋์ ๋ด ๋ชฉ์ ์กฐ๋ฅด์ง ๋ง์ธ์!"
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ๐ฆ ์ํธ: "์ปดํฌ๋ํธ๋ ์คํ ์ด์ ์ด๋ฆ์ ๋ชฐ๋ผ์ผ ํ๋ค. ์ค์ง ์ปค์คํ ํ ์ด๋ผ๋ ์จ์ดํฐ๋ฅผ ํตํด์๋ง ์ฃผ๋ฌธํ๋ผ."
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ '๋์์ ํ๋' ์ฝ๋๊ฐ ์ '๊ตฌ๋ฆฐ' ์ฝ๋์ธ์ง ๋ผ์ ๋ฆฌ๊ฒ ๋๊ผ๋ค. ๋๋ฆ ์ํ๊ด๋ฆฌ ํด ์ด๋ค๊ณ ์ฌ๊ธฐ์ ๊ธฐ ์คํ ์ด๋ฅผ ํธ์ถํด๋๋๋ฐ, ๊ทธ๊ฒ ๊ฒฐ๊ตญ ์คํ๊ฒํฐ ์ฝ๋ ๋ณ์ข
์ผ ๋ฟ์ด์๋ค๋ ๋ฆฌ๋ ๋์ ํฉํญ์ ํ ๋ง์ด ์์๋ค. ์ปดํฌ๋ํธ ์๋จ์ const { isDark, userName, logout } = useAuthStore() ๋ผ๊ณ ์์๊ฒ ํ
์ผ๋ก ๋ฝ์ ์ฐ๊ณ ๋๋ ์ฝ๋๊ฐ ๊ฐ์๊ธฐ ์๋์ด์ ํฅ๊ธฐ๋ฅผ ํ๊ธฐ๊ธฐ ์์ํ๋ค. ๊ทธ๋ฆฌ๊ณ useEffect ์์ setํจ์ ๋ฃ์๋ค๊ฐ ์ฑ ๋ฌดํ๋ฃจํ ๋์ ์ปดํจํฐ ํฐ์ง ๋ปํ๋ ํธ๋ผ์ฐ๋ง๋ ์ด์ฐธ์ ๊ณ ์ณ์ผ๊ฒ ๋ค. Zustand... ์๋ฉด ์์๋ก ๊น๊นํ์ง๋ง ์ง์ง ๋งค๋ ฅ ์๋ ๋
์์ด๋ค.
๐ก "์ ์ญ ์คํ ์ด๋ ์์ด ํคํ ๋ณด์ด๋ ์ดํญ์ด ์๋๋ค. ์์ ํฌ์ฅ์ง(Custom Hook)๋ก ๊ฐ์ธ๊ณ ์ก์ ๊ณผ ์ํ๋ฅผ ๋ถ๋ฆฌํด ์ปดํฌ๋ํธ์ ๋ฐฐ๋ฌํ์."