코드 생성 — build_runner, freezed, auto_route

Flutter 프로젝트에서 반복적인 보일러플레이트 코드를 자동 생성하는 것은 생산성의 핵심입니다. build_runner를 기반으로 하는 주요 코드 생성 도구들을 정리해보겠습니다.


build_runner란?

build_runner는 Dart의 코드 생성 프레임워크입니다. 어노테이션을 분석하여 코드를 자동 생성합니다.

BASH
# 1회 빌드
dart run build_runner build

# 파일 변경 감시 (개발 중 권장)
dart run build_runner watch

# 충돌 해결 후 재빌드
dart run build_runner build --delete-conflicting-outputs

대표적인 코드 생성 도구들

도구생성 결과생성 파일
json_serializablefromJson/toJson.g.dart
freezed불변 클래스 + copyWith + ==.freezed.dart + .g.dart
auto_route타입 안전한 라우팅 코드.gr.dart
injectableDI 등록 코드.config.dart
hive_generatorHive 어댑터.g.dart
mockitoMock 클래스.mocks.dart

freezed — 불변 데이터 클래스

DART
import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
class User with _$User {
  const factory User({
    required int id,
    required String name,
    required String email,
    @Default(false) bool isActive,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) =>
      _$UserFromJson(json);
}

// 자동 생성되는 것들:
// - copyWith
// - == / hashCode
// - toString
// - fromJson / toJson

Union 타입

DART
@freezed
sealed class AuthState with _$AuthState {
  const factory AuthState.initial() = _Initial;
  const factory AuthState.loading() = _Loading;
  const factory AuthState.authenticated(User user) = _Authenticated;
  const factory AuthState.error(String message) = _Error;
}

// 패턴 매칭
state.when(
  initial: () => const Text('초기'),
  loading: () => const CircularProgressIndicator(),
  authenticated: (user) => Text('안녕, ${user.name}'),
  error: (message) => Text('에러: $message'),
);

auto_route — 타입 안전한 라우팅

YAML
dependencies:
  auto_route: ^9.0.0

dev_dependencies:
  auto_route_generator: ^9.0.0
  build_runner: ^2.4.0

라우터 정의

DART
import 'package:auto_route/auto_route.dart';

part 'app_router.gr.dart';

@AutoRouterConfig()
class AppRouter extends RootStackRouter {
  @override
  List<AutoRoute> get routes => [
    AutoRoute(page: HomeRoute.page, initial: true),
    AutoRoute(page: DetailRoute.page, path: '/detail/:id'),
    AutoRoute(page: SettingsRoute.page),
    // 중첩 라우팅
    AutoRoute(
      page: DashboardRoute.page,
      children: [
        AutoRoute(page: OverviewRoute.page),
        AutoRoute(page: StatsRoute.page),
      ],
    ),
  ];
}

페이지 어노테이션

DART
@RoutePage()
class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 타입 안전한 네비게이션
            context.router.push(DetailRoute(id: 42));
          },
          child: const Text('상세로'),
        ),
      ),
    );
  }
}

@RoutePage()
class DetailScreen extends StatelessWidget {
  final int id;
  const DetailScreen({super.key, @PathParam('id') required this.id});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('상세 #$id')),
    );
  }
}

사용

DART
MaterialApp.router(
  routerConfig: AppRouter().config(),
)

// 네비게이션
context.router.push(const HomeRoute());
context.router.push(DetailRoute(id: 42));
context.router.pop();
context.router.replaceAll([const HomeRoute()]);

여러 코드 생성기 함께 사용

YAML
# pubspec.yaml
dependencies:
  freezed_annotation: ^2.4.0
  json_annotation: ^4.9.0
  auto_route: ^9.0.0
  injectable: ^2.4.0
  get_it: ^8.0.0

dev_dependencies:
  build_runner: ^2.4.0
  freezed: ^2.5.0
  json_serializable: ^6.8.0
  auto_route_generator: ^9.0.0
  injectable_generator: ^2.6.0

build.yaml로 빌드 최적화

YAML
# build.yaml (프로젝트 루트)
targets:
  $default:
    builders:
      # 특정 디렉토리만 감시 (빌드 속도 향상)
      json_serializable:
        generate_for:
          - lib/models/**
      freezed:
        generate_for:
          - lib/models/**
          - lib/features/**/domain/**

코드 생성 실전 팁

BASH
# 1. 충돌 시 삭제 후 재빌드
dart run build_runner build --delete-conflicting-outputs

# 2. 특정 파일만 빌드 (빠름)
dart run build_runner build --build-filter="lib/models/*"

# 3. watch 모드에서 개발
dart run build_runner watch --delete-conflicting-outputs

# 4. 생성된 파일은 .gitignore에 넣지 않기
# .g.dart, .freezed.dart 등은 커밋해야 CI/CD에서 빌드 가능

.g.dart vs .freezed.dart

확장자생성 도구내용
.g.dartjson_serializable, hivefromJson/toJson, 어댑터
.freezed.dartfreezedcopyWith, ==, toString
.gr.dartauto_route라우터 코드
.mocks.dartmockitoMock 클래스
.config.dartinjectableDI 설정

정리

  • build_runner는 Flutter의 코드 생성 기반으로, 여러 생성기를 통합 실행합니다
  • freezed로 불변 데이터 클래스와 Union 타입을 자동 생성합니다
  • auto_route로 타입 안전한 라우팅 코드를 생성합니다
  • dart run build_runner watch로 실시간 코드 생성하며 개발하세요
  • 생성된 파일(.g.dart 등)은 버전 관리에 포함시키는 것이 일반적입니다
  • build.yaml로 빌드 범위를 제한하면 속도를 개선할 수 있습니다
댓글 로딩 중...