Skip to content

๐Ÿ“ zod ๋„์ž…ํ•˜๊ธฐ

baegyeong edited this page Dec 27, 2024 · 3 revisions
๋ถ„์•ผ ์ž‘์„ฑ์ž ์ž‘์„ฑ์ผ
FE ์กฐ๋ฐฐ๊ฒฝ 24๋…„ 11์›” 24์ผ

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” API ๋ช…์„ธ์— ๋”ฐ๋ผ request/response ํƒ€์ž…์„ ๋”ฐ๋กœ ์ž‘์„ฑํ•ด๋‘” ์ƒํƒœ์ด๋‹ค.

export interface GetStockListRequest {
  limit: number;
}

export interface GetStockListResponse {
  id: string;
  name: string;
  currentPrice: number;
  changeRate: number;
  volume: number;
  marketCap: string;
}

์ด๋Š” ์ปดํŒŒ์ผ ๊ณผ์ •์—์„œ๋Š” ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์žก์•„๋‚ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋Ÿฐํƒ€์ž„ ์‹œ์—๋Š” ํƒ€์ž…์ •๋ณด๊ฐ€ ์ œ๊ฑฐ๋˜์–ด ํ™•์ธํ•  ์ˆ˜ ์—†๋‹ค.

ํƒ€์ž…๊ฒ€์‚ฌ๋Š” ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ์ผ์–ด๋‚˜๋ฉฐ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋Ÿฐํƒ€์ž„ ์‹œ ์œ ํšจ์„ฑ ๊ฒ€์ฆ๊นŒ์ง€ ํ•ด์ฃผ์ง€ ๋ชปํ•œ๋‹ค. ์ฆ‰, ํƒ€์ž… ๊ฒ€์‚ฌ๊ฐ€ ์œ ํšจ์„ฑ ๊ฒ€์ฆ์„ ๋Œ€์‹ ํ•  ์ˆ˜ ์—†๋‹ค.

๋Ÿฐํƒ€์ž„ ์‹œ์ ์— ์˜ˆ์ธกํ•  ์ˆ˜ ์—†๋Š” ๋™์ž‘๊นŒ์ง€ ํ†ต์ œํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๋กœ์ง์ด ํ•„์š”ํ•˜๋‹ค.

์ด๋ฅผ ์‰ฝ๊ฒŒ ๋„์™€์ฃผ๊ธฐ ์œ„ํ•ด์„œ zod ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋„์ž…ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

์Šคํ‚ค๋งˆ๋ฅผ ์ •์˜ํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ํƒ€์ž… ์ถ”๋ก  ๋ฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ์ ์ด ๋งค๋ ฅ์ ์œผ๋กœ ๋Š๊ปด์กŒ๋‹ค.


์‚ฌ์šฉ๋ฒ•

์„ค์น˜

์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ๋Š” yarn์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

yarn add zod

์Šคํ‚ค๋งˆ ์ •์˜

import { z } from 'zod';

export const GetStockResponseSchema = z.object({
  marketCap: z.number(),
  eps: z.number(),
  per: z.number(),
  high52w: z.number(),
  low52w: z.number(),
});

์ฃผ์‹ ์ƒ์„ธ ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ค๋Š” get ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต ์Šคํ‚ค๋งˆ๋ฅผ ์ •์˜ํ–ˆ๋‹ค.

z.number()์—๋Š” .min(), .max(), .postive(), nonnegative(), negative(), nonpositive(), finite() ๋“ฑ์„ ์ฒด์ด๋‹์œผ๋กœ ์ถ”๊ฐ€๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ z.bigint()๋„ ๋™์ผํ•˜๊ฒŒ ๊ฐ€๋Šฅํ•˜๋‹ค.


export const GetLoginStatusSchema = z.object({
  message: z.enum(['Authenticated', 'Not Authenticated']),
});

์œ„์™€ ๊ฐ™์ด z.enum()์œผ๋กœ๋„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.


export const ChatDataSchema = z.object({
  id: z.number(),
  likeCount: z.number(),
  message: z.string(),
  type: z.string(),
  createdAt: z.date(),
  liked: z.boolean(),
  nickname: z.string(),
});

export const ChatDataResponseSchema = z.object({
  chats: z.array(ChatDataSchema),
  hasMore: z.boolean(),
});

z.array()๋ฅผ ํ†ตํ•ด ๋ฐฐ์—ด ์Šคํ‚ค๋งˆ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.


ํƒ€์ž… ์ถ”๋ก 

export type GetStockResponse = z.infer<typeof GetStockResponseSchema>;

z.infer๋ฅผ ํ†ตํ•ด ์œ„์—์„œ ์ •์˜ํ•œ ์Šคํ‚ค๋งˆ๋กœ ํƒ€์ž…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

image

VSC ์ƒ์—์„œ๋„ ํƒ€์ž…์ถ”๋ก ์ด ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


์œ ํšจ์„ฑ ๊ฒ€์ฆ

.parse

const validatedData = GetStockResponseSchema.parse(data);

๊ฒ€์ฆํ•˜๋ ค๋Š” ๋ฐ์ดํ„ฐ๋ฅผ parse์— ๋„˜๊ฒจ์„œ ํ˜ธ์ถœํ•˜๋ฉด ์œ ํšจ์„ฑ ๊ฒ€์ฆ์„ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ฒ€์ฆ์— ์‹คํŒจํ•œ๋‹ค๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

.safeParse

  const result = schema.safeParse(data);

  if (!result.success) {
	  // handle error then return
    result.error
  }

์œ ํšจ์„ฑ ๊ฒ€์ฆ์— ์‹คํŒจํ–ˆ์„ ๋•Œ ์—๋Ÿฌ๋ฅผ ๋˜์ง€๊ณ  ์‹ถ์ง€์•Š๋‹ค๋ฉด safeParse๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

safeParse์˜ ๋ฆฌํ„ด๊ฐ’์œผ๋กœ๋Š” { success: true; data: T; } | { success: false; error: ZodError; }๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.



์ฐธ๊ณ ์ž๋ฃŒ

zod๋กœ ์œ ํšจ์„ฑ ๊ฒ€์ฆ๊ณผ ํƒ€์ž… ์„ ์–ธ์˜ ๋‘๋งˆ๋ฆฌ ํ† ๋ผ ์žก๊ธฐ

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์“ฐ๋Š”๋ฐ๋„ ์œ ํšจ์„ฑ ๊ฒ€์ฆ์ด ํ•„์š”ํ• ๊นŒ?

zod ๊ณต์‹๋ฌธ์„œ

Mastering Zod Validation in React: A Comprehensive Guide

๐Ÿœ ํŒ€ ๊ฐœ๋ฏธ

๐Ÿ›๏ธ ํŒ€ ๋ฌธํ™”

๊ฐœ๋ฐœ ์œ„ํ‚ค

FE

BE

Infra

๐Ÿ—ฃ๏ธ ๋ฐœํ‘œ

๐Ÿ“š ํšŒ์˜๋ก

๐Ÿ”ด ์ธํ„ฐ๋ฏธ์…˜
๐ŸŸ  1์ฃผ์ฐจ
๐ŸŸก 2์ฃผ์ฐจ
๐ŸŸข 3์ฃผ์ฐจ
๐Ÿ”ต 4์ฃผ์ฐจ
๐ŸŸฃ 5์ฃผ์ฐจ
๐ŸŸค 6์ฃผ์ฐจ

๐Ÿ’ญ ํšŒ๊ณ 

๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ ๋ฉ˜ํ† ๋ง

Clone this wiki locally