tanstack-query 사내 도입 회고 3편: OpenApi-Codegen(Orval)

2024-06-25 오전 12:28:06
React.js
react-query
tanstack-query
tanstack-query 사내 도입 회고 3편: OpenApi-Codegen(Orval)

개요

tanstack-query 사내 도입 회고 1편 에서는
tanstack-query와 codegen을 도입하게 된 배경에 대한 설명,

tanstack-query 사내 도입 회고 2편 에서는
카카오에서 tanstack-query를 도입한 방식과 develogger에 적용한 방식을 비교분석 해보았다.

이번에는 본격적으로 codegen에 대해 알아보자.


먼저, codegen 도입을 위해 여러 선택 지 중 Orval을 선택하게 되었고
도입 후 실제로 어떤 이점을 얻을 수 있는지 한번 살펴보자.

Orval을 선택하게 된 과정은 tanstack-query 사내 도입 회고 1편 참고


1. Orval 이란?

Orval은 OpenAPI 명세를 기반으로 TypeScript 코드를 생성해주는 라이브러리이다.

  • API 엔드포인트에 대한 TypeScript 타입 자동 생성
  • 각 엔드포인트 별 호출 함수 및 tanstack-query 훅 자동 생성
  • custom config 제공

2. 폴더 구조 설계

프로젝트에 Orval을 설치하고,
orval.config.ts을 통해 생성되는 코드의 폴더 구조를 다음과 같이 설계했다.

stores/remoteStore/
├── endpoints/
│   └── [API 태그별 폴더]/
│       └── [API 태그].ts
├── model/
├── mutator/
└── transformer/

2-1. 폴더구조

아래는 develogger에 적용 후 실제로 생성된 폴더 및 파일 예시 이미지이다.

image

codegen을 사용하면 Query훅을 직접 작성할 일은 거의 없고 사용할 일만 있다.
어떤 api가 어디에 속해 있는지만 잘 구분되어도 사실 크게 문제가 없었다.
백엔드(Swagger)에서 각 api를 그룹별로 나눈 tag를 기준으로 파일이 분리되어 생성되도록 하였다.

각 폴더 별로 어떤 역할을 하는지 설명해보겠다.


2-2. endpoints

Swagger의 tag 기준으로 묶어둔 api들이 이 폴더내에 구분되어 생성된다.

아래는 stores/remoteStore/endpoints/user/user.ts에 자동 생성된
내 정보 조회 api 코드 예제이다.

/** * @summary 내 정보 조회 */ export const getUsers = (signal?: AbortSignal) => { return customAxios<UserDto>({ url: `/v1/users`, method: 'get', signal }); }; export const getGetUsersQueryKey = () => [`/v1/users`]; export type GetUsersQueryResult = NonNullable<Awaited<ReturnType<typeof getUsers>>>; export type GetUsersQueryError = ErrorType<void>; export const useGetUsers = < TData = Awaited<ReturnType<typeof getUsers>>, TError = ErrorType<void> >(options?: { query?: UseQueryOptions<Awaited<ReturnType<typeof getUsers>>, TError, TData>; }): UseQueryResult<TData, TError> & { queryKey: QueryKey } => { const { query: queryOptions } = options ?? {}; const queryKey = queryOptions?.queryKey ?? getGetUsersQueryKey(); const queryFn: QueryFunction<Awaited<ReturnType<typeof getUsers>>> = ({ signal }) => getUsers(signal); const query = useQuery<Awaited<ReturnType<typeof getUsers>>, TError, TData>( queryKey, queryFn, queryOptions ) as UseQueryResult<TData, TError> & { queryKey: QueryKey }; query.queryKey = queryKey; return query; };

위처럼 생성된 코드 구조는 orval.config.ts에서 추가 커스터마이징이 가능하다.
화면에서는 useGetUsers 훅을 가져다 쓰기만 하면 된다.

const { data, isLoading, error } = useGetUsers();

처음 이 구조를 보고 생각보다 잘 잡혀있어서 놀랐었다.
쿼리 옵션이나 쿼리키를 주입 하려면 할 수도있고,
쿼리키는 api url과 parameter에 따라 적절히 자동으로 만들어준다.
또, 함수로 분리되어 있어서 필요한 부분이 있으면 따로 import해서 쓸 수도 있다.

카카오에서 고민했던 부분(react-query 사내 도입 회고 2편의 영상 참고)인
공통 옵션을 주거나, 개별 옵션을 주고 싶을때의 구조나 컨벤션 까지 커버 가능한 수준이다.


2-3. model

백엔드에서 만들어진 각종 param, dto, response Type들이 모두 model 폴더 내에 생성되고, 필요한 경우 서로 참조하도록
설계되어 있다.(스웨거 기준으로 작성된다)
endpoints폴더에서 필요한 Type들을 여기서 가져간다.


2-4. mutator

tanstack-query에서 사용될 fetch 함수 를 따로 주입 할 수 있다.
(develogger는 axios instance 사용 중)


2-5. transformer

json 또는 yaml로 작성된 open api spec(Swagger) 명세를 serialize할 때 커스터마이징 할 수 있는 함수 영역이다.


2-6. orval.config.ts

Orval이 코드를 자동으로 생성할 때 필요한 설정 파일


3. 개선점

3-1. 개발 생산성 향상

API 호출 관련 코드를 수동으로 작성할 필요가 없어져, 개발 시간을 크게 단축할 수 있었다.
또한, 백엔드 API 변경 시 스크립트를 다시 실행하는 것만으로 프론트엔드 코드를 쉽게 업데이트할 수 있었다.

회사 프로젝트는 스크립트 실행 한번에 약 1100여개의 파일과, 2만 7천줄 이상의 코드라인이 자동 생성 된다.
현재 회사 규모에서(백엔드 4명)
하루에도 API 추가 및 변경 사항은 평균 약 15여개의 파일과, 250줄 정도의 타입 변경사항이 일어난다.
이를 프론트개발자 1명이 직접 작성한다고 가정하면 매일 최소 1~2시간은 아껴지는 셈이다.


3-2. 타입 안정성 향상

자동 생성한 코드는 백엔드 API의 요청/응답 타입을 정확히 반영한다.
이를 통해 타입 관련 문제를 현저히 줄일 수 있었다.
다만 처음 도입시에는 백엔드 팀의 설득 및 조율이 필요하다.


3-3. 일관된 코드 구조

자동 생성한 코드는 일관된 구조를 가지고 있어, 팀원들이 쉽게 이해하고 사용할 수 있어서
커뮤니케이션, 컨벤션 등 많은 부분에서 장점이 느껴졌다.


4. 총평

사내 프로젝트에 tanstack-query가 도입된 경위가 어떻게 보면 흐름이 자연스럽기도 했지만,
(RTK-Query + codegen ➡️ tanstack-query + orval로 마이그레이션이 필요 했음)
애초부터 tanstack-query를 도입하자! 고 해서 도입된 느낌이 아니었다.

아마 codegen 없이 tanstack-query를 도입을 진행 했다면,
카카오 처럼 수많은 컨벤션들이 필요했을 것 같다.

처음 develogger에 tanstack-query 적용시 고민했던 쿼리 키와 api를 매핑시키는 방법,
각종 쿼리 훅들의 네이밍 컨벤션 등 많은 고민이 codegen에 의해 스무스하게 해결되어버렸다.

직접 고민했다면 깔끔한 구조를 만들어내기 힘들었을지 모른다.
컨벤션 또한 만들고 지키는게 결코 쉽지 않았을 것이다.

아무튼 막상 도입이 되고 계속 사용하다보니 개인적으로는 DX가 너무 만족스러웠다.

큰 회사 규모의 프로젝트 단위에서 tanstack-query를 어떻게들 사용하는지 궁금했는데
카카오에서 마침 컨퍼런스를 해주어서 내가 작업 했던 방향도 틀린건 아니구나 하고
참고할 수 있었다.

User Profile Icon
LKHcoding
Front-End Developer