[Flutter] JsonSerializable 적용

Hee Tae Shin·2023년 1월 11일
2

Flutter

목록 보기
3/16
post-thumbnail

상황

플러터로 프로젝트를 하다보면, 갑자기 g.dart 파일을 만드는 경우가 생긴다!
이유사용법을 알아보자!

패키지 설치

https://pub.dev/packages/json_serializable
https://github.com/google/json_serializable.dart/tree/master/example

공통된 부분

model 페이지에는 api 를 통해서 받을 속성들을 미리 지정을 해놓았고, 인스턴스 생성를 하기 생성자 선언과 맵핑을 자동으로 해주기 위한 factory 생성자가 있다.
생성자까지는 파라미터 어떤값을 넣어야하는지 지정을 해야하니깐 있어야하지만,
factory 생성자로 만든 fromJson은 반복적으로 속성을 또 넣어줘야한다.

그렇다면 반복적으로 들어가는 부분을 자동화 하면 좋지 않은가?
그 귀찮은 작업을 구해줄 패키지! 바로 JsonSerializable 이다.

import 'package:authentication_study/common/const/data.dart';

enum RestaurantPriceRange {
  expensive,
  medium,
  cheap,
}

class RestaurantModel {
  final String id;
  final String name;
  final String thumbUrl;
  final List<String> tags;
  final RestaurantPriceRange priceRange;
  final double ratings;
  final int ratingsCount;
  final int deliveryTime;
  final int deliveryFee;

  // 인스턴스화할때 매개변수 다 넣을 수 있게 만듬
  RestaurantModel({
    required this.id,
    required this.name,
    required this.thumbUrl,
    required this.tags,
    required this.priceRange,
    required this.ratings,
    required this.ratingsCount,
    required this.deliveryTime,
    required this.deliveryFee,
  });

  // factory constructor 사용
  factory RestaurantModel.fromJson({required Map<String, dynamic> json}) {
    return RestaurantModel(
      id: json['id'],
      name: json['name'],
      thumbUrl: 'http://$ip${json['thumbUrl']}',
      tags: List<String>.from(json['tags']),
      // enum 값을 매핑
      priceRange: RestaurantPriceRange.values.firstWhere(
            (e) => e.name == json['priceRange'],
      ),
      ratings: json['ratings'],
      ratingsCount: json['ratingsCount'],
      deliveryTime: json['deliveryTime'],
      deliveryFee: json['deliveryFee'],
    );
  }
}

@어노테이션!

어노테이션을 사용해 이 클래스를 자동으로 JsonSerializable 코드를 생성시킬거라고 정의 해주는 것이다.

import 'package:json_annotation/json_annotation.dart';

part 'restaurant_model.g.dart';

enum RestaurantPriceRange {
  expensive,
  medium,
  cheap,
}

()
class RestaurantModel {
  final String id;
  final String name;
  final String thumbUrl;
  final List<String> tags;
  final RestaurantPriceRange priceRange;
  final double ratings;
  final int ratingsCount;
  final int deliveryTime;
  final int deliveryFee;

  // 인스턴스화할때 매개변수 다 넣을 수 있게 만듬
  RestaurantModel({
    required this.id,
    required this.name,
    required this.thumbUrl,
    required this.tags,
    required this.priceRange,
    required this.ratings,
    required this.ratingsCount,
    required this.deliveryTime,
    required this.deliveryFee,
  });
}

// g.dart 적용하는 방법
// json -> 인스턴스를 만드는것
  factory RestaurantModel.fromJson(Map<String, dynamic> json)
  => _$RestaurantModelFromJson(json);

  // 인스턴스 -> json 으로 만드는것
  Map<String, dynamic> toJson() => _$RestaurantModelToJson(this);
flutter pub run build_runner build

g.dart 파일 자세히보기

g.dart 파일을 잘 살펴보면 RestaurantModel 을 생성하는 인스턴스가 생긴걸 볼 수 있는데,
직접 만든 factory 생성자와 thumUrl 빼고 똑같이 생긴 것을 알 수 있다.
그러니 직접 만들 필요가 없다.

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'restaurant_model.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

// 1) RestaurantModel 을 생성하는 인스턴스, 직접만든 factory 생성자와 거의 유사하다!
RestaurantModel _$RestaurantModelFromJson(Map<String, dynamic> json) =>
    RestaurantModel(
      id: json['id'] as String,
      name: json['name'] as String,
      thumbUrl: json['thumbUrl'] as String,
      tags: (json['tags'] as List<dynamic>).map((e) => e as String).toList(),
      priceRange:
          $enumDecode(_$RestaurantPriceRangeEnumMap, json['priceRange']),
      ratings: (json['ratings'] as num).toDouble(),
      ratingsCount: json['ratingsCount'] as int,
      deliveryTime: json['deliveryTime'] as int,
      deliveryFee: json['deliveryFee'] as int,
    );
// 2) 현재 인스턴스에서 다시 json 으로 바꾸는 코드가 자동으로 생긴것 
Map<String, dynamic> _$RestaurantModelToJson(RestaurantModel instance) =>
    <String, dynamic>{
    // 현재 instance 의 id 값을 'id' 키값에 넣어준다.
      'id': instance.id,
      'name': instance.name,
      'thumbUrl': instance.thumbUrl,
      'tags': instance.tags,
      'priceRange': _$RestaurantPriceRangeEnumMap[instance.priceRange]!,
      'ratings': instance.ratings,
      'ratingsCount': instance.ratingsCount,
      'deliveryTime': instance.deliveryTime,
      'deliveryFee': instance.deliveryFee,
    };

const _$RestaurantPriceRangeEnumMap = {
  RestaurantPriceRange.expensive: 'expensive',
  RestaurantPriceRange.medium: 'medium',
  RestaurantPriceRange.cheap: 'cheap',
};

model 에 g.dart 적용을 하는 방법!

// g.dart 적용하기!
  // 1) json -> 인스턴스를 만드는것
  factory RestaurantModel.fromJson(Map<String, dynamic> json) =>
      _$RestaurantModelFromJson(json);

  // 2) 인스턴스 -> json 으로 만드는것
  Map<String, dynamic> toJson() => _$RestaurantModelToJson(this);

매핑될 때 추가로 다르게 저장 하고 싶은 경우!

thumUrl 같은 경우, 앞에 http://$ip 를 붙이고 싶지만!
g.dart 파일에는 그냥 적혀져서 매핑 되어있는 것을 볼수있다.

// 이전
thumbUrl: json['thumbUrl'] as String,

하지만 절대로 g.dart 파일은 수정하지 않는다!

pathToUrl 이 실행이 되면, Json 으로부터 thumbUrl 을 가져오고,
그 값을 pathToUrl에 파라미터로 넣고 리턴값을 다시 thumbUrl 에 저장된다.
그러니 pathtoUrl 에 파라미터를 안 넣어도된다.

()
class RestaurantModel {
  final String id;
  final String name;
  (
    fromJson: pathToUrl,
  )
  final String thumbUrl;
...

// @JsonKey 를 위한 static 함수 생성
static pathToUrl(String value) {
    // value 는 thumbUrl 이다.
    return 'http://$ip$value';
  }

그리고 다시 build 해서 g.dart 파일을 만들면, 새로 업데이트되어서 생성된 모습을 볼 수 있다.

절대로 g.dart 파일은 수정하지않는 이유!

왜냐! 어차피 새로 생성되거나 수정된 .dart 파일 기준으로 g.dart 이 새로 생성되기 때문에!

꿀팁!

1. g.dart 파일 때문에 디렉토리가 복잡하다?

위의 방식대로 하면 g.dart 파일이 생길텐데, 같은 루트에 생기면 보기 불편하고 어지러울수 있다.
안드로이드 스튜디오의 프로젝트 상단에 톱니바퀴를 누르고 File Nesting... 클릭!!!


뒤에 g.dart 파일을 추가해주면, model.dart 의 하단에 포함이 된걸 볼수있다.
이게 구조 바뀌거나 파일시스템이 바뀐것이 아니라 우리가 볼때만 바뀐 것이다.

2. g.dart 파일 매번 생성하기 귀찮다?

평소에 작성하는 코드는 일회성으로 build 하는 것이다.

flutter pub run build_runner build

하지만, 변화하는것을 감지해서 자동으로 업데이트 하게 할 수 있다.

flutter pub run build_runner watch

3. 자동완성 스니펫 만들기

설정

그룹생성

템플릿텍스트작성

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

define 설정

profile
안녕하세요

0개의 댓글