홈으로 돌아가기

Zod와 TypeScript를 사용한 런타임 API 유효성 검사

이 기사는 API 데이터에 대한 TypeScript의 런타임 한계를 설명하고 Zod를 사용한 스키마 유효성 검사를 제안합니다. 중급/시니어 개발자를 위한 코드 예제와 라이브러리 비교.

TypeScript API의 런타임 유효성 검사를 위한 Zod
Advertisement 728x90

런타임 API 응답 검증: TypeScript만으로는 부족한 이유

TypeScript은 컴파일 타임에만 타입을 검사합니다. API에서 오는 데이터는 런타임에 도착하며, 인터페이스와 일치한다는 보장이 없습니다. 추가 검증 없이 불일치가 발생하면 사용자에게 노출되는 오류로 나타납니다: undefined 필드, 잘못된 타입, 누락된 객체 등.

런타임 검사 없이 발생하는 문제

TypeScript 인터페이스는 거짓된 안정감을 줍니다. 검증 없는 전형적인 코드 예시:

interface User {
  id: number;
  name: string;
  email: string;
}

const getUser = async (): Promise<User> => {
  const res = await fetch('/api/user');
  return res.json(); // 실제 데이터 검사 없음
};

TypeScript은 타입 주석을 신뢰하고 실제 서버 응답을 무시합니다. 주요 위험:

Google AdInline article slot
  • 백엔드 구조 변경: full_namefullName으로 바뀌면 undefined 발생.
  • 타입 불일치: 숫자 123 대신 문자열 "123"이 오면 덧셈 대신 연결.
  • 조건부 필드: 특수 케이스에서 address 누락, user.address.city에서 크래시.
  • 문서가 오래된 서드파티 API.

스키마 검증 라이브러리

해결책? 런타임 스키마 검증 라이브러리입니다. 실제 데이터를 파싱하고 불일치 시 오류를 발생시킵니다. TypeScript 최고 옵션:

  • Zod: 스키마에서 타입 추론, 방대한 생태계.
  • Yup: Formik과 완벽 호환.
  • Valibot: 번들 친화적, 트리셰이킹 가능.
  • Joi: Node.js 기반, 백엔드 중심.
  • ArkType: 성능 최우선.

Zod로 구현하기

Zod는 스키마, 타입, 검증을 하나로 통합합니다. 사용자 예시:

import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
});

type User = z.infer<typeof UserSchema>;

const getUser = async (): Promise<User> => {
  const res = await fetch('/api/user');
  const data = await res.json();
  return UserSchema.parse(data); // 불일치 시 오류 발생
};

parse()는 실패 시 상세 정보를 담은 ZodError를 던집니다. successerror를 가진 객체로 반환하는 safeParse()를 사용하세요.

Google AdInline article slot

실제 프로젝트에서 확장하기

대형 앱에서는 모든 엔드포인트의 스키마를 중앙화하세요. fetch를 이렇게 래핑:

const apiFetch = async <T>(url: string, schema: z.ZodSchema<T>): Promise<T> => {
  const res = await fetch(url);
  const data = await res.json();
  return schema.parse(data);
};

const users = await apiFetch('/api/users', z.array(UserSchema));

오류 처리: Sentry에 로깅, 사용자 친화적 메시지 표시. 배열의 경우 각 항목 검증.

  • 리스트에는 z.array(UserSchema) 사용.
  • 다형 응답에는 z.union().
  • 재귀 구조에는 z.lazy().
  • 데이터 정규화에는 z.transform().

주요 요점

  • TypeScript은 외부 데이터(API, localStorage, URL 파라미터)에 대한 런타임 타입을 보장하지 않습니다.
  • 스키마 검증으로 불일치를 조기 발견, 디버깅 시간 단축.
  • Zod는 프론트엔드 표준: 스키마에서 타입 생성, 쉬운 통합.
  • 별도 백엔드를 둔 팀에서 조용한 실패 방지.
  • 일관성을 위해 fetch 래퍼로 확장.

런타임 검증은 TypeScript의 컴파일 타임 검사와 실행 사이 공백을 메웁니다. 프로덕션에서 API 계약 불일치로 인한 버그를 대폭 줄입니다.

Google AdInline article slot

— Editorial Team

Advertisement 728x90

다음 읽기