🎯 학습 목표
- React Context를 생성하고 전역으로 상태를 제공하는 법을 이해한다.
- props 없이 하위 컴포넌트에서 상태를 직접 사용하는 구조를 만든다.
useContext()
훅으로 Context 값을 가져오는 방법을 익힌다.
📁 프로젝트 구조 (Context + 컴포넌트 분리 기준)
src/
├── App.jsx # 실제 앱 콘텐츠 (컴포넌트 조합)
├── index.js # ReactDOM 렌더링
│
├── components/ # UI 단위 컴포넌트들
│ ├── TodoForm.jsx
│ └── TodoItem.jsx
│
├── context/ # 상태 및 전역 로직 관리
│ ├── TodoContext.js # Context 생성 + useContext 훅
│ └── TodoProvider.jsx # 상태 정의 + Provider 구성
│
└── styles/ (선택) # SCSS 또는 CSS 모듈 파일들
🔍 각 역할 요약
폴더/파일 |
설명 |
App.jsx |
useTodoContext 로 전역 상태를 사용하고, 컴포넌트 조합 |
TodoForm.jsx |
입력창 + 버튼 UI (입력, 추가 핸들링) |
TodoItem.jsx |
리스트 하나의 UI + 토글/삭제 |
TodoContext.js |
createContext , useTodoContext() 정의 |
TodoProvider.jsx |
상태 정의 + 전역 공급자 역할 |
styles/ |
선택적: 컴포넌트별 .module.css 또는 .scss 저장소 |
📦 단계별 구현
1. TodoContext.js
생성
import { createContext, useContext } from 'react';
export const TodoContext = createContext();
export function useTodoContext() {
return useContext(TodoContext);
}
2. TodoProvider.jsx
– 상태 관리 + Context 공급자
import { useState } from 'react';
import { TodoContext } from './TodoContext';
function TodoProvider({ children }) {
const [todos, setTodos] = useState([]);
const [text, setText] = useState('');
const handleAdd = () => {
const trimmed = text.trim();
if (!trimmed) return;
const newTodo = {
id: Date.now(),
content: trimmed,
isDone: false,
};
setTodos([newTodo, ...todos]);
setText('');
};
const handleDelete = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
const handleToggle = (id) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, isDone: !todo.isDone } : todo
)
);
};
return (
<TodoContext.Provider value={{
todos,
text,
setText,
handleAdd,
handleDelete,
handleToggle,
}}
>
{children}
</TodoContext.Provider>
);
}
export default TodoProvider;
3. App.jsx
– Provider로 감싸기
import TodoProvider from './context/TodoProvider';
import TodoForm from './components/TodoForm';
import TodoItem from './components/TodoItem';
import { useTodoContext } from './context/TodoContext';
function TodoList() {
const { todos, handleToggle, handleDelete } = useTodoContext();
return (
<ul style={{ listStyle: 'none', padding: 0 }}>
{todos.map((todo) => (
<TodoItem key={todo.id}
todo={todo}
onToggle={handleToggle}
onDelete={handleDelete}
/>
))}
</ul>
);
}
function App() {
const { todos } = useTodoContext();
return (
<div style={{ padding: '30px' }}>
<h1>📘 Todo 리스트</h1>
<TodoForm />
<TodoList />
<p>총 {todos.length}개의 할 일이 있습니다.</p>
</div>
);
}
export default function RootApp() {
return (
<TodoProvider>
<App />
</TodoProvider>
);
}
4. TodoForm.jsx
리팩토링
import { useTodoContext } from '../context/TodoContext';
function TodoForm() {
const { text, setText, handleAdd } = useTodoContext();
const handleKeyDown = (e) => {
if (e.key === 'Enter') handleAdd();
};
return (
<div style={{ marginBottom: '20px' }}>
<inputtype="text"
value={text}
onChange={(e) => setText(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="할 일을 입력하세요"
/>
<button onClick={handleAdd}>추가</button>
</div>
);
}
export default TodoForm;
✅ 정리
Before |
After (Context) |
props 전달이 반복됨 |
전역 상태로 간단해짐 |
상위 → 하위 계층 따라 전달 |
하위에서도 직접 가져옴 |
유지보수 어려움 |
구조 명확, 확장 쉬움 |
이제 프로젝트가 커져도 useTodoContext()
만 사용하면 어디서든 상태 접근이 가능합니다.
GitHub - heroyooi/react-app at ch5_4
💬 댓글
※ 로그인 후 댓글을 작성할 수 있습니다.