개인화의 예술을 마스터하세요: React & Tailwind 테마 스위처 만들기

발행: (2025년 12월 12일 오후 09:50 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

Master the Art of Personalization: React와 Tailwind 테마 스위처 구축을 위한 커버 이미지

실제 프로젝트에서 왜 중요한가

다음 큰 소셜 미디어 앱을 만든다고 상상해 보세요. 세련된 프로필, 직관적인 피드, 활기찬 커뮤니티가 있습니다. 그런데 사용자가 “다크 모드가 있으면 좋겠어요”라고 요구하기 시작합니다.

잘 설계된 테마 스위처가 이 문제를 해결합니다. 우리는 React의 Context API를 활용해 전역 테마 상태를 만들고, Tailwind CSS의 dark: 변형을 이용해 테마별 스타일을 손쉽게 적용합니다. 또한 사용자의 선택을 localStorage에 저장해 다음 방문 시에도 선호도가 유지되도록 합니다.

이걸 직접 만들어 보자

Step 1: React & Tailwind 프로젝트 설정

npx create-react-app theme-switcher-app
cd theme-switcher-app
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

tailwind.config.js를 설정해 다크 모드를 활성화합니다:

// tailwind.config.js
module.exports = {
  darkMode: 'class', // HTML 요소에 'dark' 클래스를 토글합니다
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
}

Step 2: Theme Context 만들기

// src/contexts/ThemeContext.tsx
import React, { createContext, useState, useEffect, useContext } from 'react';

type Theme = 'light' | 'dark';

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

const ThemeContext = createContext(undefined);

export const ThemeProvider: React.FC = ({ children }) => {
  const [theme, setTheme] = useState(() => {
    // 먼저 localStorage를 확인하고, 없으면 시스템 선호도를 사용
    const stored = localStorage.getItem('theme') as Theme;
    if (stored) return stored;

    return window.matchMedia('(prefers-color-scheme: dark)').matches
      ? 'dark'
      : 'light';
  });

  useEffect(() => {
    const root = document.documentElement;
    root.classList.remove('light', 'dark');
    root.classList.add(theme);
    localStorage.setItem('theme', theme);
  }, [theme]);

  const toggleTheme = () => {
    setTheme(prev => (prev === 'light' ? 'dark' : 'light'));
  };

  return (
    {children}
  );
};

export const useTheme = () => {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
};

Step 3: Theme Switcher 컴포넌트 만들기

// src/components/ThemeSwitcher.tsx
import { useTheme } from '../contexts/ThemeContext';

const ThemeSwitcher = () => {
  const { theme, toggleTheme } = useTheme();

  return (
    {theme === 'light' ? '🌙' : '☀️'}
  );
};

export default ThemeSwitcher;

Step 4: 전체 코드 연결하기

// src/App.tsx
import { ThemeProvider } from './contexts/ThemeContext';
import ThemeSwitcher from './components/ThemeSwitcher';

function App() {
  return (
    <ThemeProvider>
      <div className="min-h-screen flex flex-col items-center justify-center bg-white dark:bg-gray-900 transition-colors duration-300">
        <header className="mb-8">
          <h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100">
            Welcome to My Themed App
          </h1>
        </header>
        <main className="text-center">
          <ThemeSwitcher />
          <p className="mt-4 text-gray-700 dark:text-gray-300">
            Toggle the theme and watch the magic happen.
          </p>
        </main>
      </div>
    </ThemeProvider>
  );
}

export default App;

대부분의 튜토리얼이 놓치는 부분

  • 시스템 선호도를 먼저 반영 – Context가 window.matchMedia('(prefers-color-scheme: dark)')를 먼저 확인해 기본값으로 사용하므로 앱이 네이티브하게 느껴집니다.
  • 부드러운 전환 효과 추가transition-colors duration-300 클래스로 깜빡이는 전환을 방지합니다.

성능 고려 사항

서버 사이드 렌더링(예: Next.js)을 사용할 경우 localStorage가 서버에서는 존재하지 않기 때문에 깜빡이는 현상이 나타날 수 있습니다. React가 하이드레이션되기 전에 <head>에 작은 스크립트를 삽입해 이를 완화합니다:

<script>
  const theme = localStorage.getItem('theme') ||
    (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
  document.documentElement.classList.add(theme);
</script>

핵심 정리

  • 전역 테마 상태는 React Context를 사용 – prop drilling보다 깔끔합니다.
  • Tailwind의 dark: 변형 덕분에 테마 적용이 거의 자동화됩니다.
  • localStorage에 선호도를 저장해 재방문 사용자에게 기억됩니다.
  • 초기 기본값으로 시스템 선호도를 존중합니다.
  • 부드러운 전환 효과를 추가해 완성도를 높입니다.

이제 사용자에게 테마 선택 권한을 제공해 보세요. 분명히 고마워할 겁니다.

Back to Blog

관련 글

더 보기 »

내부 구조: React

소개 나는 React를 사용하기 시작한 순간부터 이것을 하고 싶었다: 그것이 어떻게 작동하는지 이해하고 싶었다. 이것은 소스 코드를 세밀하게 검토하는 것이 아니다. In...