📍 JsonSerializable(직렬화)
Dart와 Flutter에서 JSON 데이터를 쉽게 직렬화(serialize)하고 역직렬화(deserialize)할 수 있도록 도와주는 패키지이다.
이 패키지를 사용하면 클래스를 JSON 형식으로 변환하거나 JSON 데이터를 클래스 인스턴스로 변환하는 코드를 자동으로 생성할 수 있습니다.
- 직렬화 (Serialization) 👉 클래스 인스턴스를 JSON 형식으로 변환.
- 역직렬화 (Deserialization) 👉 JSON 데이터를 클래스 인스턴스로 변환.
① json_serializable 설치하기
json_serializable의 경우 기존에 다른 패키지들과 조금 다르기
pubspec.yaml에다가 사진처럼 3개의 Dependency를 추가해줘야 한다.
restaurant_model.dart로 가서 class RestaurantModel을 보면
id, name, thumbUr ... 속성들을 다 지정해 놓음.
그리고 이 속성들은 API 요청으로부터 받을 응답에 대비해 정의해둔 거라 이름이 똑같음!
그런데 그 아래에 .fromJson을 보면 id: json['id'], name: json['name']
이런 식으로 속성을 반복적으로 또 넣어줘야 함.
tags: List<String>.from(json['tags']),
priceRange: RestaurantPriceRange.values.
firstWhere((e) => e.name == json['priceRange'])
이 2개의 파라미터만 빼면 사실상 그냥 json하고 'id', 'name'...
이 속성들을 반복해서 그냥 집어 넣어주고 있음.
그 앞에 id, name... 키값들도 똑같음!
그럼 이걸 보고 드는 생각은???
이걸 자동화할 수 있지 않을까...!!!?
그 자동화를 할 수 있도록 하는 게 바로 Json Serializable이다.
② fromJson 코드 잠시 주석 처리하기
왜 주석 처리할까? 자동으로 생성할 거니까~
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 deliveryFee;
final int deliveryTime;
RestaurantModel({
required this.id,
required this.name,
required this.thumbUrl,
required this.tags,
required this.priceRange,
required this.ratings,
required this.ratingsCount,
required this.deliveryFee,
required this.deliveryTime,
});
주석 처리하면 속성들만 정의가 되어 있고,
json으로부터 어떻게 이 값들을 인스턴스를 만들어낼지에 대한 정의는 안 된 상태.
③ @JsonSerializable( ) 작성하기
class RestaurantModel 윗줄에 @JsonSerializable( ) 애노테이션을 작성해준다.
JsonSerializable로 자동으로 코드를 생성시킬 거라고 넣어주는 거라고 보면 됨!
그리고 임포트 해 주기! 이때 중요한 것은 .g 반드시 들어가야 한다는 것!
// 임포트 공식 👉 part + '현재 파일 이름 + .g + .dart';
이렇게 작성을 마쳤다면, 터미널에 flutter pub run build_runner build 작성 후 실행시켜 준다.
그러면 파트 파일을 지정한 코드가 생성될 수 있는 모든 파일에서 코드를 생성시켜 준다.
나는 class RestaurantModel에 @JsonSerializable( )를 해놨으니까
class RestaurantModel에 대한 코드랑 g.dart 파일이 생성될 거임!
그리고 g.dart 파일은 절대 직접 수정하지 않는다!
(원래 코드를 기반으로 수정되므로...)
// 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']),
// priceRange: RestaurantPriceRange.values
// .firstWhere((e) => e.name == json['priceRange']),
// ratings: json['ratings'],
// ratingsCount: json['ratingsCount'],
// deliveryFee: json['deliveryFee'],
// deliveryTime: json['deliveryTime']);
// }
원래 코드로 돌아가서 주석 처리했던 from.json 코드랑
.g.dart 파일의 _$RestaurantModelFromJson 코드랑 거의 똑같음.
이를 통해 알 수 있는 것...!
주석 처리한 from.json 코드를 직접 작성해야 하는 수고를 덜어버렸내!??
자! 그럼 이걸 어떻게 적용해야할까?
④ factory constructor 생성
factory RestaurantModel.fromJson()
=> _$RestaurantModelFromJson(json);
=> 👉 반환한다는 뜻.
여기서 뭘 반환할거냐면 restaurant_model.g.dart에 생성된 값!
그 값은 _$RestaurantModelFromJson 이거임.
그리고 이 (json)은 어디에서 오냐면 우리가 입력 받을 거임!
.fromJson(Map<String, dynamic> json) 이렇게...
외부에서 Map<String, dynamic> 타입으로 json을 입력 받을 거임.
이렇게 하다 보면 restaurant_screen.dart에서 에러가 발생한다.
에러 발생 원인은 json 파라미터가 constructor에서 사라져버렸기 때문!
그냥 이렇게 포지션을 파라미터로 넣어주면 fromJson constructor의 정의대로 값을 넣어줄 수 있게 됨.
restaurant_model.g.dart에서 두 번째로 생성된 코드는 뭘까?
현재 인스턴스에 다시 json으로 바꿀 때 사용할 수 있는 코드가 자동으로 생성된 것!
그럼 이건 또 어떻게 쓸까?
json으로 바꿀거니까 Map<String, dynamic>을 넣어 반환해준다.
그리고 이름은 항상 toJson이라고 짓는다.
_$RestaurantModelToJson( )에는 this를 넣어준다.
현재 클래스를 넣어주면 되는 거임!
// (1) json으로부터 인스턴스를 만드는 것
factory RestaurantModel.fromJson(Map<String, dynamic> json) =>
_$RestaurantModelFromJson(json);
// (2) json으로 인스턴스를 변환하는 것
Map<String, dynamic> toJson() => _$RestaurantModelToJson(this);
(1), (2) 이 두가지를 자동화해서 코드를 생성할 수 있다!
위에서 말했든 .g.dart 파일의 코드는 직접 수정해선 안 된다.
원래 코드로 가서 수정해줘야 한다.
만약 전환하는 방식을 변경하고 싶다면, 그 속성 위에 @JsonKey( )를 넣어준다.
- fromJson 과 toJson 파라미터
⑤ @JsonKey 사용하기
그런데 지금은 toJson은 안 쓸거라서 지우고 fromJson만 정의해둘거임.
그렇다면 여기에는 어떤 함수가 들어갈까?
static 함수가 하나 들어가야 한다.
아래로 가서 static 함수를 정의해 준다. (pathToUrl은 그냥 지은 이름임)
여기서 중요한 건 첫 번째 파라미터 안에 @JsonKey를 어노테이션해 준
final String thumbUrl; 값이 들어가야 한다.
그리고 value = thumbUrl 이라고 보면 된다.
그럼 pathToUrl을 @JsonKey에 적용했을 때, 파라미터는 없어도 됨!
왜냐면 알아서 실행할 거라서 정의만 넣어줌.
pathToUrl이 실행되면 json으로부터 thumbUrl을 가져올거임.
restaurant_model.g.dart에 그렇게 정의되어 있으니깐!
그 값을 @JsonKey(fromJson: pathToUrl,)에다가 파라미터를 넣고서
pathToUrl(String value) 함수를 실행한 다음
이 'http://$ip$value'; 반환된 값이 다시 String thumbUrl; 여기에 저장됨!
이걸 저장하고 다시 빌드하면 restaurant_model.g.dart 코드에도 자동으로 적용됨!