Syw.Frontend

✅ React 기초 강좌

5단계. 간단한 프로젝트 실습

5-4. Context API로 상태 전역 관리하기

🎯 학습 목표

  • 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

💬 댓글

    ※ 로그인 후 댓글을 작성할 수 있습니다.