FrontEnd/React.js

[React.js] useContext / 테마색 바꾸기

SangHoonE 2025. 3. 11. 09:00

useContext 를 사용하여 drilling 이 없도록.
Children 컴포넌트에서 제약사항 없이 부모가 내려준 props 를 사용할 수 있도록 하겠습니다.

Result

1. useContext

useContextstore 와 기능이 매우 흡사함.
다만, 작은 규모의 프로젝트 혹은 개인프로젝트에서만 사용하는것을 권장한다.

그 외에는 store 를 사용하자 (redux, zustand ... etc)

그 이유는 아래 내용을 보면 알 수 있다. 가봅시다.

 

2. createContext & Provider

// 1. 테마 컨텍스트 생성
const ThemeContext = createContext();

//TypeScript
const ThemeContext = React.createContext<ThemeContextType | undefined>(
    undefined
);

const UseContext = () => {
    const [theme, setTheme] = useState("light");
    const toggleTheme = () => {
        setTheme(theme === "light" ? "dark" : "light");
    };
    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            <ThemeComponent_1 />
        </ThemeContext.Provider>
    );
};

처음에 컴포넌트 바깥에서 Context를 생성 (createContext) 해주었다.

그 다음에는 

<ThemeContext.Provider value={{state, action}}>
	<ThemeComponent_1 />
</ThemeContext.Provider>

이렇게 Provider로 감싸서 props로 내려주었다. (여기까지는 일반 Props 와 뭐가다른데? 할 수 있다.)

차이점은 2번째 Drilling 에서부터 나타난다.

 

3. Childrens.

ThemeComponent_1 컴포넌트를 아래와 같이 선언하고 ThemeComponent_2에서 context로 내려준 props 들을 사용할 수 있다.

UseContext > ThemeComponent_1 > ThemeComponent_2 가 되는 꼴이다

//drilling 용 컴포넌트
const ThemeComponent_1 = () => {
    return <ThemeComponent_2 />;
};

//실제 context를 사용하는 컴포넌트
const ThemeComponent_2 = () => {
    const { theme, toggleTheme } = useContext(ThemeContext);

    return (
        <div
            className={`w-full mx-auto px-4 py-8 min-h-screen transition-all duration-300 ${
                theme === "light" ? "bg-gray-50" : "bg-gray-900 text-white"
            }`}
        >
            <h1>현재 테마: {theme}</h1>
            <button onClick={toggleTheme}>테마 변경</button>
        </div>
    );
};

[테마 변경] 버튼을 클릭 하면 light <> dark 로 전환이 되면서 배경색이 변경되도록 수정하였다.

전역적으로 사용하려면  App.jsx 혹은 Index.jsx 에 선언해놓고 Header.jsx 에서 사용하면 될 것이다.

 

Epiloge

확실히 useContext 를 통하여 사용할 수 있는 부분이 눈에 보인다. (가령 테마라던가...테마라던가...테마라던가.)

하지만 규모가 조금이라도 커지면 useContext 를 사용할 수 없을 것 같다. 
유지보수 측면에서 너무 큰 비용이 들어갈 것으로 보임.

그래서 결국에는 store (Redux, Zustand) 를 사용하게 될 것 같다.

 

전체 코드

TypeScript 로 작성해서 간단하게 type 을 선언하여 사용하였다.

import React, { useContext, useState } from "react";
import { FaSun, FaMoon } from "react-icons/fa";

interface ThemeContextType {
    theme: string;
    toggleTheme: () => void;
}

// 1. 테마 컨텍스트 생성
const ThemeContext = React.createContext<ThemeContextType | undefined>(
    undefined
);

const UseContext = () => {
    const [theme, setTheme] = useState("light");
    const toggleTheme = () => {
        setTheme(theme === "light" ? "dark" : "light");
    };
    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            <ThemeComponent_1 />
        </ThemeContext.Provider>
    );
};

//drilling 용 컴포넌트
const ThemeComponent_1 = () => {
    return <ThemeComponent_2 />;
};

//실제 context를 사용하는 컴포넌트
const ThemeComponent_2 = () => {
    const context = useContext(ThemeContext);
    if (!context)
        throw new Error("ThemeContext must be used within ThemeProvider");

    const { theme, toggleTheme } = context;

    return (
        <div
            className={`w-full mx-auto px-4 py-8 min-h-screen transition-all duration-300 ${
                theme === "light" ? "bg-gray-50" : "bg-gray-900 text-white"
            }`}
        >
            <button
                onClick={toggleTheme}
                className={`p-2 rounded-full ${
                    theme === "light"
                        ? "bg-gray-200 hover:bg-gray-300"
                        : "bg-gray-700 hover:bg-gray-600"
                } transition-colors duration-200`}
                aria-label="테마 변경"
            >
                {theme === "light" ? (
                    <FaMoon className="w-5 h-5 text-gray-700" />
                ) : (
                    <FaSun className="w-5 h-5 text-yellow-300" />
                )}
            </button>
        </div>
    );
};

export default UseContext;

 

반응형