JSON → TypeScript
JSON에서 TypeScript 인터페이스를 생성합니다. 배열 요소 병합, 선택적 키 표시, 혼합 타입 union, null 구분 — 모두 브라우저에서 동작합니다.
export interface Root {
id: number
name: string
active: boolean
address: Address
roles: string[]
posts: Post[]
tags: unknown[]
}
export interface Address {
city: string
zip: null
}
export interface Post {
id: number
title: string
views: number | string
pinned?: boolean
}
인터페이스: 3
타입은 데이터에서 추론되며, 추측하지 않습니다
JSON 문서는 자신의 타입을 담지 않습니다. 값을 담을 뿐이고, 타입은 그로부터 다시 읽어내야 합니다. 단일 객체라면 쉽지만 컬렉션에서는 의외로 미묘합니다. 원하는 형태는 어느 한 레코드의 형태가 아니라 모든 레코드가 만족해야 하는 형태이기 때문입니다. 이 도구는 샘플이 담은 모든 것을 살펴 JSON 샘플에서 TypeScript 인터페이스를 추론하므로, 생성되는 타입은 우연히 맨 위에 있던 첫 행이 아니라 데이터 전체를 기술합니다.
변환은 한 방향뿐입니다. 타입을 다시 JSON으로 바꾸는 것은 값을 지어내는 일이며, 여기서의 목적은 그 반대입니다. 붙여넣은 문서가 진실의 원천이고, 모든 선언은 그로부터 도출됩니다. API 응답, 설정 파일, 로그 한 줄을 붙여넣으면, 그러지 않았다면 손으로 작성했을 인터페이스를 읽어낼 수 있습니다.
배열이 어떻게 병합되는가
흥미로운 결정은 모두 배열에서 일어납니다. 순진한 변환기는 첫 요소만 보고 멈추므로, 우연히 본 모든 필드는 필수처럼 보이고 보지 못한 모든 필드는 놓칩니다. 이 도구는 대신 모든 요소를 하나의 타입으로 병합하며, 거기서 세 가지가 따라 나옵니다.
- 일부 요소에는 있지만 다른 요소에는 없는 키는 선택적이 되어 물음표와 함께 작성됩니다. 레코드의 절반에 "middleName"이 있고 절반에 없다면, 필드는 "middleName?"이며, 이것이 바로 소비자가 처리해야 하는 것입니다.
- 요소마다 값의 종류가 다른 키는 union이 됩니다. 한 레코드에서는 숫자이고 다른 레코드에서는 문자열인 필드는 "number | string"으로 타입이 매겨집니다. 깔끔해서가 아니라, 그것이 데이터가 실제로 담는 것이고 여러분의 코드가 받아들여야 하는 것이기 때문입니다.
- 요소 안의 중첩된 객체도 같은 방식으로 재귀적으로 병합되어 자신의 인터페이스로 추출됩니다. 각각 "address"를 담은 열 개의 배열 요소는 그 열 개 모두를 기술하는 하나의 Address 인터페이스를 만듭니다.
문서 자체가 최상위에서 배열이면, 루트는 항목 인터페이스에 대한 별칭 — 예를 들어 "type Root = RootItem[]" — 이 되고, 병합된 요소 타입이 그 아래에 작성됩니다.
null과 선택적, 그리고 왜 다른가
null을 없는 키와 똑같이 다루고 싶어지지만, 그것은 잘못입니다. TypeScript에서 "name?: string"은 속성이 없을 수 있음을 뜻하고, "name: string | null"은 항상 있지만 null을 담을 수 있음을 뜻합니다. 이는 서로 다른 계약이며, 소비자는 이를 다르게 확인합니다 — "in" 대 값 비교. 이 도구는 둘을 구분해 둡니다. 데이터의 명시적 null은 union 멤버 "| null"이 되며, "string | null"이 예상대로 읽히도록 마지막에 놓입니다. 반면 일부 레코드에 그저 없는 키는 선택적이 됩니다. 둘 다인 필드 — 한 레코드에서는 null, 다른 레코드에서는 없음 — 는 둘 다로, "field?: T | null"로 나옵니다. 두 사실 모두 여러분의 데이터에 대해 참이기 때문입니다.
중첩된 객체는 이름 있는 인터페이스가 된다
중첩된 형태를 부모 안에 끼워 넣는 대신, 각 객체는 그것이 놓인 키에서 이름이 도출되는 자신의 인터페이스로 추출됩니다. "user" 객체는 User 인터페이스가 되고, 그 안의 "address"는 User가 참조하는 Address 인터페이스가 됩니다. 깊이 끼워 넣은 타입은 읽기 어렵고 재사용이 불가능하며, 이름 있는 인터페이스는 여러분이 직접 작성했을 바로 그것입니다. 배열 요소는 가능하면 단수화됩니다 — "users"는 User를, "categories"는 Category를 만들고 — 복수형이 되지 않는 키는 Item 접미사를 받아 요소도 자신의 이름을 갖습니다.
서로 다른 두 객체가 같은 이름을 가지게 되면 — 예컨대 무관한 두 "data" — 두 번째는 병합되지 않고 접미사를 받으므로, 구별되는 형태는 구별된 채로 남습니다. 루트 객체는 먼저 출력되며 이름을 바꿀 수 있습니다. "interface"와 "type" 출력 사이의 선택은 토글인데, 일부 코드베이스는 전반적으로 타입 별칭을 선호하기 때문입니다.
unknown으로의 후퇴
어떤 값은 타입 정보를 전혀 담지 않습니다. 빈 배열은 무엇이든 담을 수 있고, 빈 객체에는 기술할 키가 없습니다. 이후의 모든 것에 대해 타입 검사를 꺼 버리는 "any"에 기대는 대신, 도구는 "unknown"으로 후퇴합니다. 빈 배열은 "unknown[]"이 되고, 빈 객체는 "Record<string, unknown>"이 됩니다. 그 차이는 중요합니다. "any"는 버그를 조용히 통과시키지만, "unknown"은 소비자에게 사용 전 값을 좁히도록 강제하므로, 추론된 타입은 샘플이 말한 것과 말하지 않은 것에 대해 정직하게 남습니다.
이것들은 빈 컬렉션이 무엇을 담아야 하는지 알게 되는 순간 손으로 조여 갈 타입입니다. 그러나 데이터가 그것을 말하기 전까지 "unknown"이 정직한 답이며, 나머지 타입을 안전하게 지켜 주는 답입니다.
무엇 위에서, 어디에서 실행되는가
모든 것은 여러분의 브라우저 안에서 일어납니다. JSON은 파싱되고 타입은 여러분 자신의 기기에서 추론되며, 붙여넣은 것은 무엇도 업로드되거나 저장되거나 기록되지 않습니다. 이 덕분에 도구는 실제 API 응답이나 비밀이 담긴 설정 파일에도 안전하게 쓸 수 있습니다. 샘플은 페이지를 결코 떠나지 않습니다. 출력은 평범한 TypeScript로, "d.ts" 파일이나 모듈에 그대로 붙여넣고, 데이터가 기술하지 못한 몇 안 되는 "unknown" 필드를 조정해 쓸 수 있습니다.
자주 묻는 질문
- 제 JSON이 서버로 전송되나요?
- 아니요. 문서는 파싱되고 타입은 전적으로 브라우저에서 추론되며, 붙여넣은 것은 무엇도 업로드되거나 기록되지 않습니다. 실제 API 응답이나 설정 파일에도 안전하게 쓸 수 있습니다.
- 제 샘플에 있는데도 왜 필드가 선택적이 되나요?
- 도구가 병합한 배열의 적어도 한 요소에 그것이 없기 때문입니다. 추론은 첫 요소만이 아니라 모든 요소를 읽으므로, 일부 레코드가 빠뜨리는 키는 선택적이 됩니다. 여러분이 본 레코드가 우연히 그것을 포함했더라도, 그것이 데이터가 실제로 허용하는 타입입니다.
- 왜 필드가 string | number 같은 union이 되었나요?
- 값이 배열의 서로 다른 요소에서 서로 다른 종류였기 때문입니다 — 한 레코드에서는 문자열, 다른 레코드에서는 숫자. 병합된 타입은 둘 다 받아들여야 하므로 union으로 작성됩니다. 그것이 놀랍다면 대개 데이터가 예상보다 덜 균일하다는 뜻이며, 알아 둘 가치가 있습니다.
- 왜 도구는 any 대신 unknown을 쓰나요?
- 기술할 수 없는 값 — 빈 배열, 빈 객체 — 에 대해 "unknown"은 결과를 타입 안전하게 유지하며 소비자에게 사용 전 값을 좁히도록 강제하지만, "any"는 타입 검사를 꺼 버립니다. 빈 컬렉션이 무엇을 담는지 알게 되면 이 필드들을 손으로 조일 수 있습니다.
- interface 출력과 type 출력의 차이는 무엇인가요?
- 그들이 기술하는 타입에는 차이가 없습니다 — 둘 다 같은 형태를 만듭니다. "interface"는 객체 타입의 일반적인 관용이며 확장과 병합이 가능하고, "type" 별칭은 일부 코드베이스가 전반적으로 쓰기를 선호하는 것입니다. 토글은 출력을 프로젝트 스타일에 맞추기 위해 있습니다.
- TypeScript를 다시 JSON으로 바꿀 수 있나요?
- 아니요, 의도적으로 그렇습니다. 변환은 한 방향입니다. JSON이 들어오고 타입이 나갑니다. 반대 방향은 여러분의 데이터에 결코 없던 값을 지어내는 일이 되며, 모든 선언이 실제로 붙여넣은 것에서 도출된다는 점이 핵심입니다.
- 중첩된 객체는 어떻게 이름이 붙나요?
- 그것이 놓인 키에서 붙습니다. "user" 객체는 User가 되고, 그 안의 "address"는 Address가 됩니다. 배열 요소는 가능하면 단수화됩니다 — "categories"는 Category를 만들고 — 복수형이 되지 않는 키는 Item 접미사를 받습니다. 이름이 충돌하는 서로 다른 두 형태는 병합되지 않고 접미사를 받으므로 구별된 채로 남습니다.