TailwindCSS와 zustand-persist로 다크모드 구현하기 (React-Typescript)

Devinix·2025년 5월 23일
0
post-thumbnail

EntryPoint.tsx

import ReactQueryProvider from "./providers/ReactQueryProvider";
import DarkModeProvider from "./providers/DarkModeProvider";
import RootLayout from "./layout/RootLayout";
import { Outlet } from "react-router-dom";

export default function EntryPoint() {
  return (
    <ReactQueryProvider>
      <DarkModeProvider>
        <RootLayout>
          <Outlet />
        </RootLayout>
      </DarkModeProvider>
    </ReactQueryProvider>
  );
}

DarkModeProvider.tsx

import { useLayoutEffect, type PropsWithChildren } from "react";
import useDarkModeStore from "../stores/useDarkModeStore";

export default function DarkModeProvider({ children }: PropsWithChildren) {
  const {
    isDarkMode,
    setIsDarkMode,
    isDarkModeInitialized,
    setIsDarkModeInitialized,
  } = useDarkModeStore();

  useLayoutEffect(() => {
    if (!isDarkModeInitialized) {
      // 시스템의 다크모드 설정을 감지하는 MediaQuery 객체를 생성
      const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
      // 시스템이 다크모드인 경우 matches가 true, 라이트모드인 경우 false를 반환
      setIsDarkMode(mediaQuery.matches);
      setIsDarkModeInitialized(true);
    }
  }, [isDarkModeInitialized, setIsDarkMode, setIsDarkModeInitialized]);

  // isDarkMode 상태가 변경될 때마다 HTML 요소에 dark 클래스를 추가하거나 제거 (화면 깜빡임을 방지하기 위해 useEffect 대신 useLayoutEffect 사용)
  useLayoutEffect(() => {
    if (isDarkMode) {
      document.documentElement.classList.add("dark");
    } else {
      document.documentElement.classList.remove("dark");
    }
  }, [isDarkMode]);

  return (
    <div className="min-h-[100dvh] bg-white dark:bg-black text-black dark:text-white">
      {children}
    </div>
  );
}

useDarkModeStore.ts

import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
import STORAGE_KEYS from "../constants/storageKeys";

interface DarkModeStore {
  isDarkMode: boolean;
  isDarkModeInitialized: boolean;
  setIsDarkMode: (isDarkMode: boolean) => void;
  setIsDarkModeInitialized: (isDarkModeInitialized: boolean) => void;
}

const useDarkModeStore = create(
  persist<DarkModeStore>(
    (set) => ({
      isDarkMode: false,
      isDarkModeInitialized: false,
      setIsDarkMode: (isDarkMode: boolean) => set({ isDarkMode }),
      setIsDarkModeInitialized: (isDarkModeInitialized: boolean) =>
        set({ isDarkModeInitialized }),
    }),
    {
      name: STORAGE_KEYS.DARK_MODE,
      storage: createJSONStorage(() => localStorage),
    }
  )
);

export default useDarkModeStore;

index.css

@tailwind utilities;

:root {
  color-scheme: light dark;
}

@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
profile
프론트엔드 개발

0개의 댓글