DevToolBox무료
블로그

JSON to Dart: Flutter 모델 클래스 완전 가이드

10분 읽기by DevToolBox

JSON을 Dart 모델 클래스로 변환하는 것은 Flutter 개발에서 가장 일반적인 작업 중 하나입니다. REST API에서 데이터를 가져오든 로컬 설정을 읽든, JSON 데이터를 안전하게 파싱하고 사용하려면 잘 구조화된 Dart 클래스가 필요합니다.

모델 클래스가 필요한 이유

  • 타입 안전성 — 런타임이 아닌 컴파일 타임에 오류 포착
  • IDE 자동완성 및 리팩토링 지원
  • 프론트엔드와 백엔드 간의 명확한 데이터 계약
  • 쉬운 테스트 및 유지보수

기본 JSON-Dart 타입 매핑

JSON 타입Dart 타입예시
stringString"hello" → "hello"
number (int)int42 → 42
number (float)double3.14 → 3.14
booleanbooltrue → true
nullNull / dynamicnull → null
arrayList<T>[1,2,3] → [1,2,3]
objectMap / Class{} → User()

수동 모델 클래스 (fromJson / toJson)

가장 직접적인 방법은 모델 클래스를 수동으로 작성하는 것입니다:

// JSON input
{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com",
  "isActive": true,
  "score": 95.5
}

// Dart model class
class User {
  final int id;
  final String name;
  final String email;
  final bool isActive;
  final double score;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.isActive,
    required this.score,
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      email: json['email'] as String,
      isActive: json['isActive'] as bool,
      score: (json['score'] as num).toDouble(),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
      'isActive': isActive,
      'score': score,
    };
  }
}

중첩 객체 처리

JSON에 중첩 객체가 포함된 경우 각 레벨에 별도의 Dart 클래스를 만듭니다:

// JSON with nested object
{
  "id": 1,
  "name": "John",
  "address": {
    "street": "123 Main St",
    "city": "Springfield",
    "zipCode": "62701"
  }
}

// Dart classes
class Address {
  final String street;
  final String city;
  final String zipCode;

  Address({required this.street, required this.city, required this.zipCode});

  factory Address.fromJson(Map<String, dynamic> json) {
    return Address(
      street: json['street'] as String,
      city: json['city'] as String,
      zipCode: json['zipCode'] as String,
    );
  }

  Map<String, dynamic> toJson() => {
    'street': street, 'city': city, 'zipCode': zipCode,
  };
}

class User {
  final int id;
  final String name;
  final Address address;

  User({required this.id, required this.name, required this.address});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      address: Address.fromJson(json['address'] as Map<String, dynamic>),
    );
  }

  Map<String, dynamic> toJson() => {
    'id': id, 'name': name, 'address': address.toJson(),
  };
}

리스트와 배열 처리

JSON 배열은 Dart List 타입에 매핑됩니다:

// JSON with arrays
{
  "name": "John",
  "hobbies": ["coding", "reading", "gaming"],
  "orders": [
    {"id": 1, "product": "Laptop", "price": 999.99},
    {"id": 2, "product": "Mouse", "price": 29.99}
  ]
}

// Dart
class User {
  final String name;
  final List<String> hobbies;
  final List<Order> orders;

  User({required this.name, required this.hobbies, required this.orders});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      name: json['name'] as String,
      hobbies: List<String>.from(json['hobbies'] as List),
      orders: (json['orders'] as List)
          .map((e) => Order.fromJson(e as Map<String, dynamic>))
          .toList(),
    );
  }
}

Null Safety 모범 사례

Dart의 sound null safety에서는 nullable 필드를 올바르게 처리해야 합니다:

  • 항상 존재해야 하는 필드에는 `required` 키워드 사용
  • nullable 타입에는 `?` 접미사 사용 (예: `String?`)
  • fromJson에서 `??` 연산자로 기본값 제공
  • 값이 초기화될 것이 확실한 경우에만 `late` 키워드 사용
class User {
  final int id;
  final String name;
  final String? bio;        // nullable
  final String avatarUrl;   // with default

  User({
    required this.id,
    required this.name,
    this.bio,
    this.avatarUrl = 'https://example.com/default-avatar.png',
  });

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      bio: json['bio'] as String?,
      avatarUrl: json['avatarUrl'] as String? ?? 'https://example.com/default-avatar.png',
    );
  }
}

json_serializable 패키지 사용

대규모 프로젝트에서는 json_serializable 패키지가 보일러플레이트를 자동화합니다:

단계 1: 의존성 추가

# pubspec.yaml
dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  json_serializable: ^6.7.1
  build_runner: ^2.4.6

단계 2: 어노테이션이 있는 모델 생성

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  final int id;
  final String name;
  final String email;

  @JsonKey(name: 'is_active')
  final bool isActive;

  @JsonKey(defaultValue: 0.0)
  final double score;

  User({
    required this.id,
    required this.name,
    required this.email,
    required this.isActive,
    required this.score,
  });

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

단계 3: Build Runner 실행

dart run build_runner build --delete-conflicting-outputs

Freezed로 불변 모델 만들기

freezed 패키지는 copyWith, 동등성, JSON 직렬화가 포함된 불변 클래스를 생성합니다:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
class User with _$User {
  const factory User({
    required int id,
    required String name,
    required String email,
    @Default(false) bool isActive,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

일반적인 실수와 해결법

실수해결법
null 체크 없이 `json['key']` 사용`json['key'] as String? ?? ''` 사용 또는 null 처리
중첩 객체 변환을 잊음객체에는 `NestedClass.fromJson(json['key'])` 호출
모든 숫자에 `int` 사용`num` 사용 후 변환: `(json['price'] as num).toDouble()`
누락된 키를 처리하지 않음`json.containsKey('key')` 사용 또는 기본값 제공
toJson에서 잘못된 타입 반환중첩 객체가 자체 `.toJson()` 호출을 확인

자주 묻는 질문

JSON을 Dart 클래스로 변환하는 가장 좋은 방법은?

소규모 프로젝트에서는 수동 변환이 효과적입니다. 대규모 프로젝트에서는 json_serializable 또는 freezed 패키지로 보일러플레이트 코드를 자동 생성합니다.

Dart에서 동적 JSON 키를 어떻게 처리하나요?

알 수 없는 키가 있는 객체에는 Map<String, dynamic>을 사용합니다.

json_serializable과 freezed 중 어떤 것을 사용해야 하나요?

간단한 JSON 직렬화에는 json_serializable. 불변성, copyWith, union 타입도 필요하면 freezed를 사용합니다.

JSON-Dart 변환에서 날짜를 어떻게 처리하나요?

DateTime.parse(json['date'])로 ISO 8601 문자열을 파싱합니다. toJson에서는 dateTime.toIso8601String()을 사용합니다.

𝕏 Twitterin LinkedIn
도움이 되었나요?

최신 소식 받기

주간 개발 팁과 새 도구 알림을 받으세요.

스팸 없음. 언제든 구독 해지 가능.

Try These Related Tools

DTJSON to Dart{ }JSON FormatterKTJSON to KotlinTSJSON to TypeScript

Related Articles

YAML 문법 & 검증: 일반적인 오류와 해결 방법

YAML 문법 마스터: 들여쓰기 규칙, 파싱 오류, 데이터 타입, 모범 사례.

JSON to Kotlin 데이터 클래스: kotlinx.serialization, Moshi, Gson 가이드

JSON을 Kotlin 데이터 클래스로 온라인 변환. kotlinx.serialization, Moshi, Gson을 사용한 JSON 파싱 방법을 알아보세요.