Logo

Zod로 유효성 검증과 타입 선언의 두 마리 토끼 잡기

이전 포스팅에서 타입스크립트로 코드를 작성하는 것과 무관하게 왜 자바스크립트 프로그램에서 유효성 검증이 필요한지에 대해서 알아보았는데요.

이번 포스팅에서는 최근 개발자들로부터 많은 인기를 끌고 있는 Zod를 사용하는 전반적인 방법에 대해서 알아보겠습니다.

라이브러리 설치

프로젝트에 zod라는 패키지를 npm으로 설치합니다.

$ npm i zod

그리고 Zod를 사용하실 때는 가급적 타입스크립트는 strict 모드로 쓰시기를 추천드릴게요.

tsconfig.json
{
  "compilerOptions": {
    "strict": true
  }
}

이제 zod 패키지에서 z를 불러올 수 있으며, 이 녀석만 있으면 우리는 Zod의 모든 기능을 활용할 수 있습니다.

import { z } from "zod";

스키마 정의

Zod를 사용할 때 제일 먼저 해야되는 것은 스키마(schema)를 정의하는 것입니다.

여기서 스키마란 쉽게 말해서 데이터의 형태와 구조를 뜻하는데요. 예를 들어, 이메일, 나이, 활성화 여부로 이루어진 사용자 객체를 나타내는 스키마를 Zod로 정의해보겠습니다.

const User = z.object({
  email: z.string(),
  age: z.number(),
  active: z.boolean(),
});

z.object()를 사용하여 User 스키마가 객체의 형태이고, z.string()으로 email 속성은 문자열, age 속성은 z.number()로 숫자, active 속성은 z.boolean()으로 불리언 형태입니다.

뭐, 별 거 없죠? 그런데 API가 굉장히 간단하고 명료하지 않나요?

Zod를 처음 사용하시는 분들도 이러한 직관적인 API 덕분에 큰 어려움 없이 배울 수 있답니다.

유효성 검증

Zod 스키마를 정의하고 나면 유효성 검증을 해볼 수 있는데요. 스키마의 parse() 함수에 검증하고 싶은 값을 넘겨서 호출하면 됩니다.

User.parse({
  email: "user@test.com",
  age: 35,
  active: true,
}); // ✅ 유효성 검증 통과

User.parse({
  email: "user@test.com",
  age: "35",
}); // ❌ 유효성 검증 실패

검증이 실패할 경우에는 parse() 함수는 오류(error)를 발생시키는데요. 정확히 어느 부분에서 어떤 검증을 통과하지 못했는지가 나와서 매우 유용합니다.

콘솔
ZodError: [
  {
    code: "invalid_type",
    expected: "number",
    received: "string",
    path: ["age"],
    message: "Expected number, received string",
  },
  {
    code: "invalid_type",
    expected: "boolean",
    received: "undefined",
    path: ["active"],
    message: "Required",
  },
];

여기서 한 가지 주의할 부분은 검증이 성공했을 경우 parse() 함수가 반환하는 객체에는 검증에 통과한 속성만 포함된다는 것입니다.

예를 들어, 다음과 같이 스키마에 정의되지 않은 password 속성을 입력 객체에 포함한 경우,

const user = User.parse({
  email: "user@test.com",
  age: 35,
  active: true,
  password: "abcd1234",
});

console.log(user);

parse() 함수가 반환한 결과 객체에는 해당 속성이 제외되어 있는 것을 볼 수 있습니다.

콘솔
{
  email: "user@test.com",
  age: 35,
  active: true,
}

이것은 parse() 함수의 반환 타입이 정의된 스키마에 의해서 결정이 되기 때문입니다. 타입에 password 속성이 없는데 값에만 password 속성이 들어있다면 타입 에러가 발생했을 것입니다.

console.log(user);
//          ^? const user: { email: string; age: number; active: boolean; }

이처럼 Zod는 타입스크립트에 아주 친화적으로 설계되어 있어서 견고한 코드를 작성하는데도 도움을 줍니다.

타입 추론

뿐만 아니라 Zod는 스키마를 기준으로 타입스크립트 타입을 알아서 추론할 수도 있는데요. 이 기능을 잘 활용하면 아예 타입을 따로 작성할 필요가 없어지고 따라서 타입을 스키마와 서로 맞춰 줄 걱정이 사라집니다.

예를 들어서, 사용자 객체를 입력으로 받는 함수를 타입스크립트로 작성하려면 아래와 같이 입력 타입을 작성해줘야하는데요.

// 내가 직접 타입을 작성
interface User {
  email: string;
  age: number;
  active: boolean;
}

function processUser(user: User) {
  User.parse(user); // 유효성 검증
  // 사용자 처리 로직
}

하지만 Zod의 infer과 자바스크립트의 typeof 연산자를 사용하면 이미 정의한 스키마로 부터 타입을 뽑아낼 수 있습니다.

// 스카마로부터 타입을 추론 👍
type User = z.infer<typeof User>;

function processUser(user: User) {
  User.parse(user); // 유효성 검증
  // 사용자 처리 로직
}

스키마 하나 작성해서 유효성 검증도 하고 타입스크립트 타입도 뽑아내고 완전 꿩먹고 알먹고 아닌가요? 😁

예제의 스키마는 단순해서 타입을 직접 작성하시는 게 대수롭지 않게 느껴질 수도 있는데요. 실제 프로젝트에서는 훨씬 복잡한 스키마를 다루실 때는 Zod의 이러한 편리함이 훨씬 크게 느껴질 것입니다.

직접 작성한 타입과 스키마가 항상 서로 일치하도록 관리한다는 게 여간 번거로운 일이 아니며 까먹기도 참 쉽거든요. Zod를 사용하면 스키마 활용을 극대화하고 불필요한 타입을 작성을 최소화할 수 있어서 개발 생산성이나 유지 보수 측면에서 큰 도움을 받을 수 있습니다.

마치면서

지금까지 Zod를 사용하여 어떻게 유효성 검증과 타입 선언이라는 두 마리의 토끼를 잡을 수 있는지에 대해서 살펴보았습니다.

다음 포스팅에서는 Zod의 스키마를 작성하는 방법에 대해서 좀 더 상세히 알아보도록 하겠습니다.

Zod 관련 포스팅은 Zod 태그를 통해서 쉽게 만나보세요!