[Flutter] JsonSerializable-①RestaurantModel에 JsonSerializable 적용하기

겨레·2024년 7월 17일
0
post-thumbnail

📍 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 파라미터

  • toJson이 실행될 때 json으로 변경될 때 실행하고 싶은 함수는 toJson에 넣으면 됨.
  • fromJson이 실행됐을 때, 그러니까 json으로부터 인스턴스를 만들 때 실행하고 싶은 함수는 fromJson이 에 넣으면 됨.





⑤ @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 코드에도 자동으로 적용됨!

profile
호떡 신문지에서 개발자로 환생

0개의 댓글