Theme 및 ThemeData — 정의, 원칙 및 스타일 구성

저자: IT Sectr 게시일: 2026-07-03 읽는 시간: 8 분

ThemeThemeData — Flutter에서 테마 설정의 핵심 메커니즘으로, 전체 애플리케이션의 색상, 폰트, 여백 및 컴포넌트 스타일을 전역적으로 정의할 수 있습니다. ThemeData는 theme 매개변수를 통해 MaterialApp에 전달되는 구성 객체입니다. 공식 Flutter 문서(2026)에 따르면, 올바른 ThemeData 구성은 중복 스타일링 코드를 70–80% 줄이고 모든 화면에서 일관된 시각적 모양을 보장합니다. 내장된 상속 시스템을 통해 Theme.of(context)를 사용하여 개별 위젯 수준에서 테마를 재정의할 수 있습니다.

핵심 포인트

  • ThemeData — 색상 팔레트, 타이포그래피 및 컴포넌트 스타일을 포함한 구성 객체
  • Theme.of(context) — BuildContext를 통해 모든 위젯에서 현재 테마에 접근
  • Light/Dark — theme 및 darkTheme을 통한 라이트/다크 테마 내장 지원
  • Theme — 자식 요소에 테마를 상속하고 재정의를 허용하는 위젯
  • ColorScheme — 전체 Material 시스템의 스타일을 생성하는 조화로운 색상 세트

Theme 및 ThemeData란

ThemeData는 Material 애플리케이션의 시각적 스타일에 대한 완전한 설명을 포함하는 클래스입니다. theme 또는 darkTheme 매개변수를 통해 MaterialApp에 전달되며 모든 자식 위젯이 상속받습니다. 모든 Material Design 위젯은 ThemeData를 사용하여 스타일(버튼 색상, AppBar 크기, 텍스트 스타일, 카드 여백, 아이콘)을 가져옵니다.

ThemeData의 핵심에는 ColorScheme이 있으며, primary, secondary, surface, error 등의 색상을 정의합니다. 이 색상들로부터 모든 Material 컴포넌트의 상태(호버, 포커스, 프레스)가 자동으로 계산됩니다. 예를 들어, ElevatedButton은 배경에 primary 색상을, 텍스트에 onPrimary를 사용합니다 — 추가 구성이 필요 없습니다.

Theme 위젯은 InheritedWidget으로, 테마를 위젯 트리 아래로 전파합니다. 모든 위젯은 Theme.of(context)를 통해 현재 테마에 접근할 수 있으며, Theme.copyWith 또는 ThemeData.copyWith를 통해 재정의된 값으로 기존 테마를 기반으로 새 테마를 만들 수도 있습니다.

결론: ThemeData는 애플리케이션 스타일의 단일 소스입니다. ThemeData를 정의하여 개발을 시작하세요 — 이는 전체 프로젝트의 시각적 언어를 설정하고 다양한 화면에서 스타일이 분산되는 것을 방지합니다.

ColorScheme: 색상 팔레트의 기초

ColorScheme은 Material 3에서 테마 설정의 핵심 요소입니다. 더 이상 사용되지 않는 primarySwatch와 primaryColor를 대체하며, 모든 상태와 컴포넌트에 대한 완전한 색상 세트를 제공합니다.

ColorScheme에는 12가지 주요 색상이 있습니다: primary, onPrimary, secondary, onSecondary, surface, onSurface, error, onError, background, onBackground, outline, shadow. 각 색상에 대해 밝은 버전과 어두운 버전, 그리고 호버, 포커스, 프레스, 드래그 상태의 색상이 자동으로 계산됩니다. primary만 지정하면 프레임워크가 ColorScheme.fromSeed를 통해 나머지 색상을 자동 생성합니다.

ColorScheme.fromSeed 메서드(Material 3)는 기본 색상 seedColor를 받아 Material Design 3 알고리즘을 기반으로 완전한 조화로운 팔레트를 생성합니다. 이는 보색 및 유사색을 수동으로 선택할 필요를 없앱니다. 브랜드의 기본 색상 하나만 전달하면 ColorScheme.fromSeed가 전체 Material 시스템에 대한 일관된 팔레트를 만듭니다.

결론: 새 프로젝트에서는 ColorScheme.fromSeed를 사용하여 팔레트를 생성하세요. Material 2 호환성을 위해서는 — primarySwatch를 사용하세요. ColorScheme은 Material 3의 최소 요구 사항입니다.

타이포그래피 및 TextTheme: 앱 폰트

TextTheme은 ThemeData의 일부로, 모든 제목 수준, 부제목, 본문 및 레이블의 텍스트 스타일을 정의합니다. Material Design은 15가지 텍스트 스타일을 정의합니다: headlineLarge, headlineMedium, headlineSmall, titleLarge, titleMedium 등.

타이포그래피는 GoogleFonts를 통한 자동 폰트 로딩, 또는 커스텀 TextStyle 객체와 함께 내장된 Theme.of(context).textTheme을 통해 설정됩니다. GoogleFonts는 google_fonts 패키지를 통해 수백 가지 폰트를 제공하며, 첫 로드 후 로컬에 캐시합니다. 대안은 pubspec.yaml을 통해 프로젝트 애셋에 폰트를 포함시키는 것입니다.

적응형 타이포그래피를 위해 TextTheme은 MediaQuery.textScaleFactor를 지원합니다 — 시스템 폰트 크기가 증가하면 모든 텍스트 스타일이 비례적으로 확장됩니다. 추가로, MediaQuery.size.width에 따라 isTest를 사용하여 다양한 폼 팩터에 다른 폰트 크기를 설정할 수 있습니다.

결론: ThemeData에서 TextTheme을 정의하고 모든 텍스트 요소에 Theme.of(context).textTheme을 사용하세요. 이는 폰트 일관성을 보장하고 전역 타이포그래피 변경을 간소화합니다.

테마 상속 및 재정의

Theme은 InheritedWidget으로, 테마를 부모에서 자식으로 전파합니다. 모든 자식 위젯은 Theme.of(context)를 통해 현재 테마를 읽을 수 있을 뿐만 아니라 ThemeData.copyWith를 통해 변경 사항이 적용된 자체 복사본을 만들 수 있습니다.

상속 메커니즘: 위젯이 새 데이터로 Theme에 래핑된 경우, 모든 하위 항목은 재정의된 테마를 보는 반면, 이 래퍼 외부의 위젯은 원본을 봅니다. 이를 통해 예를 들어 특정 화면의 스타일을 분리할 수 있습니다: 헤더는 수정된 AppBarTheme이 있는 커스텀 테마를 사용하고, 나머지 앱은 표준 테마를 사용합니다.

Theme의 중첩은 무제한일 수 있습니다. Flutter는 계층적 검색을 사용합니다: Theme.of(context)를 호출하면 프레임워크가 트리를 올라가 첫 번째 Theme까지 이동하여 해당 테마를 반환합니다. Theme이 없으면 — 예외가 발생합니다. 따라서 MaterialApp은 항상 애플리케이션을 Theme으로 래핑합니다.

결론: Theme 상속은 스타일 분리를 위한 유연한 메커니즘입니다. 전역 구성을 복제하지 않고 개별 화면에 테마 재정의를 사용하세요.

다크 테마: 앱의 다크 모드

다크 테마는 Flutter의 라이트 테마와 다크 테마 간 전환 내장 기능입니다. ThemeData는 MaterialApp의 theme(라이트) 및 darkTheme(다크) 매개변수에 저장됩니다. themeMode 매개변수가 모드를 제어합니다: ThemeMode.light(항상 라이트), ThemeMode.dark(항상 다크) 또는 ThemeMode.system(시스템 설정 따름).

다크 테마를 구현할 때 모든 스타일을 처음부터 다시 만들 필요는 없습니다. ColorScheme.fromSeed(seedColor, brightness: Brightness.dark)를 darkTheme에 전달하기만 하면 됩니다. 프레임워크가 자동으로 어두운 배경에 맞게 색상을 조정합니다: surface는 어두운 회색이 되고, 텍스트는 밝아집니다. 다른 하위 테마(AppBarTheme, CardTheme, BottomNavigationBarTheme)는 명시적으로 재정의되지 않는 한 라이트 버전에서 상속됩니다.

테마 간 부드러운 전환을 위해 Flutter는 AnimatedTheme을 통한 애니메이션을 제공합니다. themeMode가 변경되면 색상과 스타일이 애니메이션되어 시각적으로 만족스러운 전환을 만듭니다. AnimatedTheme은 Duration과 Curve를 사용하여 애니메이션 속도를 구성합니다.

결론: 다크 테마는 darkTheme과 ColorScheme.fromSeed(brightness: Brightness.dark)를 사용하여 구현됩니다. 사용자의 시스템 설정을 따르려면 ThemeMode.system을 사용하세요.

Theme 및 ThemeData 코드 예제

예제 1 — ColorScheme.fromSeed를 사용한 MaterialApp을 통한 전역 테마 구성.

dart
final ThemeData appTheme = ThemeData(
  useMaterial3: true,
  colorScheme: ColorScheme.fromSeed(
    seedColor: Colors.indigo,
    brightness: Brightness.light,
  ),
  appBarTheme: const AppBarTheme(
    centerTitle: true,
    elevation: 0,
  ),
  cardTheme: const CardTheme(
    elevation: 2,
    margin: EdgeInsets.all(8),
  ),
);

return MaterialApp(
  theme: appTheme,
  themeMode: ThemeMode.system,
  home: const MyHomePage(),
);

이 예제는 Material 3, 인디고 기반 색상 구성 및 커스텀 AppBar 및 Card 스타일이 포함된 ThemeData를 만듭니다. useMaterial3는 새로운 Material 3 테마 시스템을 활성화하고, ColorScheme.fromSeed는 전체 팔레트를 생성합니다.

예제 2 — 테마 접근 및 애플리케이션 일부 재정의.

dart
Widget build(BuildContext context) {
  final theme = Theme.of(context);

  return Column(
    children: [
      Text(
        'Standard headline',
        style: theme.textTheme.headlineMedium,
      ),
      Theme(
        data: theme.copyWith(
          colorScheme: theme.colorScheme.copyWith(
            primary: Colors.orange,
          ),
        ),
        child: const SpecialSection(),
      ),
    ],
  );
}

두 번째 예제는 스타일 읽기를 위한 Theme.of(context) 사용과 전역 구성을 변경하지 않고 트리 일부에서 테마를 재정의하기 위한 copyWith가 있는 Theme() 사용을 보여줍니다. Theme 내부의 SpecialSection은 주황색 primary 색상을 받고, 나머지 앱은 원래 인디고를 유지합니다.

결론: 전역 테마는 MaterialApp에서 설정되고, Theme.of(context)를 통해 접근되며, copyWith가 있는 Theme을 통해 재정의됩니다. Material 3의 경우 ColorScheme.fromSeed를 사용하세요.

테마 구성 시 흔한 실수

실수 1: Material 3에서 ColorScheme 대신 primarySwatch 사용. primarySwatch는 더 이상 사용되지 않으며 Material 3에서 지원되지 않습니다. primarySwatch와 함께 useMaterial3: true를 사용하면 Flutter가 경고를 표시합니다. 해결책: 팔레트 생성을 위해 ColorScheme.fromSeed로 전환하세요.

실수 2: Theme.of(context) 대신 위젯에 색상을 하드코딩. 개발자가 테마의 primary 또는 secondary 색상 대신 Container에 직접 Colors.blue를 작성합니다. 테마가 변경되어도 이러한 색상은 그대로 유지되어 일관성을 깨뜨립니다. 해결책: 항상 Theme.of(context).colorScheme.primary 또는 테마의 다른 색상을 사용하세요.

실수 3: darkTheme 누락. 애플리케이션에 darkTheme이 정의되지 않은 경우, 기기에서 다크 테마가 활성화되면 Flutter가 자동으로 라이트 테마를 적용하려고 시도하며, 이는 종종 품질이 낮은 결과를 초래합니다. 해결책: 다크 테마가 우선순위가 아니더라도 항상 MaterialApp에 darkTheme을 전달하세요 — 이는 사용자에게 중요한 UX입니다.

실수 4: 빌드마다 새 테마 생성. ThemeData가 build 메서드 내에서 생성되면, 위젯이 재구성될 때마다 새 테마 객체가 생성되어 InheritedWidget 최적화가 손상됩니다. 해결책: ThemeData를 build 외부의 static 필드 또는 const 생성자로 이동하세요.

결론: 올바른 테마 설정은 중앙 집중식 접근 방식이 필요합니다. ThemeData를 상수로 이동하고, 테마의 색상 구성표를 사용하며, 항상 라이트와 다크 두 테마를 모두 정의하세요.

자주 묻는 질문

Theme과 ThemeData의 차이점은 무엇인가요?

ThemeData는 구성 데이터(색상, 폰트, 스타일)입니다. Theme은 ThemeData를 위젯 트리 아래로 전달하는 InheritedWidget입니다. Theme.of(context)는 가장 가까운 Theme이 현재 컨텍스트에서 전파하는 ThemeData를 반환합니다.

앱 테마를 동적으로 전환하려면 어떻게 하나요?

앱 상태를 통해 MaterialApp의 themeMode를 제어하세요. 상태를 ThemeMode.light에서 ThemeMode.dark로 변경하면 Flutter가 새 테마로 전체 트리를 재구성합니다. 부드러운 애니메이션을 위해 AnimatedTheme을 사용하세요.

ColorScheme.fromSeed란 무엇인가요?

ColorScheme.fromSeed는 하나의 기본 색상(seedColor)을 받아 완전한 조화로운 팔레트(primary, secondary, tertiary, surface, error 및 on-변형)를 생성하는 Material 3 메서드입니다. 하나의 브랜드 색상에서 일관된 색상 구성표를 얻을 수 있습니다.

Theme으로 커스텀 폰트를 사용하려면 어떻게 하나요?

ThemeData 내의 TextTheme을 통해: 각 수준에 fontFamily가 있는 TextStyle을 지정하세요. Google Fonts의 경우 google_fonts 패키지를 사용하여 fontFamily를 TextTheme에 직접 전달하세요. 로컬 폰트의 경우 — fonts 섹션에서 pubspec.yaml을 통해 포함시키세요.

하나의 화면에 대해서만 테마를 재정의할 수 있나요?

. copyWith를 통해 재정의된 데이터가 있는 Theme으로 화면을 감싸세요. 이 Theme 내의 모든 자식 위젯은 수정된 스타일을 받고, 나머지 앱은 원래 테마를 유지합니다. 추가 오버헤드가 발생하지 않습니다.

요약

  • ThemeData — 모든 Material 컴포넌트의 색상 팔레트, 타이포그래피 및 스타일을 포함한 구성 객체
  • ColorScheme.fromSeed — 하나의 브랜드 색상에서 Material 3의 색상 팔레트를 생성하는 주요 방법
  • Theme.of(context) — 코드 중복 없이 스타일을 읽기 위한 현재 테마 접근
  • Theme — 위젯 서브트리에서 테마를 상속 및 재정의하기 위한 InheritedWidget
  • 다크 테마 — brightness: Brightness.dark가 있는 별도의 ThemeData, themeMode로 제어
  • 권장사항: ThemeData를 static 또는 const로 이동하고, MaterialApp에서 항상 theme과 darkTheme을 정의하세요

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기