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의 자동완성도 제대로 작동해서 생산성도 올랐다. 다만 경로가 많아지면 타입 정의가 길어지는 단점은 있지만, 런타임 안정성을 생각하면 충분히 감수할 만했다.

TypeScript 4.3 template literal types로 API 경로 타입 안전하게 관리하기