Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

사고쳤어요

[React / ts] Book Store 만들기 - ③ ThemeSwitcher 만들기 본문

웹 풀스택

[React / ts] Book Store 만들기 - ③ ThemeSwitcher 만들기

kevinmj12 2025. 4. 19. 13:56

버튼을 클릭하여 라이트 테마 <-> 다크 테마를 변경할 수 있는 기능을 구현해보자.

 

// theme.ts

export type ThemeName = "light" | "dark";

type ColorKey = "primary" | "background" | "secondary" | "third";

interface Theme {
  name: ThemeName;
  color: Record<ColorKey, string>;
}

export const light: Theme = {
  name: "light",
  color: {
    primary: "brown",
    background: "lightgray",
    secondary: "blue",
    third: "green",
  },
};

export const dark: Theme = {
  name: "dark",
  color: {
    primary: "coral",
    background: "midnightblue",
    secondary: "darkblue",
    third: "darkgreen",
  },
};

export const getTheme = (themeName: ThemeName): Theme => {
  switch (themeName) {
    case "light":
      return light;
    case "dark":
      return dark;
  }
};

declare module "styled-components" {
  export interface DefaultTheme extends Theme {}
}

먼저 theme.ts에서 라이트 테마와 다크 테마일 때 스타일을 작성해준다.

 

themeContext.tsx

import { createContext, ReactNode, useEffect, useState } from "react";
import { getTheme, ThemeName } from "../../style/theme";
import { ThemeProvider } from "styled-components";
import { GlobalStyle } from "../../style/global";

const DEFAULT_THEME_NAME = "light";
const THEME_LOCALSTORAGE_KEY = "book_store_theme";

interface State {
  themeName: ThemeName;
  toggleTheme: () => void;
}

export const state = {
  themeName: DEFAULT_THEME_NAME as ThemeName,
  toggleTheme: () => {},
};

export const ThemeContext = createContext<State>(state);

export const BookStoreThemeProvider = ({
  children,
}: {
  children: ReactNode;
}) => {
  const [themeName, setThemeName] = useState<ThemeName>(DEFAULT_THEME_NAME);

  const toggleTheme = () => {
    setThemeName(themeName === "light" ? "dark" : "light");
    localStorage.setItem(
      THEME_LOCALSTORAGE_KEY,
      themeName === "light" ? "dark" : "light"
    );
  };

  useEffect(() => {
    const savedThemeName = localStorage.getItem(
      THEME_LOCALSTORAGE_KEY
    ) as ThemeName;

    setThemeName(savedThemeName === "light" ? "dark" : "light");
  }, []);

  return (
    <ThemeContext.Provider value={{ themeName, toggleTheme }}>
      <ThemeProvider theme={getTheme(themeName)}>
        <GlobalStyle themeName={themeName} />
        {children}
      </ThemeProvider>
    </ThemeContext.Provider>
  );
};

다음으로 themeContext.tsx에서 ThemeContext와 BookStoreThemeProvider를 정의해준다.

<ThemeContext.Provider>에 value로 themeName과 toggleName을 넣어주어 하위 컴포넌트들에서도 themeName과 toggleName을 사용할 수 있도록 설정해준다.

또한 테마 변경 시마다 localStorage에 해당 데이터를 저장하여 다시 웹페이지를 접속하더라도 스타일을 유지하도록 한다.

 

// ThemeSwitcher.tsx

import { useContext } from "react";
import { ThemeContext } from "../context/ThemeContext";

const ThemeSwitcher = () => {
  const { themeName, toggleTheme } = useContext(ThemeContext);

  return (
    <button
      onClick={() => {
        toggleTheme();
      }}
    >
      {themeName}
    </button>
  );
};

export default ThemeSwitcher;

마지막으로 toggleTheme을 사용할 버튼을 만들어준다.

이제 이 버튼을 누르면 toggleTheme()이 호출되어 themeName이 light, dark로 바뀌며 설정된다.