클라이언트가 전송한 객체는 항상 유효성을 검증해야 한다. 간단한 if문을 사용할
수 있지만, 애플리케이션이 복잡해지면 중첩된 if문이 많아질 수 있다.
데이터 유효성 검증 라이브러리(예: Zod)를 사용하면 객체의 구조를 간단한 문법으로 정의할 수 있으며, 데이터 유효성 검증의 복잡성을 관리할 수 있다.
Zod 라이브러리를 활용하여 데이터 유효성 검사를 하는 방법에 대해 알아보자.
app/api/users/[id]/route.tsx
import { NextRequest, NextResponse } from "next/server";
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const body = await request.json();
if (!body.name) {
return NextResponse.json({ error: "Name is Required" }, { status: 404 });
}
const userData = [
{ id: 1, name: "Jieun" },
{ id: 2, name: "Hansol" },
];
const requestedId = parseInt(params.id);
const user = userData.find((user) => user.id === requestedId);
if (!user) {
return NextResponse.json({ error: "USER NOT FOUND" }, { status: 404 });
}
return NextResponse.json({ id: requestedId, name: body.name });
}
npm install zod
tsconfig.json
{
"compilerOptions": {
"strict": true
}
}
import { z } from "zod";
const schema = z.object({
name: z.string().min(3),
email: z.string().email(),
age: z.number(),
});
schema.ts
파일을 생성한다.app/api/users/schema.ts
import { z } from "zod";
const schema = z.object({
name: z.string().min(3),
email: z.string().email(),
age: z.number(),
});
export default schema;
parse
, safeParse
)의 스키마 함수를 통해 유효성 검사를 할 수 있다.schema.parse({
name: "jieun",
email: "email@email.com",
age: 20,
}); // 통과
schema.parse({
name: "jieun",
email: "email@email.com",
}); // 검증 실패
parse
safeParse
safeParse
를 통해 유효성 검사를 하도록 PUT 메소드를 수정해보자.
validation
변수에 담긴 결과가 false
이면 유효성 검사에 실패한 것이므로 404 오류를 처리하고, 오류 메시지는 zod object 에서 제공하는 error
를 출력하도록 한다.app/api/users/[id]/route.tsx
import { NextRequest, NextResponse } from "next/server";
import schema from "../schema";
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const body = await request.json();
const validation = schema.safeParse(body);
if (!validation.success) {
return NextResponse.json(validation.error.errors, { status: 404 });
}
const userData = [
{ id: 1, name: "Jieun", email: "jieun@email.com", age: 29 },
{ id: 2, name: "Hansol", email: "hansol@email.com", age: 27 },
];
const requestedId = parseInt(params.id);
const user = userData.find((user) => user.id === requestedId);
if (!user) {
return NextResponse.json({ error: "USER NOT FOUND" }, { status: 404 });
}
return NextResponse.json({
id: requestedId,
name: body.name,
email: body.email,
age: body.age,
});
}
[
{
"code": "too_small",
"minimum": 3,
"type": "string",
"inclusive": true,
"exact": false,
"message": "String must contain at least 3 character(s)",
"path": [
"name"
]
},
{
"validation": "email",
"code": "invalid_string",
"message": "Invalid email",
"path": [
"email"
]
}
]
parse()
함수가 반환하는 객체에는 검증에 통과한 속성만 포함된다는 것이다.password
속성을 입력 객체에 포함한 경우const user = schema.parse({
name: "jieun",
email: "email@email.com",
age: 20,
password : "1234"
}); // 통과
console.log(user)
parse()
함수가 반환한 결과 객체에는 해당 속성이 제외된다.{
name: "jieun"
email: "email@email.com",
age: 20,
}
parse()
함수의 반환 타입이 정의된 스키마에 의해서 결정이 되기 때문이다.password
속성이 없는데 값에만 password
속성이 들어있다면 타입 에러가 발생했을 것// 내가 직접 타입을 작성
interface User {
email: string;
age: number;
active: boolean;
}
function processUser(user: User) {
User.parse(user); // 유효성 검증
// 사용자 처리 로직
}
infer
과 자바스크립트의 typeof
연산자를 사용하면 이미 정의한 스키마로 부터 타입을 뽑아낼 수 있다.// 스카마로부터 타입을 추론
type User = z.infer<typeof User>;
function processUser(user: User) {
User.parse(user); // 유효성 검증
// 사용자 처리 로직
}
References