본문 바로가기
Flutter

[Flutter] Freezed 추천 라이브러리

by 가드 2022. 12. 16.
728x90

 개요

자바 개발을 할 때 개발의 편의성의 도움을 주는 탑티어 라이브러리가 뭐냐고 묻는다면 나는 Lombok이라고 말해주고 싶다. Flutter에서 Lombok급 개발의 편의성에서 도움을 주는 라이브러리가 오늘 내용을 정리해 볼 Freezed 라이브러리가 아닐까 생각이 된다.

https://pub.dev/packages/freezed Freezed 라이브러리 공식문서이다.

 본문

Package 설정

# Freezed 설정 시작
flutter pub add freezed_annotation
flutter pub add --dev build_runner
flutter pub add --dev freezed
# Freezed 설정 끝

# 아래 정의는 데이터클래스 Json Serialization, Deserialization에 대한 설정이다.
flutter pub add json_annotation
flutter pub add --dev json_serializable

Terminal을 열고 Freezed 설정 추가 명령을 하나씩 실행한다. 그러면 pubspec.yaml에 최신 버전으로 자동으로 설정이 된다. 참고로 명령에 대한 오류가 발생한다면 [Flutter] zsh: command not found 이 글을 읽고 환경변수 설정을 진행해야한다.

추가로 Json 직렬화 관련 설정인데 기본적으로 API 통신하는 앱이면 무조건 해야 된다. 이것도 추가하자. 모두 완료되었으면 아래와 같이 pubspec.yaml에 설정이 추가되어 있을 것이다.

pubspec.yaml

클래스 설정

import 'package:freezed_annotation/freezed_annotation.dart';
part 'AppLaunchConfig.freezed.dart';
part 'AppLaunchConfig.g.dart';

@freezed
class AppLaunchConfig with _$AppLaunchConfig {
  factory AppLaunchConfig({
    String? noticeMessage,
    String? latestVersion,
    bool? hasUpdate,
    bool? hasForcedUpdate,
  }) = _AppLaunchConfig;

  factory AppLaunchConfig.fromJson(Map<String, dynamic> json) => _$AppLaunchConfigFromJson(json);
}
  • 데이터 클래스에 @freezed 어노테이션을 추가한다.
  • 클래스 명 옆에 with _${클래스명} 추가 정의한다.
  • factory 키워드로 생성자를 만들고 = _{클래스명} 추가한다.
  • part '{클래스명}.freezed.dart'; 를 추가하여 제너레이션 되는 파일을 선언한다.

여기까지가 Freezed에 대한 기본 설정이고 아래는 Json 직렬화 관련 제너레이션 파일을 생성하기 위한 설정이다.

  • factory 키워드로 {클래스명}.fromJson() => _${클래스명}FromJson(json); 을 정의한다.
  • part '{클래스명}.g.dart'; 를 추가하여 제너레이션 되는 파일을 선언한다.

모두 설정 완료되었어도 클래스에 에러가 발생되고 있을 것이다. 파일을 제너레이션 하지 않았기 때문이다 

flutter pub run build_runner build

터미널에서 위의 명령어를 수행하자.

generation file

클래스 파일 package 위치에 freezed.dart 파일과 g.dart 파일이 생성되었을 것이다. 프로젝트를 개발하면서 많은 모델들이 생성이 될 텐데 같은 패키지 안에 freezed와 g 파일들이 생성된다면 파일 수가 많아져서 보기가 힘들어질 것이다. 그래서 File Nesting을 설정해보자

file nesting

프로젝트 설정 > File Nesting > .dart의 Child File  .freezed.dart; .g.dart; 추가하자.

클래스 파일 하위에 정렬이 되므로 보기 편해졌다. 참고하자. 개발을 진행하다가 데이터 클래스에 대해서 정상적으로 제너레이션 파일이 생성되지 않았을 때 아래와 같은 명령어를 수행해보자.

flutter pub run build_runner build --delete-conflicting-outputs

Freezed 주요 기능

1. toString() Overriding

  final data = AppLaunchConfig(latestVersion: '1.0.0', noticeMessage : 'message', hasForcedUpdate: false, hasUpdate: false);
  print(data.toString());
  // 출력
  AppLaunchConfig(noticeMessage: message, latestVersion: 1.0.0, hasUpdate: false, hasForcedUpdate: false)

일반적으로 클래스 toString() 요청하면 인스턴스 정보만 나온다. 어떠한 값을 가지고 있는지를 볼 수 없어서 toString()을 매번 오버라이딩을 해야 하는데 Freezed를 사용하면 클래스 데이터 정보 값들을 모두 볼 수 있다.

2. Json 변환

  final data = AppLaunchConfig(latestVersion: '1.0.0', noticeMessage : 'message', hasForcedUpdate: false, hasUpdate: false);
  print(data.toJson());
  // 출력
  {noticeMessage: message, latestVersion: 1.0.0, hasUpdate: false, hasForcedUpdate: false}

클래스 toJson()을 하면 클래스 데이터가 Json 형태로 변환하게 된다.

3. 클래스 비교

  final data1 = AppLaunchConfig(latestVersion: '1.0.0', noticeMessage : 'message', hasForcedUpdate: false, hasUpdate: false);
  final data2 = AppLaunchConfig(latestVersion: '1.0.0', noticeMessage : 'message', hasForcedUpdate: false, hasUpdate: false);
  print(data1 == data2); // true


데이터 클래스를 비교하고 싶을 때 클래스 안에 데이터가 모두 같으면 같다고 판단하고자 하는 것이 99%이다. 하지만 실제로는 클래스 비교할 때 값은 같아도 다른 객체라고 판단하게 된다. Freezed는 equles와 hashcode도 모두 자동으로 오버라이딩 해준다.

4. 변수 Validation 정의

  @Assert('noticeMessage.length < 10', 'message 길이는 10보다 작아야한다.')
  factory AppLaunchConfig({
    String? noticeMessage,
    String? latestVersion,
    bool? hasUpdate,
    bool? hasForcedUpdate,
  }) = _AppLaunchConfig;

@Assert 어노테이션으로 변수의 조건에 대해 검증할 수 있는 기능을 제공해준다. noticeMessage 길이 10글자 넘어가면 'message 길이는 10보다 작아야 한다.' 에러가 발생된다.

5. 클래스 카피

final data1 = AppLaunchConfig(latestVersion: '1.0.0', noticeMessage : 'message', hasForcedUpdate: false, hasUpdate: false);
  final data2 = data1.copyWith();
  print(data2.toString()); // AppLaunchConfig(noticeMessage: message, latestVersion: 1.0.0, hasUpdate: false, hasForcedUpdate: false)
  final data3 = data1.copyWith(noticeMessage: 'message2'); 
  print(data3.toString()); // AppLaunchConfig(noticeMessage: message2, latestVersion: 1.0.0, hasUpdate: false, hasForcedUpdate: false)

생성된 클래스를 copyWith() 메소드를 통해서 데이터 값들이 그대로 복사된 클래스를 생성할 수 있고 data1.copyWith(noticeMessage: 'message2');  변경하고 싶은 변수만 변경해서 복사도 가능하다. 이 카피 기능인 Deep copy라서 정말 편하다.

클래스가 하위 오브젝트를 가지고 있으며 제일 마지막 오브젝트 값만 변경해서 클래스를 카피하고 싶을 경우

data1.{객체}.{객체}.{객체}({변수 값 변경}); <-- 이런 식으로 도 간단하게 하위 객체만 변경해서 카피가 가능하다.

 

이외에도 많은 기능들이 제공해주고 관리하는 것 같은데 나도 다 사용해보지는 않았다. 사용해본 것 중에 괜찮은 기능들만 요약해봤다. 추가적인 내용은 freezed document를 참고해보자.

※ 참고사항 ※

@freezed
class AppLaunchConfig with _$AppLaunchConfig {

  factory AppLaunchConfig({
    String? noticeMessage,
    String? latestVersion,
    bool? hasUpdate,
    bool? hasForcedUpdate,
  }) = _AppLaunchConfig;

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

  AppLaunchConfig._(); // 생성자 추가 정의
  
  void appPrint() {
    print('test');
  }
}

클래스 안에 메소드를 정의하여 사용할 수도 있는데 이때는 생성자가 하나 더 필요하다 {클래스명}._();

freezed 클래스가 변경이 될 때마다 새롭게 제너레이트 빌드를 해줘야 한다.

 마무리

개발할때 생산성을 장점을 많이 제공을 해주니 정신적으로나 육체적으로 편하고자 한다면 무조건 사용해야 되는 라이브러리라 생각된다.

300x250

댓글