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

실제 프로젝트에서 왜 중요한가
다음 큰 소셜 미디어 앱을 만든다고 상상해 보세요. 세련된 프로필, 직관적인 피드, 활기찬 커뮤니티가 있습니다. 그런데 사용자가 “다크 모드가 있으면 좋겠어요”라고 요구하기 시작합니다.
잘 설계된 테마 스위처가 이 문제를 해결합니다. 우리는 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에 선호도를 저장해 재방문 사용자에게 기억됩니다.- 초기 기본값으로 시스템 선호도를 존중합니다.
- 부드러운 전환 효과를 추가해 완성도를 높입니다.
이제 사용자에게 테마 선택 권한을 제공해 보세요. 분명히 고마워할 겁니다.