다크모드는 이제 선택이 아니라 필수입니다. 시스템 설정을 따르면서도 앱 내에서 수동 전환할 수 있는 테마 시스템을 구축합니다.


시스템 다크모드 감지

TSX
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>
  );
}

테마 시스템 구축

TSX
// 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;
TSX
// 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;
}

사용

TSX
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 테마 연동

TSX
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 속성과 연동하면 네비게이션 바도 자동으로 테마가 적용됩니다
댓글 로딩 중...