Flutter에서 API와 데이터를 주고받을 때 필수인 json_serializable과 json_annotation 사용법을 정리합니다.
직렬화/역직렬화를 자동화하면 코드가 간결해지고, 유지보수가 쉬워집니다.
📌 직렬화와 역직렬화란?
사용하는 패키지
dependencies:
json_annotation: ^4.9.0
dev_dependencies:
build_runner: ^2.4.9
json_serializable: ^6.8.0
json_annotation: 어노테이션 정의용
json_serializable: 변환 코드 자동 생성
build_runner: 자동 생성 실행 도구
예시 모델: Vlog
import 'package:json_annotation/json_annotation.dart';
part 'vlog.g.dart';
()
class Vlog {
final String title;
final String url;
final DateTime publishedAt;
final bool isFavorite;
Vlog({
required this.title,
required this.url,
required this.publishedAt,
required this.isFavorite,
});
factory Vlog.fromJson(Map<String, dynamic> json) => _$VlogFromJson(json);
Map<String, dynamic> toJson() => _$VlogToJson(this);
}
⚙️ 코드 생성
flutter pub run build_runner build
📁 .g.dart 파일의 역할은?
part 'vlog.g.dart';에 의해 연결된 .g.dart 파일은
fromJson, toJson에 사용되는 자동 생성 함수들이 들어있는 파일입니다.
Vlog _$VlogFromJson(Map<String, dynamic> json) => Vlog(
title: json['title'] as String,
url: json['url'] as String,
publishedAt: DateTime.parse(json['publishedAt'] as String),
isFavorite: json['isFavorite'] as bool,
);
Map<String, dynamic> _$VlogToJson(Vlog instance) => <String, dynamic>{
'title': instance.title,
'url': instance.url,
'publishedAt': instance.publishedAt.toIso8601String(),
'isFavorite': instance.isFavorite,
};
⚠️ .g.dart 파일은 직접 수정하지 않으며, 삭제 후 다시 생성해도 됩니다.
🛠 다양한 예제들
✅ 1. List 직렬화
()
class Playlist {
final String name;
final List<Vlog> vlogs;
Playlist({required this.name, required this.vlogs});
factory Playlist.fromJson(Map<String, dynamic> json) => _$PlaylistFromJson(json);
Map<String, dynamic> toJson() => _$PlaylistToJson(this);
}
내부에 Vlog 모델을 포함한 리스트도 자동으로 처리됩니다.
✅ 2. nullable 필드
()
class Comment {
final String? content;
final String? author;
Comment({this.content, this.author});
factory Comment.fromJson(Map<String, dynamic> json) => _$CommentFromJson(json);
Map<String, dynamic> toJson() => _$CommentToJson(this);
}
null 값도 문제 없이 변환됩니다.
✅ 3. enum 변환
enum VideoType { vlog, tutorial, review }
()
class Video {
final String title;
final VideoType type;
Video({required this.title, required this.type});
factory Video.fromJson(Map<String, dynamic> json) => _$VideoFromJson(json);
Map<String, dynamic> toJson() => _$VideoToJson(this);
}
enum은 자동으로 type: 'vlog' 형태로 변환되며,
옵션으로 @JsonValue()를 사용해 커스터마이징할 수 있습니다.
✅ 4. DateTime 포맷 커스터마이징
()
class Event {
(fromJson: _fromJson, toJson: _toJson)
final DateTime start;
Event({required this.start});
static DateTime _fromJson(String date) => DateTime.parse(date);
static String _toJson(DateTime date) => date.toIso8601String();
factory Event.fromJson(Map<String, dynamic> json) => _$EventFromJson(json);
Map<String, dynamic> toJson() => _$EventToJson(this);
}
@JsonKey()를 활용하면 원하는 포맷으로 날짜를 처리할 수 있습니다.
✅ 5. freezed와 함께 쓰기
import 'package:freezed_annotation/freezed_annotation.dart';
part 'user.freezed.dart';
part 'user.g.dart';
class User with _$User {
const factory User({
required String name,
required int age,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
freezed는 immutable class, copyWith, equality 등을 자동 생성해줍니다.
JSON 관련 기능도 json_serializable로 연동됩니다.
.g.dart와 .freezed.dart 두 파일이 생성됩니다.
flutter pub run build_runner build
🧪 테스트 예시
void main() {
final json = {
"title": "Flutter 브이로그",
"url": "https://example.com/vlog1",
"publishedAt": "2025-07-16T12:00:00Z",
"isFavorite": true
};
final vlog = Vlog.fromJson(json);
print(vlog.title); // Flutter 브이로그
final backToJson = vlog.toJson();
print(backToJson);
}