zod의 coerce를 잘 활용하자

이민선(Jasmine)·2023년 12월 30일
post-thumbnail

현재 우리 프로젝트에서는 react-hook-form과 zod를 짝짝꿍으로 사용하고 있다. form 요소의 상태를 관리할 틀? 내지는 영역?을 form schema라고 표현할 수 있다. form schema를 zod를 이용하여 선언할 수 있는데, 선언 방법은 간단하면서도 다양한 방식이 있다.

docs
https://zod.dev/?id=type-inference

꼭 react-hook-form과 짝꿍으로서가 아니더라도, 스키마 유효성 검사에 있어 다양한 방식을 제공해주기 때문에 유용하다. 간단한 스키마는 typescript로만 정의해도 정적 유효성 검사로는 충분하겠지만,

// 이럴거면 zod 왜 씀? 이라는 생각이 들게 하는 코드
// 물론 form이 복잡해지면 큰 착각임을 알게 된다.
const A = z.string();
type A = z.infer<typeof A>; // string

const u: A = 12; // TypeError
const u: A = "asdf"; // compiles

typescript가 제공하지 않는 다양한 기능을 zod가 제공해주기 때문에 form schema가 복잡해질수록 typescript를 알차게 보완해주는 라이브러리라고도 생각이 들었다.

선언한 스키마를 infer 메서드를 이용하여 추론된 type으로 호환이 가능한데, 일부 zod의 기능이 추론된 type에 영향을 주지 않는 경우도 있다.

ex. 음이 아닌 숫자를 type alias로 변환하면 number가 될 뿐이다.

const N = z.number().nonnegative()

type NonNegative = z.infer<typeof N> // number

내가 개인적으로 가장 유용하다고 느꼈던 것 중 하나는 Coercion이었다.
input 등 form 요소에 입력한 특정 value를 내가 원하는 type으로 변환시켜서 상태 관리하고 싶을 때 사용하는 것이다.

const inputSchema = z.coerce.number() // Number(input)

coerce 유용해 아주 좋아

반드시 number값이 들어가야 하는 input요소에 특정 숫자를 입력했을 때, 자꾸 number가 아닌 string 값이 저장되어서 validation check 통과를 못해 의문의 난관을 만난 적이 있다.
당시 해당 키의 zod type은 z.number()로만 지정이 되어 있었다.
input에 type=number 지정도 해보고, input에 들어가는 모든 value의 입력값을 Number로 바꿔서 넣어보기도하고, 여러 시도를 해봤다. 하지만 touched field가 되면 바로 input 내부의 값이 string으로 변환되어 버려서 validation 통과를 못하는 것이었다..

원인은 자바스크립트의 기본 동작이 사용자 입력을 문자열로 취급한다는 것이었다. 숫자를 입력하더라도 자바스크립트는 문자열로 처리하므로, zod 역시 문자열로 인식하고 expected number, got string과 같은 validation 오류를 자꾸 내뿜었던 것이었다. 아니 number type인데 string으로 처리하면 어쩌자는 거임? 그렇다는데 어쩔 수 없지. (빠르게 수긍하는 편)

이때 zod에서 coerce를 사용하면, form에 저장된 값 상태의 type을 number로 변환할 수 있다. 따라서 극적으로 validation check도 통과할 수 있었다. 일주일 걸림

stackoverflow에도 나같은 사람들이 꽤 있었다.
https://stackoverflow.com/questions/76878664/react-hook-form-and-zod-inumber-input
https://stackoverflow.com/questions/76536978/how-to-get-zod-to-read-input-as-number-and-not-text

원시값 뿐만 아니라, array나 object를 다룰 때 사용할 수 있는 validation 기능도 많다.

const rowSchema = z.object({ name: string, age: number })

type peopleSchema = z.array(rowSchema)

form이 복잡해지면 때로 계층 구조로 요소들을 관리해야할 때도 있는데, 이때 zod를 이용하여 form schema를 잡아놓고 default value에 예시를 보여주는 방식으로 진행하면 한 눈에 알 수 있어서 좋았던 것 같다.

profile
기록에 진심인 개발자 🌿

0개의 댓글