최근 프론트엔드 타입 시스템은 대부분 Typescript를 기반으로 하기 때문에, 애플리케이션의 견고함과 신뢰성을 따질 때 Typescript의 타입 시스템에 종속적이게 된다.
Typescript의 타입 시스템은 오랜 기간 쌓아 올라온 만큼 정확함을 보장하지만, 빌드 시에 js로 컴파일되기 때문에 런타임 환경에서 동적인 데이터를 트래킹하기 어렵다는 단점이 존재한다.
이를 보완하기 위해 동적으로 넘어온 데이터의 타입을 검증할 수 있 js 라이브러리가 존재하는데, 바로 zod
이다.
zod를 활용하면 다양한 형태의 타입들과 엣지 케이스에 대한 검증을 동적으로 수행할 수 있다.
이 글에서는 zod를 이용해 동적으로 들어오는 데이터들을 검증하고 검증 에러 발생 시 로깅하는 시스템을 간단하게 구축해보고자 한다.
zod를 활용한 타입 검증을 위해서는, ZodObject라는 zod 전용 객체를 정의해 줄 필요가 있다. 간단하게 zod 전용 interface라고 생각하면 되는데, 아래와 같이 interface를 ZodObject로 변환할 수 있다.
interface exampleInterface {
a: string;
b: Array<number>;
c: boolean;
}
const exampleZodObject = z.object({
a: z.string(),
b: z.array(z.number()),
c: z.boolean(),
})
이외에도 zod는 타입의 default값을 미리 초기화한다거나, 정규표현식을 테스트하는 등의 다양한 타입 검증을 하나의 ZodObject 내애서 진행할 수 있다. 또한 다른 ZodObject를 가져와서 확장하는 등의 방법을 통해 확장성 있는 검증 객체를 생성할 수 있다.
아래에는 내가 실제로 작성했던 ZodObject의 앞부분을 간략하게 가져온 것이다.
export const MYZODOBJECT = z.object({
id: z.string().optional(),
location: MY_LOCATION,
name: z.string(),
thumbnailImage: z
.union([
z.string().refine((v) => regex.URL.test(v), 'thumbnailImage > Invalid thumbnail image src'),
z.undefined(),
])
.optional(),
phoneNum: z
.union([
z
.string()
.refine((v) => regex.MY_PHONE_NUMBER.test(v), 'phoneNum > Invalid Phone Number'),
z.undefined(),
])
.optional(),
safetyPhoneNum: z
.union([
z
.string()
.refine((v) => regex.MY_PHONE_NUMBER.test(v), 'safetyPhoneNum > Invalid Phone Number'),
z.undefined(),
])
.optional(),
distance: z.number().optional(),
images: z.array(z.string()),
lessons: z.array(LESSON),
...
});
이렇게 작성된 ZodObject들은 parse
메서드를 사용해 검증할 수 있다! 만일 검증 시 에러가 발생한다면, 아래와 같이 에러 메세지가 콘솔에 찍힌다.
// 이렇게 검증 함수를 실행하고, 만일 에러가 있다면
MYZODOBJECT.parse(data);
// 이렇게 에러 메세지가 발생한다!
/// {
/// code: 'invalid_type',
/// expected: 'number',
/// received: 'string',
/// path: [ 'depth1', 2, 'depth3', 'depth4' ],
/// message: 'Expected number, received string'
/// }
에러들은 기본적으로 서비스 터미널에 찍히기 때문에, 원하는 정보만 추출해서 에러를 잘 만들어주기만 하면 된다.
대충 에러를 터미널에서 추출해서 만들면,,,아래와 같은 모양으로 만들어줄 수 있다.
ExampeObject 데이터에서 다음과 같은 에러가 발생했습니다.
에러코드: invalid_type
예상했던 타입: number
현재 타입: string
경로: depth1 > 2 > depth3 > depth4
메세지: Expected number, received string
요런 식으로 로그를 찍을 수 있도록 해주고, 슬랙 웹훅으로 슬랙에 보내지도록 만들어뒀다.
또한 이후 cron 설정을 통해 로직을 주기적으로 실행하도록 걸어둘 수도 있고, 로그를 AWS SNS SDK 등과 연동하면 다른 서비스들로 Fan-out할 수 있다.
이외에도 에러 케이스들이 예측이 가능한 경우라면, Lambda로 에러 메세지를 보내 케이스 별 에러 해결까지 자동화할 수 있을 것 같다...
이렇듯 다양한 방법으로 사용 가능한 라이브러리 zod는 런타임 환경에서 작동하기 때문에 우리가 넣어주는 모든 데이터에 대해 검증을 시도한다.
간단하지만 강력한 검증 라이브러리인 zod를 통한 타입 검증을 시도해보았다. 현재 활용은 서버에서 넘어오는 DTO를 프론트엔드 사정에 맞게 추가적으로 파싱해야하는 소요가 발생하면 이를 검증하기 위해 사용하고 있는데, 이외에도 테스팅 세분화 등 다양한 경우 활용이 가능할 것 같다고 생각이 들었다.
또한 로직의 구축이 비교적 간단하고 명료한 편이기 때문에, Microfrontend Architecture 에서 각 애플리케이션 간 데이터 통신을 진행할 때에도 애플리케이션 간 신뢰성 있는 아키텍쳐를 구축하는 것에 큰 도움을 줄 수 있을 것이라고 생각했다.
앞으로 더 많은 방법들을 고민해보면 좋을 것 같다!