swagger 적용기

홍성민·2023년 8월 28일
0

기존 api 명세 관리

201팀의 기존 api 명세 관리는 다음과 같이 노션으로 하고 있었다.

물론 보는데 문제는 없지만 json을 일일이 작성해줘야하고 변경사항이 생길 때 마다 수정을 해줘야 하는 점이 번거롭다.
또한 변경점이 잘 반영되지 않는 휴먼에러가 많이 발생했다.

마침 4차 데모데이 요구사항에 api 문서화가 있었고 rest docs와 swagger 두가지 선택지에 대해 논의할 기회가 생겼다.
우리팀은 기존에 cucumber를 사용해서 인수테스트를 철저하게 진행하고 있었기 때문에 rest docs로 인해 생기는 mock test가 불필요하다고 생각했다.
거기에 더해 적용이 쉽다는 매리트가 있는 swagger를 선택했다.

초기 설정

설정 파일

yml파일에 다음과 같은 설정을 해주었다.

springdoc:
  api-docs:
    path: "/v1/api-docs"
  swagger-ui:
    url: "/v1/api-docs"
    path: "/v1/api"
    display-request-duration: true

/v1/api를 통해 /swagger-ui/index.html 로 접속할 수 있고 해당 html 파일에 /v1/api-docs의 json 정보를 불러오는 형식이다.

configuration

@Configuration
public class SwaggerConfig {

    @Bean
    public OpenAPI publicApi() {
    // 기본적인 정보 생성
        Info info = new Info()
                .title("201 Created")
                .description("201 Created의 Open Api입니다.")
                .version("v1");
    // jwt에 대한 SecurityScheme 설정, Swagger-ui에서 token 인증이 가능해진다.
        SecurityScheme securityScheme = new SecurityScheme()
                .name("Authorization")
                .type(Type.HTTP)
                .in(HEADER)
                .bearerFormat("JWT")
                .scheme("Bearer");

        Components components = new Components().addSecuritySchemes("token", securityScheme);

        return new OpenAPI()
                .info(info)
                .components(components);
    }
}

Swagger 기능

@Tag

컨트롤러에 대한 정보를 입력한다.
name, description 파라미터를 사용해 설명을 적는다.

@ApiResponses

api에 대한 모든 응답을 @ApiResponse 배열을 통해 지정할 수 있다.
@ApiResponse의 parameter로 응답코드를 지정하는 responseCode, 해당 응답코드에 대한 설명을 넣는 description, 응답코드에 대한 반환 값을 지정하는 content를 대표적으로 사용한다.
header 반환을 명시하고 싶을 경우 header 파라미터를 사용할 수 있다.

@Operation

api의 전반적인 모든 정보를 쓸 수 있는 annotation이다. summary를 이용해 요약 설명, requestBody를 통해 리퀘스트에 대한 설명을 적을 수 있다.
해당 프로젝트에선 summary만을 사용하고 있다.

@SecurityRequirement

api에 인증과정이 필요할 경우 사용하는 어노테이션이다.
name 설정을 통해 configuration의 SecurityScheme에서 key로 설정한 값과 같은 scheme을 사용하고 있음을 명시할 수 있다.

@Parameter

컨트롤러 메서드의 파라미터에 사용할 수 있는 어노테이션이다.
name, description, required 등의 옵션으로 파라미터에 대한 설명을 보충할 수 있다.

@Schema

response, request 객체들의 필드에 대해 설명을 보충하는 어노테이션이다.
requireMode, description, defaultValue 등의 파라미터로 설명을 보충할 수 있다.
example 파라미터를 이용해 Swagger-ui에서 예시로 사용되는 값을 지정할 수 있다.
공개되기 원치 않는 필드, 응답이라면 @Schema(hidden = true)를 사용해 문서에 드러나지 않게 할 수 있다.

단점

가장 큰 단점은 아무래도 프로덕션 코드가 번잡해진다는 점이다.
이 단점을 극복하기 위해 컨트롤러를 api 정보를 가지는 interface의 구현체로 만들었다.
예시는 아래와 같다.

@Tag(name = "회원", description = "회원 관련 api")
public interface MemberApi {

    @ApiResponses(
            value = {
                    @ApiResponse(responseCode = "200"),
                    @ApiResponse(responseCode = "404", content = @Content)
            }
    )
    @Operation(summary = "프로필을 조회")
    ResponseEntity<ProfileResponse> findProfile(
            @Parameter(description = "조회할 회원의 식별자", required = true) Long id
    );
@RequestMapping("/v1/members")
@RestController
public class MemberController implements MemberApi {

    private final MemberService memberService;

    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

    @GetMapping(path = "/{id}")
    public ResponseEntity<ProfileResponse> findProfile(@PathVariable Long id) {
        ProfileResponse response = memberService.findById(id);
        return ResponseEntity.ok(response);
    }

Swagger를 적용했지만 새로 생긴 MemberApi를 implements하는 것을 제외하면 프로덕션 코드에 변화가 생기지 않았다.

추가 팁?

@ApiResponses를 통해 응답을 표현할 때 에러 상황이라 content가 없다면 content = @Content를 통해 content를 생략할 수 있다.

컨트롤러의 파라미터를 숨기고 싶다면 Shcema(hidden = true)를 통해 숨길 수 있다.

적용된 모습

와~ 너무 보기 좋아요

소감

귀찮더라도 api 명세 관련 라이브러리는 프로젝트를 시작할 때 적용하자. 힘들다.
적용이 쉽다는 매리트때문에 선택했지만 사실 rest docs와 거기서 거기인 것 같기도..
restdocs는 컨트롤러 단위 테스트가 강제된다는 점이 귀찮지만 swagger는 프로덕션 코드에 Api interface의 코드 또한 작성해야해서 두배로 작성을 해야한다면 무슨 매리트가 있나 싶다.
둘다 번거롭다면 다음엔 테스트를 수행할 수 있는 rest docs를 쓸 것 같다.

profile
안녕하세요

0개의 댓글