안녕하세요! 개발자 베베입니다.
이번 포스팅에서는 플러터에서 json 데이터를 다루는 가장 확실한 방법에 대해서 알아보도록 하겠습니다. 시작하기 전에 json이 뭔지 간단하게 알아보도록 할게요!
JSON이 뭐지?
json은 Javascript Object Notation의 축약어이며, 데이터를 저장 및 전송할 때 데이터 형식으로 자주 사용됩니다. 이름에서 알 수 있듯이 json은 자바스크립트의 객체를 표현하는 형식을 기반으로 만들어졌습니다. 다만 자바스크립트에서는 객체를 표현했다면 json에서는 데이터를 표현한다는 거죠.
json은 이렇게 생겼습니다.
{
"users": [
{
"name": "lee",
"age": 32
},
{
"name": "bebe",
"age": 26
}
]
}
Json 디코딩
json String을 Dart 자료형으로 변환하는 작업을 json 디코딩이라고 합니다. json 디코딩은 총 2단계로 진행되며 각 단계는 다음과 같습니다.
- json String을 json 자료형으로 변환
- json 자료형을 객체로 변환
json 디코딩은 결과적으로 json String으로 표현되어지는 객체 모델을 대응하는 Dart 객체로 변환하는 작업입니다. 이러한 과정을 직렬화(Serialization)이라고도 합니다.
json String을 json 자료형으로 변환
json 자료형이란 Dart에서 json String을 객체로 다루기 위해 사용하는 자료형입니다. json 자료형으로는 Map<String, dynamic>, List<dynamic>이 있습니다. 이 자료형들은 최종적으로 대응하는 객체를 만들기 위한 중간 단계의 자료형으로 사용됩니다.
변환과정을 설명하기 위해 아래와 같이 정의된 json String이 있다고 가정하겠습니다.
{
"name": "lee",
"age": 32
}
보시다 싶이 name과 age라는 이름의 필드를 가진 한 개의 json 객체입니다. 정제되지 않은 이 json String을 jsonDecode 함수를 이용하여 json 자료형으로 변환할 수 있습니다.
String json = '''
{
"name":"lee",
"age":32
}
''';
Map<String, dynamic> user = jsonDecode(json);
print(user['name']); // lee 출력
print(user['age']); // 32 출력
jsonDecode 함수는 Dart 언어의 convert 라이브러리에서 제공하고 있는 json 디코딩 함수입니다. jsonDecode 함수는 매개변수로 전달된 json String을 분석하여 Map<String, dynamic> 자료형 혹은 List<dynamic>을 반환합니다. 보시면 아시겠지만 객체의 경우에는 Map<String, dynamic>으로 반환되고 배열의 경우에는 List<dynamic>으로 반환됩니다.
Map<String, dynamic>으로 데이터를 처리할 수는 있지만 객체의 각 필드값에 접근할 때, dynamic으로 되어 있어서 타입을 확정지을 수 없는 문제가 발생합니다. 우리는 user['name']이 String인 것을 알지만 컴파일러는 알 수 없습니다. 이러한 문제를 해결하기 위해 각각의 필드가 적절한 자료형으로 매핑되도록 객체화 시켜주는 과정이 필요합니다.
json 자료형을 객체로 변환
json 자료형을 담기 위해 아래와 같이 User 클래스를 정의해줍니다. 내부에는 생성자 함수도 있고, 조금 특이할 지 모르는 factory 함수도 정의해주었습니다.
class User {
String name;
int age;
User({
this.name,
this.age,
});
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
);
}
}
fromJson 함수는 이름에서 알 수 있듯이 전달된 json 자료형을 토대로 객체를 만들어주는 함수입니다. 다음과 같이 사용할 수 있습니다.
String json = '''
{
"name":"lee",
"age":32
}
''';
User user = User.fromJson(jsonDecode(json));
print(user.name); // lee 출력
print(user.age); // 32 출력
이렇게 json 자료형을 객체화시켜서 각 필드의 자료형을 정해줄 수 있게 되었습니다.
여기까지 json String을 Dart 객체로 변환하는 과정에 대해서 알아보았습니다. 이번에는 반대의 과정인, Dart 객체를 json String으로 변환하는 과정에 대해 알아보도록 하겠습니다.
Json 인코딩
Dart 객체를 json String으로 변환하는 과정을 json 인코딩이라고 합니다. 인코딩 과정도 총 두단계로 진행됩니다. 디코딩 과정의 반대라고 생각하면 쉽습니다.
- Dart 객체를 json 자료형으로 변환
- json 자료형을 json String으로 변환
객체를 json 자료형으로 변환하는 toJson 함수
이전의 fromJson 함수와 마찬가지로 json 모델 클래스에 json 자료형으로 변환하는 함수를 정의해보도록 하겠습니다. 함수 이름은 toJson입니다. ( 반드시 toJson이어야 합니다.)
class User {
String name;
int age;
User({
this.name,
this.age,
});
Map<String, dynamic> toJson() {
return {
'name': this.name,
'age': this.age,
};
}
factory User.fromJson(Map<String, dynamic> json) {
return User(
name: json['name'] as String,
age: json['age'] as int,
);
}
}
toJson 함수는 호출되면 json 자료형을 반환합니다. Dart에서는 중괄호로 Map 자료형을 표기할 수 있는데, 위 처럼 작성하시면 Map<String, dynamic> 자료형을 표현할 수 있습니다.
jsonEncode 함수
객체를 jsonString으로 변환하기 위해 convert 라이브러리에서 제공하는 jsonEncode함수를 사용합니다. jsonEncode함수는 jsonDecode 함수와 다르게 매개변수로 json 자료형이 아닌 객체를 요구합니다. 객체가 전달되면 함수 내부에서 객체의 toJson 메서드를 호출하여 json 자료형을 얻어온 후, 그것으로 jsonString을 반환하는 구조입니다. 이러한 이유로 우리는 toJson이라는 함수를 정의해주었던 것입니다.
사용법은 다음처럼 간단합니다.
String json = jsonEncode(user);
다음 단계 : 복잡한 구조의 json 직렬화
json 표현식은 매우 자유로우며 확장이 가능합니다. 객체 내부의 필드 값으로 객체가 들어올 수 있으며 배열까지 표현할 수 있죠.
이러한 구조를 Dart 객체로 직렬화하려면 어떻게 해야할까요? 너무 어려워 할 필요 없습니다. 지금까지 설명한 내용의 반복일 뿐입니다. 몇몇 규칙만 알면 해결 방법은 단수합니다. 다만 과정이 복잡할 뿐입니다.
다음 포스팅에서는 복잡한 json String을 객체로 직렬화하는 방법에 대해서 알아보도록 하겠습니다.