๋๋ฃ๋ถ์ด ๊ณต์ ์ฃผ์ XL8 ๋ฐฐํ๋๋์ ์ธ๋ฏธ๋ ๊ณต์ ์ฉ PPT ๋ฅผ ๋ณด๊ณ , ์ํ ๊ด๋ฆฌ ๊ด๋ จํด์ ๋ด๊ฐ ์ด๋ ์ ๋ ์๊ณ ์๋์ง ๋ชจ๋ฅด๋ ๊ฑด ์๋์ง ๋ณด๊ณ ์ถ์๋ง์์ ChatGPT ๋ฅผ ์ด์ฉํด์ ๊ณต๋ถ๋ฅผ ํด๋ดค๋ค. ์ฐ์ ์๋๋ 4๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ถ์ฌ๋ฃ๊ณ ์์ ์ฝ๋๋ฅผ ๋ฌ๋ผ๊ณ ํด์ ์ ๋ฆฌํ ๋ด์ฉ.

์ ๋ฆฌํ ๋ด์ฉ
useState ์๋ จ์
- useState ์ฌ์ฉํด ์ํ ์ ๋ฐ์ดํธ
- props ์ ๋ฌ ์์ฃผ๋ก ๊ตฌ์ฑ
- ๊ฐ๋จํ ์ด๋ฒคํธ ์ฒ๋ฆฌ
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
์์ฌ๊ฒฐ์ ํฌ์ธํธ
- "์ด๊ฑด ์ํ๋ก ๊ด๋ฆฌํด์ผ ํด?"
- "๊ทธ๋ฅ ๋ณ์์ฌ๋ ๋๋ ๊ฑฐ ์๋๊ฐ?"
์์ฃผ ๊ฒช๋ ๋ฌธ์
- ๋ถํ์ํ ์ํ ์ ์ธ
- ๋๊ธฐ/๋น๋๊ธฐ ์ํ ์ ๋ฐ์ดํธ ๊ฐ๋ ๋ถ์กฑ
- props vs state ํผ๋
Props & ์ํ ํ์๊ฐ
- ์ํ ๋์ด์ฌ๋ฆฌ๊ธฐ (lifting state)
- ์ปดํฌ๋ํธ ๊ฐ ์ํ ๊ณต์ ๊ณ ๋ฏผ
- ๋ณต์กํ ์ด๋ฒคํธ ์กฐํฉ ์ฒ๋ฆฌ
function Parent() {
const [name, setName] = useState("");
return (
<div>
<Input name={name} onChange={setName} />
<Preview name={name} />
</div>
);
}
function Input({ name, onChange }: { name: string; onChange: (v: string) => void }) {
return <input value={name} onChange={(e) => onChange(e.target.value)} />;
}
function Preview({ name }: { name: string }) {
return <p>Preview: {name}</p>;
}
์์ฌ๊ฒฐ์ ํฌ์ธํธ
- "์ด ์ํ๋ ์ด๋์ ์์ด์ผ ํ ๊น?"
- "Props๋ก ์ค๊น, Context๋ก ๊ณต์ ํ ๊น?"
์์ฃผ ๊ฒช๋ ๋ฌธ์
- ๊ณผ๋ํ props drilling
- ๋ฆฌ๋ ๋๋ง ๋ณ๋ชฉ ๋ฐ์
- ์ปดํฌ๋ํธ ๊ฐ ๊ฒฝ๊ณ ๋ถ๋ถ๋ช
์ํ ํ๋ฆ ์ค๊ณ์
- Context, useReducer ์ฌ์ฉ ์์
- ์ํ ํ๋ฆ ๊ตฌ์กฐํ
- memo, useMemo, useCallback ์์
const CounterContext = React.createContext(null);
function CounterProvider({ children }) {
const [count, dispatch] = useReducer((state, action) => {
switch (action.type) {
case 'INC':
return state + 1;
default:
return state;
}
}, 0);
return (
<CounterContext.Provider value={{ count, dispatch }}>
{children}
</CounterContext.Provider>
);
}
function Child() {
const { count, dispatch } = useContext(CounterContext);
return (
<button onClick={() => dispatch({ type: 'INC' })}>
{count}
</button>
);
}
์์ฌ๊ฒฐ์ ํฌ์ธํธ
- "Prop drilling์ด ๋๋ฌด ์ฌํด. Context๋ก ํ๊น?"
- "์ด๊ฑด ๋ณต์กํ ๋ก์ง์ด๋ useReducer ์จ์ผ ํ ๊น?"
์์ฃผ ๊ฒช๋ ๋ฌธ์
- ์ฑ๊ธํ ์ฑ๋ฅ ์ต์ ํ (memo ๋จ์ฉ)
- Context ๋จ์ฉ (์ ์ญ์ฒ๋ผ ์ฌ์ฉ)
- ๋ก์ปฌ๋ก ์ถฉ๋ถํ ์ํ๋ ์ ์ญํ
๊ธฐ๋ณธ๊ธฐ ๋ง์คํฐ
- ๋ชจ๋ ์ํ ๊ด๋ฆฌ ๋๊ตฌ ์๋ จ (useState, useReducer, Context, Recoil, Zustand ๋ฑ)
- ๋ชฉ์ ์ ๋ง๋ ๋๊ตฌ ์ ํ
- ์ฑ๋ฅ ์ต์ ํ, ๋ฆฌํฉํ ๋ง ์ค๊ณ ๋ฅ๋ ฅ
// ์ํ๋ฅผ slice๋ณ๋ก ์ชผ๊ฐ์ useContext, useMemo, useReducer ์กฐํฉ
// ๋๋ ํ์์ ๋ฐ๋ผ Zustand ๋ฑ์ ์ธ๋ถ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ฉ
// ex: Zustand
import { create } from 'zustand';
const useStore = create((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
function Profile() {
const user = useStore((state) => state.user);
const setUser = useStore((state) => state.setUser);
return (
<div>
<p>{user?.name}</p>
<button onClick={() => setUser({ name: '๋ ์' })}>Set User</button>
</div>
);
}
์์ฌ๊ฒฐ์ ํฌ์ธํธ
- "์ด๋ค ์ ๊ทผ ๋ฐฉ์์ด ์ด ์๋๋ฆฌ์ค์ ๊ฐ์ฅ ์ ์ ํ๊ฐ?"
- "์ง๊ธ memoization์ ์ค์ง์ ์ธ ์ด๋์ด ์๋๊ฐ?"
- "์ ์ง๋ณด์์ฑ๊ณผ ์ฑ๋ฅ ์ฌ์ด์์ ์ต์ ๊ตฌ์กฐ๋?"
์์ฃผ ๊ฒช๋ ๋ฌธ์
- ์ค๋ฒ์์ง๋์ด๋ง
- ์ํ ๊ตฌ์กฐ/๊ฒฐ์ ๋ฌธ์ํ ๋ถ์กฑ
์ด์ ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ๋ฌธ์ ๋ฅผ ๋ด๋ฌ๋ผ๊ณ ๋ถํํ๋ค.

๐งฉ ๋ฌธ์ : ์ ์ ๋์๋ณด๋ ์ฑ
๋๋ ๋์๋ณด๋ ํ๋ฉด์ ๊ฐ๋ฐ ์ค์ด์ผ. ๊ตฌ์กฐ๋ ์๋์ ๊ฐ์:
<Dashboard>
โโโ <UserProfile /> // ์ ์ ์ด๋ฆ, ํ๋กํ ์ด๋ฏธ์ง
โโโ <UserStats /> // ์ ์ ํฌ์ธํธ, ํ๋ ํต๊ณ ๋ฑ
โโโ <UserPosts /> // ์ ์ ๊ฐ ์์ฑํ ๊ฒ์๊ธ ๋ฆฌ์คํธ
โโโ <PostItem /> // ๊ฐ๊ฐ์ ๊ฒ์๊ธ (์ข์์, ๋๊ธ ๋ฑ ํฌํจ)
๐ ์กฐ๊ฑด
- ํ์ด์ง์ ์ง์ ํ๋ฉด /api/me ์์ ์ ์ ์ ๋ณด(id, name, profileImage)๋ฅผ fetchํด์ ์ฌ์ฉํจ.
- /api/posts?userId=xxx ์์ ํด๋น ์ ์ ์ ๊ฒ์๊ธ ๋ชฉ๋ก์ fetch.
- UserProfile, UserStats, UserPosts๋ ๊ฐ๊ฐ ๋ณ๋ ํ์ผ๋ก ๋ถ๋ฆฌ๋ ์ปดํฌ๋ํธ.
- PostItem์์ ์ ์ ์ด๋ฆ์ ํ์ํ ์ผ์ด ์๋ค. (์์ฑ์: ๋ ์ ์ด๋ฐ ๋๋)
โ์ง๋ฌธ: ์ํ๋ฅผ ์ด๋ป๊ฒ ๊ด๋ฆฌํ๋ ๊ฒ ๊ฐ์ฅ ํจ์จ์ ์ผ๊น?
- ์ ์ ์ ๋ณด ์ํ์ ๊ฒ์๊ธ ์ํ๋ฅผ ์ด๋์ ๊ด๋ฆฌํ ๋?
- ์ด๋ค ์ํ ๊ด๋ฆฌ ๋๊ตฌ๋ฅผ ์ธ๋? (useState, Context, ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฑ ์ค ์ ํ)
- ์ด๋ค ๋ฐฉ์์ด ์ฑ๋ฅ, ์ ์ง๋ณด์, ์ฌ์ฌ์ฉ ์ธก๋ฉด์์ ํจ์จ์ ์ด๋ผ๊ณ ์๊ฐํด?
๐ฅ ๋ค๊ฐ ๋ตํ ๋ ์ฐธ๊ณ ํ๋ฉด ์ข์ ํฌ์ธํธ
- "User ์ ๋ณด๋ ์์ฃผ ๋ฐ๋์ง ์์" → ์ด๋ค ์ํ ๊ด๋ฆฌ ์ ๋ต์ด ์ ํฉํ ๊น?
- "PostItem๊น์ง ์ ์ ์ด๋ฆ์ด ํ์ํด" → props๋ก ๋๊ธธ์ง, ์ ์ญ ์ํ๋ก ๋์ง?
- "์ปดํฌ๋ํธ๊ฐ ์๋ก ๋ค๋ฅธ ํ์ผ์ ํฉ์ด์ ธ ์์ด" → ์ ๋ณด ๊ณต์ ๋ ์ด๋ป๊ฒ?
์ด ๋ฌธ์ ๋ฅผ ๋ณด๊ณ ์ ์ด๋ณธ ๋์ ๋ต๋ณ
1. ์ ์ ์ ๋ณด ์ํ์ ๊ฒ์๊ธ ์ํ๋ฅผ ์ด๋์ ๊ด๋ฆฌํ ๋?
๊ฒ์๋ฌผ ๋ฆฌ์คํธ์์ api ํธ์ถ ์ userId ๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์, ์ ์ ์ ๋ณด๋ Dashboard ์ปดํฌ๋ํธ์์ UserContextProvider ๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ฉด ์ข์ ๊ฒ ๊ฐ์. ์ ์ ์ ๋ณด๋ฅผ ๋ฐ์์ค๋ ๊ฑด ์ธ์ฆ์ด ํ์ํด์ ์ฟ ํค์ ๋ด๊ธด ์ธ์ฆ์ ๋ณด๋ฅผ ์ฌ์ฉํ ๊ฒ์ผ๋ก ์์๋๋๋ฐ, ServerSide Rendering ์ ํตํด์ ๋ค์ด์ค๋ ์์ฒญ์ ๋ธ๋ผ์ฐ์ req.headers ๋ฅผ ์ฐธ์กฐํด์ ๋ฏธ๋ฆฌ ์ ์ ์ ๋ณด๋ฅผ ๋ฐ์๋ ์ ์์ผ๋ฉด ๋ค๋ฅธ api ๋ค์ ํธ์ถํ๊ฑฐ๋ ui ๋ฅผ ๊ตฌ์ฑํ๋๋ฐ์ ๊ฑธ๋ฆฌ๋ ์์๊ฐ ์์๊ฑฐ์ผ. ์ ์ ์ ๋ณด๋ ์ ์ ์ ์ธํฐ๋์ ์ ์ํด ์ฝ๊ฒ ๋ณํ๋ ์ ๋ณด๋ ์๋๊ธฐ ๋๋ฌธ์ ์์ ์ปดํฌ๋ํธ์ Context ๋ก ๊ตฌ์ฑํด๋ ํฌ๊ฒ ๋ฌธ์ ๋์ง ์์ ๊ฒ ๊ฐ์.
๊ฒ์๊ธ ์ํ๋ UserPosts ์์ ๋ค๋ฃฐ ๊ฒ ๊ฐ์. ๋ค๋ฅธ ์ปดํฌ๋ํธ์๋ ์๊ด์ด ์์ด์. ๊ฐ๊ฐ์ ๊ฒ์๊ธ์ ๋ํ๋ด๋ PostItem ์ด props ๋ก ๊ฒ์๊ธ ๊ด๋ จ ์ ๋ณด๋ฅผ ๋ด๋ ค๋ฐ๋ ๊ตฌ์กฐ๊ฐ ๋ ๊ฑฐ ๊ฐ์. depth ๊ฐ ํ๋ฒ ํ๊ณ ๋ค์ด๊ฐ๋๊ฑฐ๋ผ props driling๋ ์ฌํ์ง ์์. ๋ค๋ง, PostItem ์์ ์ข์์, ๋๊ธ ์ ๋๋ฌ์ ๊ฒ์๊ธ์ ์ ๋ณด๋ฅผ ์ ๋ฐ์ดํธ ํด์ผ ํ ๋, api ๋ฅผ ์์ฒญํ๊ณ ์๊ฐ์ด ๊ฑธ๋ฆฌ๋๊น ๋๊ด์ ์ ๋ฐ์ดํธ๋ฅผ ํ๋๊ฒ ์ข์ ๊ฒ ๊ฐ์์ ๊ฒ์๊ธ ๊ฐ๊ฐ์์ ๋ด๋ ค๋ฐ์ ์ข์์๋ ๋๊ธ ์ ๋ณด๋ฅผ state ์ initial ๊ฐ์ผ๋ก ๋ฐ์์ ํ์ํ๊ณ , ์ธํฐ๋์ ํ์๋ ์ฆ์ state ๋ฅผ ์ ๋ฐ์ดํธํ๊ณ api ๋ฅผ ์์ฒญํ๋ ์์ผ๋ก ๊ตฌํํ ๊ฑฐ์ผ. api ์ ์๋ต์ด ์ค๋ฉด ์ด state ๋ฅผ ์ ์งํ ์ง / ์ค๋ฅ๋ฅผ ํ์ํ ์ง ๊ฒฐ์ ํด์ผ ํ ๊ฒ ๊ฐ์. ๋ง์ฝ react-query ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฉด, ๋๊ด์ ์ ๋ฐ์ดํธ๋ก ํ์ง ์๊ณ ๊ฒ์๊ธ ๋ฆฌ์คํธ๋ฅผ ๋ถ๋ฌ์ค๋ api ๋ฅผ ํธ์ถํ๋ ๊ณณ์์ ์ฌ์ฉํ๋ key ๋ฅผ ๊ฐ์ง๊ณ ์ข์์๋ ๋๊ธ ์ํ๋ฅผ ํน์ id ์ ๊ฒ์๊ธ์์ ์ ๋ฐ์ดํธ ํ๋ ๋ฐฉ์์ ์ ์ฉํ ์๋ ์์ ๊ฒ ๊ฐ์. ๋ญ๊ฐ ์ข์์ง ๊ณ ๋ฏผ์ด๋ค.
2. ์ด๋ค ์ํ ๊ด๋ฆฌ ๋๊ตฌ๋ฅผ ์ธ๋?
์ ์ ์ ๋ณด๋ Context. ๋๋จธ์ง๋ state ๋ก ๊ฐ๋ฅํ ๊ฒ ๊ฐ์. react-query ๋ฅผ ์ฐ๋ฉด ์ข์๋ฐ ๊ผญ ์ธ ํ์๋ ์์ด.
3. "์ปดํฌ๋ํธ๊ฐ ์๋ก ๋ค๋ฅธ ํ์ผ์ ํฉ์ด์ ธ ์์ด" → ์ ๋ณด ๊ณต์ ๋ ์ด๋ป๊ฒ?
Context ๋ก ์กฐํฉํด์ ๋ฌถ๊ฑฐ๋ global ์ํ๋ฅผ ๋ค๋ฃจ๋ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์์ด.
GPT ์จ์ ํผ๋๋ฐฑ
1. ์ข์๋ ์
1. Context ์์น์ ์ ์ ์ ๋ณด์ ํน์ฑ์ ๋ํ ํ๋จ
- "์ ์ ์ ๋ณด๋ ์์ฃผ ๋ฐ๋์ง ์๊ณ ์์ Context์ ๋ฌ๋ ๋ฌธ์ ๋์ง ์์ ๊ฒ ๊ฐ๋ค."
- ์ ํํด. ์ด๋ฐ ์์ผ๋ก ๋ถ๋ณ์ ๊ฐ๊น์ด ๋ฐ์ดํฐ๋ ์์์ ๋๊ณ Context๋ก ๋ฐฐํฌํ๋ ๊ฒ ์ข๊ณ , ๋ ๋๋ง ๋น์ฉ์ด ํฐ ๋ฐ์ดํฐ๋ ํผํ๋ ๊ฒ ํต์ฌ์ด์ผ.
- SSR์์ ๋ฏธ๋ฆฌ ์ ์ ์ ๋ณด๋ฅผ ๋ฐ์๋๋ ์ ๋ต๊น์ง ์ธ๊ธํ ๊ฑด ์ง์ง ๐
- → getServerSideProps or app/layout.tsx ์์ค์์ ํ๋ฆฌํจ์นญํ๋ ๊ตฌ์กฐ, ์ค๋ฌด์์ ๋งค์ฐ ์์ฃผ ์.
2. ๊ฒ์๊ธ์ ๋ก์ปฌ ์ํ๋ก, props๋ก ์ ๋ฌํ๋ ์ ๋ต
- "UserPosts ๋ด์์ ๊ฒ์๊ธ ์ํ ๊ด๋ฆฌ, PostItem์ props๋ก ๋๊ธฐ๊ธฐ"
- ๋ณต์ก๋ ์ต์ํ์ ์ง์คํ ๊ตฌ์กฐ. depth 1์ด๋ผ props๋ก ์ถฉ๋ถํ๋ค๋ ํ๋จ๋ ํฉ๋ฆฌ์ .
- ๊ตณ์ด Context๋ ๊ธ๋ก๋ฒ ์ํ ์ ์ฐ๊ณ ์ง์ญํ ํ ๊ฒ๋ ์ํ์ด.
3. ๋๊ด์ ์ ๋ฐ์ดํธ ์ ๋ต ๊ณ ๋ฏผ
- "state ์ด๊ธฐ๊ฐ์ผ๋ก ๋ด๋ ค๋ฐ๊ณ , optimistic update ์ ์ฉ → API ์๋ต์ ๋ฐ๋ผ rollback"
- ์ด๊ฑด ํ์คํ ์ํ ๊ด๋ฆฌ ๊ณ ์๋ง์ด ๋ฐ๋ก ๋ ์ฌ๋ฆฌ๋ ๋ฐฉ์์ด์ผ.
- ์ค์ ์์๋ onMutate, onError, onSettled ํจํด (react-query) ์ด๋ ๋ฑ ๋งค์นญ๋จ.
2. ๊ฐ์ ํฌ์ธํธ & ๋์ ์ ์
๐ PostItem ์ ์ํ๋ฅผ ์ด๋์ ๊ด๋ฆฌํ ์ง
- ํ์ฌ ๊ตฌ์กฐ๋ ์ข์ง๋ง, ๋ค์๋ ๊ณ ๋ ค ๊ฐ๋ฅํด:
๋์ 1: PostItem ๋ด๋ถ์์๋ง ์ํ ๋ณ๊ฒฝ ๊ด๋ฆฌ
tsx
๋ณต์ฌํธ์ง
function PostItem({ post }: { post: Post }) {
const [likes, setLikes] = useState(post.likes);
const handleLike = () => {
setLikes((prev) => prev + 1); // optimistic
api.likePost(post.id).catch(() => setLikes((prev) => prev - 1)); // rollback
};
}
- ์ด๊ฑด ์ง๊ธ ๋งํ ๋ฐฉ์์ด๊ณ ์ข์.
๋์ 2: UserPosts์์ ์ ์ฒด posts ์ํ๋ฅผ ๊ฐ์ง ๋ค, PostItem์ ์ฝ๋ฐฑ์ ๋ฐ์ ์ ๋ฐ์ดํธ๋ง ์์ฒญ
tsx
๋ณต์ฌํธ์ง
function UserPosts() {
const [posts, setPosts] = useState<Post[]>(...);
const updatePost = (id, patch) => {
setPosts((prev) =>
prev.map((p) => (p.id === id ? { ...p, ...patch } : p))
);
};
return posts.map((post) => (
<PostItem key={post.id} post={post} onUpdate={updatePost} />
));
}
- ์ฅ์ : ์ค์์์ ๋ชจ๋ ๊ฒ์๊ธ ์ํ๋ฅผ ๊ด๋ฆฌ
- ๋จ์ : ์กฐ๊ธ ๋ ๋ณต์กํด์ง๊ณ , ์ฑ๋ฅ ์ด์ ์์ ์ ์์
3. ๊ฒฐ๋ก
- ์ ์ฒด์ ์ธ ๊ตฌ์กฐ ํ๋จ: ๋งค์ฐ ํ๋ฅญํด. ์ค์ ๊ณผ๋ 90% ์ด์ ์ผ์น.
- ๋ถ๋ณ ๋ฐ์ดํฐ๋ Context, UI์ ๋ฏผ๊ฐํ ์ํธ์์ฉ์ ์ง์ญ ์ํ,
- ์ด๊ฑธ ์์ฐ์ค๋ฝ๊ฒ ๊ตฌ๋ถํด๋๋ค๋ ๊ฑด ์ํ ๊ด๋ฆฌ ๋ง์คํฐ์ ๊ฑฐ์ ๋ค ์๋ค๋ ๋ป์ด์ผ.
- react-query ์ธ๊ธ๋ ์ข์๊ณ , ๊ผญ ์ ์จ๋ ๋๋ค๋ ๊ฒ๋ ์ํฉ ํ๋จ๋ ฅ ์์.
ํผ๋๋ฐฑ๋ ์ ์ฃผ๊ณ ๋ฌธ์ ๋ ์ ๋ด์ค์ ์ฌ๋ฐ์๋ค. ๋ค์์ ๋ ์ด์ด์ ํด๋ด์ผ์ง.
Comment