본문 바로가기
⚙️백엔드 : Backend/DataBase

[ Superform | Zod ] 스키마 분할 -> superRefine 처리 -> superValidate 요청

by 예옹이 2024. 11. 21.

안녕하세요 예옹이입니다~

이번 웹프로젝트에서 제가 맡은 메뉴는 한번에 54개의 컬럼을 작업해주어야했습니다.

컬럼 값이 너무 많아 애초에 서버에서 schema를 나눠서 클라이언트로 줄까 생각했지만,

한번에 기본값으로 길게 주되 받고나서 그 값을 나누는 작업을 서버에서 진행해보고 싶었습니다!

 

const schema = z.object({
  id: z.string().optional(),
  accidentMgmtNo: z.string().min(1, { message: '사고관리번호를 입력해주세요.' }),
  companyBusinessRegistrationNo: z.string({ required_error: '사업자등록번호를 입력해주세요.' }),
  companyId: z.string({ required_error: '회사를 선택해주세요.' }),
  companyName: z.string().min(1, { message: '회사명을 입력해주세요.' }),
  companyEmployeeCnt: z.string().min(1, { message: '근로자수를 입력해주세요.' }),
  companyIndustryType: z.object({
    id: z.string().min(1, { message: '업종을 선택해주세요.' }),
    name: z.string(),
  }),
  companyAddress1: z.string().min(1, { message: '주소를 입력해주세요.' }),
  companyAddress2: z.string().nullable().optional(),
  
  ...
  
  writerContact: z.string().min(1, { message: '작성자 연락처를 입력해주세요.' }),
  submissionDate: z.string().min(1, { message: '작성일을 입력해주세요.' }),
  industrialAccidentEmployer: z.object({
    id: z.string().min(1, { message: '사업주를 선택해주세요.' }),
    name: z.string(),
  }),
  industrialAccidentWorkerRepresentative: z.object({
    id: z.string().min(1, { message: '근로자대표를 선택해주세요.' }),
    name: z.string(),
  }),
  isElectronicSignature: z.enum(['Y', 'N']).default('N'),
  injuryTypes: z.string().optional(),
  injuryParts: z.string().optional(),
  customInjuryTypes: z.string().optional(),
  customInjuryParts: z.string().optional(),
});

 

그래서 이렇게 긴 스키마에서 필요한 부분만 가져오고 싶을때는

const injurySchema = schema.pick({
  injuryTypes: true,
  injuryParts: true,
  customInjuryTypes: true,
  customInjuryParts: true,
});

schema.pick() 을 하면 원하는 값만 가져올 수 있습니다.

 

마찬가지로 원하지 않는 부분을 제외해서 가져오고 싶을때는

const industrialAccidentSchema = schema.omit({
  injuryTypes: true,
  injuryParts: true,
  customInjuryTypes: true,
  customInjuryParts: true,
})

schema.omit() 을 하면 원하지 않는 값은 제외하고 가져올 수 있습니다.

 

그리고 복잡한 유효성 검사가 필요할 시에는 기존 schema에 검사를 진행하고
그 스키마에 .pick() 혹은 .omit()을 하면 될 줄 알았는데 에러가 발생할겁니다.

그럴땐 기존 스키마를 처리해서 만든 새 변수에 유효성 검사를 진행하면 됩니다.

const industrialAccidentSchema = schema
  .omit({
    injuryTypes: true,
    injuryParts: true,
    customInjuryTypes: true,
    customInjuryParts: true,
  })
  .superRefine((data, ctx) => {
    if (data.isInhouseContractor === 'Y') {
      if (!data.contractorName) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          path: ['contractorName'],
          message: '사내수급인 소속일 경우 원도급인 사업장명은 필수입니다.',
        });
      }
      if (!data.contractorAccidentMgmtNo) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          path: ['contractorAccidentMgmtNo'],
          message: '사내수급인 소속일 경우 사업장 산재관리번호는 필수입니다.',
        });
      }
    }

    if (data.isDispatchedWorker === 'Y') {
      if (!data.dispatchEmployerName) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          path: ['dispatchEmployerName'],
          message: '파견근로자일 경우 파견사업주 사업장명은 필수입니다.',
        });
      }
      if (!data.dispatchAccidentMgmtNo) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          path: ['dispatchAccidentMgmtNo'],
          message: '파견근로자일 경우 사업장 산재관리번호는 필수입니다.',
        });
      }
    }
  });

 

 

만약 action에서 새롭게 처리한 두가지 스키마를 모두 활용하고싶어서

export const actions = {
  default: async ({ locals: { db }, request }) => {
    const form = await superValidate(request, zod(industrialAccidentSchema));
    const injuryForm = await superValidate(request, zod(injurySchema));

이렇게 request를 2번 요청하면 에러가 발생합니다.

 

그럴땐

export const actions = {
  default: async ({ locals: { db }, request }) => {
    const formData = await request.formData();

    const form = await superValidate(formData, zod(industrialAccidentSchema));
    const injuryForm = await superValidate(formData, zod(injurySchema));

새로운 변수(formData)로 request를 받고 superValidate를 진행하면 됩니다

 

 

 

 

출처

https://zod.dev/README?id=pickomit

 

GitHub - colinhacks/zod: TypeScript-first schema validation with static type inference

TypeScript-first schema validation with static type inference - colinhacks/zod

github.com

 

https://superforms.rocks/faq

 

FAQ

FAQ I see the data in $form, but it’s not posted to the server? The most common mistake is to forget the name attribute on the input field. If you’re not using dataType: 'json' (see nested data), the form is treated as a normal HTML form, which require

superforms.rocks