TypeScript 4.3 template literal types로 API 경로 타입 안전하게 관리하기
문제 상황
프로젝트에서 REST API 경로를 문자열로 관리하다 보니 오타로 인한 404 에러가 자주 발생했다. /api/users/:id 같은 경로를 하드코딩하면 리팩토링 시 누락되는 경우도 많았다.
// 기존 방식
const getUserPath = (id: string) => `/api/users/${id}`;
const getPostPath = (userId: string, postId: string) =>
`/api/users/${userId}/posts/${postId}`;
Template Literal Types 적용
TypeScript 4.1부터 지원하는 template literal types를 활용해 경로를 타입으로 정의했다.
type ApiPath =
| '/api/users'
| '/api/posts'
| `/api/users/${string}`
| `/api/users/${string}/posts/${string}`;
function fetchApi(path: ApiPath) {
return fetch(path);
}
// OK
fetchApi('/api/users');
fetchApi('/api/users/123');
// Error: Argument of type '/api/user' is not assignable
fetchApi('/api/user'); // 오타 감지
동적 경로 생성 헬퍼
타입 안전성을 유지하면서 동적으로 경로를 생성하는 헬퍼 함수도 만들었다.
type Route = {
users: `/api/users/${string}`;
userPosts: `/api/users/${string}/posts/${string}`;
};
const route = {
users: (id: string): Route['users'] => `/api/users/${id}`,
userPosts: (userId: string, postId: string): Route['userPosts'] =>
`/api/users/${userId}/posts/${postId}`,
};
// 타입 체크와 자동완성 지원
const path = route.users('123');
결과
도입 후 API 경로 관련 버그가 거의 사라졌다. IDE의 자동완성도 제대로 작동해서 생산성도 올랐다. 다만 경로가 많아지면 타입 정의가 길어지는 단점은 있지만, 런타임 안정성을 생각하면 충분히 감수할 만했다.