JSON to Dart

fromJson 및 toJson이 포함된 Dart 클래스를 JSON에서 생성

예시 시도
루트 클래스 이름:

JSON 입력

Dart 출력

로컬에서 실행 · 시크릿 붙여넣기 안전
Dart 클래스가 여기에 표시됩니다…

JSON to Dart 변환이란?

JSON to Dart 변환은 원시 JSON 객체를 받아 타입이 지정된 필드, 명명된 생성자, fromJson 팩토리, toJson 메서드가 포함된 Dart 클래스 정의를 생성합니다. Dart는 Flutter에서 런타임 리플렉션을 지원하지 않습니다(dart:mirrors가 비활성화되어 있음). 따라서 명시적인 매핑 코드를 작성하지 않으면 JSON을 타입이 지정된 객체로 역직렬화할 수 없습니다. 모든 REST API 응답, Firebase 문서, 설정 페이로드는 타입 안전하게 필드에 접근하려면 대응하는 Dart 모델 클래스가 필요합니다.

JSON을 위한 일반적인 Dart 모델 클래스는 각 키에 대한 final 필드, 명명된 매개변수가 있는 생성자(nullable이 아닌 필드에는 required 키워드 사용), String에서 dynamic으로의 Map을 읽는 fromJson 팩토리 생성자, String에서 dynamic으로의 Map을 반환하는 toJson 메서드로 구성됩니다. 중첩된 JSON 객체는 별도의 클래스가 됩니다. 배열은 타입이 지정된 List 필드가 됩니다. nullable JSON 값은 타입에 ? 접미사를 붙이는 Dart의 null 안전성 문법을 사용합니다.

이러한 모델 클래스를 직접 작성하려면 각 JSON 키를 읽고, Dart 타입을 결정하고, 각 필드에 대한 fromJson 캐스트를 생성하고(.map().toList()를 사용한 리스트 매핑 포함), toJson 맵 리터럴을 구성하고, 중첩 객체마다 이 과정을 반복해야 합니다. 12개의 필드와 2개의 중첩 객체가 있는 JSON 객체라면 3개의 클래스, 6개의 팩토리 라인, 수십 개의 캐스트 표현식이 필요합니다. 변환기는 이 모든 것을 한 번의 붙여넣기로 수 밀리초 안에 처리합니다.

JSON to Dart 변환기를 사용하는 이유

JSON에서 Dart 모델 클래스를 수동으로 작성하려면 필드 이름을 확인하고, 샘플 값에서 타입을 추론하고, 올바른 null 처리로 fromJson 캐스트를 작성하고, 중첩 객체마다 이 과정을 반복해야 합니다. API 구조가 변경되면 모든 필드 업데이트가 생성자, fromJson, toJson을 모두 건드립니다. 변환기는 이러한 반복 작업을 없애줍니다.

즉시 클래스 생성
JSON을 붙여넣으면 1초도 안 되어 생성자, fromJson 팩토리, toJson 메서드가 포함된 완전한 Dart 클래스를 얻습니다. 중첩 객체와 리스트가 자동으로 감지되어 매핑됩니다.
🔒
개인정보 보호 우선 처리
변환은 JavaScript를 사용하여 전적으로 브라우저에서 실행됩니다. JSON 데이터가 기기 밖으로 나가지 않습니다. API 키, 토큰, 운영 페이로드가 비공개로 유지됩니다.
📝
null 안전 출력
생성된 클래스는 Dart의 완전한 null 안전성을 사용합니다. nullable JSON 필드는 ? 타입 접미사를 받으며 생성자에서 required 키워드를 생략합니다. null이 아닌 필드는 required로 표시됩니다.
📦
설치 또는 가입 불필요
페이지를 열고 JSON을 붙여넣으면 됩니다. Dart SDK, pub 의존성, 계정이 필요 없습니다. 브라우저가 있는 모든 기기에서 사용 가능합니다.

JSON to Dart 활용 사례

Flutter 앱 개발
http 또는 dio 패키지가 사용하는 REST API 응답을 위한 모델 클래스를 생성합니다. 백엔드가 반환하는 JSON을 붙여넣으면 jsonDecode와 위젯 렌더링에 바로 사용할 수 있는 Dart 클래스를 얻습니다.
Firebase / Firestore 연동
Firestore 문서 스냅샷을 위한 타입이 지정된 모델 클래스를 생성합니다. 샘플 문서를 JSON으로 붙여넣어 Dart 클래스를 생성하고, 저장소 레이어에서 snapshot.data()와 함께 fromJson을 사용하세요.
상태 관리 모델
Riverpod, Bloc, 또는 Provider를 위한 타입이 지정된 상태 객체를 정의합니다. 예상 상태 구조를 JSON으로 붙여넣으면 상태 지속성과 복원을 위한 toJson이 포함된 불변 Dart 클래스를 얻습니다.
API 클라이언트 코드 생성
Retrofit 또는 Chopper API 클라이언트를 위한 요청 및 응답 모델을 생성합니다. 샘플 JSON 페이로드를 붙여넣으면 OpenAPI 응답 스키마와 일치하는 Dart 클래스를 얻습니다.
자동화 테스트
실제 API 응답에서 타입이 지정된 테스트 픽스처를 구축합니다. JSON 샘플을 붙여넣어 모델 클래스를 생성하고, 픽스처 구조를 직접 작성하지 않고도 단위 테스트와 위젯 테스트에서 바로 사용하세요.
Dart 패턴 학습
Flutter를 배우는 학생들은 어떤 JSON 구조든 붙여넣어 Dart가 이를 어떻게 표현하는지 확인할 수 있습니다: final 필드, 명명된 생성자, 팩토리 fromJson 패턴, 실제 적용된 null 안전성 어노테이션.

JSON to Dart 타입 매핑

모든 JSON 값은 특정 Dart 타입에 매핑됩니다. 아래 표는 변환기가 각 JSON 타입을 어떻게 변환하는지 보여줍니다. 대안 열은 덜 일반적이거나 수동 매핑 시나리오에서 사용되는 타입을 보여줍니다.

JSON 타입예시 값Dart 타입대안
string"hello"StringString
number (integer)42intint
number (float)3.14doubledouble
booleantrueboolbool
nullnulldynamicNull / dynamic
object{"k": "v"}NestedClassMap<String, dynamic>
array of strings["a", "b"]List<String>List<String>
array of objects[{"id": 1}]List<Item>List<Item>
mixed array[1, "a"]List<dynamic>List<dynamic>

Dart JSON 직렬화 방식

Dart와 Flutter는 JSON 직렬화를 처리하는 여러 방법을 제공합니다. 수동 fromJson/toJson은 가장 단순한 방식으로 코드 생성이 필요 없습니다. 대규모 프로젝트에서는 json_serializable, freezed 같은 build_runner 기반 솔루션이 컴파일 시점에 보일러플레이트를 생성하여 오류를 줄이고 모델을 JSON 계약과 일관성 있게 유지합니다.

방식설명출처
json_serializableCode-generation-based JSON serialization. Generates .g.dart files at build time via build_runner. Type-safe and compile-time verified.Official (Google)
freezedGenerates immutable data classes with copyWith, fromJson/toJson, equality, and pattern matching support. Built on top of json_serializable.Community (rrousselGit)
built_valueGenerates immutable value types with serialization. Enforces immutability patterns. Used in larger codebases with strict data modeling.Google
dart_mappableAnnotation-based mapper that generates fromJson, toJson, copyWith, and equality. Simpler setup than freezed with similar features.Community
Manual fromJson/toJsonHand-written factory constructors and toJson methods. No code generation needed. Full control over the mapping logic.Built-in Dart

수동 vs json_serializable vs freezed

Dart에는 JSON 모델 클래스를 위한 세 가지 일반적인 방식이 있습니다. 수동 fromJson/toJson은 의존성이 없습니다. json_serializable은 매핑 코드를 자동화합니다. freezed는 json_serializable 위에 불변성, copyWith, 패턴 매칭을 추가합니다.

Manual fromJson/toJson
fromJson 팩토리와 toJson 메서드를 직접 작성합니다. 의존성 없음, build_runner 단계 없음. 캐스팅 로직과 오류 처리에 대한 완전한 제어권을 가집니다. 모델이 몇 개뿐인 소규모 프로젝트나 코드 생성기가 표현할 수 없는 커스텀 역직렬화가 필요할 때 최적입니다. 이 도구가 생성하는 출력 형식입니다.
json_serializable
클래스에 @JsonSerializable()을 어노테이션하고 dart run build_runner build를 실행합니다. 생성기가 _$ClassFromJson 및 _$ClassToJson 함수가 포함된 .g.dart 파일을 생성합니다. 필드 이름 변경, 기본값, 커스텀 변환기를 위한 @JsonKey를 지원합니다. 중대형 Flutter 프로젝트의 표준 선택입니다.
freezed
@freezed로 어노테이션하면 fromJson, toJson, copyWith, 동등성(== 및 hashCode), toString이 자동으로 생성된 불변 클래스를 얻습니다. sealed class 패턴을 위한 유니온 타입을 지원합니다. freezed와 json_serializable 모두 개발 의존성으로 필요합니다. 상태 관리 모델과 복잡한 도메인 객체에 최적입니다.

코드 예시

이 예시들은 JSON 역직렬화에 생성된 Dart 클래스를 사용하는 방법, build_runner로 json_serializable을 설정하는 방법, JavaScript와 Python에서 프로그래밍 방식으로 Dart 클래스를 생성하는 방법을 보여줍니다.

Dart (manual fromJson / toJson)
import 'dart:convert';

class User {
  final int id;
  final String name;
  final String email;
  final bool active;
  final Address address;
  final List<String> tags;

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

  factory User.fromJson(Map<String, dynamic> json) => User(
    id: json['id'] as int,
    name: json['name'] as String,
    email: json['email'] as String,
    active: json['active'] as bool,
    address: Address.fromJson(json['address'] as Map<String, dynamic>),
    tags: (json['tags'] as List<dynamic>).map((e) => e as String).toList(),
  );

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

class Address {
  final String street;
  final String city;
  final String zip;

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

  factory Address.fromJson(Map<String, dynamic> json) => Address(
    street: json['street'] as String,
    city: json['city'] as String,
    zip: json['zip'] as String,
  );

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

void main() {
  final jsonStr = '{"id":1,"name":"Alice","email":"alice@example.com","active":true,"address":{"street":"123 Main","city":"Springfield","zip":"12345"},"tags":["admin","user"]}';
  final user = User.fromJson(jsonDecode(jsonStr));
  print(user.name); // -> Alice
  print(jsonEncode(user.toJson())); // -> round-trip back to JSON
}
Dart (json_serializable + build_runner)
// pubspec.yaml dependencies:
//   json_annotation: ^4.8.0
//   dev_dependencies:
//     build_runner: ^2.4.0
//     json_serializable: ^6.7.0

import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart'; // generated by: dart run build_runner build

@JsonSerializable()
class User {
  final int id;
  final String name;
  final String email;
  @JsonKey(name: 'is_active')
  final bool isActive;
  final List<String> tags;

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

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

// Run code generation:
// dart run build_runner build --delete-conflicting-outputs
JavaScript (generate Dart classes from JSON)
function jsonToDart(obj, name = "Root") {
  const classes = [];
  function infer(val, fieldName) {
    if (val === null) return "dynamic";
    if (typeof val === "string") return "String";
    if (typeof val === "number") return Number.isInteger(val) ? "int" : "double";
    if (typeof val === "boolean") return "bool";
    if (Array.isArray(val)) {
      const first = val.find(v => v !== null);
      if (!first) return "List<dynamic>";
      return `List<${infer(first, fieldName)}>`;
    }
    if (typeof val === "object") {
      const cls = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
      build(val, cls);
      return cls;
    }
    return "dynamic";
  }
  function build(obj, cls) {
    const fields = Object.entries(obj).map(([k, v]) =>
      `  final ${infer(v, k)} ${k};`
    );
    classes.push(`class ${cls} {\n${fields.join("\n")}\n}`);
  }
  build(obj, name);
  return classes.join("\n\n");
}

console.log(jsonToDart({ id: 1, name: "Alice", scores: [98, 85] }, "User"));
// class User {
//   final int id;
//   final String name;
//   final List<int> scores;
// }
Python (generate Dart model from JSON)
import json

def json_to_dart(obj: dict, class_name: str = "Root") -> str:
    classes = []

    def infer(val, name):
        if val is None:
            return "dynamic"
        if isinstance(val, bool):
            return "bool"
        if isinstance(val, int):
            return "int"
        if isinstance(val, float):
            return "double"
        if isinstance(val, str):
            return "String"
        if isinstance(val, list):
            if not val:
                return "List<dynamic>"
            return f"List<{infer(val[0], name)}>"
        if isinstance(val, dict):
            cls = name[0].upper() + name[1:]
            build(val, cls)
            return cls
        return "dynamic"

    def build(obj, cls):
        fields = [f"  final {infer(v, k)} {k};" for k, v in obj.items()]
        classes.append(f"class {cls} {{\n" + "\n".join(fields) + "\n}")

    build(obj, class_name)
    return "\n\n".join(classes)

data = json.loads('{"id": 1, "name": "Alice", "active": true}')
print(json_to_dart(data, "User"))
# class User {
#   final int id;
#   final String name;
#   final bool active;
# }

자주 묻는 질문

Flutter에서 JSON 역직렬화에 dart:mirrors를 사용할 수 없는 이유는?
Flutter는 dart:mirrors(리플렉션 라이브러리)를 비활성화합니다. 트리 쉐이킹이 컴파일 시점에 어떤 코드가 사용되는지 알아야 하기 때문입니다. 리플렉션은 컴파일러가 미사용 클래스를 제거하지 못하게 하여 앱 크기를 증가시킵니다. 그래서 Flutter 프로젝트는 런타임 리플렉션 대신 명시적인 fromJson/toJson 메서드나 코드 생성이 필요합니다.
json_serializable을 사용해야 할까요, 아니면 fromJson을 직접 작성해야 할까요?
모델 클래스가 10개 미만인 프로젝트에서는 직접 작성한 fromJson/toJson이 간단하며 빌드 의존성을 추가하지 않습니다. 모델이 10개를 초과하거나 자주 변경되면 json_serializable이 시간을 절약하고 복사-붙여넣기 오류를 줄여줍니다. 어노테이션에서 매핑 코드를 생성하므로 필드 추가 시 build_runner만 다시 실행하면 됩니다.
변환기는 중첩된 JSON 객체를 어떻게 처리하나요?
중첩된 JSON 객체는 각각 별도의 Dart 클래스가 됩니다. "street"와 "city" 키를 가진 객체가 담긴 "address" JSON 필드가 있다면, 변환기는 해당 필드와 타입을 가진 Address 클래스를 생성하고 부모 필드를 Address 타입으로 지정합니다. fromJson 팩토리는 중첩된 맵을 캐스팅하여 Address.fromJson에 전달합니다.
JSON 필드가 null이면 어떻게 되나요?
null 필드는 ? 접미사가 붙은 dynamic으로 타입이 지정됩니다. 변환기가 null 값만으로는 의도한 타입을 결정할 수 없기 때문입니다. API 계약에서 타입을 확인한 후 dynamic?을 올바른 타입(String?, int? 등)으로 교체하세요. 그러면 Dart의 null 안전성이 컴파일 시점에 올바른 null 검사를 강제합니다.
유효한 Dart 식별자가 아닌 JSON 키는 어떻게 처리하나요?
"first-name"이나 "2nd_place" 같은 JSON 키는 유효한 Dart 필드 이름이 아닙니다. 수동 fromJson에서는 팩토리 생성자에서 임의의 JSON 키를 임의의 Dart 필드 이름에 매핑할 수 있습니다. json_serializable에서는 @JsonKey(name: 'first-name')으로 필드에 어노테이션합니다. 변환기는 키 이름을 자동으로 camelCase Dart 필드로 변환합니다.
이 도구의 출력에 freezed를 적용할 수 있나요?
이 도구는 수동 fromJson/toJson이 있는 표준 Dart 클래스를 생성합니다. freezed로 전환하려면 클래스가 @freezed 어노테이션을 사용하도록 변경하고, 직접 작성한 생성자와 팩토리를 제거한 뒤 freezed 믹스인을 추가하세요. 그런 다음 build_runner를 실행하여 .freezed.dart 및 .g.dart 파일을 생성합니다. 생성된 출력의 필드 이름과 타입이 그대로 전달됩니다.
원시 맵과 타입이 지정된 Dart 클래스의 차이는 무엇인가요?
원시 맵(String에서 dynamic으로의 Map)은 타입 검사 없이 JSON 데이터에 직접 접근합니다. 호출 지점에서 모든 값을 캐스팅해야 하며, 키 이름의 오타는 런타임에 조용히 실패합니다. 타입이 지정된 Dart 클래스는 fromJson 경계에서 타입 불일치를 감지하고, 필드 이름에 대한 IDE 자동완성을 제공하며, 안전한 리팩토링을 가능하게 합니다. 성능 차이는 무시할 수 있으며, 이점은 전적으로 정확성과 유지보수성에 있습니다.