TypeScript 4.2 Template Literal Types로 API 라우트 타입 안전하게 관리하기
문제 상황
프로젝트의 API 클라이언트 코드에서 경로 오타로 인한 런타임 에러가 자주 발생했다. /api/users/:id 같은 동적 경로를 문자열로 관리하다 보니 타입 체크가 불가능했다.
// 기존 방식
const endpoint = `/api/user/${userId}`; // users가 아닌 user 오타
Template Literal Types 활용
TypeScript 4.2에서 추가된 Template Literal Types를 사용해 경로 타입을 정의했다.
type ApiVersion = 'v1' | 'v2';
type Resource = 'users' | 'posts' | 'comments';
type ApiRoute = `/api/${ApiVersion}/${Resource}`;
type DynamicRoute<T extends string> = `${T}/${string}`;
// 사용
const route: ApiRoute = '/api/v1/users'; // OK
const route2: ApiRoute = '/api/v1/user'; // Error
const userRoute: DynamicRoute<'/api/v1/users'> = '/api/v1/users/123'; // OK
API 클라이언트 적용
타입을 활용해 API 클라이언트를 리팩토링했다.
class ApiClient {
get<T>(route: ApiRoute | DynamicRoute<ApiRoute>): Promise<T> {
return fetch(route).then(res => res.json());
}
}
const client = new ApiClient();
await client.get('/api/v1/users'); // OK
await client.get('/api/v1/user'); // Compile Error
결과
컴파일 타임에 경로 오타를 잡아낼 수 있게 되어 런타임 에러가 크게 줄었다. IDE 자동완성도 지원되어 개발 경험이 개선되었다. 다만 너무 복잡한 타입은 컴파일 속도에 영향을 줄 수 있어 적절한 수준으로 사용하는 것이 중요했다.