‘타입 정의만으로는 아직 불안한’ 당신에게

TypeScript로 코드를 작성하고 타입 정의를 완벽하게 마쳤습니다. 컴파일 에러도 전혀 없죠. 하지만 막상 실행해 보니 API에서 예기치 않은 데이터가 들어와 앱이 종료되는 쓴 경험을 해보신 적이 있나요?

Zod를 사용하여 JSON 유효성 검사 간소화하기

TypeScript의 타입은 컴파일 시점에만 존재하며, 브라우저에서 실행될 때는 사라집니다. 즉, ‘외부로부터 오는 데이터’가 정말로 올바른지는 실행 시점(런타임)에 직접 확인해야만 합니다.

Zod: 타입 안정성을 ‘런타임’으로 가져오기

이때 구원투수로 등장하는 것이 바로 Zod입니다.
Zod는 데이터의 ‘스키마(설계도)‘를 정의하고, 실행 시점에 데이터가 그 설계도대로 들어왔는지 엄격하게 체크합니다.

  • 부적절한 데이터를 입구에서 차단: string을 기대했는데 null이 들어오면 그 즉시 에러를 발생시켜 이후 로직을 보호합니다.
  • 타입 정의도 동시에 획득: 스키마를 정의하면 해당 스키마로부터 TypeScript 타입을 자동으로 추출할 수 있습니다(z.infer<T>).

스키마 작성의 번거로움을 ‘제로’로

Zod는 매우 강력하지만, 복잡한 JSON 구조에 맞춰 스키마를 일일이 수동으로 작성하는 것은 꽤나 고된 작업입니다.

저희 사이트의 **JSON→Zod 변환 도구**는 바로 이런 ‘스키마 정의의 번거로움’을 해결하기 위해 만들어졌습니다. 가지고 있는 JSON 데이터를 붙여넣기만 하면, 그에 대응하는 Zod 스키마를 눈 깜짝할 사이에 생성해 줍니다.

기본적인 워크플로우

  1. API에서 반환된 JSON 데이터를 복사합니다.
  2. 도구에 붙여넣어 Zod 스키마(z.object({...}))를 생성합니다.
  3. 생성된 코드를 프로젝트에 복사하여 붙여넣고, Schema.parse(data)로 유효성 검사를 시작하세요!
import { z } from 'zod';

// 도구로 생성된 스키마
const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
});

// 실행 시점의 체크와 타입 추출을 동시에 수행
type User = z.infer<typeof UserSchema>;
const safeData = UserSchema.parse(rawData);

API 응답에서의 실전 예시

실무에서는 목록 API의 일부 필드만 null이 되거나, 날짜가 문자열로 오거나, 숫자 ID가 문자열로 바뀌는 작은 차이가 생깁니다.

{
  "id": "123",
  "name": "Ada",
  "lastLoginAt": null
}

이 JSON에서 생성한 스키마는 그대로 쓰는 것이 아니라 사양에 맞춰 조정합니다. id를 숫자로 변환하는 설계라면 z.coerce.number(), 로그인 시각이 없어도 된다면 z.string().datetime().nullable()처럼 고칩니다. 도구로 초안을 만들고 경계 조건만 사람이 검토하는 흐름으로 하면, 수작업 실수를 줄이면서 실제 API 사양에도 맞출 수 있습니다.

마치며

데이터의 경계선을 지키는 것은 시스템의 견고함을 지키는 것과 같습니다.
‘아마 괜찮을 거야’라는 막연한 추측을 Zod를 통한 ‘확신할 수 있는 검증’으로 바꾸세요. 수작업의 번거로움은 도구에 맡기고, 여러분은 서비스의 핵심 로직 구현에 더 집중하시길 바랍니다.

자주 묻는 질문

TypeScript 타입이 있으면 Zod는 필요 없나요?

TypeScript 타입은 컴파일 시점에만 존재하고 런타임에는 사라집니다. 외부 API나 폼 입력 등 ‘밖에서 오는 데이터’가 타입대로라는 보장이 없으므로, 런타임에 검증하는 Zod 같은 라이브러리가 필요합니다. Zod는 z.infer로 타입도 함께 얻을 수 있어 타입과 검증을 이중으로 관리하지 않아도 됩니다.

Zod로 타입도 생성된다면 JSON→TypeScript 변환은 필요 없나요?

런타임 검증이 필요하면 Zod 스키마에서 z.infer로 타입을 도출하는 것이 단일 소스로 관리하기 편합니다. 반면 검증이 필요 없는 신뢰할 수 있는 내부 데이터라면 JSON→TypeScript 변환이 더 간단합니다. 용도에 따라 선택하세요.

생성된 스키마를 그대로 써도 되나요?

초안으로는 그대로 쓸 수 있지만 실제 API 사양에 맞춘 조정을 권장합니다. 숫자 문자열 변환은 z.coerce.number(), null 허용은 .nullable(), 선택 항목은 .optional()처럼 고치고, 경계 조건만 사람이 검토하면 수작업 실수를 줄일 수 있습니다.