코딩/리액트

[새싹 성동 2기] Context API와 useContext 훅을 사용하여 테마 적용하기

insu90 2024. 12. 6. 18:22

 

React에서는 상태를 전역적으로 관리하고, 여러 컴포넌트 간에 데이터를 쉽게 공유할 수 있는 방법을 제공합니다. 그 중 하나가 Context API입니다. Context API를 사용하면, 상태를 부모 컴포넌트에서 자식 컴포넌트로 props로 전달하지 않고도, 전역적으로 데이터를 관리하고 사용할 수 있습니다.

이번 포스트에서는 테마를 전역 상태로 관리하는 예제를 통해 useContext 훅을 사용하는 방법을 소개합니다.

 

Context API 사용법

  1. Context 생성: React.createContext()를 사용하여 Context 객체를 생성합니다.
  2. Provider 정의: Context.Provider를 사용하여 하위 컴포넌트들에게 값을 제공(전달)합니다.
  3. Context 소비: useContext 훅을 사용하여 Context의 값을 하위 컴포넌트에서 소비(사용)합니다.

예제: 테마를 적용한 페이지

1. 테마 전환 버튼

우리는 먼저 사용자가 클릭할 수 있는 버튼을 만들어, 테마를 변경할 수 있도록 할 것입니다. 여기서는 light와 dark 테마 두 가지를 지원하며, 테마에 맞는 스타일을 적용합니다.

2. Blog, News 컴포넌트

블로그와 뉴스 컴포넌트에서는 현재 테마에 맞춰 스타일을 동적으로 변경합니다.

1단계: 테마를 전역적으로 관리하기 위한 ThemeContext 생성

먼저 ThemeContext를 생성하여 테마 상태를 전역에서 사용할 수 있게 합니다.

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

// 테마에 맞는 스타일을 반환하는 함수
// theme이 'light'일 경우 배경색은 흰색(#fff), 텍스트 색은 검정(#000)으로 반환
// theme이 'dark'일 경우 배경색은 어두운 회색(#333), 텍스트 색은 흰색(#fff)으로 반환
const getThemedStyle = theme => ({
  backgroundColor: theme === "light" ? "#fff" : "#333",  // 테마에 따라 배경색 변경
  color: theme === "light" ? "#000" : "#fff"  // 테마에 따라 글자색 변경
});

// Context 생성: 테마와 테마 변경 기능을 공유하기 위해 Context를 생성
const ThemeContext = createContext();

// Provider 정의: 자식 컴포넌트에 테마 정보와 테마 변경 함수를 제공하는 컴포넌트
const ThemeProvider = ({ children }) => {
  // 현재 테마 상태를 관리하는 useState 훅
  const [theme, setTheme] = useState('light');  // 초기값은 'light'

  // 테마를 변경하는 함수 (현재 테마가 'light'이면 'dark'로, 'dark'이면 'light'로 변경)
  const changeTheme = () => setTheme(theme === "light" ? "dark" : "light");

  return (
    // ThemeContext.Provider를 사용해 자식 컴포넌트에게 테마 정보와 테마 변경 함수 전달
    <ThemeContext.Provider value={{ theme, changeTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

 

  • createContext()를 사용하여 ThemeContext를 생성합니다.
  • ThemeProvider는 theme 상태와 이를 변경하는 changeTheme 함수를 ThemeContext.Provider를 통해 자식 컴포넌트에 전달합니다.

2단계: useContext로 테마 소비하기

각 컴포넌트에서 useContext(ThemeContext)를 사용하여 theme 값을 소비하고, 이를 기반으로 스타일을 변경합니다.

ThemedButton 컴포넌트

const ThemedButton = () => {
  // useContext 훅을 사용하여 ThemeContext에서 현재 테마(theme)와 테마 변경 함수(changeTheme)를 가져옴
  const { theme, changeTheme } = useContext(ThemeContext);

  return (
    // 버튼 클릭 시 changeTheme 함수가 실행되어 테마가 변경됨
    // 버튼의 스타일은 getThemedStyle 함수로 현재 테마에 맞게 적용됨
    <button onClick={changeTheme} style={getThemedStyle(theme)}>
      테마 변경
    </button>
  );
};

 

 

  • useContext(ThemeContext)를 사용해 theme과 changeTheme를 가져옵니다.
  • 버튼을 클릭하면 changeTheme가 호출되어 테마가 변경됩니다.

Blog 컴포넌트

const Blog = () => {
  // useContext 훅을 사용하여 ThemeContext에서 현재 테마(theme)를 가져옴
  const { theme } = useContext(ThemeContext);

  return (
    // div의 스타일을 getThemedStyle 함수로 현재 테마에 맞게 적용
    <div style={getThemedStyle(theme)}>
      <h1>블로그</h1>
      {/* 블로그 내용 */}
      <p>헌법개정안이 제2항의 찬성을 얻은 때에는 헌법개정은 확정되며, 대통령은 즉시 이를 공포하여야 한다.</p>
      <p>대법원장의 임기는 6년으로 하며, 중임할 수 없다. 정당의 설립은 자유이며, 복수정당제는 보장된다.</p>
    </div>
  );
};
  • useContext(ThemeContext)를 사용하여 theme 값을 가져와 스타일을 동적으로 변경합니다.

News 컴포넌트

const News = () => {
  // useContext 훅을 사용하여 ThemeContext에서 현재 테마(theme)를 가져옴
  const { theme } = useContext(ThemeContext);

  return (
    // div의 스타일을 getThemedStyle 함수로 현재 테마에 맞게 적용
    <div style={getThemedStyle(theme)}>
      <h1>뉴스</h1>
      {/* 뉴스 내용 */}
      <p>국군의 조직과 편성은 법률로 정한다. 군인은 현역을 면한 후가 아니면 국무위원으로 임명될 수 없다.</p>
      <p>모든 국민은 법률이 정하는 바에 의하여 공무담임권을 가진다.</p>
    </div>
  );
};

 

  • useContext(ThemeContext)를 사용하여 테마 값을 받아와 해당 테마에 맞는 스타일을 적용합니다.

3단계: App 컴포넌트에서 ThemeProvider 적용

마지막으로 ThemeProvider를 최상위 컴포넌트에 적용하여, 하위 컴포넌트들에서 테마를 사용할 수 있도록 합니다.

const App = () => {
  return (
    // ThemeProvider로 전체 앱을 감싸서 하위 컴포넌트들이 테마를 공유하도록 함
    <ThemeProvider>
      {/* 페이지 제목 */}
      <h1>테마가 적용된 페이지</h1>
      
      {/* 테마 변경 버튼 */}
      <ThemedButton />
      
      {/* 테마가 적용된 블로그 콘텐츠 */}
      <Blog />
      
      {/* 테마가 적용된 뉴스 콘텐츠 */}
      <News />
    </ThemeProvider>
  );
};

 

아래는 이를 모두 정리하 전체 코드입니다.

 

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

// 테마에 맞는 스타일을 반환하는 함수
// 'light' 테마일 경우 배경색을 흰색(#fff), 글자색을 검정색(#000)으로 설정
// 'dark' 테마일 경우 배경색을 어두운 회색(#333), 글자색을 흰색(#fff)으로 설정
const getThemedStyle = theme => ({
  backgroundColor: theme === "light" ? "#fff" : "#333",  // 테마에 따른 배경색
  color: theme === "light" ? "#000" : "#fff"  // 테마에 따른 글자색
});

// ThemeContext 생성: 테마 정보와 테마 변경 기능을 공유하기 위한 Context
const ThemeContext = createContext();

// ThemeProvider 컴포넌트 정의: 자식 컴포넌트에 테마와 테마 변경 함수를 제공
const ThemeProvider = ({ children }) => {
  // 테마 상태를 관리하는 useState 훅, 초기값은 'light'
  const [theme, setTheme] = useState('light');

  // 테마를 'light'와 'dark' 사이에서 전환하는 함수
  const changeTheme = () => setTheme(theme === "light" ? "dark" : "light");

  return (
    // ThemeContext.Provider로 자식 컴포넌트에게 theme과 changeTheme을 제공
    <ThemeContext.Provider value={{ theme, changeTheme }}>
      {children} {/* 자식 컴포넌트를 렌더링 */}
    </ThemeContext.Provider>
  );
};

// ThemedButton 컴포넌트: 테마를 변경하는 버튼
const ThemedButton = () => {
  // useContext로 ThemeContext에서 테마와 테마 변경 함수를 가져옴
  const { theme, changeTheme } = useContext(ThemeContext);

  return (
    // 버튼 클릭 시 changeTheme 함수가 호출되어 테마가 변경됨
    // 버튼 스타일은 현재 테마에 맞게 적용
    <button onClick={changeTheme} style={getThemedStyle(theme)}>
      테마 변경
    </button>
  );
};

// Blog 컴포넌트: 블로그 콘텐츠와 테마 적용
const Blog = () => {
  // useContext로 ThemeContext에서 현재 테마를 가져옴
  const { theme } = useContext(ThemeContext);

  return (
    // 블로그 콘텐츠에 테마 스타일을 적용
    <div style={getThemedStyle(theme)}>
      <h1>블로그</h1>
      {/* 블로그 내용 */}
      <p>헌법개정안이 제2항의 찬성을 얻은 때에는 헌법개정은 확정되며, 대통령은 즉시 이를 공포하여야 한다.</p>
      <p>대법원장의 임기는 6년으로 하며, 중임할 수 없다. 정당의 설립은 자유이며, 복수정당제는 보장된다.</p>
    </div>
  );
};

// News 컴포넌트: 뉴스 콘텐츠와 테마 적용
const News = () => {
  // useContext로 ThemeContext에서 현재 테마를 가져옴
  const { theme } = useContext(ThemeContext);

  return (
    // 뉴스 콘텐츠에 테마 스타일을 적용
    <div style={getThemedStyle(theme)}>
      <h1>뉴스</h1>
      {/* 뉴스 내용 */}
      <p>국군의 조직과 편성은 법률로 정한다. 군인은 현역을 면한 후가 아니면 국무위원으로 임명될 수 없다.</p>
      <p>모든 국민은 법률이 정하는 바에 의하여 공무담임권을 가진다.</p>
    </div>
  );
};

// App 컴포넌트: ThemeProvider로 앱을 감싸고, 테마가 적용된 여러 컴포넌트를 렌더링
const App = () => {
  return (
    // ThemeProvider로 자식 컴포넌트들이 테마 정보를 사용할 수 있도록 함
    <ThemeProvider>
      <h1>테마가 적용된 페이지</h1>
      {/* 테마 변경 버튼 */}
      <ThemedButton />
      {/* 블로그 콘텐츠 */}
      <Blog />
      {/* 뉴스 콘텐츠 */}
      <News />
    </ThemeProvider>
  );
};

// App 컴포넌트를 내보냄
export default App;

 

 

Context API와 useContext 훅을 사용하면 전역 상태를 쉽게 관리할 수 있으며, props 전달을 복잡하게 하지 않고도 여러 컴포넌트에 데이터를 공유할 수 있습니다. 위 예제에서는 테마를 Context로 관리하여, 테마 변경 시 각 컴포넌트에서 쉽게 스타일을 업데이트할 수 있도록 했습니다.

 

 

 

 

 

*생성형 AI 활용한 클라우드&보안 전문가 양성캠프 과정의 교육내용 정리 자료입니다.