LearningWhy Library?
런타임 검증 트랙 - Zod
TypeScript의 정적 타입만으로는 막을 수 없는 외부 경계의 위험을 Zod로 걸러내는 입문 가이드입니다.
백엔드에서의 타입은 경계 계약을 강제합니다. 프론트도 결국은 같은 문제를 만납니다.
코드 레벨에서 선언한 타입(type, interface)은 컴파일 단계에서만 동작하고,
실제로 유입되는 값(HTTP 응답, LocalStorage, 라우트 파라미터, 서드파티 응답)에서는 런타임 검증이 별도 필요합니다.
이 트랙은 Zod를 통해 타입 시스템의 약한 지점, 즉 “런타임 경계”를 안정화하는 방법만 다룹니다.
왜 런타임 검증이 필요한가
TypeScript 타입은 빌드 결과에 남지 않습니다.
따라서 다음 지점에서 오류가 터집니다.
- API 응답 스키마가 바뀌었는데 클라이언트가 구버전 가정으로 접근
- nullable 처리 누락으로 런타임 예외 발생
- 에러 응답과 성공 응답을 같은 shape로 다루다가 분기 처리 누락
정적 타입은 “코드를 바꿀 수 있는 사람”에게 경고를 주는 도구이고, Zod는 “바뀐 값이 들어왔을 때” 실제로 막아내는 도구입니다.
요청 스키마와 응답 스키마를 분리하는 게 중요한 이유
프로젝트 규모가 커질수록 이 분리는 필수입니다.
Request schema: API로 보내기 직전의 값 검사 포인트(필수값, 형식, 기본값, 허용 값)Response schema: API에서 받은 값의 후처리 보장 포인트(타입/필수성/에러 형태/분기)
둘을 동일하게 모델링하면 아래처럼 혼합됩니다.
- 전송 규칙과 수신 규칙이 동일 계층으로 섞임
- 실패 지점을 추적하기 어려워짐
- “실패는 어디서 잡아야 하는가”가 흐려짐
반대로 분리하면 트래킹 포인트가 명확해집니다.
RequestParseError: 보내기 전 차단ResponseParseError: 받아오기 후 계약 위반 탐지- 화면 fallback는 수신 실패/유효성 실패로 분리해 처리 가능
응답 중심으로 보는 합타입/곱타입 사고
백엔드에서 API 응답은 거의 항상 분기가 있습니다.
- 성공/실패, 빈 응답/일반 응답, 권한 없음/요금제 제한
Zod로는 이를 태그 합집합으로 먼저 표현하고, 성공 케이스 안에서 공통 성분을 합성해 사용하면 됩니다.
핵심은 응답을 먼저 분기(성공/실패)로 나눈 뒤, 각 분기의 공통 성분을 재사용 가능한 기본 스키마로 묶는 것입니다.
이 트랙에서 주의할 점
- Zod를 만든다고 모든 런타임 입력이 안전해지는 건 아니므로, 네트워크/상태/재시도 정책과 함께 사용한다.
- 모든 응답을 하나의 거대한 스키마로 포장하면, 실제 분기별 추적이 어려워지므로 응답 케이스를 먼저 분리한다.