[React] Context API 사용해보기 (다크모드 예제)

신효경·2025년 7월 29일
0

Context API란?

📌리액트에서 컴포넌트 간 전역 상태를 공유할 수 있게 해주는 기능
📌부모 → 자식 → 손자 → 증손자… 이렇게 여러 단계로 props를 전달하는 게 번거로울 때, Context를 사용하면 중간 단계 없이 바로 필요한 컴포넌트에 값을 전달 가능
📌사용 예시

  • 로그인 상태
  • 다크 모드 / 테마 정보
  • 다국어(언어) 설정
  • 전역 alert/토스트
  • 전역 설정값 등

이처럼 여러 컴포넌트에서 공통으로 써야 하는 값들을 Context로 관리


📁 프로젝트 구성

src/
├── index.js
├── App.js
├── MainApp.js
├── components/
│   ├── MyThemeProvider.js
│   ├── ThemeContext.js
├── HookThemeButton.js

1️⃣ index.js: 앱의 진입점에서 Provider로 컴포넌트를 감싼다

const root = ReactDOM.createRoot(document.getElementById('root'));

🖊️HTML에서 <div id="root"> 를 찾아서 React 앱의 시작점으로 연결하고 이후 여기에 컴포넌트들을 렌더링할 수 있도록 "루트"를 만드는 코드


2️⃣ ThemeContext.js

📍 ThemeContext는 전역 상태를 공유하기 위한 통로

  • 이 코드는 전역에서 사용할 수 있는 ThemeContext 객체를 생성
  • 기본값은 mode: 'light', toggleMode: 빈 함수

3️⃣ MyThemeProvider.js

MyThemeProvider에 아래와 같은 return이 일어나는데

컴포넌트출처역할
ThemeContext직접 만든 Context (React)mode, toggleMode 전역 상태를 관리, toggleMode()는 빈 함수지만 실제 값은 MyThemeProvider에서 넣음
ThemeProviderMUI(Material UI)에서 제공실제 테마 스타일을 적용하는 기능, 전체 앱을 감싸는 전역 테마 설정자
MyThemeProviderThemeContext.Provider와 ThemeProvider를 묶는 종합 관리자 역할
CssBaselineMUI 제공브라우저 기본 스타일 초기화, 브라우저 기본 스타일을 리셋해서 일관된 스타일링이 가능
childrenReact props이 컴포넌트 안에 감싼 자식 요소들 (앱 전체 등), provider로 감싼 자식 컴포넌트들이 실제 렌더링됨

이렇게 큰 그림을 먼저 그리면 조금 더 쉽게 이해할 수 있다

📍 MyThemeProvider는 그 통로를 통해 데이터를 실제로 공급하는 역할

export default function MyThemeProvider({children}) {
  • 전역에서 테마 값을 공급하는 최상위 Provider

  const [mode, setMode] = useState('light');
  • mode 상태는 'light' 또는 'dark' 값을 가짐
  • 기본값은 'light'로 이 상태에 따라 앱의 전체 테마 모드가 바뀌게 됨

  const themeConfig = {
    mode,
    toggleMode: () => {
      setMode(prev => prev === 'light' ? 'dark' : 'light');
    }
  };
  • Context로 전달할 값을 구성
  • mode: 현재 테마 모드 (위의 코드에서 useState를 이용해서 light를 기본값으로 설정함)
  • toggleMode: 라이트/다크 모드를 토글하는 함수
  • setMode: mode의 현재 상태값을 인자로 전달받은 함수를 호출해서, 새로운 상태값으로 업데이트
  • prev: setMode에 의해 자동으로 전달되는 현재 상태값
  • 이 객체는 나중에 ThemeContext.Provider를 통해 어느 곳에서든 접근 가능

  const theme = createTheme({
    mode,
  • MUI의 createTheme() 함수로 커스텀 테마 객체를 만듦
  • mode는 그냥 넘기지만 사실 MUI는 palette.mode를 기준으로 동작

    palette:{
      mode,
  • MUI 테마에서 핵심으로 여기에 따라 MUI의 Button, Card, Typography 등 기본 컴포넌트의 색상이 달라짐

      ...(mode === 'light'
        ? {
            primary: amber,
          }
        : {
            primary: {
              main: grey[500],
              contrastText: '#fff',
            },
            background: {
              default: grey[900],
              paper: grey[900],
            }
          })
    }
  • 삼항 연산자로 테마를 다르게 설정
  • primary, background 같은 키워드는 MUI(Material UI)에서 정해 놓은 공식적인 테마 키(key)

✅ primary란?

  • 버튼, 링크 등 주요 인터페이스 색상에 사용되는 "주 색상"
  • MUI 컴포넌트에서는 이걸 자동으로 참조해서 색을 입힘
  • 예를 들어, MUI의 <Button color="primary" /> 는 theme.palette.primary.main 색을 사용

✅ background란?

  • 앱의 배경을 설정하는 데 사용하는 키
  • 내부적으로 default, paper 같은 세부 속성이 있음
background: {
  default: '#121212', // 전체 배경색
  paper: '#1d1d1d',    // 카드나 박스 같은 컴포넌트의 배경색
}

✅ 왜 저런 이름들을 쓰는가?

  • createTheme()는 단순한 객체가 아니라, MUI에서 제공하는 공식 설계에 맞춰서 theme을 만들기 위한 함수
  • 즉, 우리가 정한 이름이 아니라 MUI가 약속한 키 이름들을 써야 자동 적용됨
키 이름역할
primary주요 색상 (버튼, 토글 등)
secondary보조 색상
error오류 메시지나 경고 색상
background.default전체 배경색
background.paper카드, 다이얼로그 등 박스 배경

4️⃣ HookThemeButton.js

import { useContext } from "react";
...
  const {mode, toggleMode} = useContext(ThemeContext);

  • useContext는 React의 훅(hook) 중 하나로, Context로부터 값을 꺼내 쓸 수 있게 해줌

  • 여기서는 ThemeContext에서 현재 테마 값 (mode)과 테마 전환 함수 (toggleMode)를 가져오는 데 사용


import { Button } from "@mui/material";
  • MUI에서 제공하는 스타일이 적용된 버튼 컴포넌트
  • 일반 <button> 보다 디자인과 기능이 풍부하며, MUI 테마 (theme.palette.primary)와 연동됨

import ThemeContext from "./components/ThemeContext";
  • MyThemeProvider 컴포넌트에서 값을 제공하는 Context가져옴

export default function HookThemeButton(){
  • MyThemeProvider의 자식으로 쓰이는 HookThemeButton라는 이름의 React 함수형 컴포넌트

  return (
    <Button onClick={toggleMode} variant="contained">
      Mode {mode}
    </Button>
  );
  • <Button variant="contained">이 내부적으로 테마의 primary.main 값을 참조해서 배경색을 정함
    • MUI(Material UI)의 디자인 시스템이 primary를 기본 테마 색상으로 정해두었기 때문
    • amber 팔레트를 지정한 경우 amber[500] (즉, #ffc107)이 기본값이기 때문에 주황색 빛이 도는 버튼이 생성됨

    📌 요약

    상황버튼 배경색
    custom theme에서 primary 설정함내가 설정한 primary 색상
    primary 설정 안 함→ MUI 기본 색상 (파랑 계열)
    variant="contained"만 있음→ primary 기본값 사용
    variant="contained" + color="secondary"→ secondary 색상 적용

🔁 전체 실행 흐름 요약

1. index.js

MyThemeProvider로 감싸고 HookThemeButton을 자식으로 전달

2. MyThemeProvider

내부적으로 ThemeContext.Provider로 전역 상태를 감싸고
MUI 테마(ThemeProvider)도 적용

3. HookThemeButton

useContext()로 테마 값과 토글 함수 받아와서 버튼 UI로 렌더링하고 상태 변경 처리

✅ 결과적으로 얻는 장점

  • 전역 상태 관리
    • 여러 컴포넌트에서 공통 테마 값을 쉽게 공유
  • 관심사 분리
    • index.js는 진입점
    • Provider는 상태 관리
  • 재사용성
    • MyThemeProvider는 다른 프로젝트에도 바로 적용 가능
    • MUI와 Context 통합 디자인 시스템과 상태 관리를 한 번에 구현

구현 후 실행 화면

0개의 댓글