TypeScript 4.5 Template Literal Types로 API 경로 타입 안전하게 관리하기
문제 상황
백오피스 프로젝트에서 REST API 경로를 문자열로 관리하다가 /api/users/:id 같은 경로에서 오타가 발생해 런타임 에러가 났다. 특히 동적 파라미터가 들어가는 경로에서 실수가 잦았다.
// 기존 방식
const getUserPath = (id: string) => `/api/user/${id}`; // 오타: users가 아닌 user
fetch(getUserPath('123')); // 404 에러
Template Literal Types 적용
TypeScript 4.5부터 Template Literal Types를 활용하면 경로 패턴을 타입으로 강제할 수 있다.
type APIPath =
| '/api/users'
| '/api/users/:id'
| '/api/posts'
| '/api/posts/:id/comments';
type ExtractParams<T extends string> =
T extends `${infer _Start}:${infer Param}/${infer Rest}`
? { [K in Param | keyof ExtractParams<`/${Rest}`>]: string }
: T extends `${infer _Start}:${infer Param}`
? { [K in Param]: string }
: {};
function buildPath<T extends APIPath>(
path: T,
params: ExtractParams<T>
): string {
let result = path as string;
Object.entries(params).forEach(([key, value]) => {
result = result.replace(`:${key}`, value);
});
return result;
}
// 사용
const path1 = buildPath('/api/users/:id', { id: '123' }); // OK
const path2 = buildPath('/api/users/:id', { userId: '123' }); // 타입 에러
실전 적용
API 클라이언트 레이어에 적용하니 경로 관련 버그가 사전에 차단됐다. 자동완성도 지원돼서 개발 경험이 개선됐다.
class APIClient {
get<T extends APIPath>(path: T, params: ExtractParams<T>) {
return fetch(buildPath(path, params));
}
}
const client = new APIClient();
client.get('/api/users/:id', { id: '123' }); // 타입 안전
아직 복잡한 쿼리 파라미터까지는 커버하지 못하지만, 기본적인 경로 파라미터 관리는 확실히 안전해졌다.