TypeScript 4.4 템플릿 리터럴 타입으로 API 경로 타입 안전하게 관리하기
문제 상황
프로젝트에서 RESTful API 호출 시 경로를 문자열로 직접 작성하고 있었다. 리팩토링 과정에서 /api/users/:id 경로를 /api/user/:id로 변경했는데, 클라이언트 코드 일부를 수정하지 못해 404 에러가 발생했다.
// 기존 코드
const user = await fetch(`/api/users/${userId}`);
const posts = await fetch(`/api/user/${userId}/posts`); // 오타
해결 방법
TypeScript 4.4에서 강화된 템플릿 리터럴 타입을 활용해 API 경로를 타입으로 정의했다.
type UserId = string;
type PostId = string;
type ApiRoute =
| `/api/users`
| `/api/users/${UserId}`
| `/api/users/${UserId}/posts`
| `/api/posts/${PostId}`;
function apiCall<T>(route: ApiRoute): Promise<T> {
return fetch(route).then(res => res.json());
}
// 이제 타입 체크로 오타를 방지할 수 있다
apiCall(`/api/users/${userId}`); // OK
apiCall(`/api/user/${userId}`); // 컴파일 에러
더 나아가 경로 파라미터를 추출하는 유틸리티 타입도 만들었다.
type ExtractParams<T extends string> =
T extends `${infer _Start}/:${infer Param}/${infer Rest}`
? Param | ExtractParams<`/${Rest}`>
: T extends `${infer _Start}/:${infer Param}`
? Param
: never;
type UserRouteParams = ExtractParams<'/api/users/:userId/posts/:postId'>;
// 결과: 'userId' | 'postId'
결과
- API 경로 오타로 인한 런타임 에러 사전 방지
- IDE 자동완성으로 개발 생산성 향상
- 리팩토링 시 타입 체커가 수정 누락 지점을 알려줌
템플릿 리터럴 타입은 생각보다 강력했다. 다음에는 이를 활용해 라우터 타입을 더 개선해볼 계획이다.