React๋ ๋ํ์ ์ธ SPA(Single Page Application) ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค.
SPA๋ ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋ ์ด๋ฅผ ๊ฐ์งํด ํ์ํ ๋ถ๋ถ๋ง ๋ฆฌ๋ ๋๋ง ํ๋ ๋ฐฉ์์ผ๋ก ๋์ํ๊ธฐ ๋๋ฌธ์, ์ํ ๊ด๋ฆฌ๊ฐ ๋งค์ฐ ์ค์ํ๋ค.
React์์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ์์ผ๋ก๋ ํฌ๊ฒ ๋ ๊ฐ์ง๋ก ๋๋ ์ ์๋ค.
๋ด๋ถ | ์ธ๋ถ |
useState ContextAPI |
Redux, Redux Toolkit Recoil, MobX, Zustand, Jotai ... |
์ ์ญ ์ํ๊ด๋ฆฌ์ ์ฅ์
Props Drilling
์์ ์ปดํฌ๋ํธ์์ ํ์ ์ปดํฌ๋ํธ์๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ ํ ๋, ์ง์ ์ ์ผ๋ก ์ฌ์ฉํ์ง ์๋ ์ค๊ฐ ์ปดํฌ๋ํธ๋ค๊น์ง props๋ฅผ ๊ณ์ ๋๊ฒจ์ค์ผ ํ๋ ํ์์ ์๋ฏธํ๋ค.
์ด์ ์ ํ๋ todo list ๋ง๋ค๊ธฐ์๋ props drilling์ ์ฐพ์๋ณผ ์ ์๋ค. (๊ธธ์ด๊ฐ ๊ธธ์ด์ ธ์ ์ํ ์ธ ๋ด์ฉ์ ์๋ตํ๋ค..)
function App() {
const [isLoading, data] = useFetch('http://localhost:3000/todo');
const [todo, setTodo] = useState([]);
useEffect(() => {
if (data) setTodo(data);
}, [isLoading]);
return (
<>
<TodoInput setTodo={setTodo} />
<TodoList todo={todo} setTodo={setTodo} />
</>
);
}
const TodoInput = ({ setTodo }) => {
const inputRef = useRef(null);
const addTodo = () => {
const newTodo = {
// ์๋ต
};
return (
<div className="todo-input">
<input ref={inputRef} />
<button onClick={addTodo}>์ถ๊ฐ</button>
</div>
);
};
const TodoList = ({ todo, setTodo }) => {
return (
<ul className="todo-list">
{todo.map((el) => (
<Todo key={el.id} todo={el} setTodo={setTodo} />
))}
</ul>
);
};
// UPDATE, DELETE
const Todo = ({ todo, setTodo }) => {
const [isEdit, setIsEdit] = useState(false);
const [editValue, setEditValue] = useState(todo.content);
// ๋๋ฌด ๊ธธ์ด์ ์๋ต..............
return (
<li>
{isEdit ? (
<>
<input value={editValue}
onChange={(e) => setEditValue(e.target.value)} />
<div className="btn-container">
<button onClick={updateTodo}>์ ์ฅ</button>
<button onClick={() => setIsEdit(false)}>์ทจ์</button>
</div>
</> ) : (
<>
<div>{todo.content}</div>
<div className="btn-container">
<button onClick={editHandler}>์์ </button>
<button onClick={deleteTodo}>์ญ์ </button>
</div>
</>
)}
</li>
);
};
์ฝ๋๋ฅผ ๋ณด๋ฉด const [todo, setTodo] = useState([])๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ๊ณณ์ด Todo์ TodoInput ์ปดํฌ๋ํธ์ธ๋ฐ, ๋ ์ปดํฌ๋ํธ ๋ชจ๋์๊ฒ ์ํ๋ฅผ ๋๊ฒจ์ฃผ๊ธฐ ์์์๋ ์ต์์ ์ปดํฌ๋ํธ์ธ App์์ ์ํ๋ฅผ ์์ฑํด props๋ฅผ ๋๊ฒจ์ค ์ ๋ฐ์ ์๋ค.
์ด๋ TodoList ์ปดํฌ๋ํธ์์๋ ์ฌ์ฉํ์ง ์๊ณ ์์ด๋ Todo ์ปดํฌ๋ํธ์์ ์ฌ์ฉํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ค์ง ์ ๋ฌ ๋ชฉ์ ์ผ๋ก props๋ฅผ ๋๊ฒจ์ฃผ๊ณ ์๋๋ฐ, ๋๋ฆด์ง ํ๋ ๊ฒ์ฒ๋ผ Props๋ฅผ ๊ณ์ ๋ด๋ ค์ค๋ค๊ณ ํ์ฌ Props Drilling ํ์์ด๋ผ๊ณ ํ๋ค.
์ง๊ธ์ ๊ฒฝ์ฐ๋ ์ปดํฌ๋ํธ๊ฐ 3๋จ๊ณ ๊น์ด์ ๊ณ์ธต์ด๊ณ , ์ปดํฌ๋ํธ์ ๊ฐ์๋ ์ ์ด์ ์์ง๊น์ง ํฌ๊ฒ ๋ฌธ์ ๊ฐ ์์ง๋ง ๋ง์ฝ ๊ทธ ๊น์ด๊ฐ ๋ ๊น์ด์ง๊ณ ์ปดํฌ๋ํธ์ ๊ฐ์๊ฐ ๋์ด๋๊ณ ์ํ๋ค์ด ๋์ด๋๋ค๋ฉด ์ฌ๋ฌ ๋ฌธ์ ์ ์ด ๋ฐ์ํ๋ค.
- ์ปดํฌ๋ํธ์ ๊ตฌ์กฐ๊ฐ ๋ ๋ณต์กํด์ง
- ์ฝ๋์ ๊ฐ๋ ์ฑ์ด ๋งค์ฐ ๋จ์ด์ง๊ณ ์ ์ง ๋ณด์๊ฐ ์ด๋ ค์์ง
- props๋ฅผ ์ ๋ฌํ๋ ๊ณผ์ ์์ ์ค๊ฐ์ ์ ๋ฌ ๋ชฉ์ ์ผ๋ก๋ง ๊ด์ฌํ๊ฒ ๋ ์ปดํฌ๋ํธ๋ค๊น์ง ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ => ์ฑ๋ฅ ์ ํ
useState๋ก๋ง ์ํ๋ฅผ ๊ด๋ฆฌํ๋ฉด ์๋ฌด๋ฆฌ ์ฒด๊ณ์ ์ธ ์ค๊ณ์ฌ๋ ๊ท๋ชจ๊ฐ ์ปค์ง๋ฉด ์ด๋ฌํ ๋ถ์์ฉ์ด ๋ฐ์ํ ์ ์๋ค.
๋๋ฌธ์ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์ํ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ์ ์ญ ์ํ ๊ด๋ฆฌ๋ฅผ ํ ์ ์๊ฒ ํด์ฃผ๋ ๋๊ตฌ๊ฐ ํ์ํ๋ค
์ ์ญ ์ํ ๊ด๋ฆฌ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ฉด App์์ ์ง์ ์ํ๋ฅผ ๋ง๋ค์ง ์์๋ ๋๋ฉฐ, ์ค๊ฐ์ ๊ณ ํต๋ฐ๋ TodoList๋ props ์ ๋ฌ์ ํ์ง ์์๋ ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ๋๋ฌธ์ ๊ท๋ชจ๊ฐ ํฐ ํ๋ก์ ํธ์ผ์๋ก ์ด ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
Context API
๋ฆฌ์กํธ์ ๋ด์ฅ๋ API๋ก ์ ์ญ์ ์ผ๋ก ์ํ๋ฅผ ๊ณต์ ํ ๋ ์ฌ์ฉ๋๋ค.
๊ณต์๋ฌธ์ : https://react.dev/learn/passing-data-deeply-with-context
Context API๋ฅผ ์ฌ์ฉํ๋ ์ด์
- props drilling ๋ฌธ์ ํด๊ฒฐ (props๋ฅผ ์ผ์ผ์ด ์ฌ์ฉํ์ง ์์๋ ๊ฐ ๊ณต์ ๊ฐ ๊ฐ๋ฅํด์ง)
- ์ํ๋ฅผ ํ์ํ ๊ณณ์์ ๋ฐ๋ก ๊บผ๋ด ์ฌ์ฉ => ์ ์ง ๋ณด์๊ฐ ์ฌ์์ง๋ฉฐ ํ๋ก์ ํธ๊ฐ ์ปค์ง ์๋ก ์ฝ๋๊ฐ ๊ฐ๊ฒฐํด์ง
=> context๋ props๋ฅผ ์ ๋ฌํ์ง ์๊ณ ๋ ํ์ํ ์ปดํฌ๋ํธ์์ ๋ฐ๋ก ๊บผ๋ด ์ฌ์ฉํ ์ ์์ด ์ฌ์ฉ๋๋ค!
์ฌ์ฉ ๋ฐฉ๋ฒ
์๋ ์ธ๊ฐ๋ React์์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต๋๋ ๋ด์ฅ ๊ฐ์ฒด/ํจ์๋ค์ด๋ค.
- createContext() - ์ ์ญ ์ํ ์์ฑ, React ๋ด์ฅ ํจ์
- Provider - ์ ์ญ ์ํ ์ฐ๊ฒฐ, createContext๋ก ์์ฑ๋ ๊ฐ์ฒด์ ์์ฑ
- useContext() - context ๊ฐ์ฒด์์ ์ ์ญ ์ํ๋ฅผ ๊บผ๋ด์ด, React ๋ด์ฅ Hook
์์
ํ๋ฒํ ์นด์ดํฐ ๊ธฐ๋ฅ์ ์ฝ๋๋ค.
์๋๋ ๊ทธ๋ฅ app.jsx์์ useState ๋ถ๋ฌ์์ ์ฌ์ฉํ๋ ๋ฐฉ์์ด์๋๋ฐ
context API๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์ผ๋จ ๋ฐ๋ก context ํ์ผ์ ํ๋ ๋ง๋ค์ด์ค๋ค.
(๋ฌด์กฐ๊ฑด ์ด๋ ๊ฒ ๋ง๋ค์ด์ผํ๋ ๊ฑด ์๋์ง๋ง ๊ถ์ฅ๋๋ ๋ฐฉ์์ด๋ผ๊ณ ํ๋ค.
src/
โโโ context/
โ โโโ counterContext.jsx
counterContext.jsx
import { useContext, createContext, useState } from 'react';
// ์ ์ญ context ๊ฐ์ฒด ์์ฑ
const CounterContext = createContext();
// Provider ์ปดํฌ๋ํธ
export function CounterProvider({ children }) {
// ์ํ ์์ฑ
const [Counter, setCounter] = useState(0);
return (
// context ๊ฐ์ฒด์๊ฒ counter์ setCounter๋ฅผ ์ ๋ฌ
<counterContext.Provider value={[Counter, setCounter]}>
// ์ด Provider ์์ ๊ฐ์ธ์ง ์ปดํฌ๋ํธ๋ฅผ ์๋ฏธํจ
{children}
</counterContext.Provider>
);
}
// useCounter() : ์ปค์คํ
ํ
// ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์ฝ๊ฒ useContext๋ฅผ ์ธ ์ ์๊ฒ ํจ
export function useCounter() {
return useContext(CounterContext);
}
- createContext()๋ก ์ context ๊ฐ์ฒด CounterContext ์์ฑ
- CounterProvider : Context์ Provider ์ญํ ์ ํ๋ ์ปดํฌ๋ํธ
- CounterContext.Provider : context๋ฅผ ์ ๊ณตํ๊ณ , value๋ก counter, setCounter๋ฅผ ๋ฃ์
- CounterContext๋ก {children}์ ๊ฐ์ธ ์์ ์ปดํฌ๋ํธ๋ค์ด ๋ชจ๋ context ๊ฐ์ฒด์ ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก ํจ
- useCounter๋ผ๋ ์ปค์คํ
ํ
์ ์์ฑ
- useContext(counterContext)๋ก [counter, setCounter]๋ฅผ ๋ฐ๋ก ์ฌ์ฉํ๊ฒ ํจ
Main.jsx
createRoot(document.getElementById('root')).render(
<CounterProvider>
<App />
</CounterProvider>,
);
App์ CounterProvider๋ก ๊ฐ์ธ App ๋ด๋ถ์์ ์ ์ญ ์ํ๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ํจ (App์ด children ์ญํ )
์ด ๋ CounterProvider๋ ๋น์ฐํ ์์์ import ํด์ผ๋จ..
App.jsx
import './App.css';
import { useCounter } from './context/counterContext';
function App() {
const [counter, setCounter] = useCounter();
return (
<>
<div>counter:{counter}</div>
<button
onClick={() => {
setCounter((prev) => prev + 1);
}}>
+
</button>
<button
onClick={() => {
setCounter((prev) => prev - 1);
}}>
-
</button>
</>
);
}
export default App;
- [counter, setCounter] =useCounter => ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌ๋๊ณ ์๋ ์ํ
- useState๊ฐ ์๋ ์ปค์คํ ํ useCounter๋ฅผ ๋ถ๋ฌ์ ์ด๋ฏธ ๋ง๋ค์ด์ ธ์๋ ์ํ๋ฅผ ๊ณต์ ํ๊ณ ์์
'react' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
React / ์ต์ ํ ํจ์ (0) | 2025.04.23 |
---|---|
React / ์ ์ญ ์ํ ๊ด๋ฆฌ (2) - Redux, Redux Toolkit (0) | 2025.04.22 |
React / openweatherapi ์ฌ์ฉํ๊ธฐ (0) | 2025.04.17 |
React / Todo-List ๋ง๋ค๊ธฐ (2) (0) | 2025.04.17 |
React / ์คํ์ผ๋ง (3) - tailwindcss (0) | 2025.04.17 |