Swagger로 API 문서 자동화를 해보자

콜트·2021년 3월 10일
14

Edit. 앱 개발기

목록 보기
4/11


직전 프로젝트에서는 구글 스프레드 시트를 이용해서 API 명세서를 작성했었다. 하지만 좀 더 가시적으로 문서화할 수 있으면 좋을 것 같다는 생각이 계속 들었다. 실제로, 구글 스프레드 시트에 작성된 명세서에 있던 오탈자로 인해 클라이언트 측에서 상당 시간을 낭비한 경우도 있었다. 그래서 API 문서화 도구인 Swagger를 사용해서 API를 문서화 하기로 했다. 잘 정리되어 있는 문서는 팀의 생산성을 높여줄 것이 분명하므로 충분히 가치있다고 생각된다.

Swagger란?

Swagger 는 REST API를 설계, 빌드, 문서화 및 사용하는 데 도움이되는 OpenAPI 사양을 중심으로 구축 된 오픈 소스 도구 세트입니다. - About Swagger Specification

왜 사용할까?

Swagger를 사용하는 이유는 다음과 같다.

  • 적용하기 쉽다.. Spring REST Docs라는 문서화 도구와는 달리 Swagger는 코드 몇 줄만 추가하면 만들 수 있다!
  • 테스트 할 수 있는 UI를 제공한다. Spring REST Docs는 테스트를 돌리면서 성공하는지 실패하는지 확인하지만 Swagger는 문서 화면에서 API를 바로 테스트 할 수 있다.

어떻게 사용할까?

우선, build.gradle에 의존성을 추가해준다(2.9.2 버전 기준).

// swagger
compile 'io.springfox:springfox-swagger2:2.9.2'
compile 'io.springfox:springfox-swagger-ui:2.9.2'

Configuration

다음으로는 설정 클래스를 정의한 뒤 @EnableSwagger2 어노테이션을 선언해주고 @Bean 어노테이션을 이용해서 Docket 객체를 빈으로 등록해주면 된다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;

@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

    @Bean
    public Docket apiV1() {
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("groupName1")
                .select()
                .apis(RequestHandlerSelectors.
                        basePackage("com.app.edit"))
                .paths(PathSelectors.ant("/v1/api/**"))
                .build()
                .apiInfo(apiInfo());
    }

    @Bean
    public Docket apiV2() {
        return new Docket(DocumentationType.SWAGGER_2)
                .useDefaultResponseMessages(false)
                .groupName("groupName2")
                .select()
                .apis(RequestHandlerSelectors.
                        basePackage("com.app.edit"))
                .paths(PathSelectors.ant("/v2/api/**"))
                .build()
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfo(
                "Title",
                "Description",
                "version 1.0",
                "https://naver.com",
                new Contact("Contact Me", "https://daum.net", "colt@colt.com"),
                "Edit Licenses",
                "https://naver.com",
                new ArrayList<>()
        );
    }
}

@EnableSwagger2

  • Swagger2 버전을 활성화 하겠다는 어노테이션이다.

Docket

  • Swagger 설정을 할 수 있게 도와주는 클래스이다.
  • API 자체에 대한 정보는 컨트롤러에서 작성한다.

useDefaultResponseMessages()

  • false로 설정하면 Swagger에서 제공해주는 응답코드(200, 401, 403, 404)에 대한 기본 메시지를 제거해준다.

groupName()

  • Docket Bean이 한 개일 경우 생략해도 상관없으나, 둘 이상일 경우 충돌을 방지해야 하므로 설정해줘야 한다.

select()

  • ApiSelectorBuilder를 생성하여 apis()paths()를 사용할 수 있게 해준다.

apis()

  • api 스펙이 작성되어 있는 패키지를 지정한다.
  • 즉, 컨트롤러가 존재하는 패키지를 basepackage로 지정하여 해당 패키지에 존재하는 API를 문서화 한다.

paths()

  • apis()로 선택되어진 API중 특정 path 조건에 맞는 API들을 다시 필터링하여 문서화한다.
  • PathSelectors.any()로 설정하면 패키지 안에 모든 API를 한 번에 볼 수 있다.

apiInfo()

  • 제목, 설명 등 문서에 대한 정보들을 설정하기 위해 호출한다.
  • 파라미터 정보는 다음과 같다.

ParameterBuilder + globalOperationParameters()

  • parameterBuilderglobalOperationParameters()를 이용해서 아래와 같이 Swagger의 전체 API에서 보여줄 파라미터를 설정해줄 수도 있다.

globalResponseMessage()

  • 아래와 같이 globalResponseMessage()로 모든 operation에 공통된 응답 메시지를 작성해줄 수도 있다.
@Bean
public Docket apiV1() {
    List<ResponseMessage> responseMessages = new ArrayList<>();
    responseMessages.add(new ResponseMessageBuilder()
            .code(200)
            .message("OK")
            .build());
    responseMessages.add(new ResponseMessageBuilder()
            .code(404)
            .message("Not Found Error")
            .build());
    responseMessages.add(new ResponseMessageBuilder()
            .code(500)
            .message("Internal Server Error")
            .build());
    return new Docket(DocumentationType.SWAGGER_2)
            .groupName("groupName1")
            .select()
            .apis(RequestHandlerSelectors.
                    basePackage("com.app.edit"))
            .paths(PathSelectors.ant("/v1/api/**"))
            .build()
            .apiInfo(apiInfo())
            .globalResponseMessage(RequestMethod.GET, responseMessages);
}

코드 작성이 완료되었다면 서버를 실행시킨 뒤, http://localhost:8080/swagger-ui.html 로 들어가면 API가 문서화된 화면을 볼 수 있다.

swagger-ui.html로 접속하는 이유는, springfox-swagger-ui가 해당 파일을 만들어주기 때문이다.

Controller

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Api(value = "hello", tags = {"swagger", "v1", "api"})
@RequestMapping("/v1/api")
@RestController
public class SwaggerController {

    @ApiOperation(value = "this is test!", notes = "note!!!!!")
    @PostMapping("/test")
    public String test(@ApiParam(name = "first param", value = "first value", required = true) String input,
                       @ApiParam(name = "second param", value = "second value", required = false) String input2) {
        return "test";
    }
}

@Api

  • 해당 클래스가 Swagger 리소스라는 것을 명시해준다.
    • value : 사용자 지정 이름을 붙일 수 있다. tags 사용시 무시된다.
    • tags : 사용하여 여러 개의 태그를 정의할 수도 있다.

@ApiOperation

  • 한 개의 Operation을 선언한다.
    • value : API에 대한 요약을 작성한다. 제대로 표시되려면 120자 이하여야 한다.
    • notes : API에 대한 자세한 설명을 작성한다.

@ApiParam

  • 파라미터에 대한 정보를 명시한다.
    • name : 파라미터 이름을 작성한다.
    • value : 파라미터 설명을 작성한다.
    • required : 필수 파라미터이면 true, 아니면 false를 작성한다.

주의사항

API 문서 URL을 알고 있다면 아무나 들어와서 테스트를 할 수도 있다. 따라서 사전에 접근권한의 제한 등을 통해 권한이 있는 사용자만 접근할 수 있도록 Spring Security 등의 설정을 해주어야한다!


참고자료


profile
개발 블로그이지만 꼭 개발 이야기만 쓰라는 법은 없으니, 그냥 쓰고 싶은 내용이면 뭐든 쓰려고 합니다. 코드는 깃허브에다 작성할 수도 있으니까요.

6개의 댓글

comment-user-thumbnail
2022년 8월 30일

좋은 글 감사합니다!

1개의 답글
comment-user-thumbnail
2023년 7월 28일

안녕하세요 좋은글 감사합니다.
스웨거 설정 올려주신글 보고 있는데요.
http://localhost:8181/swagger-ui.html 접속시
Unable to infer base url. This is common when using dynamic servlet registration or when the API is behind an API Gateway. ~
라고 발생하는데 해결 방법을 못찾고 있습니다.
혹시 어느 부분을 봐야 하는지 답변 주시면 정말 감사하겠습니다.

1개의 답글