리액트 Context에 대해 알아보고 다크모드를 만들어보자! (feat: custom hook)

2026. 3. 30. 05:43·web/react

 

리액트를 사용하다 보면 컴포넌트 구조가 깊어지는 경우가 있다. 나는 예전에 리액트를 처음 공부할 때, atomic단위로 컴포넌트를 잘게잘게 쪼개어 개발한 경험이 있는데, 이때 상위 컴포넌트와 하위의 하위의 하위~ 컴포넌트의 깊이가 너무 깊어졌고, 이때 상위 컴포넌트의 데이터를 맨 밑바닥의 컴포넌트에게 전달해야 할 때, 불필요한 데이터를 중간단계에 위치한 컴포넌트들이 전달하는 불필요한 상황을 경험했다. 공부해보니 이를 'Props Drilling' 현상이라고 한다.

 

오늘 설명할 Context API는 이런 번거로움 없이 데이터를 신의탑 웹툰의 '신수'마냥 전역적으로 공유할 수 있게 해주는 도구이다.

(신수는 공기와 비슷한,,, 애니만화 덕후라 죄송합니다;;)

그럼 이것을 어떻게 사용할 수 있을까?

 

 

 


 

Context 사용법

 

1. Context를 생성한다. createContext로 생성하며 이는 데이터를 담을 바구니라고 생각하면 된다.

2. Provider를 설정한다. 데이터를 공유할 범위를 정하고 값을 넣는다.

3. Consumer를 사용한다. useContext로 필요한 곳에서 데이터를 꺼내 쓴다.

 

 

예시 코드

import { createContext, useContext } from 'react';

// 1. 바구니 만들기
const ThemeContext = createContext('light');

function App() {
  return (
    // 2. Provider로 감싸고 값 전달하기
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return <ThemeButton />;
}

function ThemeButton() {
  // 3. useContext 훅으로 데이터 바로 꺼내기
  const theme = useContext(ThemeContext);
  return <button>현재 테마: {theme}</button>;
}

 


 

다크 모드 응용 코드

 

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

// 1. Context 생성 (기본값 설정)
const ThemeContext = createContext();

export default function App() {
  const [theme, setTheme] = useState('light');

  // 테마를 전환하는 함수
  const toggleTheme = () => {
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  };

  // 2. Provider로 상태와 함수를 객체로 전달
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      <div style={{
        backgroundColor: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#000' : '#fff',
        height: '100vh',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center'
      }}>
        <h1>{theme.toUpperCase()} 모드입니다</h1>
        <ThemeToggleButton />
      </div>
    </ThemeContext.Provider>
  );
}

// 3. 자식 컴포넌트 (중간 단계 없이 직접 데이터 사용)
function ThemeToggleButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <button onClick={toggleTheme} style={{ padding: '10px 20px', cursor: 'pointer' }}>
      {theme === 'light' ? '🌙 다크모드로 변경' : '☀️ 라이트모드로 변경'}
    </button>
  );
}

 

value={{ theme, toggleTheme }}로 상태 theme와 함수인 toggleTheme까지도 전달하는 것을 볼 수 있다. 살짝 혁신적이다.

 

아래 자식컴포넌트의 useContext를 사용하여 데이터를 직접 사용한 것을 볼 수 있다!

쓰고싶을때 어디서나 저장해두었던 데이터를 사용하여 좀 더 편리하게 데이터를 다룰 수 있다.

 

 


 

한단계 더 나아가서 구조를 더 깔끔하게 관리하기 위해 파일을 분리해보도록 하겠다.

 

파일 분리 및 커스텀 훅 사용

 

 

ThemeContext.js

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

// 1. Context 생성
const ThemeContext = createContext();

// 2. 전역 상태를 관리할 Provider 컴포넌트
export function ThemeProvider({ children }) {
  const [isDarkMode, setIsDarkMode] = useState(false);

  const toggleTheme = () => setIsDarkMode((prev) => !prev);

  return (
    <ThemeContext.Provider value={{ isDarkMode, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 3. 외부에서 편하게 쓸 수 있는 커스텀 훅
export function useTheme() {
  return useContext(ThemeContext);
}

 

 

 

 

App.js

import { ThemeProvider } from './ThemeContext';
import Home from './Home';

function App() {
  return (
    <ThemeProvider>
      <Home />
    </ThemeProvider>
  );
}

 

 

 

 

Home.js

import { useTheme } from './ThemeContext';

function Home() {
  // useContext(ThemeContext) 대신 직접 만든 훅 사용!
  const { isDarkMode, toggleTheme } = useTheme();

  return (
    <div style={{ background: isDarkMode ? '#222' : '#fff', color: isDarkMode ? '#fff' : '#000' }}>
      <h1>{isDarkMode ? '다크 모드' : '라이트 모드'}</h1>
      <button onClick={toggleTheme}>모드 변경</button>
    </div>
  );
}

 

 

우선 첫번째 바뀐점은 컴포넌트를 3단계로 분리했다는 것이다. 좀더 코드가 간결해지고 유지보수가 편해진다.

두번째로는 ThemeContext.js파일에서 useTheme라는 커스텀훅을 만들어 사용했다. 이를 통하여 consumer를 적용할 컴포넌트에서 (Home.js) useContext(ThemeContext)라고 길게 쓸 필요없이 useTheme라고 만들어놓은 커스텀훅을 사용하면 코드가 더 줄어든다!

또한 커스텀훅을 만들때 if (!context) throw new Error(...)와 같은 예외처리를 넣어서 Provider 밖에서 사용하였을때의 에러를 미리 방지할 수도 있다. 

 

 


마치며

 

이렇게 react의 context API와 Custom Hook를 이용하여 간단한 다크모드를 구현해보았다.

하지만 context API도 완벽하지는 않은것이 Context의 값이 만약 바뀌면 해당 Context를 사용하는 모든 컴포넌트가 리렌더링 된다는 점에서, 입력창의 타이핑 데이터와 같이 너무 자주 바뀌는 컴포넌트에서 효율이 좋진 못하다고 한다.

 

요새는 Redux나 Zustand와 같은 좀더 편리한 전역 상태 관리 라이브러리를 많이 사용하는 추세이긴 하지만, 그 전 단계로 상태관리의 개념을 이해하는데에도 도움이 많이 되었던 것 같다. 

 

오늘 포스팅은 여기서 끝~

저작자표시 (새창열림)

'web > react' 카테고리의 다른 글

리액트에서 왜 불변성을 지켜야 할까?  (0) 2026.03.13
useRef는 어떻게 데이터를 변화시키지 않을 수 있을까?  (0) 2026.02.27
리액트 공식문서 과제 오답노트  (0) 2026.02.19
리액트 공식문서 - 틱택토 게임  (0) 2026.02.11
리액트 기본 - 공식문서 리딩  (0) 2026.02.11
'web/react' 카테고리의 다른 글
  • 리액트에서 왜 불변성을 지켜야 할까?
  • useRef는 어떻게 데이터를 변화시키지 않을 수 있을까?
  • 리액트 공식문서 과제 오답노트
  • 리액트 공식문서 - 틱택토 게임
noeyh
noeyh
기록하고 성장하는 개발자, 최유현 입니다. github : https://github.com/Choiyuhyeon
    티스토리 홈 로그아웃
  • noeyh
    CreateU
    noeyh
  • 전체
    오늘
    어제
  • 글쓰기 관리
    GitHub
    Notion
    • 분류 전체보기 (31)
      • web (22)
        • html (5)
        • css (4)
        • js (5)
        • react (6)
        • next (1)
      • server (0)
      • linux (0)
      • figma (0)
      • ml (0)
      • algorithm (5)
      • git (1)
      • 미니프로젝트 (0)
      • 프로젝트 (0)
      • 정보 (0)
      • 일상 (0)
      • memo (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • hELLO· Designed By정상우.v4.10.6
noeyh
리액트 Context에 대해 알아보고 다크모드를 만들어보자! (feat: custom hook)
상단으로

티스토리툴바