카테고리 없음

더 엄격한 타입 검증하기

Ahyeon, Jung 2024. 11. 8. 00:14

단순히 매핑하는 것보다 타입을 활용하면 더 유연한 필터링이 가능해진다. 하지만 객체 내부에서 다른 타입이 들어오게 된다면, 여전히 에러를 보이게 된다. 이때 zod, ts-io 등의 라이브러리을 통해 더 엄격한 타입 검증을 할 수 있다.

 

타입 검증 라이브러리

ts-io

 

Home

Runtime type system for IO decoding/encoding

gcanti.github.io

 

ts-io는 타입스크립트 기반의 타입 검증을 제공한다. 복잡한 데이터 구조나 타입이 필요한 경우 유용하며, 규모가 큰 프로젝트에서 유리하다.

zod

 

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

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

github.com

 

좀 더 간단하고 직관적인 타입 검증 라이브러리다. react-hook-form과의 결합이 좋다고 한다.

 

zod를 통해 타입 검증 추가하기

프로젝트가 크지 않고, zod가 좀 더 최근까지 관리를 하고 있어서 zod를 선택했다. 스키마만 정해주면 나름 편리하게 사용할 수 있다.

zod 설치

pnpm add zod

 

스키마 지정

타입 외에 스키마를 따로 추가해줘야한다. 타입스크립트랑 결합하는 ts-io는 타입 정의만으로 타입과 검증을 동시에 할 수 있다고 한다. 

export type CourseCardType = {
  id: number;
  title: string;
  description: string;
  isFree?: boolean;
  logoUrl?: string;
};

export const CourseCardSchema = z.object({
  id: z.number(),
  title: z.string(),
  description: z.string(),
  isFree: z.boolean().optional(),
  logoUrl: z.string().optional(),
});

 

스키마를 통한 타입 검증

타입 검증 후에는 어떻게 처리할 것인지 결정하면 된다. 나의 경우 예상하지 않은 응답값이 오는걸 막고자 했기 때문에 타입이 다르면 아예 제거했다. 하지만 안내하는 것이 중요하다면 에러 로직을 넣을 수 있다.

export function mapCoursesToViewModel(
  courses: ResponseCoursesType[]
): CourseCardType[] {
  return courses.map((course) => {
    const mappedCourse: Partial<CourseCardType> = {};

    Object.keys(course).forEach((key) => {
      if (key in ({} as CourseCardType)) {
        (mappedCourse as any)[key] = course[key as keyof ResponseCoursesType];
      }

      mappedCourse.isFree =
        course.enroll_type === 4 && typeof course.is_free === "boolean"
          ? undefined
          : course.is_free;

      mappedCourse.description = course.short_description;
      mappedCourse.logoUrl = course.logo_file_url;
    });

    const validatedCourse = CourseCardSchema.safeParse(mappedCourse);

    if (!validatedCourse.success) {
      const validCourse = { ...mappedCourse };

      validatedCourse.error.errors.forEach((error) => {
        const invalidKey = error.path[0];
        delete validCourse[invalidKey as keyof CourseCardType];
      });

      return validCourse as CourseCardType;
    }

    return validatedCourse.data;
  });
}

 

자바스크립트 변환 코드

타입을 이용하면 좋겠지만, 타입은 아래와 같이 자바스크립트로 변환된 후 실행되기 때문에, 런타임에서는 타입 활용이 불가능하다. API 응답이나 외부 데이터가 타입에 맞는지 검증하는 작업은 런타임에서만 가능하다. 따라서 타입 검증을 위한 스키마를 지정해주어야 한다.


원래는 이게 e2e 테스트로 검증해야한다고 생각했는데, 런타임 우려사항이라서 매핑에 추가해주었다.