https://flutter-ko.dev/docs/development/data-and-backend/json
JSON 데이터
웹 서버와 통신할 때, 구조화된 데이터를 적절하게 다루기 위해서 JSON 형식에 데이터는 필수적이다.
내가 원하는 데이터 객체를 다루기 위해 JSON의 필수적인 직렬화 개념을 이해하고 다뤄보자.
일반적인 객체 코드형태
https://www.dhlottery.co.kr/common.do?method=getLottoNumber&drwNo=1041
위와 같은 복권 api를 호출하면 아래와 같은 JSON 형식의 문자열을 반환받는다
String json = 'LotteryNumber{drwNo: 1041, drwNoDate: 2022-11-12, drwtNo1: 6, drwtNo2: 7, drwtNo3: 9, drwtNo4: 11, drwtNo5: 17, drwtNo6: 18, bnusNo: 45, firstWinamnt: 935091165, firstPrzwnerCo: 25}';
/*
// 정형화한 모습이다
LotteryNumber {
drwNo: 1041,
drwNoDate: 2022 - 11 - 12,
drwtNo1: 6,
drwtNo2: 7,
drwtNo3: 9,
drwtNo4: 11,
drwtNo5: 17,
drwtNo6: 18,
bnusNo: 45,
firstWinamnt: 935091165,
firstPrzwnerCo: 25
}
*/
이러한 json 형식에 문자열을 적절히 저장하거나 가공하기 위해서는
디코딩(역직렬화) 과정으로 자료구조 (일반적으로 key:value 구조인 Map) 형식으로 먼저 변환해주고나서
map 자료구조를 이용핸 객체에 데이터를 담아 객체를 만든다.
# 로또번호 객체 코드: LotteryNumber - 생성자만 있는 상태
class LotteryNumber {
final int drwNo;
final String drwNoDate;
final int drwtNo1;
final int drwtNo2;
final int drwtNo3;
final int drwtNo4;
final int drwtNo5;
final int drwtNo6;
final int bnusNo;
final int firstWinamnt; // 당첨금
final int firstPrzwnerCo;
// 생성자
LotteryNumber(
this.drwNo,
this.drwNoDate,
this.firstWinamnt,
this.drwtNo1,
this.drwtNo2,
this.drwtNo3,
this.drwtNo4,
this.drwtNo5,
this.drwtNo6,
this.bnusNo,
this.firstPrzwnerCo
);
}
getter & setter가 없이 만들어둔 기본적 형태의 클래스이다.
그렇다면 json형식으로 제공받은 string으로 LotteryNumber 객체를 하나 만들기 위해서는 어떻게 해야할까?
Map이라는 중간지 자료구조를 거쳐치기 위해서 json.encode / decode 를 이용한다.
JSON -> MAP -> OBJECT 변환과정
import 'dart:convert';
.
.
.
// json string -> Map<String, dynamic> 으로 변환
Map<String, dynamic> map = json.decode("LotteryNumber{drwNo: 1041, drwNoDate: 2022-11-12, drwtNo1: 6, drwtNo2: 7, drwtNo3: 9, drwtNo4: 11, drwtNo5: 17, drwtNo6: 18, bnusNo: 45, firstWinamnt: 935091165, firstPrzwnerCo: 25}");
// 해당 자료구조를 이용해 객체 생성
LotteryNumber ln = new LotteryNumber(map["drwNo"], map["drwNoDate"], ... );
위와같은 메소드를 통해 Map 자료구조에 구조적으로 담아주는 작업을 하는게
convert 패키지의 json.encode / decode 메소드이다.
그래야 직렬화 되어있는 문자열 사이에 Key값과 Value값을 구분하여 접근할 수 있기 때문이다.
map에 담긴 구조적인 데이터를 보다 쉽게 객체에 담아줄수 있는데,
이 과정을 객체 코드안에
fromJson, toJson이라는 메소드라는 이름으로 일반적으로 구현한다.
# 로또번호 객체 코드: LotteryNumber - fromJson, toJson 메소드의 구현
class LotteryNumber {
final int drwNo;
final String drwNoDate;
final int drwtNo1;
final int drwtNo2;
final int drwtNo3;
final int drwtNo4;
final int drwtNo5;
final int drwtNo6;
final int bnusNo;
final int firstWinamnt; // 당첨금
final int firstPrzwnerCo;
// 생성자
LotteryNumber(
this.drwNo,
this.drwNoDate,
this.firstWinamnt,
this.drwtNo1,
this.drwtNo2,
this.drwtNo3,
this.drwtNo4,
this.drwtNo5,
this.drwtNo6,
this.bnusNo,
this.firstPrzwnerCo
);
factory LotteryNumber.fromJson(Map<String, dynamic> json){
return LotteryNumber(
json['drwNo'] as int,
json['drwNoDate'] as String,
json['firstWinamnt'] as int,
json['drwtNo1'] as int,
json['drwtNo2'] as int,
json['drwtNo3'] as int,
json['drwtNo4'] as int,
json['drwtNo5'] as int,
json['drwtNo6'] as int,
json['bnusNo'] as int,
json['firstPrzwnerCo'] as int,
);
}
;
Map<String, dynamic> toJson(LotteryNumber lotteryNumber){
return <String, dynamic>{
'drwNo': lotteryNumber.drwNo,
'drwNoDate': lotteryNumber.drwNoDate,
'drwtNo1': lotteryNumber.drwtNo1,
'drwtNo2': lotteryNumber.drwtNo2,
'drwtNo3': lotteryNumber.drwtNo3,
'drwtNo4': lotteryNumber.drwtNo4,
'drwtNo5': lotteryNumber.drwtNo5,
'drwtNo6': lotteryNumber.drwtNo6,
'bnusNo': lotteryNumber.bnusNo,
'firstWinamnt': lotteryNumber.firstWinamnt,
'firstPrzwnerCo': lotteryNumber.firstPrzwnerCo,
};
};
// 출력을 위헤 재정의
@override
String toString() {
return 'LotteryNumber{drwNo: $drwNo, drwNoDate: $drwNoDate, drwtNo1: $drwtNo1, drwtNo2: $drwtNo2, drwtNo3: $drwtNo3, drwtNo4: $drwtNo4, drwtNo5: $drwtNo5, drwtNo6: $drwtNo6, bnusNo: $bnusNo, firstWinamnt: $firstWinamnt, firstPrzwnerCo: $firstPrzwnerCo}';
}
}
두가지 메소드를 추가한 상태는 json 형식에 데이터를 객체로 더욱 쉽게 담을 수 있다.
이런 방식은 현재 정형화 되어있고, 위와같은 메소드를 한번에 자동으로 제너레이트 할 수가 있다.
// json string -> map -> object 순서로 변환됨
LotteryNumber lotteryNumber = LotteryNumber.fromJson(json.decode(제이쓴데이터));
// object -> map -> string 순서로 직렬화
String json = json.encode(LotteryNumber.toJson(lotteryNumber));
이러한 규칙은 어디에서나 적용되는데,
toJson과 fromJson을 모든 객체마다 작성하는건 너무 번거롭다
그래서 모두 한번에 자동으로 만들어주는 패키지 json_annotation가 존재한다.
JSON을 다루는 객체 메소드 자동생성
json_annotation 을 이용해 대규모 프로젝트에서 사용하는 객체 제너레이트를 알아보자
# pubspec.yaml
dependencies:
# 다른 의존성들
json_annotation: ^2.0.0
dev_dependencies:
# 다른 개발 의존성들
build_runner: ^1.0.0
json_serializable: ^2.0.0
# 로또번호 객체 코드: LotteryNumber - json_annotation형태로 작성
import 'package:json_annotation/json_annotation.dart';
// 아래와 같은 형식으로 part 추가
part 'lottery_number.g.dart';
// 제일 중요한 필수 어노테이션
@JsonSerializable()
class LotteryNumber {
final int drwNo;
final String drwNoDate;
final int drwtNo1;
final int drwtNo2;
final int drwtNo3;
final int drwtNo4;
final int drwtNo5;
final int drwtNo6;
final int bnusNo;
final int firstWinamnt; // 당첨금
final int firstPrzwnerCo;
// 생성자
LotteryNumber(
this.drwNo,
this.drwNoDate,
this.firstWinamnt,
this.drwtNo1,
this.drwtNo2,
this.drwtNo3,
this.drwtNo4,
this.drwtNo5,
this.drwtNo6,
this.bnusNo,
this.firstPrzwnerCo
);
factory LotteryNumber.fromJson(Map<String, dynamic> json) => _$LotteryNumberFromJson(json);
Map<String, dynamic> toJson() => _$LotteryNumberToJson(this);
// 출력을 위헤 재정의
@override
String toString() {
return 'LotteryNumber{drwNo: $drwNo, drwNoDate: $drwNoDate, drwtNo1: $drwtNo1, drwtNo2: $drwtNo2, drwtNo3: $drwtNo3, drwtNo4: $drwtNo4, drwtNo5: $drwtNo5, drwtNo6: $drwtNo6, bnusNo: $bnusNo, firstWinamnt: $firstWinamnt, firstPrzwnerCo: $firstPrzwnerCo}';
}
}
위와 같은 규칙으로 작성해주고나서,
아래 명령어를 콘솔에 입력하면
flutter pub run build_runner build # 일회성 생성
or
flutter pub run build_runner watch # 지속적 변경
, _$객체명FromJson , _$객체명ToJson 형태의 메소드를 가지는
lottery_number.g.dart 를 자동으로 생성해줍니다.
https://flutter-ko.dev/docs/development/data-and-backend/json 더 많은 내용은 여기를 참조
'Study > Flutter' 카테고리의 다른 글
[Flutter] Firebase Cloud Messaging - FCM 테스트발송 (2) | 2022.12.23 |
---|---|
[Flutter] 빌드 오류, 컴파일 오류시 해결법 (0) | 2022.12.15 |
[Flutter] FireStore 사용하기 (0) | 2022.08.17 |
[Flutter] FireAuth 로그인과 인증 (0) | 2022.08.04 |
[Flutter] Firebase flutter 설정 (0) | 2022.08.03 |