API를 개발하려면 명세가 필요하다. 이 API가 무슨 기능을 하고 어떤 로직을 수행하며 그 로직을 수행하기 위해 어떤값을 입력받아야 하는지 그리고 출력값은 어떻게 되는지 등을 정리한 자료를 명세라고 한다.
옛날에는 API를 개발하면 명세 문서도 작성하고 그 API의 내용이 변경될 때마다 명세 문서도 업데이트를 했어야 했다.
이러한 과정은 번거롭고 오히려 개발의 생산성을 떨어뜨릴 수 있다.
하지만, Swagger를 사용하면 명세 문서 작성 필요없이 API를 개발할 수 있다.
Swagger는 API의 명세 문서를 자동으로 생성하며 Swagger UI를 사용하여 API에 대한 이해와 테스트가 가능하다.
스프링부트 2.x 시절에는 Springfox 패키지를 통해 생성하였는데 스프링부트 3.x로 넘어오면서 springdoc-openapi 패키지를 통해 생성하는 것으로 변경되었다.
build.gradle 파일에
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
다음과 같이 의존성을 추가하면 된다.
Swagger 설정을 위해 클래스를 만들어주어야 하는데
@Configuration
public class SwaggerConfiguration {
//설정 로직
}
}
설정 코드를 작성할 클래스에 @Configuration 에너테이션을 추가하면 된다.
이제 @Bean을 추가하여 설정을 하면 되는데 Springfox 시절과 변경된 점이 있다.
@Configuration
public class SwaggerConfiguration {
@Bean
public GroupedOpenApi api(){
return GroupedOpenApi.builder()
.group("com.example.demo")
.packagesToScan("com.example.demo")
.build();
}
}
GroupedOpenApi는 어떤 API에 대한 명세를 만들지, 즉 어느 범위까지 명세를 만들면 되는지 설정하는 것이다.
우선 group은 demo 패키지로 설정하여 전체를 그룹으로 설정하였다.
그 다음 packagesToScan은 문서화할 컨트롤러의 범위를 설정하는 메서드로 모든 API를 문서화할거라면 그룹과 동일하게 지정하면 된다.
demo 패키지의 하위 패키지인 PackageA만 문서화하고 싶으면 인자로 "com.example.demo.PackageA"로 입력하면 된다.
API에 대한 설명을 작성하기 위해서는 @Operation 에너테이션을 사용하고 매개변수를 통해 설정이 가능하다.
예를 들어, GET API에 대한 설명 제목을 "상품의 정보를 조회하는 메서드"으로 지정하고 세부설명으로 "@RequestParam으로 상품 고유번호를 인자로 받아 해당 상품을 조회할 수 있음"으로 해보자.
그러기 위해서는 에너테이션을 다음과 같이 설정하면 된다.
@Operation(summary = "상품의 정보를 조회하는 메서드", description = "@RequestParam으로 상품 고유번호를 인자로 받아 해당 상품을 조회할 수 있음")
매개변수에 대한 설명을 추가하고자 할 경우 @Parameter를 사용하면 된다.
소괄호를 열어 속성을 입력할 수 있는데 다음과 같이 사용할 수 있다.
- description : 설명을 작성
- example : 예시 값 제공
- required : 필수 여부 (true/false)
자 그렇다면 매개변수의 명세를 다음과 같이 만들어보겠다.
- 설명 : 상품의 고유번호를 입력해야
number를 통해 상품을 조회- 예시 : "http://localhost:8080/product/1"
- 필수 여부 : true(이 파라미터가 필수적으로 입력해야 하는 것인지 아닌지를 설정)
@Operation(summary = "상품의 정보를 조회하는 메서드",
description = "@RequestParam으로 상품 고유번호를 인자로 받아 해당 상품을 조회할 수 있음")
@GetMapping("/{productID}")
public ResponseEntity<ProductResponseDTO> getProduct(
@Parameter(description = "상품의 고유번호를 입력해야 `number`를 통해 상품을 조회",
example = "http://localhost:8080/product/1",
required = true)
@PathVariable("productID") Long number){
ProductResponseDTO productResponseDTO = productService.getProduct(number);
return ResponseEntity.status(HttpStatus.OK).body(productResponseDTO);
}
스캔할 컨트롤러의 범위를 지정하면 그 컨트롤러들에 대해서만 명세를 만드는데 전체에 대한 설명을 추가할 수 있다.
예를 들어, 제목은 "상품을 조회,저장,수정,삭제하는 메서드"로 정하고 이에 대한 설명으로 "MariaDB와 연동하여 상품에 대한 정보를 데이터베이스에서 조작하고 상품의 고유번호를 통해 상품을 조회하거나 삭제할 수 있으며 상품의 이름을 수정할 수 있습니다."로 추가해보겠다.
@OpenAPIDefinition 에너테이션을 사용하면 되는데 다음과 같이 Application 클래스에 추가하면 된다.
@OpenAPIDefinition(
info = @Info(
title = "상품을 조회,저장,수정,삭제하는 메서드",
version = "1.0.0",
description = "MariaDB와 연동하여 상품에 대한 정보를 데이터베이스에서 조작하고 상품의 고유번호를 통해 상품을 조회하거나 삭제할 수 있으며 상품의 이름을 수정할 수 있습니다."
)
)
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
자 이제 직접 실행하여 결과를 확인해보자.
이렇게 명세를 작성하면 다른 개발자가 테스트를 할 경우 이 API들이 무슨 용도이고 왜 만들었는지 바로 알 수 있어 개발의 생산성이 더 높아질 것이다.
https://springdoc.org/index.html#Introduction
springdoc를 참고하였으며 Springfox와 비교해 달라진 점들은 Migrating from Springfox를 참고하면 된다.
추가로 컨트롤러로 사용하는 클래스에 @Controller만 있을 경우 Restful API 앤드포인트로 스캔하지 못하기 때문에 @Controller + @ResponseBody의 조합인 @RestController를 사용해야 한다.