🐨CoalaCoding
DocsExamplesTry itBoardB반
🐨CoalaCoding

개발자를 위한 한국어 웹 기술 문서

문서

  • JavaScript
  • Web Publishing
  • React
  • Python

커뮤니티

  • 게시판
  • 예제 모음
  • Try it 에디터

기타

  • GitHub
  • 관리자
© 2026 CoalaCoding. All rights reserved.
  • 01_hook
  • 02_시작
  • 03_react-vite-설치
  • 04_노코드리액트
  • 05_csrssrreact-렌더링-참조글
  • 06_컴포넌트
  • 07_시작하기
  • 08_2-scaffolding-react-project
  • 09_props
  • 10_3-웹문서-작성하기
  • 11_컴포넌트
  • 12_map과컴포넌트
  • 13_event
  • 14_4-리액트로-변경하기
  • 15_컴포넌트-데이터관리
  • 16_composition
  • 17_앱제작-준비
  • 21_환경설정
  • 24_컴포넌트-전략
  • 25_리액트-네이버-로그인
  • 26_리액트교안
  • 0_React 학습 전 필수 JavaScript 문법
  • 1_React 개발 환경 이해
  • 2_React 소스 코드 수정, 배포 및 렌더링
  • 3_JSX 문법과 컴포넌트
  • 4_Props (속성)
  • 5_이벤트 처리
  • 6_Hook
  • 7_useContext로 전역 상태 관리
  • 8. React Router
  • 9_CSS 리액트에 넣는 5가지 방법
  1. 홈
  2. 문서
  3. React
  4. React 기초
  5. 7_useContext로 전역 상태 관리

7_useContext로 전역 상태 관리

코드 블록의 Try it Yourself 버튼으로 직접 실행할 수 있다.

구문

1. useContext로 전역 상태 관리

1.1. Context가 필요한 이유

리액트에서 데이터는 보통 부모 → 자식 방향으로 Props를 통해 전달한다. 그런데 컴포넌트가 깊게 중첩되어 있으면, 중간에 있는 컴포넌트들이 직접 쓰지도 않는 데이터를 계속 받아서 아래로 넘겨줘야 한다. 이 문제를 Prop Drilling(프롭 드릴링) 이라고 한다.

Context를 사용하면 중간 컴포넌트를 거치지 않고, 필요한 컴포넌트가 직접 데이터를 꺼내 쓸 수 있다.

1.1.1. Props 전달의 문제점 (Prop Drilling)

user 데이터가 App에 있는데, 실제로 사용하는 곳은 맨 아래의 Profile이다. 중간의 Page, Header는 데이터를 쓰지 않지만 그냥 아래로 전달하는 역할만 한다.

// user를 Profile까지 보내려면 모든 중간 컴포넌트를 거쳐야 함
function App() {
  const [user, setUser] = useState('김철수');
  return <Page user={user} />;         // Page에 넘김
}

function Page({ user }) {
  return <Header user={user} />;       // Header에 넘김 (Page는 user를 쓰지 않음)
}

function Header({ user }) {
  return <Profile user={user} />;      // Profile에 넘김 (Header도 user를 쓰지 않음)
}

function Profile({ user }) {
  return <div>{user}</div>;            // 여기서만 실제로 사용
}

컴포넌트가 10단계라면 10번을 전달해야 한다. Context를 쓰면 이 문제가 해결된다.

1.2. Context 기본 사용법

Context는 3단계로 사용한다: ① 만들기 → ② 제공하기 → ③ 꺼내 쓰기

1.2.1. 1단계: Context 생성

createContext()로 데이터를 담을 그릇(Context 객체) 을 만든다. 이름은 자유롭게 붙여도 되지만 보통 무엇Context 형태로 짓는다.

import { createContext } from 'react';

const UserContext = createContext();
// UserContext라는 빈 그릇을 만들었다.
// 나중에 이 안에 데이터를 넣어서 아래 컴포넌트들에게 전달할 것이다.

1.2.2. 2단계: Provider로 값 제공

Context.Provider로 공유할 범위를 감싸고, value에 전달할 데이터를 넣는다. Provider 안에 있는 모든 컴포넌트는 이 값에 접근할 수 있다.

function App() {
  const [user, setUser] = useState('김철수');

  return (
    // UserContext.Provider로 감싸면 안에 있는 모든 컴포넌트가 user를 쓸 수 있다
    <UserContext.Provider value={user}>
      <Page />
      {/* Page, Page 안의 Header, Header 안의 Profile 모두 접근 가능 */}
    </UserContext.Provider>
  );
}

1.2.3. 3단계: useContext로 값 사용

useContext(Context이름)을 호출하면 Provider가 제공한 값을 바로 꺼낼 수 있다. 몇 단계를 거치든 상관없이 직접 가져온다.

import { useContext } from 'react';

function Profile() {
  // UserContext에서 값을 꺼낸다. Props 없이도 바로 사용 가능!
  const user = useContext(UserContext);

  return <div>사용자: {user}</div>;
}

1.3. 다크모드 구현 예제

Context를 실제로 활용하는 대표적인 사례다. 다크모드 상태를 Context에 넣어두면, 화면 어디서든 버튼 하나로 테마를 바꿀 수 있다.

전체 구조를 먼저 보면:

App
└── ThemeProvider  ← isDark 상태와 toggleTheme 함수를 Context에 넣음
    └── Layout     ← isDark로 배경색 변경
        ├── ToggleButton  ← toggleTheme으로 모드 전환
        └── Content       ← isDark로 텍스트 표시

1단계: Context 생성

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();
// 테마 정보(isDark, toggleTheme)를 담을 그릇을 만든다

2단계: Provider 컴포넌트 만들기

상태 관리와 Provider 역할을 하나의 컴포넌트로 묶는다. children은 이 컴포넌트 안에 들어올 자식 컴포넌트들을 의미한다.

function ThemeProvider({ children }) {
  // isDark: 현재 다크모드인지 여부 (기본값: false = 라이트모드)
  const [isDark, setIsDark] = useState(false);

  // 버튼을 누를 때마다 true ↔ false 를 번갈아 바꾸는 함수
  // prev는 현재 값이다. !prev 는 반대값을 의미한다
  const toggleTheme = () => setIsDark(prev => !prev);

  return (
    // isDark(현재 모드)와 toggleTheme(변경 함수) 두 가지를 함께 전달
    <ThemeContext.Provider value={{ isDark, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

3단계: isDark로 스타일 변경하기

isDark 값에 따라 배경색과 글자색을 다르게 지정한다. 삼항연산자 조건 ? 참일때값 : 거짓일때값 을 사용한다.

function Layout() {
  // ThemeContext에서 isDark만 꺼낸다
  const { isDark } = useContext(ThemeContext);

  const style = {
    background: isDark ? '#1a1a1a' : '#ffffff',  // 다크: 어두운 회색 / 라이트: 흰색
    color:      isDark ? '#ffffff' : '#000000',  // 다크: 흰 글자  / 라이트: 검정 글자
    minHeight: '100vh',
    padding: '20px',
  };

  return (
    <div style={style}>
      <ToggleButton />
      <Content />
    </div>
  );
}

4단계: 토글 버튼

toggleTheme을 꺼내서 버튼 클릭 시 실행한다. 현재 모드에 따라 버튼 텍스트도 바꾼다.

function ToggleButton() {
  // isDark와 toggleTheme 둘 다 꺼낸다
  const { isDark, toggleTheme } = useContext(ThemeContext);

  return (
    // 버튼을 누르면 toggleTheme 실행 → isDark가 반전됨
    <button onClick={toggleTheme}>
      {isDark ? '☀️ 라이트 모드' : '🌙 다크 모드'}
      {/* 다크모드일 때: 라이트 모드로 바꾸는 버튼 표시 */}
      {/* 라이트모드일 때: 다크 모드로 바꾸는 버튼 표시 */}
    </button>
  );
}

5단계: 콘텐츠

function Content() {
  const { isDark } = useContext(ThemeContext);

  // 현재 어떤 모드인지 텍스트로 표시
  return <p>현재 모드: {isDark ? '다크' : '라이트'}</p>;
}

6단계: 앱 루트에서 Provider로 감싸기

ThemeProvider로 감싸야 그 안의 모든 컴포넌트가 Context를 쓸 수 있다. 감싸지 않으면 useContext를 호출해도 값이 없어서 오류가 난다.

function App() {
  return (
    <ThemeProvider>   {/* 이 안에 있는 컴포넌트는 모두 ThemeContext에 접근 가능 */}
      <Layout />
    </ThemeProvider>
  );
}

핵심 포인트:

  • ThemeProvider가 isDark와 toggleTheme을 Context에 넣어 전달
  • Layout, ToggleButton, Content 모두 Props 없이 바로 꺼내 사용
  • toggleTheme은 prev => !prev 패턴으로 이전 값을 기준으로 토글

1.4. 완료 (유저 예제)

Context로 유저 이름을 전역 관리하는 예제다. setUser도 함께 전달하면 하위 컴포넌트에서 값을 변경할 수도 있다.

import { createContext, useContext, useState } from 'react';

// Context 생성
const UserContext = createContext();

function App() {
  const [user, setUser] = useState('김철수');

  return (
    // user(현재 이름)와 setUser(변경 함수)를 함께 전달
    <UserContext.Provider value={{ user, setUser }}>
      <Page />
    </UserContext.Provider>
  );
}

// Page는 user를 직접 쓰지 않는다. 그냥 Profile을 렌더링할 뿐
function Page() {
  return (
    <div>
      <h1>페이지</h1>
      <Profile />
    </div>
  );
}

function Profile() {
  // Props를 받지 않았는데도 user와 setUser를 바로 사용할 수 있다
  const { user, setUser } = useContext(UserContext);

  return (
    <div>
      <p>사용자: {user}</p>
      {/* 버튼 클릭 시 setUser로 이름을 변경 → 화면이 자동으로 다시 그려진다 */}
      <button onClick={() => setUser('이영희')}>이름 변경</button>
    </div>
  );
}

목차

  • 구문