JSON to TypeScript 변환
JSON 데이터를 자동으로 TypeScript 인터페이스 또는 타입 정의로 변환합니다
// JSON을 입력하면 자동으로 TypeScript 타입 정의가 생성됩니다JSON to TypeScript란?
JSON to TypeScript는 JSON 데이터를 자동으로 TypeScript 타입 정의로 변환하는 도구입니다. JSON 구조를 분석하여 해당하는 interface 또는 type 선언을 생성하고, TypeScript 프로젝트에서 타입 안전성과 코드 힌트를 얻는 데 도움을 줍니다. 실제 데이터나 코드베이스에 적용할 때는 파서, 테스트, 프로젝트 규칙으로 결과를 다시 확인하는 것이 중요합니다.
사용 방법
사용 방법
- 왼쪽 입력란에 JSON 데이터를 붙여넣거나, 샘플 버튼을 클릭하여 예제를 로드하세요
- 옵션을 설정하세요: 루트 타입 이름 설정, 인터페이스/타입 스타일 선택, export 추가 여부 등
- 오른쪽에 해당 TypeScript 타입 정의가 자동으로 생성됩니다
- '복사' 버튼을 클릭하여 생성된 타입 정의를 클립보드에 복사하세요
- 타입 정의를 TypeScript 프로젝트에 붙여넣어 사용하세요
타입 생성 팁
- 대표적인 JSON 샘플을 사용하세요. 단일 예제만으로는 실제 API의 모든 선택적 필드, nullable 값 또는 유니온 타입을 알 수 없습니다.
- 커밋하기 전에 생성된 이름을 검토하세요. 특히 중첩 객체의 경우 자동 인터페이스 이름이 너무 일반적일 수 있습니다.
활용 사례
기술 원리
JSON to TypeScript는 근본적으로 '구조 추론' 과정입니다. 도구는 JSON 트리의 각 노드를 재귀적으로 순회하며, 각 값의 런타임 타입을 TypeScript 타입으로 매핑합니다: string -> string, number -> number, boolean -> boolean, null -> null, object -> 하위 인터페이스, array -> 요소 타입의 배열. 출력은 TypeScript `interface` 선언 집합(또는 'type' 출력 모드를 선택한 경우 `type` 별칭)과 객체 값 필드에서 추출된 모든 중첩 인터페이스로 구성됩니다. 추론의 어려운 부분은 배열의 유니온 vs 단일 타입 결정입니다. 배열의 모든 요소가 동일한 타입이면 출력은 `T[]`(동종 배열)입니다. 요소가 다른 타입이면 해당 타입의 유니온이 출력됩니다: `[1, 'a', true]`는 `(number | string | boolean)[]`가 됩니다. 입력이 리터럴과 구조화된 값을 혼합한 객체(예: `{ id: 1, name: 'Alice', tags: ['admin', 'editor'] }`)인 경우, 각 필드는 고유한 타입을 가집니다. 혼합 기본 값을 가진 객체에 대해 페이지는 단일 인터페이스(유니온가 아닌)를 출력합니다. 이것이 TypeScript 코드가 기대하는 것이며 JSON 객체의 레코드 타입으로서의 본질과 일치하기 때문입니다. 여러 샘플 객체(일반적인 사용 사례: JSON 배열 붙여넣기)는 모양의 교차로 병합됩니다. 모든 샘플에 존재하는 필드는 필수, 어떤 샘플에서든 누락된 필드는 선택 사항(`fieldName?: T`)이 됩니다. 이는 API 응답에 적절한 동작입니다. 200 OK 응답은 하나의 모양, 404 응답은 다른 모양이며, 두 유니온이 올바른 TS 타입입니다. 단일 샘플의 경우 모든 필드가 필수입니다. 이름 정규화는 대부분의 'JSON to TS' 도구가 부족한 부분입니다. JSON 키는 보통 snake_case(`user_id`, `created_at`) 또는 camelCase(`userId`, `createdAt`)입니다. TypeScript 관례는 인터페이스 이름은 PascalCase(`UserProfile`, `AuditEntry`), 속성 이름은 camelCase입니다. 페이지는 JSON 키를 camelCase로 변환(또는 이미 camelCase이거나 단일 단어이면 유지)하고 인터페이스 이름은 PascalCase로 변환합니다. 내부 객체는 명명된 인터페이스로 추출됩니다: `{ user: { name, age } }`는 `interface User { name: string; age: number; }`가 되고 부모는 `interface Root { user: User; }`가 됩니다. 재귀 구조(`children: TreeNode[]` 필드가 있는 트리 노드)는 순환이 명확하도록 정의되도록 `interface` 대신 전방 `type` 선언을 사용하여 자기 참조를 얻습니다. '루트' 토글은 배열 루트와 객체 루트의 차이입니다. JSON 배열은 wrap-array 토글에 따라 `type Root = Item[]` 또는 `interface Root { items: Item[]; ... }`가 됩니다. JSON 객체는 최상위 레벨에서 단일 인터페이스가 됩니다. 날짜 문자열(RFC 3339 / ISO 8601)은 `string` 대신 `Date`로 태그할 수 있습니다. UUID, 이메일, URL은 strict 모드 토글이 켜져 있을 때 브랜디드 타입(`type UUID = string & { readonly __brand: 'UUID' };`)으로 태그할 수 있습니다. JSDoc 생성은 휴리스틱입니다. `email`이라는 이름의 필드(값이 기본 이메일 정규식과 일치하는 경우)는 선언 위에 `/** user email */` 같은 주석을 받습니다. `id`, `name`, `url`, `created_at`/`createdAt`, `updated_at`/`updatedAt`, `description`, `title`도 마찬가지입니다. 주석은 최선의 노력이며 사람이 작성한 것처럼 가장하지 않습니다. 사람이 편집할 시작점을 제공합니다. 페이지는 또한 출력이 그대로 모든 TS 파일에 삽입될 수 있도록 `export` 수정자를 출력하고, 스키마가 재생성될 때 수동 편집이 덮어쓰여지는 것을 방지하기 위해 `// generated by json-to-typescript, do not edit by hand` 헤더를 포함합니다.
- 기본 타입 매핑: JSON의 string/number/boolean/null은 TypeScript의 string/number/boolean/null에 일대일로 매핑. 큰 정수(>2^53)는 BigInt가 필요. 값이 Number.MAX_SAFE_INTEGER를 초과하면 페이지는 `bigint`로 태그.
- 배열 타입 추론: 모든 요소를 순회하고 타입의 유니온을 취함. 동일한 타입은 `T[]`로 축소, 다른 타입은 `(A | B | C)[]`를 형성. 빈 배열은 요소 타입을 알 수 없으므로 `unknown[]`가 됨.
- 중첩 객체 추출: 내부 객체는 자동으로 명명된 인터페이스로 리프트됨. 재귀 구조는 TypeScript의 타입 검사기에서 전방 참조가 명확하도록 `type Name = ...`(interface가 아닌)를 사용.
- 선택 필드 감지: 여러 샘플 객체를 병합할 때 모든 샘플에 존재하는 필드는 필수, 어떤 샘플에서든 누락된 필드는 선택 사항(`fieldName?: T`). 단일 샘플 입력은 모든 필드를 필수로 표시.
- 이름 규약: JSON 키는 속성 이름에 camelCase로 변환(user_id -> userId, created_at -> createdAt), 인터페이스 이름은 PascalCase(UserProfile, AuditEntry). 단일 단어 키는 유지. 키의 숫자는 유지. 선행 숫자는 유효한 TS 식별자를 유지하기 위해 밑줄 접두사 추가.
- 루트 토글: 배열 루트 -> `export type Root = Item[]`(또는 wrap-array가 켜져 있으면 `{ items: Item[]; }`로 감싸짐). 객체 루트 -> 단일 `export interface Root { ... }`. 재귀 구조는 먼저 스텁 전방 선언을 받음.
- JSDoc 주석: 알려진 필드 이름(email, id, name, url, createdAt, updatedAt, description, title)에 대한 휴리스틱 주석 생성. 출력에는 `export` 수정자와 수동 편집을 억제하기 위한 `// generated, do not edit by hand` 헤더가 포함.
- Strict 모드: 브랜디드 타입(UUID, Email, URL, Date), readonly 수정자, exactOptionalPropertyTypes, noUncheckedIndexedAccess에 옵트인. strict 모드가 켜져 있으면 페이지는 `type UUID = string & { readonly __brand: 'UUID' };`를 출력하여 다운스트림 코드가 원시 문자열과 타입 값을 구별할 수 있도록 함.
예시
단순 객체를 interface로 변환
{"id": 1, "name": "Alice", "active": true}
->
interface User {
id: number;
name: string;
active: boolean;
}중첩 객체에서 하위 인터페이스 생성
{"user": {"name": "Alice", "address": {"city": "NYC"}}}
->
interface Root {
user: User;
}
interface User {
name: string;
address: Address;
}
interface Address {
city: string;
}배열을 readonly 튜플로 변환
[{"id": 1}, {"id": 2}]
->
interface Item {
id: number;
}
type Root = readonly Item[];자주 묻는 질문
어떤 TypeScript 구조체를 생성하나요?
기본은 객체 형태마다 하나씩 인터페이스를 만드는 것입니다. 선택 필드에는 ? 표시가 붙고, 배열은 T[]가 됩니다. 타입이 섞인 배열은 유니언이 되며, 인식된 포맷의 문자열(예: ISO 날짜)은 브랜디드 타입을 명시적으로 켜지 않는 한 그대로 string으로 남습니다. 일부 페이지는 'interface' 대신 'type' 별칭을 출력하기도 합니다.
중첩 객체는 어떻게 처리되나요?
각각의 고유한 중첩 형태마다 별도의 인터페이스가 생성되며, 이름은 속성 경로(User, UserAddress, UserPreferences)를 따라 붙습니다. 두 속성이 같은 형태를 공유할 때 중복 제거 여부는 설정에 따라 다르므로, 출력을 확인하고 필요하면 직접 통합해 주세요.
속성은 필수인가요, 선택인가요?
기본적으로 샘플에 등장한 속성은 required로 표시됩니다. 일부 배열 요소에는 있지만 다른 요소에는 없는 속성은 `?`로 선택 표시됩니다. 샘플이 가능한 형태들 중 하나의 예시일 뿐이라면 'all optional' 옵션을 켜세요.
null과 undefined는 어떻게 다뤄지나요?
JSON의 null은 TypeScript에서도 `null`이 됩니다(undefined가 아닙니다). 어떤 속성이 어떨 때는 문자열, 어떨 때는 null이라면 `string | null`로 표현됩니다. JSON에는 undefined가 없으므로 생성기도 `undefined`를 직접 내보내지 않습니다.
유니언 타입과 판별 유니언은 어떻게 처리되나요?
배열에 서로 다른 객체 형태가 섞여 있으면 인터페이스의 유니언으로 출력됩니다. 다만 판별자 필드를 자동으로 인식하지는 않으므로, 완전한 타입 좁히기를 원한다면 'kind' 같은 태그를 사용한 판별 유니언으로 직접 다시 작성하세요.
JSON이 업로드되나요?
아니요. 변환은 JS 기반 JSON 파서와 문자열 템플릿으로 브라우저 안에서 이루어집니다. 붙여 넣은 JSON은 기기 밖으로 나가지 않습니다.
백엔드가 실제로 반환하는 형태와 일치할까요?
샘플의 정확도만큼만 일치합니다. 런타임 데이터는 항상 스키마(Zod, io-ts, Yup)로 검증하세요. TypeScript 타입은 런타임에 사라지므로, 백엔드가 예시에서 벗어나도 잡아 주지 못합니다.