OpenAPI Specification3 사용간 주의할 점

겔로그·2024년 5월 18일
0

당근마켓에서 커뮤니티실 API Design-First 접근방식 정착기라는 글을 작성했는데 API Design-First 접근방식을 도입하면서 겪은 내용들을 적어주셔 읽는 동안 많은 공감을 하게 되었습니다.

하지만 본문에서는 OAS(OpenAPI Specification) 관련해 도입하는 과정에서 겪을 수 있는 러닝 커브, 주의할 점에 대해서 세부적으로 다뤄지지 않은 부분이 있는 것 같아 약간의 아쉬움이 있었습니다.

이번 글에서는 회사에서 OAS를 사용하면서 겪었던 러닝 커브와 실수, 주의할 점에 대해 다루어 보고자 합니다.

OAS(OpenAPI Specification)란?

우선 OAS가 무엇인지부터 짧게 설명하고 글을 시작하겠습니다. OpenAPI Specification란, HTTP API에 대한 언어 독립적인 표준 인터페이스를 정의하는 명세입니다

기존에 Spring을 사용하던 개발자들은 API 문서 도구로 Swagger 또는 Spring Rest Docs등 API 스펙을 관리하기 위한 도구를 사용했습니다. 개발간 다양했던 API 명세와 도구를 하나의 표준으로 정립하기 위해 OAS를 고안하였으며 현재는 대부분의 도구들이 OAS를 지원하고 있어 REST API에서는 표준과 같은 역할을 수행하고 있습니다.

OAS를 사용하면 다음과 같은 이점을 누릴 수 있습니다.

  • Swagger를 이용한 API 문서화
  • API Test 지원
  • yaml 파일로 관리되는 API 명세
  • API class generating 기능 제공

주의할 점

OpenAPI Specification은 yaml 파일에 정의한 대로 코드를 generating하는 기능을 제공합니다. 이를 통해 개발자는 API 명세를 정의만 하면 이와 관련된 모든 코드를 생성해주기 때문에 단순 작업을 방지해 줍니다.

다만 OAS 명세 및 동작 방식을 제대로 모를 경우 문제가 될 수 있습니다.

1. 명세 정의가 정상적으로 되지 않더라도 클래스가 생성된다.

OpenAPI3 Specification공식 문서를 볼 경우 오브젝트 정의 및 필드 정의에 대한 내용이 담겨 있습니다.

문제는 일부 크리티컬한 필드 설정에서는 비정상적인 에러임을 인지할 수 있으나, 마이너한 내용은 정상적으로 generating 될 수 있어 개발자가 정상 동작으로 판단하고 넘어갈 수 있는 문제가 존재합니다.

OAS 명세에 대해 정확하게 알고 있을 경우 이러한 문제를 겪지 않으실 수 있겠지만 명세를 사람이 하는 이상 자주 이런 문제가 발생할 수 있기 때문에 항상 swagger와 generating 되는 클래스 내용을 꼼꼼하게 확인해보시길 바랍니다.

제가 경험한 케이스는 다음과 같습니다.

1. oneOf와 required를 거꾸로 정의할 경우

  • oneOf 필드와 연관된 모든 객체가 영향을 받아 generating 되는 클래스가 아예 달라질 수 있습니다.

2. generating되는 클래스 명을 주의하라.

객체를 명세할 때 내부 properties가 동일한 경우, generating간 클래스 명칭은 top class ~ bottom class의 모든 객체 명이 연쇄적으로 붙는 클래스가 생성됩니다.

아래와 같은 정의를 보겠습니다.

components:
  schemas:
    Header:
      type: object
      required:
        - isSuccessful
        - resultCode
        - resultMessage
      properties:
        isSuccessful:
          type: boolean
          default: true
          example: true
        resultCode:
          type: integer
          default: 0
          example: 0
        resultMessage:
          type: object
          properties:
            message:
              type: string
              example: '성공'             

객체를 생성할 경우 아래와 같은 객체가 생성됩니다.

package api.generated.model

import java.util.Objects
import com.fasterxml.jackson.annotation.JsonProperty
import com.nhn.cloud.notification.hub.api.generated.model.HeaderResultMessage
import jakarta.validation.constraints.DecimalMax
import jakarta.validation.constraints.DecimalMin
import jakarta.validation.constraints.Email
import jakarta.validation.constraints.Max
@@ -23,9 +24,10 @@ data class Header(
    @get:JsonProperty("isSuccessful", required = true) val isSuccessful: kotlin.Boolean = true,

    @get:JsonProperty("resultCode", required = true) val resultCode: kotlin.Int = 0,

    @field:Valid
    @get:JsonProperty("resultMessage", required = true) val resultMessage: HeaderResultMessage
) {

}

이와 같이 객체 내부에 객체를 properties로 정의할 경우 클래스명이 길어지는 문제가 발생합니다.

예시: FriendtalkTemplateCarouselListInnerAttachmentButtonsInner

'의도대로 클래스 내부 필드가 잘 생겼는데 뭐가 문제야?'하시는 분들도 있겠지만 generating 되는 코드로 서비스 로직을 구현할 경우 가독성이 떨어지는 이슈가 있습니다.

이런 문제를 개선하기 위해서는 최대한 내부에서 사용하는 object도 별도로 정의하는 것을 권장드립니다.

3. 중복되는 properties가 있을 경우 경로가 같을 경우 덮어쓰기 된다.

2번과 유사한 이슈입니다. 동일한 경로로 generating할 경우 내부 properties가 동일한 객체들에 대해서는 마지막에 정의된 클래스 네이밍으로 생성되는 문제가 존재합니다.

도메인 클래스와 요청 클래스를 정의했을때 내부 properties가 동일하게 정의되어 있을 경우 yaml 정의 위치에 따라 생성되는 클래스가 달라지는 문제가 존재합니다.

이런 문제를 개선하기 위해서는 최대한 내부에서 사용하는 object도 별도로 정의하거나 생성되는 패키지 위치를 서로 다르게 정의하는 것을 권장드립니다.

4. 잘 동작하는지 꾸준히 확인하라

명세를 바꾼 이후 oas가 비정상일 경우를 지속적으로 확인해야 합니다.

Intellij IDE를 사용하실 경우 OpenAPI Plugin을 활용하여 설정한 yaml 파일이 정상적인지 실시간으로 확인할 수 있습니다.

다만, 앞서 1~3번에서 말씀드린 것처럼 서비스가 실행되지만 실제로는 의도한대로 정의가 되지 않는 케이스가 종종 발생합니다. 해당 케이스는 작업자가 명세한 API를 꼼꼼하게 확인해야 알 수 있으므로 master/develop에 머지하기 이전에 한 번 더 확인해보시는 것을 권장드립니다.

5. 기타 이슈

1~4번 외에도 언어별로 mustache(코드 변환 파일) 이슈로 인해 문제가 발생할 수 있습니다. 이러한 내용들은 openapi github issue 목록을 확인해보시면 대부분 잘 나와있을 것이며, 언어별로 generate간 설정할 수 있는 여러 옵션이 있으니 해당 내용도 잘 확인해보시고 이용하면 좋을 것 같습니다.

kotlin을 사용하면서는 enum 첫글자를 소문자로 생성하는 이슈와 모든 객체가 기본적으로 data class로 생성되는 이슈가 있었습니다. 이러한 부분들은 option을 통해 수정 가능하지만 전체 코드에 적용되다보니 영향도 파악을 꼼꼼하게 하신뒤 설정을 적용하시는 것을 권장드립니다.

아직 해결이 안된 이슈는 자체적으로 mustache 파일을 직접 수정해서 적용하는 것인데 해당 방안은 최후의 보루이기에 지양합니다.

결론

API 개발간 OAS를 사용할 경우 이전에 비해 많은 부분에서 이점을 얻을 수 있습니다. 다만 OAS를 정의하는 과정에서 어느정도 명세에 대한 러닝 커브를 겪으시게 될거라 생각합니다.

내가 정의한 내용이 잘 적용됐는지, 정상적으로 동작하는지 꼼꼼하게 확인하시면서 OAS를 제대로 사용하신다면 API 개발간 둘도 없는 좋은 친구가 될 것이라 생각됩니다.

OAS Documentation을 보고도 잘 이해가 되지 않으시거나 하나하나 명세하는 것이 귀찮으신 분들은 Microsoft에서 개발한 TypeSpec을 한 번 참고해보시길 바랍니다.

객체지향적으로 정의하면 자체적으로 oas yaml 파일로 변경해주는 도구인데 꽤나 괜찮은 것 같네요.

읽어주셔서 감사합니다.

Reference

profile
Gelog 나쁜 것만 드려요~

0개의 댓글