햄스터 갬성 블로그

OpenAPI Generator 템플릿 변경하기

이전에 진행하던 프로젝트에서 타입스크립트를 적극적으로 활용하지 못했다. 그 이유는 촉박한 개발 시간에 수백 개의 타입을 일일이 입력하기 버거웠기(+귀찮음) 때문이다. 그러던 중 옆 팀에서 개발한 꿀 라이브러리를 알게 되었다. 스웨거에 정의된 타입을 자동으로 타입스크립트 파일로 전환시키는 딱 찾던 기능이었다. 스웨거에 명세(specification)만 잘 작성되어 있다면 커맨드 한 줄로 그 성가신 작업이 단숨에 해결되었다. 위 라이브러리는 vue 기반이었고, 다행히 당시 프로젝트를 모두 vue로 진행하던 터라 찰떡이었다.

이후 새로운 프로젝트에서 react를 사용하게 되었고, 이에 맞춰 새로운 라이브러리 개발이 필요했다. 나는 OpenAPI Generator를 사용하는 방법을 모색했지만 변환된 타입 포맷이 우리가 원하는 방식이 아니었다. 특히, enum을 처리하는 방식이 우리 팀 내에서 다루던 것과 상이했다. 다행히 템플릿을 변경할 수 있어 우리가 바라는 포맷 대로 타입을 뽑을 수 있었다.

결국 기존 라이브러리를 react에 맞춰 수정하는 방식으로 결정해 위 해결책이 쓰일 일은 없었다. 그렇지만 OpenAPI Generator의 템플릿을 변경하는 방법이 (영문 기준으로도) 잘 정리된 글이 없었기 때문에 이번 기회에 글로 남기게 되었다.

기본적인 커맨드 #

우선 OpenApi Generator 설치 방법은 공식 홈페이지에 자세히 나와 있으니 참고하면 된다.

기본적인 사용 방법은 다음과 같다. 각 flag에 대한 자세한 설명 역시 공식 홈페이지에 나와 있다. 이번 포스팅에서는 generator로 typescript-axios를 사용하도록 하겠다.

openapi-generator generate \
-i ${SPEC_FILE_PATH} \
-g ${GENERATOR_TYPE} \
-o ${OUTPUT_DIRECTORY} \
-t ${CUSTOM_TEMPLATE} \

openapi-generator generate -i SWAGGER_URL -g typescript-axios -o ./output --skip-validate-spec

위 커맨드를 실행하면 output 폴더에 타입스크립트 타입과 axios api 호출 코드가 자동 생성된다. 기본으로 제공하는 템플릿을 사용하면 타입 출력물 예시는 아래와 같다.

export interface Student {
/**
*
* @type {number}
* @memberof Student
*/

'age'?: number;
/**
*
* @type {string}
* @memberof Student
*/

'residence'?: ResidenceEnum;
}
export const ResidenceEnum = {
Seoul: 'SEOUL',
Incheon: 'INCHEON',
Busan: 'BUSAN',
} as const;

위 출력물에는 필요없는 정보가 포함되고 우리가 원하는 enum 처리 방식이 아니므로 이번 포스팅에서 아래와 같이 나타나도록 바꾸는 과정을 다룰 것이다.

export interface Student {
age?: number;
residence?: 'SEOUL' | 'INCHEON' | 'BUSAN'
}

템플릿 생성과 mustache #

커맨드 flag 중 -t를 통해 기본적으로 제공하는 템플릿이 아닌 우리가 자체적으로 설정한 템플릿을 사용할 수 있다. 처음부터 템플릿을 작성하려면 비효율적이므로 기본 템플릿에 우리 입맛대로 수정하는 방향이 더 낫다. 다음 커맨드로 기본 템플릿을 생성하자.

openapi-generator author template -g typescript-axios -o custom_template

custom_template 폴더에 .mustache 확장자로 된 여러 파일을 생성된 것을 확인할 수 있다. mustache는 간단한 템플릿 엔진으로 쉽게 생각해서 (정확한 비유는 아니지만) model을 view로 바꾸는 과정이다. 인풋으로 들어온 변수를 미리 정해놓은 틀에 맞춰 다양한 언어 파일로 변환시킨다. 매뉴얼을 확인하면 더 자세히 알 수 있지만 아래 필수적인 문법을 설명한다.

  1. {{key}} : key에 대응하는 값을 출력한다.
// template
Hi, I'm {{name}}!

// input
{ name: 'Juho' }

// output
Hi, I'm Juho!
  1. {{#key}} {{/key}} : if문으로 key가 true면 사이에 적힌 구문을 실행한다.
// template
{{#isHuman}}
Juho
{{/isHuman}}

// input
{ isHuman: true }

// output
Juho

2-1. 만약 key에 대응하는 값이 리스트면 그 요소들을 열거한다.

// template
{{#students}}
{{age}}
{{/students}}

// input
{ students: [ { age: 13 }, { age: 16 }, { age: 19 } ] }

// output
13
16
19
  1. {{^key}} {{/key}} : 위 2번과 반대로 key가 false면 사이에 적힌 구문을 실행한다. key에 대응하는 값이 리스트면 그 요소들을 열거한다.
// template
{{^isHuman}}
Hamster
{{/isHuman}}

// input
{ isHuman: false }

// output
Hamster

커스텀 템플릿으로 변경하기 #

custom_template 폴더의 modelGeneric.mustache 파일에 타입을 정의하는 템플릿이 있다. 우선 필요 없는 부분을 모두 삭제하자. enum을 정의하는 부분과 프로퍼티를 설명하는 부분(@type, @member 등)을 삭제한 후, 아래 코드의 수정이 필요하다.

'{{baseName}}'{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{#isNullable}} | null{{/isNullable}}{{/isEnum}};
}

{{{datatypeWithEnum}}} 부분을 수정해야 'SEOUL' | 'INCHEON' | 'BUSAN'와 같은 방식으로 enum을 표현할 수 있다. 우선, 위 enum에 대응하는 key가 무엇인지 알아야 한다. 이를 아는 방법은 아래와 같은 커맨드를 추가해 템플릿 엔진의 인풋으로 들어가는 값이 무엇인지 확인해야 한다. generate 커맨드의 마지막에 --global-property debugModels=true을 추가하면 각종 로그와 템플릿 엔진의 값이 모두 출력된다. 로그를 보면 아래와 같이 포맷으로 enum 멤버가 전달되는 것을 확인할 수 있다.

...
"allowableValues" : {
"enumVars" : [...],
"values" : [ "SEOUL", "INCHEON", "BUSAN" ],
}
...

values에 담긴 값이 'SEOUL' | 'INCHEON' | 'BUSAN'와 같이 표현될 수 있도록 다음 과정을 거쳐 템플릿을 변경해야 한다.

  1. 우선, 내부값에 접근하기 위해 allowableValuesvalues가 유효한 값인지 확인해야 한다.
{{#allowableValues}}{{#values}}{{/values}}{{/allowableValues}}
  1. 이제 값을 작은 따음표와 함께 출력하고, 각 멤버를 구분하기 위해 |를 공백과 함께 추가한다.
{{#allowableValues}}{{#values}}'{{.}}' | {{/values}}{{/allowableValues}}
  1. 이 때, 마지막 멤버 뒤에는 |가 출력되면 안 되므로 이를 방지하는 조건문을 추가한다.
{{#allowableValues}}{{#values}}'{{.}}'{{^-last}} | {{/-last}}{{/values}}{{/allowableValues}}

여기까지 완성된 템플릿으로 다시 generator를 돌리면 우리가 원하는 방식대로 타입스크립트 타입 파일이 출력될 것이다. 이 글은 간단한 튜토리얼로 따라오는데 꼭 필요한 정보만 설명했다. 이외에도 수많은 옵션이 존재하므로 아래 레퍼런스를 참고해 원하는 커스텀 템플릿을 만들 수 있다.

Reference
OpenAPI Generator 공식 홈페이지
OpenAPI Generator 템플릿 문서
mustache 매뉴얼