<타입 타이핑 이제 그만! open api로 우아하게 클라이언트 생산성 높이기(feat OAS with openapi-ts 1탄)>

강민수·2024년 11월 2일
2

아하 모먼트

목록 보기
6/9
post-thumbnail

최근 새로운 프로젝트에서 여태 했던 고민을 해결한 경험을 소개한다.

1) 어떤 고민을 했는가?

원래 필자는 서버에서 데이터를 주고 받기 위해, api 핸들러 및 타입 정의를 했었다.

아래처럼...


// 타입 정의
type RequestParams = {
  userId: string;
  startDate: string;
  endDate: string;
};

type ApiResponse<T> = {
  success: boolean;
  data: T;
  error?: string;
};

// 데이터 응답 타입 정의
type UserData = {
  id: string;
  name: string;
  email: string;
  createdAt: string;
};

// API 핸들러 함수
const getUserData = async (params: RequestParams): Promise<ApiResponse<UserData[]>> => {
  try {
    const response = await fetch(`/api/users?userId=${params.userId}&startDate=${params.startDate}&endDate=${params.endDate}`);
    const data: UserData[] = await response.json();

    return { success: true, data };
  } catch (error) {
    return { success: false, data: [], error: error instanceof Error ? error.message : "Unknown error" };
  }
};

// 사용 예시
(async () => {
  const params: RequestParams = { userId: "123", startDate: "2024-01-01", endDate: "2024-12-31" };
  const response = await getUserData(params);

  if (response.success) {
    console.log("User Data:", response.data);
  } else {
    console.error("Error:", response.error);
  }
})();

그런데... 이게 딱 봐도 알 수 있듯이 한 두개 정도면 크게 상관이 없다.

하지만, 1개, 2개 쌓이다 보면 파일도 많아지고 결정적으로

매우 구찮은 작업이다.

그래서 한 때는 이걸 어떻게 자동화할 수 없을까 고민했다.

그 결과 알게 된 것이 open api 스펙을 활용한 코드젠인데...

하지만, 시중에는 뭔가 내가 썩 끌리게 만들어진 제네레이터가 그다지 없었다.
대략 찾아봤던 글이나 오픈소스를 나열해 보자면 아래와 같다.

물론 훌륭한 오픈 소스들이자, 글이었지만, 당장 적용하기에는 다소 무리가 있었다.
일단, 직접 만드는 것도 고려는 했지만, 복잡도 다양한 예외 케이스 처리 등에 있어서 어려움이 있다는 것을 판단했다.

2. 낮아 보여도 높은 허들들

물론 그 이후에도 open api 스펙을 활용해서 제네레이팅을 시도했지만, 다양한 것들이 약간 낮은 허들로 존재했다.

  1. 팀원들과의 협의 필요
  • 모든 기술이 마찬가지겠지만, 기술은 함께 사용하는 사람들의 협의가 필요하다. 이 협의가 없다면, 결국 이 사람 혼자에게 의존성이 생겨버리고... 누구도 손대지 못하는 무소 불위의 코드가 되어 버린다.

그렇지 않으려면... 이 open api 스펙을 활용한 기술의 타당성과 논의가 필요하다.

그래서 필자도, 프론트 팀 팀원들에게 해당 기술에 대해 의견을 구했다.

대부분은 긍정적으로 검토하였으나,

but 결론적으로는 현재 핸들러로 작성된 프로젝트들에서는 직접적인 도입이 어렵다고 생각이 모아졌다.

왜? why?

그도 그럴것이, 기존 코드들과 공존을 시키던가 아니면, 싹 다 갈아엎어야 되는 구조인데...

그걸 현재 서비스 중인 프로젝트에 적용하기에는 무리였다고 판단했다.

  1. 백엔드 개발자에게 json 업로드 요청

다음은 백엔드 개발자에게 json 파일에 대해 url로 업로드 요청을 해야만 했다. 현재 우리 서비스 api docs들은 물론 open api 스펙으로 만들어져 있고, 다운도 가능했다.

하지만, 이 문서가 업로드는 되어 있지 않았기에... json을 직접 다운 받아서 개발자가 수동으로 static 파일로 가지고 있어야만 했다...

이건, 또 다른 비효율 아닌가...

그래서 api docs의 json 파일을 업로드해 주면 해결되는 문제였다.

일단 이건 추후, 작업이 필요할 경우에 다시 요청드려야겠다고 생각했다.

그렇게... 기억 속 저 너머로 이 open-api spec은 사라져 가는 거 같았다.

3. openapi-ts

그러다 우연히 찾아온... openapi-ts라는 녀석.

그 녀석이 참 맛있었다. ㅎㅎ

요약하자면, open api 기반 json yaml파일을 기반으로 typescript 단일 파일로 변환해주는 일종의 제네레이팅 라이브러리다.

물론, 이렇게만 하면 기존의 다른 앞선 orval 등과 다르지 않지만, 여기서 다른 점은 핸들러를 직접적으로 생성하지 않으며, 전체 타입 정의도 하나의 파일 안에 모여져 있다.

물론, 이렇게 되면, 관심사의 분리나 확장성이 떨어지지 않냐고 반문할 수도 있겠다.

하지만, 사실 그렇지는 않다.

  1. 왜냐면, 우리가 필요한 것은 타입세이프한 타입 파일일 뿐이다.
  2. 따라서, 필요한 타입들만 꺼내서 쓰고, 그걸 다른 기술 혹은 라이브러리 들과 결합시키는 측면에서 본다면 오히려 확장성이나 유연성이 높다.
  3. 컴파일 시, 빌드 소스 측면에서 과도한 파일 분리는 비효율적이다.

그래서 이 openapi-ts에서는 이를 활용한 패키지들(fetch, react-query 등)이 존재하지만, 꼭 필수적이지는 않다.

본인의 입맛에 맞게 알아서 커스텀하면 된다.
조금만 잘 살펴보면, 타입 파일 자체의 타입들도 쉽게 파악할 수 있도록 층위가 잘 나뉘어져 있다.(이건 추후 2부에서 코드로 살펴보자)

막 복잡하게 config 설정이나 그런 것들이 가타부타 할 필요가 없다.

orval의 경우, config가 필수이다...

openapi-ts는 이런 게 전혀 필요 없다.

물론, 그렇다고 openapi-ts만 쓰라는 것은 아니다.
각자의 상황이나 조건에 고려해서 맞게 쓰면 되는 것이 라이브러리기에... 오해하지는 말자!

필자는 여기에 openapi-fetch와 openapi-react-query를 활용해 보다 생산성을 높일 수 있었는데, 그 내용은 추후 2부에서 어떻게 접근하고 장 단점 등을 살펴보겠다.

다음은 실전편.

어떤 취지와 계기를 토대로 이 기술을 도입했는 지 좀 길게 적어본 것 같다.
다음 글에서는 이를 어떻게 실무에서 적용했고, 검토해서 활용해 봤는 지, 마지막으로 느낀 점 등을 토대로 이번 시리즈를 마무리 지어볼 예정이다.

곧 2부에서 뵙겠다.

profile
개발도 예능처럼 재미지게~

0개의 댓글

관련 채용 정보