Front-end에서 OAS generator를 어떻게 쓰면 좋을까?
최근 프로젝트에 OAS-generator를 도입해서 사용하고 있다. 사용하기로 결정할 때 고민이나 확인이 필요한 점이 많았고, 적용 이후 좋은 점도 많은 것 같아서 OAS-Generator 사용 경험에 대해 글을 쓰려한다.
아마 OAS Generator가 무엇인지 궁금한 분들, 알고는 있지만 도입에 고민이 되시는 분들, 이미 사용하고 있지만 잘 사용하고 있는지 망설여지는 분들이 이 글을 읽고 좋은 모티브나 경험을 배워갔으면 좋겠다.
이 글에서 말하고 있는 건 다음과 같다.
- OAS-generator 가 어떤 것인지.
- OAS-generator 를 사용했을 때의 장단점.
- OAS-generator 를 어떻게 사용해야하는지.
- 설정
- 커스텀 템플릿
- 최적화
- Rest API로 Front-end 개발을 진행해 본 경험
- Mustache 문법을 읽을 수 있는 지식 (Optional)
- 글을 이해하는데는 필요없지만 실제로 사용한다면 꼭 알아야한다.
OAS Generator, 어떤 것?
OAS Generator를 알아보기 전에 OAS
는 어떤 것인지 먼저 알아보자.
OAS? Open Api Specification
OAS는 Open Api Specification의 약자이다.
The OpenAPI Specification (OAS) is a vendor neutral description format for HTTP-based remote APIs.
OAS에 대한 문서 를 보면 OAS는 HTTP 기반 API에 대해서 기계, 사람이 모두 이해할 수 있는 문서를 작성하는 규칙을 명명한 것이라고 한다. 예를 들어, swagger 문서에서 아래 이미지처럼 링크를 누르면 OAS 문서로 연결된다. Text로 구성되어 있는 JSON 혹은 yaml파일이 OAS(Open Api Specification)가 되는 것이다.
OAS Generator
OAS가 API의 json/yaml 문서라는 것을 알았다. 그렇다면 OAS Generator는 어떤 것일까?
- OAS Generator는 OAS yaml 파일로 Source code를 생성하는 도구이다.
- 즉, API Swagger → OAS text 파일(.yaml) → Source Code(.ts)로 변환한다.
- Geneartor 종류에 따라 다양한 output(Java, Kotlin, typescript, etc.)을 만들 수 있다.
- Web Front End 진영에서는 typescript-axios나 typescript-fetch를 주로 쓰는 것으로 알고 있다.
앞으로 설명하겠지만 OAS의 결과물은 다음과 같은 방식으로 자동 생성되고, 이 코드들을 프로젝트 내부에 사용하는 것으로 이점을 얻는다. (code sandbox)
// Auto generated codes
...
deletePet: async (petId: number, apiKey?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'petId' is not null or undefined
assertParamExists('deletePet', 'petId', petId)
const localVarPath = `/pet/{petId}`
.replace(`{${"petId"}}`, encodeURIComponent(String(petId)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication petstore_auth required
// oauth required
await setOAuthToObject(localVarHeaderParameter, "petstore_auth", ["write:pets", "read:pets"], configuration)
if (apiKey !== undefined && apiKey !== null) {
localVarHeaderParameter['api_key'] = String(apiKey);
}
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
...
위와 같은 예시는 OAS-generator를 이용해서 생성한 코드의 일부분이다. 각 API에 대해서 path, mehtod, header 등에 대해서 값을 미리 넣어서 생성하고, request, response에 대해서 매칭시켜준다.
그래서, 뭐가 좋은데?
기존 방법과 차이점
우리는 개발을 하면서 API를 연계한다. OAS Generator를 사용하지 않으면 위처럼 "AS-IS"의 순서로 개발을 진행한다. API 문서를 체크해서 URL, method 등을 확인하고 Request, Response에 대한 type을 정의한다. 그리곤, axios와 관련 된 함수를 생성해서 프로젝트 내부의 API call 로직에 사용한다. 이 과정에서 method를 잘못 확인한다거나, Request / Response type을 하나씩 옮겨야한다는 등 실수가 발생 할 수 있고 번거로움이 발생하기도 한다. 또한 API 문서가 업데이트된다면 API가 업데이트 되었음을 인지하고, 변경 내용을 따라가서 수정해야하는 노력도 필요하다.
반면에, OAS Generator를 사용한다면, 아래와 같은 "TO-BE" 순서로 진행한다. API docs로부터 yaml파일(OAS spec)을 가져온다. 보통 yaml파일은 API문서에 따라 자동으로 생성된다. 다음, yaml파일을 이용해서 Axios 함수를 생성하고, request / resposne에 대한 타입까지 함께 생성된다. 이를 그대로 API call 로직에 사용한다. API 문서가 업데이트 되더라도 OAS Generator를 다시 실행하는 것으로 이전 API과 다른 점이 git diff로 표시되고 어떤 부분이 변경되었는지, 어떤 부분을 확인해야하는지 쉽게 알 수 있다.
즉, 개발자가 직접 작성하는 부분을 줄여 반복작업을 줄이고 휴먼에러를 최소화해주는 것이 장점이다.
좋기만 한가요?
장점을 이야기 했지만 물론 신경 써야할 점~~(단점)~~이 존재한다.
1. API 문서로부터 yaml파일을 추출 할 수 있어야한다.
회사 별, 프로젝트 별 API문서를 관리하는 방식은 천차만별이라고 생각한다. 위에 든 예시로는 swagger를 사용하는 것을 전제로 하였고 다른 다양한 방법으로 API 문서를 공유하거나 API 문서 없이 개발을 진행 할 수도 있을 것이다. 만약, API문서가 yaml파일을 생성하는 것이 불가능하다면 아마도 OAS Generator는 유효하게 쓰일 수 있는 대안이 아닐 수도 있다.
2. API 문서는 정확해야한다.
OAS Generator는 API 문서를 기반으로 코드를 생성하기 때문에 API 문서가 정확하지 않다면 자동 생성 코드도 정확하지 않게 된다. 예를 들어서, Request params에 필드들이 모두 optional로 붙어있다면 자동 생성 타입이 모두 optional로 생성되고 무분별하게 optional이 남발되는 상황이 발생하여 type check의 이점을 잃어버릴 수 있다. 또한, 실제 Server는 업데이트 되었지만 API 문서가 업데이트 되지 않은 상황이라면 OAS Generator는 오히려 프로젝트를 복잡하게 만들어버린다. API 문서를 통해 코드를 생성하는만큼 API 문서는 정확해야한다.
3. 중복 코드, bundle size 증가.
OAS Generator는 API 문서와 template을 이용해서 코드를 자동 생성하는 방식을 사용한다. 따라서 template의 특정 패턴이 반복되고 이는 자연스럽게 중복코드를 만든다. 이는 확실한 bundle size의 증가로 이어진다. 프로젝트 개발이 편해지고, 유지보수가 좋아진다고 한들, 퍼포먼스에 악영향을 준다면 사용이 꺼려질 수 밖에 없다. 이 글의 말미에도 optimize에 대해서 후술 하겠지만, OAS Generator 적용 전, 후에 대해서 bundle size 비교는 필요한 사항이다.
4. 초기 설정 비용과 설정의 유지보수
OAS Generator를 적용하면서 느꼈던 것은 초기 설정 값이 복잡하여 러닝커브가 생각보다 높다는 점이었고, 프로젝트에 맞게 여러 설정 및 템플릿을 커스텀하게 되었다. 커스텀하는 부분이 있다는 소리는 앞으로의 프로젝트 운영에 알아야하는 점이 많아진다는 것이므로 문서화나 팀 내부에 충분히 공유를 하는 등 여러 단계들이 필요하다는 점이다.
특히, 3번에서 서술하였다시피 프로젝트 퍼포먼스를 위해서 Optimization을 진행하였다면 이에 대한 히스토리 공유도 필요할 것이다.
Next
자, 여기까지가 OAS Generator를 사용하면 어떤 장점, 단점이 있는지 대략적인 설명이 되었으리라 생각한다. 이제, 어떻게 사용을 할지 알아보는 시간을 가지자.
OAS Generator 사용 방법
서론이 길었다. 이제 OAS Generator를 어떻게 사용하는지 설명한다. Custom Template을 제외한 용도는 대부분 CLI를 사용하고 설정을 수정하는 정도이다.
code sandbox에서 아래 예시로 보여주는 코드를 확인 할 수 있다.
1. yaml 파일을 가져온다.
OAS Generator는 local에 있는 yaml파일을 이용해서 코드를 자동 생성하는 방식이다. 또한, 1:1 대응이므로 하나의 yaml파일에서 하나의 code를 생성한다. 만약 API문서가 다수의 yaml파일 을 가지고 있다면 지금 설명하는 플로우를 여러번 실행해야한다.
위 예시에서는
petstore.yaml
파일을 가져왔다.
2. open-api-generator-cli로 code를 generate 한다.
open-api-generator-cli를 이용해서 template과 input, output path를 지정해서 code를 생성한다.
npm install @openapitools/openapi-generator-cli
openapi-generator-cli generate -g typescript-axios -i ./src/yaml/petstore.yaml -o ./src/generate
위와 같이 openapi-generator-cli에 대한 의존성을 추가하고,
openapi-generator-cli
를 실행한다.
-g
는 generator를 설정하는 옵션이며 여기서는typescript-axios
를 사용한다.-i
는 input을 의미하며 타겟이 되는 yaml파일 위치를 지정한다.-o
는 코드를 생성할 위치를 지정한다.
위 코드를 실행하면 아래와 같이 src/generate
디렉토리 내부에 코드들이 자동생성된다.
api.ts
내부에는 api request와 response 타입을 포함하고 있는 axios util 함수와, type이 함께 생성된다.
3. Generate 된 코드를 사용한다.
이제는 generate 된 코드를 사용하면 된다.
typescript를 기반으로 생성된 코드이기 때문에 request와 response의 타입체크는 성공적으로 진행된다.
data
객체를 참조했을 때 Pet
내부 타입인 id, name, category 등을 알려준다.
Next
사용 방법을 간단히 요약하면 다음과 같이 간단하다.
yaml파일 준비 -> OAS generator cli 실행 -> 생성된 코드 사용.
하지만, 모든 도구들이 그렇듯 실제 프로젝트에 사용하려면 상황에 맞게 커스터마이징 하는 과정이 필요하다. 공식 문서에서는 여러 방법의 커스터마이징 가이드가 있지만, 내가 사용했던 것은 template을 custom해서 사용하는 방식이었다. 앞으로 서술할 커스터마이징 방식도 template을 커스텀 하는 방법에 대한 내용이 주를 이룬다.
OAS Generator Config
OAS generator 설정도 몇가지 있다. 주로 CLI 옵션과 openapitools.json 설정파일의 설정을 조정한다.
OAS Generator 설정 - CLI
-g
: generator를 설정하는 옵션이며 여기서는typescript-axios
를 사용한다.-i
: 대상 yaml 파일 위치-o
: 생성된 파일 위치 경로-c
: generator 설정 파일-t
: 커스텀 template 설정 파일 경로
CLI는 generate
명령어를 사용하며 옵션은 크게 5가지를 사용한다.
-t
옵션은 커스텀 템플릿을 지정할때 쓰이는데 앞으로의 글에서 이야기 할 예정이다.
OAS Generator 설정 - Config file
{
"$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json",
"spaces": 2,
"generator-cli": {
"version": "5.3.1"
},
"supportsES6": true,
"withSeparateModelsAndApi": true,
"apiPackage": "api",
"modelPackage": "models",
"enumPropertyNaming": "original",
"enumNameSuffix": "",
"useSingleRequestParameter": false
}
이 설정 파일은 CLI 옵션에서 -c
에 들어가는 파일이며, 주로 generator의 설정 값이 들어간다.
이 예시에서는 typescript-axios
를 사용하며 자세한 옵션은 이쪽 을 참고하자.
OAS Generator Template
범용성을 띄고 만들어진 설정인 기본 설정을 그대로 사용하면 좋겠지만, 실제 프로젝트에 사용한다면 상황에 맞게
수정을 해야하는 부분이 존재한다. 문서 Customization 가이드 항목도 있지만,
이 중에 가장 직접적으로 도움이 되었던 항목은 Retrieving Templates,
즉, 기본으로 등록되어 있는 Template을 가져와서 프로젝트에 맞게 수정해서 사용하는 것이다.
지금 설명하는 방식은 CLI에서 기재한 -t
옵션에 들어가는 템플릿과 크게 연관되어있다.
Custom Template은 언제 사용할까?
기본 설정을 사용하는 Template을 사용하지 않고, 이를 수정해서 사용하고 싶을 떄 사용하는 방법이다.
예를 들면, 아래와 같이 addPet
이나 deletePet
메소드 명 뒤에 Axios
같은 suffix를 붙이고 싶다면
template을 수정하면 된다.
- Before using custom template
- After using custom template
export class PetApi extends BaseAPI {
public addPet(body: Pet, options?: AxiosRequestConfig) {
return PetApiFp(this.configuration).addPet(body, options).then((request) => request(this.axios, this.basePath));
}
public deletePet(petId: number, apiKey?: string, options?: AxiosRequestConfig) {
return PetApiFp(this.configuration).deletePet(petId, apiKey, options).then((request) => request(this.axios, this.basePath));
}
...
}
export class PetApi extends BaseAPI {
public addPetAxios(body: Pet, options?: AxiosRequestConfig) {
return PetApiFp(this.configuration).addPet(body, options).then((request) => request(this.axios, this.basePath));
}
public deletePetAxios(petId: number, apiKey?: string, options?: AxiosRequestConfig) {
return PetApiFp(this.configuration).deletePet(petId, apiKey, options).then((request) => request(this.axios, this.basePath));
}
...
}
물론 간단한 예시일 뿐이고, parameter를 추가한다거나, 다른 함수를 추가하는 커스텀도 가능하다.
Flow of Custom Template
이전까지 설명한 flow는 위와 같이 yaml파일에서 Axios 함수를 생성하는 플로우이다.
여기서 Custom Template을 추가한다면 아래와 같은 느낌으로 flow가 변한다.
OAS Generator는 yaml파일에서 JSON 데이터를 추출해낸다. 이 JSON 데이터는 path도 들어있고, method도 들어있고,
API에 대한 각종 정보들이 있다. 이 JSON 데이터들이 Template에 주입되고 Axios 함수로 생성된다.
여기서 다룰 커스텀은 Template을 변경하는 방법에 대해서이다.
Template 파일 가져오기
openapi-generator-cli author template -g typescript-axios -o ./mustaches
openapi-generator
는 generate
스크립트 이외에 author
스크립트도 존재한다.
author
스크립트로 typescript-axios
템플릿을 가져온다면 다음과 같은 template이 생성된다.
/mustaches
ㄴ api.mustache
ㄴ apiinner.mustache
ㄴ baseApi.mustache
ㄴ common.mustache
ㄴ configuration.mustache
...