Freezed

freezed | Dart Package
freezed_annotation | Dart package

이번에는 가장 좋아하는 라이브러리인 바로 Freezed에 대해서 살펴보도록 하겠다.

Freezed는 Dart Package에서 작성 날짜 기준으로 Likes 2520 / Pub Points 140 / Popularity 99%를 받고 있는 인기 라이브러리 중 하나이다.

개발하면서 매 프로젝트마다 무조건 사용하는 라이브러리가 몇 개 있는데, 대표적인게 바로 freezed이다.

freezed는 강력한 기능을 제공하는 Code generator이다.

제가 freezed를 좋아하는 부분은 크게 2가지가 있는데, 이 두 가지가 모든 사람이 해당 라이브러리를 사용하는 이유일 것이다.

첫 번째는 바로 jsonserializable이다. json 직렬화라는 것인데, API 통신을 하게 되면 xml이나 json 형태의 데이터를 가져오게 된다.
요새 xml은 공공기관 데이터가 아닌이상 잘 사용되지 않고 있고 json을 주로 사용하고 있는데, json을 그냥 가져올 수 가없어서 변환 과정을 거쳐야 하는데 이 기능을 Flutter에서는 jsonserializable이 도와주고 있다.

두 번째는 copy 기능인데, 바로 Deep Copy이다. 보통 Bloc을 주로 사용하는 분들은 copyWith라는 기능이 익숙한데, Bloc이나 freezed 등 copy 기능을 사용하지 않으신 분들은 이해가 잘 되지 않을 수도 있지만 한 번 쓰면 무조건 쓰는 기능이다.

이외에도 toString, union class 등 다양한 기능을 제공해주고 있다.

천천히 살펴보자.

dependencies

먼저 가장 중요한 점이 아래에 있는 버전은 제가 사용한 버전이기에 각 프로젝트에 맞는 환경에서의 버전을 넣어주어야 하는데, 이 부분이 조금 짜증날 수도 있다. 버전 맞추기가 조금 힘들긴 해요...

제 환경은 Flutter 3.3.10 / Dart 2.18.6 / stable 입니다.

freezed를 dependencies에 추가해 보자

dependencies:
	freezed_annotation: ^2.2.0

freezed는 code generator이기에 build runner도 같이 넣어주어야 한다.

dev_dependencies:
	build_runner: ^2.3.3
    freezed: ^2.3.2
  	json_serializable: ^6.1.3

Build Runner

Code generator라는 기능이 있는데, 말 그대로 코드 자동 생성기 정도로 생각하면 된다. code를 직접 만들지 않고 자동으로 생성한다? 이상하게 보일 수는 있지만 사실 Json 형태 데이터 직접 변환 작업을 해보면 정말 개노가다 입니다.
끊임없이 반복되는 코드를 계속 만들고 있는거야 말로 시간 낭비 아닐까요?? ㅎㅎ 프로젝트 커지면 나중에 class만 수십개 수백개 까지 만들어져요..

json 데이터 타입을 만들어 주는 다른 방법도 있지만 build runner 돌리면 정말 간단합니다.

먼저 build runner를 작동시키는 방법은 아주 간단합니다.

IDE 터미널 창을 열고 프로젝트 레벨에서 아래 코드를 복사해서 넣어주면 끝입니다 !!

빌드 러너가 한 번만 작동되고 종료됨.

flutter pub run build_runner build

빌드 러너를 계속 작동시켜 파일이 변경될 때를 계속 수신하고 있음.

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

freezed

freezed가 무엇인지에 대해서 아래 캡쳐본을 보면서 대략적으로 살펴보자.

freezed는 union 타입의 객체를 생성하고 있다.
아래 .fromJson 저 명령어 하나로 바로 json 타입은 끝이다. 엄청 간단하다.

위에 살펴본 freezed의 규칙이 하나 있는데 바로 part'fileName.freezed.dart형태를입력해주어야한다.json을사용할꺼라면part{fileName}.freezed.dart' 형태를 입력해 주어야 한다. json을 사용할꺼라면 part'{fileName}.g.dart'를 추가해준다.

@freezed annotaion도 추가를 반드시 하여야 하고 이 준비가 끝났다면 위에서 작성한 build runner 돌려주면 끝이난다.
build runner 돌리면 왼쪽에 .freezed.dart, .g.dart 파일 2개가 생성되는데 이게 바로 자동 생성기로 생성된 파일들이다.
자동 생성된 파일은 수정할 필요가 없으며, 파일을 들어가보면 어떤식으로 파일이 구성되는지를 볼 수 있다.

추가로 아래 보면 .empty() 추상 클래스가 있는데 해당 클래스는 객체에 null을 주기 싫을 때 사용하면 유용하다. 초기화 클래스라고 생각하면 된다.
하지만 .empty()는 freezed의 문법에 지원하지 않은 기능이므로 원래는 사용하면 에러가 나면서 파일이 자동생성 되지 않을 것이다.
이렇게 생성된 클래스 안에 다른 기능을 가지는 메소드를 추가하고 싶다면 위에 선언한 객체명.언더바()를 추가만 해주면 freezed가 알아서 code generator에서 제외 시킨다.

json

이렇게 생성된 객체를 API 통신으로 부터 받아올 때는 fromJson 기능을 사용하고 반대로 body안에 객체를 json으로 전송할 때는 toJson의 기능을 사용하면 된다.

toJson

Person person;
person.toJson();

fromJson

Person person = Person.fromJson(_response.data);

firestore

Firestore의 데이터 변환시에도 사용할 수 있다.
json과 똑같이 fromFirestore, toFirestore를 사용할 수 있는데, freezed 객체안에 아래 코드가 추가되어 있어야 한다.

  factory Person.fromFireStore(DocumentSnapshot<Map<String, dynamic>> doc) {
    return Person.fromJson(doc.data()!);
  }

deepcopy

Deepcopy는 깊은 복사라 해서 class의 원하는 변수의 값을 변경할 수 있는 기능이다. 만약에 class로 생성된 변수 중 하나의 변수만 바꾸고 싶다면 어떻게 할 것인가?

예제로 확인해 보자.

우선 아래와 같이 CarOrderData라는 객체를 freezed로 만들어 주고 option에 해당하는 CarOptionData도 freezed로 만들어 준다.

 CarOptionData _order = CarOrderData(
      orderNumber: 1,
      orderName: "지구공",
      option: CarOptionData(name: 'G90', color: 'Black', isNavi: true)),

이렇게 생성된 데이터의 orderName만 "지팔공"으로 변경하고 싶다고 해보자 이럴 때 아래처럼 해주면 된다. 엄청 간단하다.

_order = _order.copyWith(orderName: "지팔공");

이번엔 CarOptionData안에 있는 색상을 "Black"에서 "Red"로 바꿔보자.

_order = _order.copyWith(option: _order.option.copyWith(color: "Red"));

만약에 리스트 형태의 객체를 바꾸고 싶다면 index값을 찾아서 바꿔주기만 하면된다.

이 기능은 꼭 freezed가 아니어도 만들 수 있는데, 작성해야 할 코드가 어렵지는 않지만 그냥 귀찮다... 모든 클래스를 다 만들고 있을 순 없으니.. 그냥 freezed 사용해 보세요 ㅎㅎ

toString()

또 하나의 유용한 기능 짧게 살펴보자.

보통 class의 값을 콘솔 로그에 남기고 싶을 때 객체를 .toString()하면 어떻게 출력되는지 다들 해보셨을 것이다.
class가 인스턴스 되었다고만 나와지 class를 형성하고 있는 데이터까지는 출력되지 않는다.

자 이렇게 Car class를 만들어서 print해보면 결과는 "Instance of 'Car'" 이렇게 나온다.
접근해서 출력하여야만 값을 출력할 수 있는데, freezed의 .toString()은 다르다.

class Car {
  final String name;
  final String color;
  Car(this.name, this.color);
}

Car _car = Car("G70", "Blue");
print(_car.toString());

이번엔 Car 객체를 freezed로 생성하고 출력해 보자. 결과는 바로..

flutter: Car(name: 지칠공, color: Blue)

이렇게 잘 출력되는 것을 볼 수 있다.


class Car with _$Car {
  const factory Car({
    required String name,
    required String color,
  }) = _Car;
}

Car _car = const Car(name: "지칠공", color: "Blue");
print(_car.toString());

마무리

이번 글에서는 freezed에 대해서 알아보았는데, 원래 deepcopy, json을 자세히 다루기 위해서 Open API 활용해서 간단한 예제를 만들어 보려고 했지만 요새 시간이 없어서 자세히는 다루지 못했다.

하지만 적절한 예제를 만들어서 다음번에는 좀 더 깊게 작성하도록 하겠다.

profile
Flutter Developer

0개의 댓글