플러터 최강 code generator freezed 사용하기

반응형

원글: https://steemit.com/hive-137029/@anpigon/flutter-code-generator-freezed

 

플러터 최강 code generator freezed 사용하기 — Steemit

freezed는 코드 제너레이터 기능 뿐만 아니라 많은 유용한 기능을 제공하고 있습니다. freezed는 모델를 정의하는 신텍스가 매우 간단하고 간결합니다. 생성자와 속성을 모두 정의할 필요도 없습니

steemit.com

freezed는 코드 제너레이터 기능 뿐만 아니라 많은 유용한 기능을 제공하고 있습니다.
freezed는 모델를 정의하는 신텍스가 매우 간단하고 간결합니다. 생성자와 속성을 모두 정의할 필요도 없습니다. 그리고 새로운 오브젝트로 복제하기 위한 copyWith 함수를 제공합니다. 나머지 사용 가능한 모든 기능에 대해서는 freezed index 문서 샘플 코드를 참고하세요.

flutter.dev 공식 문서에는 json_serializable 를 사용하여 모델 클래스를 만드는 방법으로 가이드 하고 있습니다. 제 생각에는 freezed와 json_serializable를 같이 사용하는 것이 가장 좋은 것 같습니다.

__

Freezed 설치하기

freezed를 제대로 사용하려면 아래 디펜시를 설치해야 합니다.

flutter pub add freezed_annotation
flutter pub add --dev build_runner
flutter pub add --dev freezed
flutter pub add --dev json_serializable

모든 디펜던시 설치가 잘 되었다면 pubspec.yaml 은 다음과 같이 되어 있어야 합니다.

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  freezed_annotation: ^1.1.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^1.0.0
  freezed: ^1.1.1
  build_runner: ^2.1.7
  json_serializable: ^6.1.4

__

Freeze 사용하기

다음은 freezed에 맞게 User 클래스를 작성하는 방법입니다.
user.dart 파일을 생성하고 User 클래스 아래와 같이 작성합니다.

import 'package:freezed_annotation/freezed_annotation.dart';

/// 이 구문은 `User` 클래스가 생성된 파일의 private 멤버 변수에 접근할 수 있습니다. 
/// 이 값은 *.g.dart 형식이며, *는 현재 파일 이름입니다.
part 'user.freezed.dart';
part 'user.g.dart';

/// @freezed는 JSON serialization 로직이 필요하다고 
/// code generator에 알려주는 어노테이션입니다.
@freezed
class User with _$User {
  factory User({
    required String name,
    required String email,
  }) = _User;

    /// map에서 새로운 User 인스턴스를 생성하기 위해 필요한 팩토리 생성자입니다. 
    /// `_$UserFromJson()` 생성자에 map을 전달합니다.
    /// 생성자는 현재 클래스의 이름을 따릅니다. 이 경우에는 User입니다.
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

필요에 의해 네이밍 전략을 바꾸기도 쉽습니다.
예를 들어 API가 snake_case 명을 가지고 있는 객체를 반환하는데 모델 클래스에서는 lowerCamelCase 를 사용하려는 경우, @JsonKey 어노테이션와 함께 name 파라미터를 사용할 수 있습니다.

factory User({
    required String name,
    required String email,

    /// json_serializable에게 "registration_date_millis"는
    /// registrationDateMillis에 매핑되어야 한다고 알려주는 구문입니다.
    @JsonKey(name: 'registration_date_millis') required DateTime registrationDateMillis,
}) = _User;

서버 데이터가 불확실한 경우가 있으므로 클라이언트의 데이터를 확인하고 보호해야 합니다.
그런 경우 @JsonKey 어노테이션은 아래와 같이 사용합니다.

/// JSON에 이 키가 포함되어 있지 않거나 값이 'null'인 경우,
/// json_serializable에 "defaultValue"를 사용하도록 지시합니다.
@JsonKey(defaultValue: false) required bool isAdult;

/// required가 'true'인 경우,
/// json_serializable에 JSON에 키가 포함되어야 함을 알리고
/// 키가 없으면 예외가 발생합니다.
@JsonKey(required: true) required String id;

/// ignore가 'true'인 경우,
/// 생성된 코드가 이 필드를 무시하라고 json_serializable에 알립니다.
@JsonKey(ignore: true) String? verificationCode;

__

Code Generator 유틸리티 실행하기

freezed 클래스를 처음 생성할 때, 아래의 이미지와 비슷한 에러를 겪게 됩니다.

에러가 뜨는 것이 정상입니다. 모델 클래스를 위해 필요한 코드들이 아직 생성되지 않아서 에러가 뜨는 것입니다. 이 에러를 해결하기 위해서는 serialization 보일러 플레이트를 생성하는 코드 제너레이터를 실행하면 됩니다

터미널에서 code generator 명령어를 실행합니다.

flutter pub run build_runner build

위 명령어가 실행되고 나면 user.dart 파일이 있는 폴더에 user.freezed.dart와 user.g.dart 파일이 자동 생성됩니다. 그리고 오류도 사라집니다.

__

VSCode 익스텐션 설치하기

터미널에서 build_runner 명령어를 직접 입력해도 되지만,
build_runner를 편하게 실행할 수 있게 도와주는 VSCode 익스텐션이 있습니다.
2개 익스텐션을 소개하겠습니다.
본인에게 편한 것을 설치해서 사용하면 됩니다.

사실 flutter_mobx는 사실 mobx 코드 작성을 도와주는 익스텐션입니다.
그래서 다운로드 수는 flutter_mobx 익스텐션이 훨씬 더 많습니다.

Build Runner 익스텐션

Build Runner를 설치하게 되면 왼쪽 사이드바 하단에 메뉴가 나타납니다.

Flutter Mobx 익스텐션

flutter_mobx를 설치하게 되면 하단바에 build_runner 실행 버튼이 나타납니다.

__

Json Serializable와 Freezed 비교하기

단순하게 비교하기 위해서 code generator를 사용하지 않고 작성한 코드와 Json Serializable를 사용하여 작성한 코드, 그리고 Freezed를 사용하여 작성한 코드를 비교해보겠습니다.

Freezed와 Json Serializable를 사용하지 않고 작성한 User 코드는 아래와 같습니다.

class User {
  User({
    required String name,
    required String email,
  });

  late final String name;
  late final String email;

  factory User.fromJson(Map<String, dynamic> json) => User(
        name: json['name'],
        email: json['email'],
      );

  Map<String, dynamic> toJson() => {
        "name": name,
        "email": email,
      };
}

Json Serializable를 사용하여 작성한 코드는 아래와 같습니다.

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  User(this.name, this.email);

  String name;
  String email;

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

  Map<String, dynamic> toJson() => _$UserToJson(this);
}

마지막으로 Freezed를 사용하여 작성한 코드는 아래와 같습니다.

import 'package:freezed_annotation/freezed_annotation.dart';

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

@freezed
class User with _$User {
  factory User({
    required String name,
    required String email,
  }) = _User;

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

위 코드에서 큰 차이가 보이나요?
Freezed를 사용한 코드가 가장 간결한 것을 확인 할 수 있습니다.

잘 모르겠다면 이렇게 비교하면 차이가 확실하게 보일 것 같습니다.

Freezed를 사용하면 멤버 변수를 선언할 필요가 없어서 코드 작성 시간이 매우 줄어듭니다.
그리고 freezed가 제공하는 copy, toString, toJson, assert 등 함수를 정말 유용하게 사용할 수 있습니다.

__

마지막으로 freezed 를 이해하는데 유용한 유튜브와 블로그가 있어서 공유합니다.

반응형