테마와 다크모드 — 동적 스타일링 시스템
다크모드는 이제 선택이 아니라 필수입니다. 시스템 설정을 따르면서도 앱 내에서 수동 전환할 수 있는 테마 시스템을 구축합니다.
시스템 다크모드 감지
import { useColorScheme } from 'react-native';
function App() {
const colorScheme = useColorScheme(); // 'light' | 'dark' | null
return (
<View style={{
flex: 1,
backgroundColor: colorScheme === 'dark' ? '#1a1a1a' : '#ffffff',
}}>
<Text style={{ color: colorScheme === 'dark' ? '#fff' : '#000' }}>
현재 모드: {colorScheme}
</Text>
</View>
);
}
테마 시스템 구축
// theme/colors.ts
export const lightColors = {
background: '#ffffff',
surface: '#f5f5f5',
text: '#1a1a1a',
textSecondary: '#666666',
primary: '#007AFF',
border: '#e0e0e0',
card: '#ffffff',
error: '#FF3B30',
success: '#34C759',
};
export const darkColors = {
background: '#000000',
surface: '#1c1c1e',
text: '#ffffff',
textSecondary: '#ababab',
primary: '#0A84FF',
border: '#38383a',
card: '#2c2c2e',
error: '#FF453A',
success: '#30D158',
};
export type ThemeColors = typeof lightColors;
// theme/ThemeContext.tsx
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import { useColorScheme } from 'react-native';
import { MMKV } from 'react-native-mmkv';
const storage = new MMKV();
type ThemeMode = 'light' | 'dark' | 'system';
interface ThemeContextType {
colors: ThemeColors;
isDark: boolean;
mode: ThemeMode;
setMode: (mode: ThemeMode) => void;
}
const ThemeContext = createContext<ThemeContextType | null>(null);
export function ThemeProvider({ children }: { children: ReactNode }) {
const systemScheme = useColorScheme();
const [mode, setModeState] = useState<ThemeMode>(
() => (storage.getString('themeMode') as ThemeMode) ?? 'system'
);
const setMode = (newMode: ThemeMode) => {
setModeState(newMode);
storage.set('themeMode', newMode);
};
const isDark = mode === 'system'
? systemScheme === 'dark'
: mode === 'dark';
const colors = isDark ? darkColors : lightColors;
return (
<ThemeContext.Provider value={{ colors, isDark, mode, setMode }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
return ctx;
}
사용
function SettingsScreen() {
const { colors, mode, setMode } = useTheme();
return (
<View style={{ flex: 1, backgroundColor: colors.background, padding: 20 }}>
<Text style={{ color: colors.text, fontSize: 20, fontWeight: 'bold' }}>
테마 설정
</Text>
{(['system', 'light', 'dark'] as const).map((option) => (
<Pressable
key={option}
onPress={() => setMode(option)}
style={{
flexDirection: 'row', alignItems: 'center',
padding: 16, borderBottomWidth: 1, borderBottomColor: colors.border,
}}
>
<Text style={{ color: colors.text, flex: 1 }}>
{option === 'system' ? '시스템 설정' : option === 'light' ? '라이트' : '다크'}
</Text>
{mode === option && <Text style={{ color: colors.primary }}>✓</Text>}
</Pressable>
))}
</View>
);
}
function Card({ title, description }: { title: string; description: string }) {
const { colors } = useTheme();
return (
<View style={{
backgroundColor: colors.card,
borderRadius: 12,
padding: 16,
borderWidth: 1,
borderColor: colors.border,
}}>
<Text style={{ color: colors.text, fontSize: 16, fontWeight: 'bold' }}>{title}</Text>
<Text style={{ color: colors.textSecondary, marginTop: 4 }}>{description}</Text>
</View>
);
}
React Navigation 테마 연동
import { NavigationContainer, DefaultTheme, DarkTheme } from '@react-navigation/native';
function App() {
const { isDark, colors } = useTheme();
const navigationTheme = {
...(isDark ? DarkTheme : DefaultTheme),
colors: {
...(isDark ? DarkTheme.colors : DefaultTheme.colors),
primary: colors.primary,
background: colors.background,
card: colors.card,
text: colors.text,
border: colors.border,
},
};
return (
<NavigationContainer theme={navigationTheme}>
<RootNavigator />
</NavigationContainer>
);
}
정리
useColorScheme()으로 시스템 다크모드 설정 을 감지합니다- 테마 Context를 만들어 앱 전체에서 일관된 색상 을 사용하세요
system,light,dark세 가지 모드를 지원하면 사용자 경험이 좋습니다- 테마 설정을 MMKV/AsyncStorage에 저장 하여 앱 재시작 후에도 유지하세요
- React Navigation의
theme속성과 연동하면 네비게이션 바도 자동으로 테마가 적용됩니다
댓글 로딩 중...