ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OpenAPI TypeGenerator 도입하여 타입 걱정 없이 개발하기
    Web 2023. 10. 2. 00:11

     

    들어가며

    백엔드와 협업을 할 때 API 명세를 보며 해당 API의 주소, Method ,Request, Response 등의 타입을 정의하는 것과

    API 스펙이 변경되었을 때 다시 문서를 확인하고 위의 행동을 반복하는 것은 귀찮고 시간이 많이 소요된다

    우리 회사에서 어떻게 위의 일을 처리하고 있는지, OpenAPI TypeGenerator가 어떤 도움을 주고 있는지 살펴보려고 한다.

     

    OpenAPI TypeGenerator가 어떤 문제를 해결할 수 있을까?

    OpenAPI Typegenerator은 OpenApi specification을 기반으로 여러 가지 데이터 형식을 자동으로 생성할 수 있는 라이브러리이다.

    OpenAPI Typegenerator로한번 잘 구축해 두면
    타입스크립트를 사용할 때 어쩔 수 없이 거쳐야 하는 프로세스인 타입 정의를 명령어 실행 한 번만으로 간소화할 수 있으며
    type-safe 하게 api를 최신버전으로 관리할 수 있어 유지 보수에 상당한 자원이 절약된다 (실제로 많이 체감된다)

     

    도입 및 활용 방법

    타입 생성 과정은 다음과 같다

    • OpenAPI Typegenerator NPM 설치
    npm install -D openapi-typescript
    // or
    yarn add -D openapi-typescript

     

    • OpenApi specification 정의 작성
      프론트엔드에서는 해당 과정을 직접 진행할 일이 없지 않을까 싶다 (대부분 swagger을 사용하고 있기 때문에)
      우리는 OpenApi specification을 전달해 주는 api를 따로 제공하여 타입 제너레이트를 할 때마다 받아서 사용하고 있다
      만약에 따로 OpenApi specification를 전달받을 수 없다고 해도 API문서 툴에서 다운로드하여 저장하면 된다

     

    • generate 스크립트 명령어 작성 및 실행
      tpye generate는 앞으로 두고두고 쓰일 테니 간단히 사용하기 위해서 package.json의 scripts에 다음 명령어를 정의한다
      한번 퀵하게 해보고 싶으면 https://api.openapi-generator.tech/api-docs 을 specification 파일에 써볼 수 있다
    "scripts": {
        ...
        "typegen": "openapi-typescript {specification 파일} --output {생성할 폴더}",
        ...
      },

     

    그리고 스크립트를 실행해 준다

    npm run typegen
    
    yarn run typegen

     

    • 생성된 코드 사용
      이 글에서는 이전 프로젝트에서 사용하던 swagger를 사용해 타입 코드를 생성해 볼 예정이다
      (swagger v5부터 사용 가능)

    예시용 사이드 프로젝트 스웨거 스펙을 가져와 보았다

    정상적으로 스크립트가 실행되었다면 --output에 지정한 폴더에 엄청나게 긴 코드 파일이 생성되어 있을 것이다

     

     

    openapi-typescript가 생성해 준 type들을 쌩으로 사용하기엔 너무 불편함으로
    좀 더 편하게 사용하기 위해서 ts-toolbelt를 사용하겠다

    npm i -D ts-toolbelt
    // or
    yarn add -D ts-toolbelt

     


    open-api에서 뽑아쓸 수 있는 타입 많이 있지만 가장 많이 쓰이는 타입을 뽑아보자면

     

    1. get 요청 시 필요한 파라미터(parameter) type
    2. get 요청 시 결과값(response) type
    3. post 요청시 넣어야 할 body 값 (request body) type

    위의 3개 타입만 있다면 대부분 상황에 필요한 타입은 받을 수 있어 이 글에선 여기까지 작성하겠지만

    ts-toolbelt를 잘 이해하고 있다면 이것보다 더 잘 커스텀하여 사용할 수 있을듯하다

     

    import type { O } from "ts-toolbelt";
    
    import type { components, paths } from "./openapi";
    
    // get api 에서 사용할 path parameter type
    export type OpenAPIQueryParameter<T extends keyof paths> = O.Path<
      paths,
      [T, "get", "parameters", "query"]
    >;
    // get api 에서 리턴할 response type
    export type OpenAPIQueryResponse<T extends keyof paths> = O.Path<
      paths,
      [T, "get", "responses", 200, "content", "application/json"]
    >;
    // post api 에 보내야할 resquest body type
    export type OpenAPIRequestBody<T extends keyof paths> = O.Path<
      paths,
      [T, "post", "requestBody", "content", "application/json"]
    >;

     

    이렇게 잘 정의하였다면 실제로 사용해 보도록 하자

     

    사용해보기

    react-query를 사용해 api호출을 해볼 건데 일반적인 흐름은 동일하다

    1. react-query로 api 호출하기

      const { data } = useQuery({
        queryKey: ["test"],
        queryFn: () => apiClient.get("test"),
      });

    2. 정의한 타입 적용하기

      const { data } = useQuery({
        queryKey: ["test"],
        queryFn: () => apiClient.get<OpenAPIQueryResponse<"test">>("test"),
      });

    끝이다

     

    어 이게 끝이라고?

    api 주소를 입력할 필요가 없다

    api 주소가 자동완성까지 된다고?

    실제로 api를 정의하고 -> interface 정의도하고 -> react-query로 호출하는 과정이 한 개 과정으로 줄어들었다

    타입이 잘 추론되고 있는지도 확인해 보면

    잘되는중

    잘 되고 있는 걸 확인할 수 있다

     

    끝으로

    지금까지는 장점과 방법만을 설명했지만 그렇다고 단점이 없지는 않다
    실제 약 8개월 정도 사용해 본 바로는

     

    1. Swagger api 문서에 타입을 의존하기 때문에 타입이 다르게 오면 백엔드팀에 매번 요청하여 문서를 수정해야 한다는 점

    애초에 잘 정의해서 보내줘야 하는 게 맞지만, 사람이 하는 일인지라 실수할 수 있기 때문에 가끔 있는 일이다

    이럴 땐 수정될 때까지 빨간 줄을 애써 무시하면서 작업하거나, 작업을 잠시 끊고 다른 작업을 해야 하는데 참 쉽지가 않다

     

    2. paging 처리된 데이터를 받을 때 items 칼럼에 타입이 담아지지 않고 올 수 있다는 점
    회사에서도 이 부분을 해결하는데 시간이 좀 걸렸다

     

    몇 가지 단점이 있어도 OpenAPI TypeGenerator의 진가는 백엔드에서 api 타입이 변경되었을 때 typegen 한번으로 대응할 수 있다는점 이라고 생각하는데, 뚜렷한 장점이 있어 한번쯤 사용해 볼 만한 방법이라고 생각한다

     

Designed by Tistory.