import z from 'zod'

const envSchema = z.object({
  REACT_APP_FEATURE_VAC_ASK: z.string(),
  REACT_APP_FEATURE_RECORDS: z.string(),
  NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
})

const windowSchema = z.object({
   SOMETHING_COOL: z.string()
})

export const ENV = envSchema.parse(process.env)
export const WINDOW = windowSchema.parse(window)

#280

vitest에서 사용할 수 있는 사용자 지정 assertion을 추가하기.

import { expect } from 'vitest'
import type { ZodTypeAny } from 'zod'

expect.extend({
  /**
   * @param received 테스트할 Response 객체
   * @param schema 검증할 Zod 스키마
   */
  async toMatchSchema(received: Response, schema: ZodTypeAny) {
    const response = await received.json()
    const result = await schema.safeParseAsync(response)

    return {
      message: () => '',
      pass: result.success,
    } satisfies ExpectationResult
  },
})

vitest.d.ts에 기본 인터페이스를 확장하기.

import type { ZodTypeAny } from 'zod'

interface CustomMatchers<R = unknown> {
  toMatchSchema(schema: ZodTypeAny): Promise<R>
}

todoResponse의 응답 데이터가 todoSchema에 정의된 Zod 스키마와 일치하는지 확인한다.

test('todo', async () => {
  expect(todoResponse.ok).toBeTruthy()
  expect(todoResponse).toMatchSchema(todoSchema)
})

#317

https://www.jacobparis.com/content/zod-search-params-remix

  • 검색 매개변수별로 데이터 유효성 검증
  • useSearchParamsZod를 활용해 모든 검색 매개변수를 병합하고 유효성 검증
import { useMemo } from 'react'
import { useSearchParams } from 'react-router-dom'
import { z } from 'zod'

const CombinedSchema = PaginationSchema.merge(FilterSchema)
  .merge(SortSchema)
  .merge(SearchSchema)
  .partial()

export function useSearchParamsWithSchema() {
  const [searchParams, setSearchParams] = useSearchParams()

  const parsedParams = useMemo(() => {
    // searchParams를 객체로 변환
    const paramsObject = Object.fromEntries(searchParams.entries())
    // 결합된 스키마로 유효성 검사 및 파싱
    const result = CombinedSchema.safeParse(paramsObject)

    if (result.success) {
      return result.data
    }

    console.error(result.error)

    return {}
  }, [searchParams])

  /**
   * 새로운 검색 매개변수로 업데이트하는 함수입니다.
   *
   * 1. 현재 검색 매개변수의 복사본을 생성합니다.
   * 2. 새로운 매개변수를 순회하며 값을 설정하거나 삭제합니다.
   * 3. 최종적으로 업데이트된 매개변수를 state에 설정합니다.
   */
  const updateSearchParams = (newParams: Record<string, string>) => {
    const updatedParams = new URLSearchParams(searchParams)

    Object.entries(newParams).forEach(([key, value]) => {
      if (value) {
        // 업데이트된 매개변수로 상태 갱신
        updatedParams.set(key, value)
      } else {
        // 값이 없으면 삭제
        updatedParams.delete(key)
      }
    })

    // 업데이트된 매개변수로 상태 갱신
    setSearchParams(updatedParams)
  }

  return {
    parsedParams,
    updateSearchParams,
  }
}
#367