1P by GN⁺ | ★ favorite | 댓글 1개
  • Zod 4가 1년간의 개발 끝에 안정 버전으로 공개되며, Zod 3의 설계 한계를 고치고 성능·번들 크기·TypeScript 컴파일 효율을 크게 개선함
  • 2021년 5월 나온 Zod 3는 GitHub 스타 2,700개·주간 다운로드 60만에서 현재 37.8k 스타·주간 3,100만 다운로드 규모로 성장했고, 24개 마이너 버전 이후 주요 개선에 호환성 깨지는 변경이 필요해짐
  • 벤치마크에서 문자열 파싱은 14배, 배열 파싱은 7배, 객체 파싱은 6.5배 빨라졌으며, tsc 타입 인스턴스화도 간단한 예제에서 25,000회 초과에서 약 175회로 줄어듦
  • 핵심 번들 크기는 gzip 기준 Zod 3 12.47kb에서 Zod 4 일반 버전 5.36kb로 줄었고, 트리 셰이킹 가능한 함수형 API인 Zod Mini1.88kb까지 작아짐
  • 새 기능은 JSON Schema 변환, 메타데이터 레지스트리, 재귀 객체 타입 추론, 국제화, 에러 pretty-printing, 템플릿 리터럴 타입, z.stringbool(), 통합 error 파라미터, 개선된 z.discriminatedUnion() 등을 포함함

Zod 4가 새 메이저 버전이 된 이유

  • Zod 4는 1년간의 활발한 개발 끝에 안정 버전이 됐으며, 더 빠르고 작고 tsc 효율이 높은 기반으로 오래 요청된 기능들을 구현함
  • Zod v3.0은 2021년 5월 출시됐고, 당시 GitHub 스타는 2,700개, 주간 다운로드는 60만이었음
  • 현재 Zod는 GitHub 스타 37.8k, 주간 다운로드 3,100만을 기록함
    • 베타가 나온 6주 전에는 주간 다운로드가 2,300만이었음
  • 24개 마이너 버전이 쌓이면서 Zod 3 코드베이스는 한계에 도달했고, 가장 많이 요청된 기능과 개선에는 호환성 깨지는 변경이 필요했음
  • Zod 4는 가장 많은 추천을 받은 공개 이슈 10개 중 9개를 닫음

파싱 성능과 TypeScript 컴파일 효율

  • Zod 4는 저장소에서 직접 실행 가능한 벤치마크를 제공함
  • 파싱 성능 개선:
    • 문자열 파싱: 14배 빨라짐
    • 배열 파싱: 7배 빨라짐
    • 객체 파싱: 6.5배 빨라짐
  • 객체 파싱 벤치마크는 Moltar validation library benchmark를 실행함
  • tsc --extendedDiagnostics 기준 간단한 파일 컴파일에서 타입 인스턴스화가 크게 줄어듦
    • "zod/v3" 사용 시 25,000회 초과
    • "zod/v4" 사용 시 약 175회
  • Zod 4는 ZodObject와 다른 스키마 클래스의 제네릭을 재설계하고 단순화해 인스턴스화 폭증을 피함
  • .extend().omit()을 반복 연결하는 패턴은 Zod 3에서 컴파일에 4000ms가 걸렸고, 추가 .extend() 호출은 "Possibly infinite" 오류를 유발할 수 있었음
  • 같은 패턴이 Zod 4에서는 400ms에 컴파일되어 10배 빨라짐
  • 향후 tsgo 컴파일러와 함께 쓰면 Zod 4의 에디터 성능은 더 큰 스키마와 코드베이스까지 확장될 수 있음

번들 크기와 Zod Mini

  • 단순한 z.boolean() 검증 스크립트를 rollup으로 번들링해 핵심 번들 크기를 비교함
  • gzip 기준 핵심 번들 크기:
    • Zod 3: 12.47kb
    • Zod 4 일반 버전: 5.36kb
  • Zod 4 일반 버전의 핵심 번들은 Zod 3보다 약 57% 작고, 2.3배 줄어듦
  • Zod의 메서드 중심 API는 근본적으로 트리 셰이킹이 어렵고, 단순한 z.boolean() 스크립트도 .optional(), .array() 같은 사용하지 않은 메서드 구현을 함께 끌어옴
  • Zod Minizod와 1:1로 대응하는 함수형·트리 셰이킹 가능 API를 제공함
    • Zod가 메서드를 쓰는 곳에서 Zod Mini는 일반적으로 래퍼 함수를 사용함
    • 파싱 메서드는 Zod와 Zod Mini가 동일함
    • 범용 .check() 메서드로 정제(refinement)를 추가함
  • 일반 Zod는 대부분의 사용 사례에 계속 권장되며, 비정상적으로 엄격한 번들 크기 제약이 있는 프로젝트는 Zod Mini를 고려할 수 있음
  • zod/mini 사용 시 gzip 번들 크기는 1.88kb
    • Zod 3 대비 85%, 6.6배 감소함
    • 비교 표: Zod 3 12.47kb, Zod 4 일반 5.36kb, Zod 4 Mini 1.88kb
  • 자세한 내용은 zod/mini 문서에서 볼 수 있음

메타데이터와 JSON Schema

  • Zod 4는 스키마에 강타입 메타데이터를 추가하는 새 시스템을 도입함
  • 메타데이터는 스키마 자체가 아니라, 스키마와 typed metadata를 연결하는 schema registry에 저장됨
  • z.registry()로 레지스트리를 만들 수 있고, 스키마의 .register() 메서드로 편의 등록도 가능함
  • Zod는 공통 JSON Schema 호환 메타데이터를 받는 전역 레지스트리 z.globalRegistry를 내보냄
  • .meta() 메서드는 스키마를 z.globalRegistry에 편리하게 추가함
  • Zod 3 호환성을 위해 .describe()는 계속 제공되지만, Zod 4에서는 .meta()가 권장됨
  • Zod 4는 z.toJSONSchema()를 통한 first-party JSON Schema 변환을 제공함
  • z.globalRegistry의 메타데이터는 JSON Schema 출력에 자동 포함됨
  • 생성되는 JSON Schema 커스터마이징 정보는 JSON Schema docs에 있음

새 스키마 표현과 타입 기능

  • Zod 4는 재귀 객체 타입을 적절히 추론할 수 있음
    • 재귀 타입과 상호 재귀 타입을 표현할 수 있음
    • Zod 3의 재귀 타입 패턴과 달리 타입 캐스팅이 필요 없음
    • 결과 스키마는 일반 ZodObject 인스턴스이며 전체 메서드를 사용할 수 있음
  • File 인스턴스 검증을 위한 File schema가 추가됨
  • z.templateLiteral()은 TypeScript의 템플릿 리터럴 타입을 표현함
    • 문자열화 가능한 Zod 스키마 타입은 내부 정규식을 저장함
    • 대상에는 문자열, z.email() 같은 문자열 포맷, 숫자, boolean, bigint, enum, literal, undefined/optional, null/nullable, 다른 template literal이 포함됨
    • z.templateLiteral 생성자는 이들을 하나의 super-regex로 이어 붙임
    • z.email() 같은 문자열 포맷은 제대로 강제되지만, custom refinement는 강제되지 않음
    • 자세한 내용은 template literal docs에 있음
  • 고정 폭 정수와 부동소수점 타입을 표현하는 새 숫자 포맷이 추가됨
    • 적절한 inclusive minimum/maximum 제약이 이미 추가된 ZodNumber 인스턴스를 반환함
  • JavaScript number로 안전하게 표현할 수 있는 범위를 넘는 bigint 숫자 포맷도 추가됨
    • 적절한 inclusive minimum/maximum 제약이 이미 추가된 ZodBigInt 인스턴스를 반환함
  • z.literal()은 이제 여러 값을 선택적으로 받을 수 있음

에러, 국제화, 문자열 포맷

  • Zod 4는 에러 메시지를 여러 언어로 전역 번역하는 새 locales API를 도입함
  • 지원 로케일 전체 목록은 Customizing errors에 있으며, 지원 언어가 추가될 때 갱신됨
  • Zod는 ZodError를 사용자 친화적인 형식의 문자열로 바꾸는 최상위 z.prettifyError 함수를 구현함
    • 현재 포맷은 설정할 수 없음
    • 향후 변경될 수 있음
    • 기존 zod-validation-error를 사용 중이면 계속 사용할 수 있음
  • email 등 모든 문자열 포맷은 z 모듈의 최상위 함수로 승격됨
    • 더 간결하고 트리 셰이킹에 유리함
    • z.string().email() 같은 메서드 대응 API는 계속 사용할 수 있지만 deprecated 상태임
    • 다음 major 버전에서 제거될 예정임
  • z.email() API는 custom regular expression을 지원함
    • canonical email regex는 하나로 정해져 있지 않으며, 애플리케이션마다 더 엄격하거나 느슨한 기준을 택할 수 있음
    • Zod는 편의를 위해 공통 정규식 몇 가지를 내보냄

불리언 변환과 에러 커스터마이징 단순화

  • 기존 z.coerce.boolean()은 falsy 값을 false, truthy 값을 true로 바꾸는 단순한 API임
    • false, undefined, null, 0, "", NaN 등은 false가 됨
    • 이 동작은 다른 z.coerce API와 맞음
  • Zod 4는 env 스타일의 더 정교한 boolean coercion을 위해 z.stringbool() 을 도입함
    • truthy 값과 falsy 값을 커스터마이징할 수 있음
    • 자세한 내용은 z.stringbool() docs에 있음
  • Zod 4의 호환성 깨지는 변경 대부분은 에러 커스터마이징 API와 관련됨
  • 에러 커스터마이징은 단일 error 파라미터로 통합됨
    • messageerror로 대체됨
    • message 파라미터는 계속 지원되지만 deprecated 상태임
    • invalid_type_errorrequired_error는 함수 문법의 error로 대체됨
    • errorMap도 함수 문법의 error로 대체됨

조합성, 정제, 변환

  • z.discriminatedUnion()은 기존에 지원하지 않던 여러 스키마 타입을 지원함
    • union과 pipe가 포함됨
    • discriminated union을 다른 discriminated union의 멤버로 사용할 수 있어 조합 가능해짐
  • Zod 3에서는 refinement가 원본 스키마를 감싸는 ZodEffects 클래스에 저장됐음
    • 이 구조 때문에 .refine().min() 같은 다른 스키마 메서드와 섞어 쓰기 불편했음
  • Zod 4에서는 refinement가 스키마 내부에 저장되어 .refine()과 다른 스키마 메서드를 자연스럽게 함께 사용할 수 있음
  • .overwrite() 메서드는 inferred type을 바꾸지 않는 transform을 표현함
    • .transform()은 출력 타입이 런타임에서 introspectable하지 않고, transform 함수가 무엇이든 반환할 수 있는 블랙박스임
    • 이 때문에 JSON Schema로 안전하게 변환할 방법이 없음
    • .overwrite()는 원래 클래스의 인스턴스를 반환함
    • overwrite 함수는 refinement로 저장되며 inferred type을 수정하지 않음
  • 기존 .trim(), .toLowerCase(), .toUpperCase().overwrite()를 사용해 다시 구현됨

라이브러리 작성자를 위한 기반

  • Zod Mini 추가로 Zod와 Zod Mini가 공유하는 핵심 기능을 담은 zod/v4/core 서브패키지가 만들어짐
  • zod/v4/core는 Zod를 단순 라이브러리에서 다른 라이브러리에 넣어 쓸 수 있는 빠른 검증 기반으로 확장함
  • 스키마 라이브러리를 만드는 경우 Zod와 Zod Mini 구현을 참고해 zod/v4/core 위에 구축할 수 있음
  • 라이브러리 작성자를 위해 For library authors 가이드가 제공됨
    • Zod 위에 구축할 때의 best practice를 다룸
    • Zod 3과 Zod 4, Mini를 동시에 지원하는 방법에 대한 공통 질문에 답함

댓글과 토론

Hacker News 의견들
  • 작성자임, 무엇이든 물어보세요. 버전 정책에 대해서는 이 접근의 이유를 꽤 자세히 정리해 둠: https://github.com/colinhacks/zod/issues/4371
    결국 npm은 Zod가 처한 상황을 잘 다루도록 설계되어 있지 않음. 수십~수백 개 라이브러리가 Zod의 인터페이스/클래스를 직접 가져와 자기 공개 API에 쓰고 있어서, Zod가 메이저 버전을 올릴 때마다 그 라이브러리들도 새 메이저 버전을 내야 함
    단독으로 보면 합리적이지만 Zod에서는 모두에게 고통스러운 버전 눈사태가 생길 수 있고, 생태계 상당수가 v3에 영원히 고정될 것 같았음
    그래서 Go 방식과 비슷하게, 패키지가 새 호환성 깨짐 버전을 내는 대신 새 하위 경로를 추가하는 방식을 택함. TypeScript 생태계에서는 라이브러리가 zod@^3.25.0 하나만 피어 의존성으로 두고 "zod/v3""zod/v4"에서 필요한 것을 가져와 두 버전을 동시에 지원할 수 있음

    • 큰 코드베이스에서 tsc 성능 향상이 특히 반갑고, 판별 유니언 변경도 지금까지 부족했던 특정 시나리오에 크게 도움이 될 것 같음
      다만 특수한 사정 때문에 다소 이상한 버전 정책을 택한 이유는 이해하지만, 전이 의존성의 Zod 버전 충돌을 걱정할 필요가 없는 팀 입장에서는 4.0.0 패키지도 있었으면 함
      이해한 게 맞다면 import를 "zod/v4"로 바꿔야 하는데, 이는 매우 시끄러운 변경이고 IDE가 'zod'에서 자동 import하는 문제를 린트 규칙으로 잡아야 해서 골치 아플 수 있음
    • 이 방식은 매우 합리적임. 이전 버전 보안 업데이트도 같은 코드베이스 릴리스 안에서 함께 제공할 수 있다는 뜻임
    • 모바일이라 글에 이미 있는지 못 봤지만, TypeScript에서 .optional()을 고치는 작업이 상위 9~10개 이슈 해결에 포함되는지 궁금함: https://github.com/colinhacks/zod/issues/635
      이게 Zod에서 가장 큰 고통이었지만, 그래도 Zod가 워낙 좋아서 감수하며 쓰고 있음
    • 새 기능 덕분에 로컬에서 해 둔 많은 임시 해킹을 없앨 수 있을 것 같음. 폼 이름 오타를 피하려고 https://github.com/raflymln/zod-key-parser의 자체 버전을 쓰고 있는데, 이런 기능이 라이브러리에 직접 들어가지 않은 점이 의외였음
      이것이 Zod 범위 밖이라고 보는지, 아니면 아직 구현할 시간이 없었던 것인지 궁금함. 관련 논의는 https://github.com/colinhacks/zod/discussions/2134에 있음
    • Zod 4의 가장 이른 버전이 3.25.0이라는 뜻이라면, 이건 좀 말이 안 되는 것처럼 들림
      몇 달 동안 Zod 알파 버전을 써 왔고 그냥 package.json을 고쳐 업그레이드하고 싶었는데, 이제는 git 히스토리 전체를 뒤져야 할 것 같음
      프로젝트는 매우 고맙게 쓰고 있지만, 이 피드백만큼은 필요 이상으로 어렵게 만든 것 같음. 3.x와 함께 4.x도 배포할 수 있지 않았을까 싶음
  • “마이그레이션을 단순화하기 위해 Zod 4를 zod@3.25 릴리스의 일부로 Zod 3와 함께 배포하고, "/v4" 하위 경로에서 가져오게 한다”는 내용은 npm 의존성 관리가 완전히 엉망이라는 신호로 보임
    피어 의존성이 너무 망가져서 v4가 v3인 척해야 하는 상황이 됨

    • 작성자임. 이 접근의 이유는 여기 꽤 자세히 적어 둠: https://github.com/colinhacks/zod/issues/4371
      npm이 Zod가 처한 상황을 잘 관리하지 못한다는 점은 맞음. 다만 Zod는 거의 어떤 라이브러리도 받지 않는 제약을 받음. 수십~수백 개 라이브러리가 "zod"에서 인터페이스/클래스를 직접 가져와 자기 공개 API에 사용함
      이런 라이브러리는 Zod가 메이저 버전을 올릴 때마다 새 메이저 버전을 내야 하고, Zod에서는 그게 버전 눈사태를 일으켜 모두에게 고통스러울 수 있음. 솔직히 생태계 상당수가 v3에 영원히 고정될 것 같았음
      그래서 Go와 비슷하게, 호환성 깨짐 릴리스마다 새 패키지 버전을 내지 않고 새 하위 경로를 추가하는 방식을 택함. TypeScript 생태계에서는 zod@^3.25.0 하나의 피어 의존성으로 "zod/v3""zod/v4"를 동시에 지원할 수 있음
    • “피어 의존성이 망가져서 v4가 v3인 척한다”는 결론은 맞지 않아 보임. Zod v4가 v3 안에 포함된 건 사용자가 점진적으로 옮기도록 하기 위해서라고 봄
      즉 사용처를 하나씩 import ... from 'zod/v4'로 리팩터링하고, 끝나면 완전히 v4로 올리는 흐름임
    • 부정적인 말이면 뭐든 추천받는 느낌임. 이 결정은 npm 자체의 본질적 한계라기보다, 호환성 깨짐 변경이 많은 라이브러리를 점진적으로 바꾸게 해 주는 실용적인 방식에 가까움
    • 수년간 npm만 써서 시야가 좁아졌을 수도 있지만, v3에서 v4로 한 번에 크게 옮기지 않고 반복적으로 마이그레이션하는 더 나은 방법이 무엇인지 궁금함
    • 이건 npm만의 문제가 아님. 어떤 의존성이 또 다른 전이 의존성에서 예전 버전에 의존하는 문제는 사실상 모든 생태계에서 생김
      오히려 npm은 필요하면 여러 버전을 동시에 실행하거나 전이 의존성 그래프 일부를 선택적으로 덮어쓸 수 있어서 탈출구가 더 많음
      어떤 패키지 관리 시스템이 이 문제를 해결하나? Maven 같은 “안정적” 플랫폼도 Apache Commons가 v2에서 v3로 갈 때처럼 새 네임스페이스로 버전을 내며 이런 문제를 처리함
  • 오래전부터 궁금했던 질문이 있음. Zod가 여기에 맞을 수 있다고 들었지만 문서를 봐도 어떻게 해야 할지 잘 모르겠음
    서버 API가 표현할 수 있는 것보다 더 정교한 타입을 반환한다고 해 보자. 예를 들어 api/:postid/author가 User를 반환하지만, 일반 User일 수도 있고 익명 User일 수도 있어서 username, location 같은 필드가 null로 올 수 있음. 이 경우 User 객체를 판별 유니언으로 표현하고 싶을 수 있음
    다른 엔드포인트에서 오는 객체도 타입 변환이 필요할 수 있음. User가 때로 Post[]를 포함하고, Post가 모더레이터 글이면 특수 속성을 가질 수도 있음
    예전에는 normalizeUser()normalizePost() 같은 함수를 작성했지만 금방 지저분해졌음. 엔드포인트마다 User/Post 모델의 서로 다른 부분집합을 반환해서 normalizePost를 5개쯤 만들게 되었는데, 너무 난잡함. 이런 문제는 보통 어떻게 푸는지 궁금함

    • 판별 유니언을 쓰면 될 것 같음
      status 같은 판별 필드를 기준으로 successfailed 객체를 나누는 식임. 모더레이터용 특수 속성이 많지만 전부 나열하거나 검사하고 싶지 않다면 통과 동작을 정의할 수 있음
      여러 메서드가 서로 다른 스키마를 갖더라도 공통 부분이 있다면 공유 객체 스키마를 만들고, 거기에 추가 필드를 얹은 객체들을 만들면 됨. 가능성이 많으면 여전히 지저분하겠지만, 이미 지저분한 상황이라면 검증기가 적어도 그 지저분함을 검증해 줄 수 있음
    • 이상적으로는 User가 어떤 형태일 수 있는지에 대한 단일 진실 공급원이 있어야 함. 이는 User와 AnonymousUser 같은 판별 유니언일 수 있음
      풀스택 TypeScript가 아니라면, 예를 들어 Python 백엔드에서는 여러 User 형태를 Pydantic 모델과 유니언으로 정의하고, OpenAPI/GraphQL 스키마 생성과 코드 생성을 통해 TypeScript 클라이언트 타입을 만들 수 있음
    • 사용 사례를 더 알아야 풀 수 있지만, 유니언 안의 모든 타입에 "user_type" 같은 판별 속성을 추가하면 일반 경우와 특수 경우를 다루기 쉬워짐
      예를 들어 user.user_type === 'authenticated'를 검사하면 타입 시스템이 그 뒤에는 user.name을 쓸 수 있음을 알게 됨
    • 지금 경우에 직접 도움이 되진 않지만, 이런 걸 위해 GraphQL이 만들어졌음
      GraphQL 쿼리용 TypeScript 라이브러리는 선택한 필드 형태에서 응답 형태를 동적으로 추론할 수 있음. { user { username posts { text } } }를 선택하면 posts가 있는 응답 타입이 되고, { user { username } }만 선택하면 posts 속성은 완전히 빠짐
    • 서버가 타입을 내보내야 함. 클라이언트에서 타입을 손으로 쓰는 건 말이 안 됨
      서버는 자신이 보내는 데이터를 알고 있으니 그 정보를 메타데이터로 클라이언트에 제공해야 함. 실무적으로는 Python 백엔드가 Pydantic 스키마를 쓰고, 이를 기반으로 OpenAPI 명세를 자동 생성한 뒤 클라이언트가 타입을 생성하는 식이 될 수 있음
  • Zod는 봐 온 대안들보다 훨씬 낫지만, 이런 명시적 검증이 필요하다는 사실 자체가 현대 웹 개발이 어디로 왔는지에 대한 실패처럼 느껴질 때가 있음
    풀스택 개발에서 같은 형태를 여러 방식으로 설명해야 하는 게 너무 답답함. JS 입력 검증, API 정의용 Swagger, 서버 측 입력 검증, 스키마 적합성을 위한 ORM, 서버와 클라이언트의 별도 TypeScript 정의까지 필요해져서 지루함

    • TypeScript가 정적 검사/컴파일 타임 전용 시스템을 고집하는 건 생태계 전체에 안타까운 상처임. TypeScript가 런타임 검사기가 되길 바라는 건 아니지만, 클래스·함수·객체에 대해 유용하게 쓸 수 있는 타입 데이터를 내보내 주길 바람
      TypeScript가 우리가 가진 최선의 단일 진실 공급원처럼 느껴지지만, 실제로는 리플렉션을 시도하는 도구들이 각자 자기 모델과 빌더를 다시 정의하고 있음. 리플렉션이 핵심인 큰 영역만 5개가 있고, 각각 구현도 여러 개임
      예전 TypeScript 타입 방출 도구인 reflect-metadata가 런타임 타입 정보를 일부 제공하지만, 아주 오래된 데코레이터와 메타데이터 명세를 대상으로 함: https://www.npmjs.com/package/reflect-metadata
      어쩌면 통합의 초입에 와 있을 수도 있음. Standard Schema 프로젝트는 주요 검증 라이브러리 대부분의 지원을 받는 듯함. 다만 이를 API 정의나 ORM 도구까지 확장하는 건 아직 매우 초기임
      https://github.com/standard-schema/standard-schema?tab=readm...
    • 사실 이게 이런 도구의 핵심임. 한 번만 정의하고, 그 아래의 모든 것을 동적으로 생성하면 됨
      Zod 스키마에서 한 번 바꾸면 타입 검사와 함께 앱 전체로 전파됨. Zod 스키마가 단일 진실 공급원이 되는 셈임
    • 보통 데이터를 여기저기 다시 정의할 필요는 없음. Zod로 변환하거나 Zod에서 변환하는 도구가 엄청 많음
      이미 JSON Schema/Swagger 스키마가 있다면 거기서 Zod를 생성하면 됨. TypeScript ORM을 쓴다면 거의 확실히 Zod 생성기가 있을 것임
      솔직히 Zod가 너무 널리 쓰여서 내게는 스택의 다른 것들이 의존할 수 있는 통합 스키마 정의가 됨. 타입을 직접 정의하므로 개발자들이 실제로 최신 상태를 유지함. 회사의 Swagger 문서는 변경보다 항상 뒤처짐
    • 모든 API는 실험적이고 계속 변화·진화하는 상태이며, 웹도 마찬가지임
      현상 유지가 괜찮은 경우는 그냥 결과만 원하고 방식에는 관심 없는 클라이언트와 고용주를 상대할 때뿐임. 그런 맥락에서는 이런 불행한 복잡성 계층들이 적어도 청구 가능한 시간을 늘려 줌
    • 현대 웹 개발이 복잡하다고 불평하는 사람들 중 상당수는, 이 모든 것을 그냥 TypeScript와 Zod로 대체하자는 제안에도 질색할 것 같음
      물론 Zod가 자신이 고른 TypeScript 스키마 정의 및 검증 라이브러리라는 전제에서임
  • Zod 4는 좋아 보이지만 최신 개선을 포함해도 ArkType이 여전히 한 자릿수 배 이상 빠름
    하위 호환성과 문법 호환성을 위해서는 완전한 새 출발 라이브러리만큼 훨씬 빠르게 만들기 어려울 때가 있음. 최근 프로젝트에서 이런 도구들을 모두 분석했고, 부분적으로 이 이유 때문에 ArkType을 선택함. 또 TypeScript 사용감도 더 좋게 느껴졌음

    • 속도 지표를 봤는데, 그 속도가 어디서 차이를 만드는지 설명해 줄 수 있는지 궁금함
      우리는 Zod를 폼 검증에만 써서 “이게 왜 중요하지?”라는 생각이 듦. 사람들이 고처리량 API 입력 메시지 같은 걸 검증해서 성능이 더 중요해지는 경우가 있는 건가 싶음
    • ArkType을 TypeBox보다 선택한 이유가 궁금함
    • 흥미롭게도 조사할 때 그 도구는 보지도 못했음. 나도 특히 TypeScript 사용감을 보고 있었음
      그래도 Zod에서 갈아타진 않을 것 같음
    • ArkType은 쓰기 악몽 같고, Zod는 쓰기 좋음
  • Zod 팀의 새 릴리스를 축하함. 너무 부정적으로 들릴 위험을 감수하더라도, 마이그레이션 가이드에 적힌 호환성 깨짐 변경 수를 생각하면 몸서리쳐짐
    Zod에 크게 의존하는 프로젝트라면 개발자의 집중과 시간이 많이 필요한 벅찬 작업이 될 것 같음. 직장에서 4~5년 된 프런트엔드 프로젝트 몇 개를 유지보수해 봐서 특히 공감함
    큰 React 프로젝트는 보통 많은 라이브러리에 의존하고, 각 라이브러리가 큰 변경을 내놓을 때 문서까지 부족하면 순식간에 압도됨. JavaScript로 일할 때 가장 싫은 부분 중 하나이며, 모든 걸 맞춰서 계속 부드럽게 돌아가게 하는 일이 끝없는 오르막길처럼 느껴짐

    • 완전히 동의함. 큰 Next.js 앱 몇 개를 운영하는데, 지난 1년 동안 Next.js 14 → 15의 대규모 캐시 변경, Next.js pages → app router, React 18 → 19, Eslint 8 → 9, Tailwind 3 → 4를 겪어야 했음
      솔직히 악몽이었고, 차라리 Django로 만들었으면 좋았겠다고 생각함. 예상과 달리 Tailwind 3 → 4 마이그레이션이 가장 고통스러운 축에 들었음
    • 그래서 이들이 별도 mini 에디션과 함께 이중 제공 방식을 택한 것임. 패키지 관리자를 건드리지 않고 점진적 마이그레이션을 하기가 쉬움
      mini 에디션에 관심 없는 사용자는 그 부분을 신경 쓸 필요가 없음. 다만 mini 에디션의 트리 쉐이킹 이점이 너무 커서 대안 개발을 자극하고 있었고, Zod는 이를 받아들이거나 쇠퇴해야 했음
    • 그냥 LLM을 쓰면 됨
  • 프로덕션에서 수천 명의 사용자를 둔 SaaS를 직접 운영하는 풀스택 개발자로서, SPA를 만들지 않고 JS 프런트엔드 프레임워크도 쓰지 않기로 해서 내가 전혀 모르는 문제가 얼마나 많은지 가끔 기분 좋게 깨닫게 됨
    Zod/ArkType 같은 것이 필요하다는 생각조차 해 본 적 없고, 처음 들어 봤으며 쓸 일도 없음
    SPA를 만드는 데 들어가는 노력, 복잡성, 도구가 얼마나 많은지 놀라움. SPA를 만들지 않기로 하면 특정 문제군 전체가 그냥 존재하지 않음. 대신 브라우저를 원래 설계된 대로 전체 페이지 새로고침과 백엔드 개발 중심으로 쓰고, Laravel Livewire 같은 백엔드 전용 프레임워크로 가끔 반응성을 더함
    접근 제어부터 검증, 상태 관리까지 모든 게 단순함. 앱은 빠르고, 반응형이고, 현대적이며, SEO 친화적이고, 프로덕션에서 수천 명에게 제공 중임

    • 5개 이상 팀이 함께 일하는 두 큰 프로젝트에서 Zod와 Protobuf를 써서 스키마 진화가 잘 되도록 했음. 가벼운 프런트엔드라도 백엔드에서 스키마를 검증하는 무언가를 쓰는 건 의미가 있음
      결국 어딘가에는 스키마가 있음. 데이터베이스 시스템에 정의될 수도 있고, ORM 모델에 정의될 수도 있음. 하지만 큰 프로젝트에서 데이터베이스 시스템이나 ORM에 스키마를 두면 매우 어려운 문제가 생길 수 있어서 우리는 Zod와 Protobuf를 씀. Protobuf도 Zod나 비슷한 것으로 완전히 대체할 수 있다고 봄
      영국 Post Office 스캔들에는 Horizon이라는 IT 시스템이 관련되어 있음: https://en.wikipedia.org/wiki/British_Post_Office_scandal
      세부 내용을 읽어 보니 Zod 같은 스키마 검증이 있었다면 이 스캔들을 막는 데 기여했을 것 같음. 집단소송 법원 문서의 기술 부록에서도 스키마 사용 부재가 언급됨: https://en.wikipedia.org/wiki/Bates_%26_Others_v_Post_Office...
    • Zod는 SPA와 관계없고 프런트엔드 전용 도구도 아님. 코드에서 입력 검증을 하지 않는 건가?
    • 눈을 감으면 문제군 전체가 존재하지 않게 되기도 함
    • 서버 측 검증은 어떻게 하는지 궁금함
      지금은 방어적으로 if 문을 잔뜩 쓰는 것처럼 들림. 더 나쁘게는 HTML 클라이언트 측 폼 검증에만 의존하는 것일 수도 있음
    • 우리에게는 데이터 파이프라인 일부로 쓰는 백엔드 전용 Zod 스키마가 엄청 많음. 그래서 SPA 문제를 해결하기 위해서만 존재하는 도구처럼 보이지 않음
  • 전문가가 아니라 조심스럽지만, JSON Schema가 스키마 기반이라 TypeScript가 아닌 언어에서도 검증기를 구현할 수 있어 좋은 선택일 수 있다고 생각했음
    https://ajv.js.org가 그런 JSON Schema 라이브러리 중 하나임. Zod와 비교하면 어떤지 궁금함

    • Zod는 JSON 검증만을 위한 도구가 아님. 변환 없이는 JSON으로 표현할 수 없는 객체, 예를 들어 날짜나 클래스 인스턴스 등을 검증할 수 있음
      원하면 JSON 변환기로도 쓸 수 있음. 예를 들어 문자열을 받아 ISO 날짜 문자열인지 검증하고 Date 객체를 출력하는 스키마를 작성할 수 있음
    • Zod 4는 Zod 스키마를 JSON Schema로 변환하는 기능을 네이티브로 지원함. 예전에도 서드파티 라이브러리로는 가능했음
      중요한 차이는 전처리와 refine임. Zod에서는 검증 전에 콜백을 제공할 수 있고, 이는 매우 유용하지만 JSON으로 표현할 수 없음. 예를 들어 날짜 검증 전에 MM/DD/YYYYDD/MM/YYYY로 바꾸는 일이 생각보다 자주 유용함
    • 그런 경우 JSON Schema는 좋은 선택이고, TypeBox는 이를 생성하는 괜찮은 방법임
      검증에는 AJV를 쓰거나 TypeBox 자체 스키마 검증 시스템을 쓸 수 있음. 후자가 훨씬 빠르지만 동기식만 지원하고, AJV는 데이터베이스 확인 같은 비동기 검증 옵션이 있음
    • 언어 간 검증에는 JSON Schema가 맞음. 이를 OpenAPI로 감싸면 괜찮은 API 문서도 공짜로 얻음
  • 이 주제 주변의 부정적 반응이 놀라움
    Zod v4 초기 버전을 테스트했고 새 API가 마음에 들었지만, 마이그레이션 경로가 어떻게 될지 매우 걱정했음. 아예 새 패키지 이름으로 배포하자고 제안할 생각도 있었음
    그런데 작성자의 접근은 영리함. 모든 의존성이 업데이트되기를 기다리지 않고도 나 같은 사람이 v4를 즉시 도입할 수 있게 해 줌

    • 좋은 경로임. 검증 같은 것에서 전부 아니면 전무 식 마이그레이션을 감당할 방법은 없음
  • 새 프로젝트에 Zod를 막 도입하기 시작했는데, 이보다 더 좋은 시점은 없었음
    지금 보이는 내용 기준으로는 v4로 옮기려면 바꿔야 할 것이 정말 많았을 것 같음