유효성 검증
Hono는 매우 간단한 유효성 검사 기능만 제공한다. 하지만, 이를 서드파티 유효성 검사 도구와 함께 사용하면 강력한 기능을 발휘할 수 있다. 또한, RPC 기능을 통해 API 스펙을 타입으로 클라이언트와 공유할 수 있다.
수동 유효성 검사
먼저, 외부 라이브러리를 사용하지 않고 들어오는 값을 검증하는 방법을 소개한다.
hono/validator에서 validator를 가져온다.
import { validator } from 'hono/validator'폼 데이터를 검증하려면 첫 번째 인자로 form을 지정하고, 두 번째 인자로 콜백 함수를 전달한다. 콜백 함수에서 값을 검증하고, 검증된 값을 반환한다. validator는 미들웨어로 사용할 수 있다.
app.post(
'/posts',
validator('form', (value, c) => {
const body = value['body']
if (!body || typeof body !== 'string') {
return c.text('Invalid!', 400)
}
return {
body: body,
}
}),
//...핸들러 내부에서 c.req.valid('form')을 사용해 검증된 값을 가져올 수 있다.
, (c) => {
const { body } = c.req.valid('form')
// ... 작업 수행
return c.json(
{
message: 'Created!',
},
201
)
}검증 대상은 form 외에도 json, query, header, param, cookie가 있다.
WARNING
json을 검증할 때는 요청에 반드시 Content-Type: application/json 헤더가 포함되어야 한다. 그렇지 않으면 요청 본문이 파싱되지 않고 경고가 발생한다.
app.request()를 사용해 테스트할 때 content-type 헤더를 설정하는 것이 중요하다.
다음과 같은 애플리케이션이 있다고 가정하자.
const app = new Hono()
app.post(
'/testing',
validator('json', (value, c) => {
// 통과 검증기
return value
}),
(c) => {
const body = c.req.valid('json')
return c.json(body)
}
)테스트는 다음과 같이 작성할 수 있다.
// ❌ 동작하지 않음
const res = await app.request('/testing', {
method: 'POST',
body: JSON.stringify({ key: 'value' }),
})
const data = await res.json()
console.log(data) // undefined
// ✅ 정상 동작
const res = await app.request('/testing', {
method: 'POST',
body: JSON.stringify({ key: 'value' }),
headers: new Headers({ 'Content-Type': 'application/json' }),
})
const data = await res.json()
console.log(data) // { key: 'value' }WARNING
header를 검증할 때는 소문자를 키로 사용해야 한다.
Idempotency-Key 헤더를 검증하려면 idempotency-key를 키로 사용해야 한다.
// ❌ 동작하지 않음
app.post(
'/api',
validator('header', (value, c) => {
// idempotencyKey는 항상 undefined
// 따라서 이 미들웨어는 예상치 못하게 항상 400을 반환
const idempotencyKey = value['Idempotency-Key']
if (idempotencyKey == undefined || idempotencyKey === '') {
throw HTTPException(400, {
message: 'Idempotency-Key is required',
})
}
return { idempotencyKey }
}),
(c) => {
const { idempotencyKey } = c.req.valid('header')
// ...
}
)
// ✅ 정상 동작
app.post(
'/api',
validator('header', (value, c) => {
// 헤더 값을 정상적으로 가져올 수 있음
const idempotencyKey = value['idempotency-key']
if (idempotencyKey == undefined || idempotencyKey === '') {
throw HTTPException(400, {
message: 'Idempotency-Key is required',
})
}
return { idempotencyKey }
}),
(c) => {
const { idempotencyKey } = c.req.valid('header')
// ...
}
)여러 개의 검증기 사용
요청의 다른 부분을 검증하기 위해 여러 개의 검증기를 포함할 수도 있다:
app.post(
'/posts/:id',
validator('param', ...),
validator('query', ...),
validator('json', ...),
(c) => {
//...
}Zod 활용하기
Zod는 대표적인 타사 검증 도구 중 하나다. 타사 검증 도구를 사용하는 것을 권장한다.
Npm 레지스트리에서 설치한다.
npm i zodyarn add zodpnpm add zodbun add zodzod에서 z를 불러온다.
import { z } from 'zod'스키마를 작성한다.
const schema = z.object({
body: z.string(),
})콜백 함수에서 스키마를 사용해 검증하고, 검증된 값을 반환한다.
const route = app.post(
'/posts',
validator('form', (value, c) => {
const parsed = schema.safeParse(value)
if (!parsed.success) {
return c.text('Invalid!', 401)
}
return parsed.data
}),
(c) => {
const { body } = c.req.valid('form')
// ... 작업 수행
return c.json(
{
message: 'Created!',
},
201
)
}
)Zod Validator Middleware
Zod Validator Middleware를 사용하면 더 쉽게 작업할 수 있다.
npm i @hono/zod-validatoryarn add @hono/zod-validatorpnpm add @hono/zod-validatorbun add @hono/zod-validator그리고 zValidator를 불러온다.
import { zValidator } from '@hono/zod-validator'이후 다음과 같이 작성한다.
const route = app.post(
'/posts',
zValidator(
'form',
z.object({
body: z.string(),
})
),
(c) => {
const validated = c.req.valid('form')
// ... 유효성 검사된 데이터 사용
}
)