TypeScript 4.2 template literal types로 API 경로 타입 안전하게 관리하기
문제 상황
프로젝트에서 REST API 경로를 문자열로 관리하다 보니 오타로 인한 404 에러가 자주 발생했다. 특히 동적 경로 파라미터가 들어가는 경우 /users/${userId}/posts/${postId} 같은 형태를 하드코딩하다 보면 실수가 잦았다.
// 기존 방식
const getUserPost = (userId: string, postId: string) => {
return fetch(`/api/users/${userId}/post/${postId}`); // posts가 아니라 post...
};
Template Literal Types 적용
TypeScript 4.2가 출시되면서 template literal types가 추가됐다. 이를 활용해 API 경로를 타입 레벨에서 관리할 수 있게 됐다.
type ApiVersion = 'v1' | 'v2';
type Resource = 'users' | 'posts' | 'comments';
type ApiPath = `/api/${ApiVersion}/${Resource}`;
// '/api/v1/users' | '/api/v1/posts' | ... 자동 생성
type UserId = string;
type PostId = string;
type UserPostPath = `/api/users/${UserId}/posts/${PostId}`;
const buildPath = <T extends string>(template: T, params: Record<string, string>): T => {
let result = template as string;
Object.entries(params).forEach(([key, value]) => {
result = result.replace(`{${key}}`, value);
});
return result as T;
};
const path = buildPath('/api/users/{userId}/posts/{postId}', {
userId: '123',
postId: '456'
});
적용 결과
타입 추론이 정확해지면서 IDE에서 자동완성도 잘 되고, 잘못된 경로를 사용하면 컴파일 단계에서 에러가 발생한다. 런타임 에러를 사전에 방지할 수 있어 API 클라이언트 코드의 안정성이 높아졌다.
다만 너무 복잡한 유니온 타입을 만들면 컴파일 속도가 느려지는 이슈가 있어, 적절한 수준에서 타협점을 찾는 것이 필요했다.