Kaapi와 함께 현대 백엔드 구축: 요청 검증 파트 2

발행: (2026년 1월 12일 오전 04:33 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

이 시리즈는 TypeScript를 사랑하고 Hapi의 설계 철학을 이해할 수 있는 백엔드 개발자를 위해 작성되었습니다.

다시 검증…?

아마도 Joi, ArkType, Valibot, 혹은 Zod를 사용한 이전 요청 검증 기사를 따라해 보셨을 겁니다.

멋진 Kaapi 앱을 만들고, 라우트는 깔끔하게, 검증은 탄탄하게 구성했습니다. 모든 것이 깔끔하고 안심이 되었습니다.

아마도 다음과 같은 코드로 시작했을 겁니다:

import { z } from 'zod';

app
  .base()
  .zod({
    params: z.object({
      name: z.string().min(3).max(10)
    })
  })
  .route({
    method: 'GET',
    path: '/hello/{name}',
    handler: function (request, h) {
      return `Hello ${request.params.name}!`;
    }
  });

솔직히 말해서?
그 코드는 완벽히 작동합니다.

하지만 어느 순간 앱이 작은 규모를 벗어나기 시작합니다.

라우트가 점점 늘어나고, 인라인으로 정의하지 않게 됩니다. 라우트는 기능별로 파일을 나누어 관리하게 되고, 그때문에 이전에 사용하던 깔끔한 체이닝 방식이 더 이상 맞지 않게 됩니다.

그래서 생기는 질문은:

라우트가 앱과 더 이상 가깝게 위치하지 않을 때, 검증을 라우트와 가깝게 유지하려면 어떻게 해야 할까요?

바로 우리가 여기서 해결하려는 문제입니다.

새로운 설정 (아마 이미 하셨을 겁니다)

  1. Routes를 독립적으로 정의app.route(...)에 등록되는 단일 위치.
  2. 검증, 타입 안전성, 그리고 좋아하는 검증기를 선택할 자유.

한 번에 하나씩 검증기를 살펴보겠습니다.

Source:

Joi: 익숙한 경로

If you’re using Joi, nothing changes. That’s intentional.

npm i joi
import { Kaapi, KaapiServerRoute } from '@kaapi/kaapi';
import Joi from 'joi';

const app = new Kaapi({ /* ... */ });

const route: KaapiServerRoute = {
  method: 'GET',
  path: '/hello/{name}',
  options: {
    validate: {
      params: Joi.object({
        name: Joi.string().min(3).max(10).required()
      })
    }
  },
  handler: function (request, h) {
    return `Hello ${request.params.name}!`;
  }
};

app.route(route);

No magic. No surprises.
If you’ve used Hapi before, this should feel like coming home.

Zod: TypeScript와 대화하는 검증

이제 제네릭을 뒤섞지 않고 강력한 타입 추론을 원한다고 가정해 보세요.

npm i zod @kaapi/validator-zod

Kaapi 확장하기

import { Kaapi } from '@kaapi/kaapi';
import { validatorZod } from '@kaapi/validator-zod';

const app = new Kaapi({ /* ... */ });

await app.extend(validatorZod);

라우트 정의하기

import { withSchema } from '@kaapi/validator-zod';
import { z } from 'zod';

const route = withSchema({
  params: z.object({
    name: z.string().min(3).max(10)
  })
}).route({
  method: 'GET',
  path: '/hello/{name}',
  handler: function (request, h) {
    return `Hello ${request.params.name}!`;
  }
});

app.route(route);

제네릭이 없습니다. 타입은 스키마에서 바로 가져옵니다. 검증을 한 번만 작성하면 TypeScript가 자동으로 따라옵니다.

Valibot: 같은 아이디어, 다른 맛

Valibot은 Zod와 같은 라인에서 활동하지만 약간 다른 문법을 가지고 있습니다.

npm i valibot @kaapi/validator-valibot

Kaapi 확장

import { Kaapi } from '@kaapi/kaapi';
import { validatorValibot } from '@kaapi/validator-valibot';

const app = new Kaapi({ /* ... */ });

await app.extend(validatorValibot);

라우트 정의

import { withSchema } from '@kaapi/validator-valibot';
import * as v from 'valibot';

const route = withSchema({
  params: v.object({
    name: v.pipe(v.string(), v.minLength(3), v.maxLength(10))
  })
}).route({
  method: 'GET',
  path: '/hello/{name}',
  handler: function (request, h) {
    return `Hello ${request.params.name}!`;
  }
});

app.route(route);

Source:

ArkType: 읽을 수 있는 스키마

npm i arktype @kaapi/validator-arktype

Kaapi 확장

import { Kaapi } from '@kaapi/kaapi';
import { validatorArk } from '@kaapi/validator-arktype';

const app = new Kaapi({ /* ... */ });

await app.extend(validatorArk);

라우트 정의

import { withSchema } from '@kaapi/validator-arktype';
import { type } from 'arktype';

const route = withSchema({
  params: type({
    name: '3 <= string <= 10'
  })
}).route({
  method: 'GET',
  path: '/hello/{name}',
  handler: function (request, h) {
    return `Hello ${request.params.name}!`;
  }
});

app.route(route);

실제로 중요한 점

  • 라우트는 독립적으로 정의됩니다.
  • 각 라우트는 원하는 검증기를 선택합니다.
  • 등록은 매우 간단하게 유지됩니다:
app.route(route);
  • Joi는 바로 사용할 수 있습니다.
  • Zod, Valibot, 그리고 ArkType은 withSchema를 통해 플러그인됩니다.

같은 앱. 같은 API. 다른 도구들.

그래서… 어떤 검증기를 당신이 사용해야 할까요?

목표권장 검증기
코드 최소화Joi 또는 ArkType
제네릭 없이 강력한 타입 추론Zod 또는 Valibot
사람이 읽기 쉬운 스키마ArkType
우수한 자동 문서화Zod, Valibot, 또는 Joi

Kaapi는 선택을 강요하지 않습니다.
전체 도구 상자를 제공합니다. 사용하고 싶은 드라이버를 선택하세요.

소스 코드

전체 예제가 필요하신가요?

👉 github.com/kaapi/kaapi‑examples

shygyver/kaapi-monorepo-playground

예제는 validation-app 디렉터리 아래에 있습니다.

실행 방법은 README를 확인하세요.

더 많은 Kaapi 기사들이 곧 공개됩니다.

이번 글은 검증을 적절한 위치에 두는 것에 관한 내용이었습니다.

📦 지금 시작하세요

npm install @kaapi/kaapi

🔗 자세히 알아보기: Kaapi Wiki

Back to Blog

관련 글

더 보기 »

2025년 나의 Node.js API 모범 사례

Node.js는 이제 10년 넘게 프로덕션 API를 구동해 왔으며, 2025년이 되면서 더 이상 “새롭다”거나 실험적인 것이 아니라 인프라가 되었습니다. 그 성숙도는 c...

🚀 GraphQL APIs 설명 (실제 Node.js 예제와 함께)

REST는 어디에나 존재하지만, GraphQL은 현대 API가 구축되는 방식을 바꾸고 있습니다. 이 게시물에서는 다음을 배울 수 있습니다: - GraphQL이 실제로 무엇인지 - 전문 용어 없이 어떻게 작동하는지 - A rea...