MYMONDAY.ai
서비스스튜디오도구데이터·포맷73CSS 생성기19색상·이미지10인코딩·보안10웹·SEO11개발·데브옵스10파일·내보내기18계산기6가이드

CSV Dart 클래스 생성기

CSV를 붙여 넣으면 한 행을 모델링하는 fromJson·toJson 포함 Dart 클래스가 나와 Flutter 프로젝트에 바로 넣을 수 있습니다.

  • 브라우저에서 처리
  • 가입 없음
  • 무료

도구 작업 공간

예제

옵션

끄면 열 이름이 column_1, column_2 …가 되고 JSON은 배열이 됩니다.

비어 있거나 구분자만 있는 줄을 버려서 뒤 레코드가 밀리지 않게 합니다.

채워진 셀이 모두 맞을 때만 열이 숫자·불리언이 됩니다. 01234나 20자리 ID는 텍스트로 남습니다. 끄면 모든 셀이 적은 그대로 텍스트입니다.

대소문자 구분 없이 쉼표로 구분합니다. 빈 셀은 항상 null입니다.

CSV 입력

6줄181byte구분자: 쉼표 (자동 감지)

결과

⌘/Ctrl + Enter로 복사
결과 · csv_row.dart
typedef CsvRow = List<CsvRowItem>;

class CsvRowItem {
  final int id;
  final String customer;
  final double total;
  final bool paid;
  final String? note;
  final String? shipped;

  CsvRowItem({
    required this.id,
    required this.customer,
    required this.total,
    required this.paid,
    required this.note,
    required this.shipped,
  });

  factory CsvRowItem.fromJson(Map<String, dynamic> json) => CsvRowItem(
        id: json['id'] as int,
        customer: json['customer'] as String,
        total: (json['total'] as num).toDouble(),
        paid: json['paid'] as bool,
        note: json['note'] as String?,
        shipped: json['shipped'] as String?,
      );

  Map<String, dynamic> toJson() => {
        'id': id,
        'customer': customer,
        'total': total,
        'paid': paid,
        'note': note,
        'shipped': shipped,
      };
}
행3
열6
타입1
크기878byte

파일을 읽은 결과

변환기가 보는 앞쪽 레코드와 열마다 정해진 타입입니다.

줄id숫자customer텍스트total숫자paid불리언note텍스트shipped텍스트
21001Ada Lovelace87.90trueGift wrap, please2026-03-14
31002Grace Hopper19falseNA2026-03-15
41003Linus1200.5trueLine one line two·

이 CSV Dart 생성기는 CSV나 TSV의 헤더와 값을 읽어 final 필드, 이름 있는 매개변수 생성자, fromJson 팩토리, toJson 메서드를 가진 평범한 Dart 클래스와 행 목록용 typedef를 씁니다. 스프레드시트 데이터를 읽는 Flutter·Dart 프로젝트에서 CSV를 먼저 JSON으로 바꾸거나, 파싱한 CSV 행으로 맵을 만들어 쓸 때를 위해 만들었습니다.

필드 이름은 Dart 관례를 따릅니다. first name과 user-id는 firstName, userId가 되고, class나 type 같은 예약어에는 Value 접미사가 붙으며, 원래 헤더는 JSON 키로 남습니다. 타입은 모든 행을 보고 정합니다. 정수와 소수가 섞인 열은 double이 되어 num을 거쳐 디코딩되고, 빈 셀이 있는 열은 String?, double?처럼 널 허용이 됩니다.

CSV는 브라우저 안에서 처리되며 서버로 전송되지 않습니다. 한도는 입력 20 MiB, 데이터 행 50,000개, 열 200개이고, 다운로드 파일 이름은 csv_row.dart처럼 루트 이름을 스네이크 케이스로 바꾼 것입니다.

사용 방법

  1. CSV 불러오기

    행을 붙여 넣거나 .csv, .tsv 파일을 엽니다. 헤더 행이 필드 이름과 JSON 키를 모두 정합니다.

  2. 루트 이름 설정

    루트 타입 이름의 기본값 CsvRow는 typedef CsvRow = List<CsvRowItem>;과 class CsvRowItem을 만듭니다. Products처럼 복수형을 쓰면 typedef Products = List<Product>;가 됩니다.

  3. 감지 여부 선택

    "숫자·불리언·null 감지"를 켜면 열이 int, double, bool, String이 되고, 빈 셀이나 null 단어가 있는 열은 널 허용이 됩니다. 끄면 모든 필드가 널 비허용 String입니다.

  4. 결과 확인

    "파일을 읽은 결과" 패널에서 열마다 정해진 타입을 확인합니다. 클래스에는 final 필드, required 이름 매개변수 생성자, fromJson, toJson이 있습니다.

  5. 프로젝트에 추가

    코드를 복사하거나 .dart 파일로 내려받습니다. fromJson에는 디코딩한 JSON 맵이나, 헤더와 파싱한 행 하나로 만든 맵 중 값의 타입이 이미 변환된 것을 넘기세요.

작동 원리

생성되는 클래스의 구성

출력은 typedef Root = List<RootItem>;으로 시작하고, 그 뒤에 final 필드, RootItem({required this.id, ...}) 형태의 생성자, 항목마다 명시적으로 캐스트하는 RootItem.fromJson(Map<String, dynamic> json) 팩토리, 원래 헤더 텍스트를 키로 쓰는 Map<String, dynamic>을 반환하는 toJson()이 이어집니다. 키는 작은따옴표 Dart 문자열이며, 헤더의 $는 문자열 보간으로 읽히지 않도록 \$로 이스케이프합니다.

널 허용 필드도 생성자에서 required를 유지합니다. CSV 행에는 키가 항상 있고 값만 null일 수 있기 때문입니다. 모든 행에서 비어 있는 열은 dynamic이 되어 캐스트 없이 그대로 전달되며 required도 붙지 않습니다.

  factory CsvRowItem.fromJson(Map<String, dynamic> json) => CsvRowItem(
        id: json['id'] as int,
        score: (json['score'] as num?)?.toDouble(),
        note: json['note'] as String?,
      );

이름 규칙과 예약어

헤더를 공백, 구두점, 소문자에서 대문자로 바뀌는 지점에서 단어로 나눈 뒤, ASCII 영문자와 숫자만으로 필드는 lowerCamelCase, 클래스는 UpperCamelCase로 합칩니다. 숫자로 시작하게 되는 이름에는 field 접두사가 붙어 1st place는 field1stPlace가 됩니다. class, default, type, set, required 같은 예약어와 문맥 키워드에는 Value가 붙습니다.

이름처럼 ASCII 영문자가 하나도 없는 헤더는 field, 그다음은 field2, field3이 됩니다. 이때도 fromJson과 toJson은 원래 헤더를 키로 읽고 씁니다.

헤더필드JSON 키
idid'id'
first namefirstName'first name'
user-iduserId'user-id'
classclassValue'class'
typetypeValue'type'
1st placefield1stPlace'1st place'
total $total'total \$'
이름field'이름'

타입과 캐스트

감지 단계에서 열마다 종류가 하나로 정해지고, 생성기는 그다음 모든 행을 봅니다. 정수만 있으면 as int로 읽는 int입니다. 소수가 하나라도 있는 열은 double이 되고 (json['x'] as num).toDouble()로 디코딩합니다. jsonDecode는 2를 int로 돌려주므로 그냥 as double이면 예외가 나기 때문입니다. true, false 열은 bool입니다.

빈 셀이나 null 단어가 하나라도 있는 열은 널 허용이 되고 캐스트도 as String?, as int?, (json['x'] as num?)?.toDouble()로 바뀝니다. 감지를 끄면 모든 필드가 as String으로 읽는 String이며, 문자열 데이터에서는 항상 성공합니다.

열의 값Dart 타입fromJson 디코딩
1, 2intjson['x'] as int
10, 12.5double(json['x'] as num).toDouble()
true, FALSEbooljson['x'] as bool
Ari, (빈 셀)String?json['x'] as String?
3, N/Aint?json['x'] as int?
9.5, (빈 셀)double?(json['x'] as num?)?.toDouble()
02134, 10001Stringjson['x'] as String
(빈 셀), NAdynamicjson['x']

실제 예시

기본 설정
입력
id,name,active
1,Ari,true
2,Bea,false
출력
typedef CsvRow = List<CsvRowItem>;

class CsvRowItem {
  final int id;
  final String name;
  final bool active;

  CsvRowItem({
    required this.id,
    required this.name,
    required this.active,
  });

  factory CsvRowItem.fromJson(Map<String, dynamic> json) => CsvRowItem(
        id: json['id'] as int,
        name: json['name'] as String,
        active: json['active'] as bool,
      );

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

루트 타입 이름 CsvRow, 감지 켬입니다.

이름이 바뀐 필드, 예약어, 섞인 숫자
입력
id,first name,class,price
1,Ari,A,1.5
2,B,C,2
출력
typedef Product = List<ProductItem>;

class ProductItem {
  final int id;
  final String firstName;
  final String classValue;
  final double price;

  ProductItem({
    required this.id,
    required this.firstName,
    required this.classValue,
    required this.price,
  });

  factory ProductItem.fromJson(Map<String, dynamic> json) => ProductItem(
        id: json['id'] as int,
        firstName: json['first name'] as String,
        classValue: json['class'] as String,
        price: (json['price'] as num).toDouble(),
      );

  Map<String, dynamic> toJson() => {
        'id': id,
        'first name': firstName,
        'class': classValue,
        'price': price,
      };
}

루트 타입 이름은 Product입니다. price에 1.5와 2가 있어 double이 되었고, num을 거쳐 디코딩하므로 정수 2도 받습니다.

빈 셀은 널 허용 필드가 됨
입력
id,score,note
1,9.5,
2,,gift
3,8,NA
출력
typedef CsvRow = List<CsvRowItem>;

class CsvRowItem {
  final int id;
  final double? score;
  final String? note;

  CsvRowItem({
    required this.id,
    required this.score,
    required this.note,
  });

  factory CsvRowItem.fromJson(Map<String, dynamic> json) => CsvRowItem(
        id: json['id'] as int,
        score: (json['score'] as num?)?.toDouble(),
        note: json['note'] as String?,
      );

  Map<String, dynamic> toJson() => {
        'id': id,
        'score': score,
        'note': note,
      };
}

비어 있는 score와, 비어 있거나 NA인 note 때문에 두 필드 모두 널 허용이 되었습니다. score에는 9.5와 8이 섞여 있어 double?입니다.

감지를 끈 경우
입력
id,price
1,10
2,12.5
출력
typedef CsvRow = List<CsvRowItem>;

class CsvRowItem {
  final String id;
  final String price;

  CsvRowItem({
    required this.id,
    required this.price,
  });

  factory CsvRowItem.fromJson(Map<String, dynamic> json) => CsvRowItem(
        id: json['id'] as String,
        price: json['price'] as String,
      );

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

모든 필드가 String이라 CSV 원문 텍스트로 바로 만든 맵도 받을 수 있습니다.

자주 하는 실수

  • fromJson에는 CSV 행이 아니라 맵이 필요함

    fromJson은 Map<String, dynamic>을 기대합니다. csv 패키지는 값의 리스트를 돌려주므로 헤더와 각 행을 묶어 맵을 만들거나, 파일을 먼저 JSON으로 변환해 디코딩하세요.

  • 값의 타입이 이미 변환되어 있어야 함

    CSV 원문 텍스트로 만든 맵에는 문자열만 들어 있어 json['id'] as int가 "1"에서 예외를 던집니다. 같은 감지 설정으로 CSV를 JSON으로 변환해 디코딩하거나, 여기서 감지를 꺼 모든 필드를 String으로 만드세요.

  • 널 허용 필드도 required

    final String? note;도 생성자에서는 required this.note이므로, 코드에서 객체를 직접 만들 때는 note: null을 명시해야 합니다. 매개변수를 생략 가능하게 하려면 required를 지우세요.

  • 모든 행에서 비어 있는 열

    이런 열은 캐스트 없는 dynamic이 되어 그 필드의 타입 안전성을 잃습니다. 샘플에 그 열의 실제 값을 하나 이상 넣거나 타입을 직접 정하세요.

  • 라틴 문자가 없는 헤더

    한글처럼 다른 문자로만 된 헤더는 field, field2 같은 이름이 되어 데이터의 의미가 드러나지 않습니다. 복사한 뒤 필드 이름을 바꾸세요. fromJson과 toJson의 JSON 키는 그대로 정확합니다.

  • json_serializable 어노테이션 없음

    출력은 코드 생성을 쓰지 않는 수작업 스타일입니다. 프로젝트가 json_serializable이나 freezed를 쓴다면 이 클래스를 초안으로 보고 어노테이션을 직접 붙이세요.

자주 묻는 질문

json_serializable이나 freezed와 함께 쓸 수 있나요?

수동 fromJson·toJson을 가진 평범한 Dart 클래스입니다. 그대로 써도 되고, 필드 목록만 json_serializable이나 freezed 클래스로 복사해도 됩니다.

CSV 행을 클래스에 어떻게 불러오나요?

같은 설정으로 CSV를 JSON으로 변환해 jsonDecode로 디코딩한 뒤 각 요소를 fromJson에 넘기세요. Dart에서 CSV를 직접 파싱한다면 헤더와 각 행으로 맵을 만들고, 값을 먼저 생성된 타입으로 변환해야 합니다.

ID 열이 왜 int로 나오나요?

감지를 켜면 앞자리 0이 없는 정수만 든 열은 int가 됩니다. 00123처럼 앞자리 0이 있는 ID는 이미 String으로 남습니다. 모든 ID를 텍스트로 두려면 감지를 끄세요.

소수 필드를 왜 num을 거쳐 디코딩하나요?

jsonDecode는 2.5가 함께 있는 열이라도 2를 int로 돌려줍니다. (json['price'] as num).toDouble()은 둘 다 받지만 as double은 정수 값에서 TypeError를 던집니다.

널 안전성을 지원하나요?

지원합니다. 열에 빈 셀이나 null 단어가 없으면 필드는 널 비허용이고, 있으면 String?, int?, double?, bool?과 그에 맞는 캐스트가 쓰입니다.

예약어는 어떻게 처리되나요?

class나 type처럼 Dart 예약어나 문맥 키워드인 헤더에는 Value가 붙고, 원래 헤더는 JSON 키로 남습니다.

데이터가 업로드되나요?

아니요. CSV와 생성된 클래스는 브라우저 안에서 처리되며 서버로 전송되지 않습니다. 여기서 모델링하는 고객 목록이나 사내 내보내기 데이터는 내 컴퓨터에 머뭅니다.

마지막 업데이트: