티스토리 뷰
API(Application Programming Interface)는 애플리케이션 간에 데이터를 주고받거나 서비스를 요청하고 제공하기 위한 인터페이스입니다. 클라이언트와 서버 간에 API를 통해 통신하는 만큼 API 문서화는 개발 과정에 있어 중요한 요소 중 하나입니다. Spring 프레임워크에서 API 문서화 방법에는 크게 Rest Docs와 Swagger 2가지가 존재하는데 우아한테크코스 Votogether팀에서 왜 Swagger를 선택하였는지와 적용 방법에 대해 정리해 보았습니다.
Spring REST Docs vs Swagger
Spring Rest Docs
Spring REST Docs
는 스프링 프레임워크에서 제공하는 API 문서 자동화 도구이다. Spring REST Docs는 Spring MVC 테스트 프레임워크, REST Assured로 작성된 테스트에서 생성된 스니펫을 사용한다. 테스트 기반으로 문서화가 동작하기에 문서의 정확성을 보장하는데 도움이 된다. 문서화를 위한 코드가 테스트 코드에 존재하기에 프로덕션 코드에 영향을 주지 않는다는 장점도 존재한다.
테스트가 실패하면 문서를 생성할 수 없다. 성공하는 테스트 코드로부터 스니펫을 생성하고 해당 스니펫 조각들을 모아 하나의 온전한 문서를 만들 수 있다. 따라서 온전하게 API 스펙에 알맞은 문서를 만들어낼 수 있지만, 사용하기 위한 설정이 복잡하고 추가적인 학습이 필요하다.
Swagger
Swagger
는 API를 문서화하고 테스트할 수 있는 오픈 소스 프레임워크이다. 테스트를 기반으로 문서화를 진행하는 Spring REST Docs와 달리 어노테이션을 통해 간편하게 API 문서를 자동으로 만들 수 있다. 자체적으로 사용자 친화적인 UI도 제공해주고 있기 때문에 문서를 쉽게 읽고 테스트할 수 있다.
문서화에 사용되는 어노테이션이 프로덕션 코드에 존재하기에 가독성을 떨어뜨리는 단점도 존재한다. API 스펙이 변했을 때 어노테이션 메타 데이터를 적절하게 바꿔주지 않으면 잘못된 정보를 전달할 수도 있다. 하지만 간편한 설정과 사용, 사용자 친화적인 UI는 큰 장점으로 다가온다.
Swagger를 선택한 이유
Votogether팀에서는 Spring REST Docs를 사용해 본 팀원도, 사용해보지 않은 팀원도 있다. Swagger도 마찬가지로 사용해 본 팀원도, 사용해보지 않은 팀원도 있었다. 각 기술에 대한 장단점은 명확했기에 현재 우리 상황에 알맞은 기술을 선택해 보기로 하였다. 현재 매 스프린트마다 빠르게 개발을 진행해야만 하는 상황이었기에 복잡한 설정과 학습 난도가 있는 Spring REST Docs보다는 간편하게 API 문서화가 가능한 Swagger를 사용하기로 하였다.
기능 개발이 어느 정도 마무리되고 리팩토링 시점에서 Swagger로 프로젝트가 관리가 어려워졌을 때 Spring REST Docs로 수정해도 괜찮을 것 같다는 점도 하나의 근거가 되었다. 불편한 점을 직접 경험하고 수정하는 것과 경험하지 않고 선택했을 때 얻는 가치가 다를 것 같았기 때문이다. 또한 기존에 작성해 둔 테스트를 바탕으로 Spring REST Docs로 전환하는 것은 많은 리소스가 들지 않을 것이라 판단했다.
Spring REST Docs를 사용하고자 한다면 해당글을 참고해 볼 수 있다.
Swagger
라이브러리 추가
Swagger를 설정하기 위한 라이브러리는 2가지가 존재한다.
SpringFox
는 2015년에 나온 라이브러리로 오래된 라이브러리이다. 또한 2020년 7월 이후로 업데이트가 멈추었기 때문에 Spring Boot 특정 버전부터는 제대로 적용되지 않는 문제가 존재한다.
SpringDoc
은 최근에 나온 라이브러리이고 글을 작성하고 있는 시점에 마지막 업데이트가 2023년 4월이다. 가장 최근에 나온 Spring Boot 3.x 버전도 여전히 지원하고 있다.
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
따라서 SpringDoc을 사용하기로 결정하였고 가장 최근에 업데이트된 버전을 선택하여 라이브러리를 추가하였다. springdoc-openapi
라이브러리는 스프링 부트 프로젝트를 사용하여 API 문서 생성을 자동화하는데 도움을 준다. 다양한 주석을 기반으로 API 의미를 추론하기 위해 런타임에 애플리케이션을 검사하여 작동한다.
해당 라이브러리는 다음을 지원한다.
- OpenAPI 3
- Spring Boot V3
- JSR-303
- Swagger-ui
- OAuth2
설정 추가
@Configuration
public class OpenAPIConfig {
private final String devUrl;
private final String prodUrl;
public OpenAPIConfig(
@Value("${votogether.openapi.dev-url}") final String devUrl,
@Value("${votogether.openapi.prod-url}") final String prodUrl
) {
this.devUrl = devUrl;
this.prodUrl = prodUrl;
}
@Bean
public OpenAPI openAPI() {
final Server devServer = new Server();
devServer.setUrl(devUrl);
devServer.description("개발 환경 서버 URL");
final Server prodServer = new Server();
prodServer.setUrl(prodUrl);
prodServer.description("운영 환경 서버 URL");
final Info info = new Info()
.title("VoTogether API")
.version("v1.0.0")
.description("보투게더 API");
return new OpenAPI()
.info(info)
.servers(List.of(devServer, prodServer));
}
}
Swagger를 사용하고자 하는데 OpenAPI에 대한 설정을 하고 있는 것을 확인할 수 있다. Swagger와 OpenAPI가 무엇이고 어떤 차이가 있는지에 대해 먼저 알아보자.
Swagger
는 OpenAPI Specification의 이전 이름이다. 초기 Swagger는 API 개발 도구로 시작되었으며 API를 설계, 빌드, 문서화하는 데 사용되었다. 추가적으로 API 문서화, 클라이언트 코드 생성, 테스트 기능 등을 제공하면서 API 개발자들의 생산성을 향상하는데 도움이 되었다.
OpenAPI Specification
은 API를 설계화하고 문서화하기 위한 표준적인 형식과 구조를 정의한다. 따라서 Swagger는 OpenAPI Specification을 구현하고 사용할 수 있는 도구 중 하나로 API를 설계하고 문서화할 수 있도록 돕는다.
다시 한번 정리하자면 아래와 같다.
- OpenAPI Specification은 API 설계와 문서화를 위한 표준적인 형식과 구조를 제공하는 오픈 표준이다.
- Swagger는 OpenAPI Specification을 구현하고 사용하는 도구와 프레임워크이다.
따라서 위의 설정은 OpenAPI 객체를 통해 API의 다양한 측면을 정의하고 문서화하는 데 사용된다. 어떤 서버에서 동작하는지 정보를 담은 Server, API 문서화 정보를 담은 Info를 바탕으로 OpenAPI를 빈으로 등록하게 된다.
애플리케이션을 실행시키고 Swagger UI를 확인할 수 있는 경로인 http://localhost:8080/swagger-ui.html
에 접속하면 위와 같은 화면을 확인할 수 있다.
추가 설정 및 환경 변수
# HTML 형식의 swagger 문서의 사용자 경로 지정
springdoc:
swagger-ui:
path: /swagger-ui.html
# JSON 형식의 OpenAPI 문서의 사용자 경로 지정
springdoc:
api-docs:
path: /api-docs
votogether:
openapi:
dev-url: http://localhost:8080
prod-url: http://운영서버 URL
application.properties
나 application.yml
파일을 통해 추가적인 설정과 별도로 관리해야 할 값을 환경 변수로 등록해 사용할 수 있다. 위의 코드에서 확인할 수 있듯 Swagger에서 제공하는 UI를 접속하기 위한 경로라던지, JSON 형식의 문서를 확인하기 위한 경로도 사용자가 원하는 경로로 변경할 수 있다.
기본적으로 UI에 접속하기 위한 경로가 /swagger-ui.html
이었기에 해당 내용은 적용하지 않았고, JSON 형식의 문서도 확인할 상황이 없을 것 같기에 우선 적용하지 않기로 결정했다. 경로를 수정하거나 JSON 문서를 확인하고 싶은 경우 해당 값을 설정해서 사용해도 좋을 것 같다.
어노테이션
API 문서화에 적용할 수 있는 많은 어노테이션이 존재하지만 자주 사용되는 주요 어노테이션에 대해서만 정리한다.
@Tag
@Tag
어노테이션은 API 엔드포인트에 태그를 할당하여 관련된 엔드포인트를 그룹화하고 문서에서 카테고리를 형성하는 데 사용된다. 따라서 주로 @RestController가 붙어있는 클래스에 사용된다.
@Tag(name = "헬스 체크", description = "헬스 체크 API")
@RequiredArgsConstructor
@RequestMapping("/health-check")
@RestController
public class HealthCheckController {
private final HealthCheckService healthCheckService;
...
}
name
을 통해 카테고리의 이름을 정하고, description
을 통해 해당 카테고리에 대한 설명을 작성할 수 있다. 이를 통해 API 문서의 가독성과 탐색성을 향상할 수 있다.
@Operation
@Operation
어노테이션은 API 엔드포인트의 작업에 대한 설명을 추가하고 세부 정보를 제공하는 데 사용된다. 주로 @RestController 클래스 내부의 메서드에 사용된다.
@Operation(summary = "헬스 체크 조회", description = "서버의 작동 여부 확인을 위해 헬스 체크를 조회한다.")
@ApiResponse(responseCode = "200", description = "헬스 체크 조회 성공")
@GetMapping
public ResponseEntity<String> check() {
final String response = healthCheckService.check();
return ResponseEntity.ok(response);
}
summary
를 통해 작업의 요약, description
을 통해 작업의 구체적인 설명을 작성할 수 있다. 작업에 대한 정보를 명시적으로 제공함으로 API 문서의 가독성과 이해도를 향상할 수 있다.
@ApiResponse
@ApiResponse
어노테이션은 API 응답에 대한 설명과 상태 코드를 정의하는 데 사용된다. @Operation과 마찬가지로 클래스 내부 메서드에 사용된다.
@Operation(summary = "헬스 체크 조회", description = "서버의 작동 여부 확인을 위해 헬스 체크를 조회한다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "헬스 체크 조회 성공"),
@ApiResponse(responseCode = "400", description = "헬스 체크 조회 실패")
})
@GetMapping
public ResponseEntity<String> check() {
final String response = healthCheckService.check();
return ResponseEntity.ok(response);
}
responseCode
를 통해 상태 코드를 정의하고, description
을 통해 응답에 대해 설명한다. @ApiResponses를 통해 여러 개의 @ApiResponse를 등록할 수 있다. 이를 통해 API의 다양한 응답 상황에 대해 이해하고 처리할 수 있다.
@Schema
@Schema
어노테이션은 API 모델의 속성을 정의하고 문서화하는 데 사용된다. 다시 말해서 요청과 응답에 사용되는 DTO 클래스나 필드에 사용할 수 있다.
@Schema(description = "헬스 체크 응답")
public class HealthCheckDto {
@Schema(description = "헬스 체크 코드", example = "1000")
private int code;
@Schema(description = "헬스 체크 메시지", example = "health")
private String message;
}
description
을 통해 속성을 설명하고, example
을 통해 예시 값을 정의할 수 있다. 이뿐 아니라 메서드 매개변수, 메서드 반환값 등 다양한 위치에서 사용될 수 있다. 이를 통해 모델 구조와 속성의 의미를 이해하고 API를 더욱 쉽게 활용할 수 있다.
참고
'Spring > Spring Boot' 카테고리의 다른 글
@Embeddable 사용 시 NullPointerException 문제 해결 (6) | 2023.09.17 |
---|---|
[Spring Boot] Apple OAuth 로그인 구현(1) - Apple OAuth 정리 (0) | 2023.08.06 |
[Spring Boot] @DataJpaTest 데이터베이스 환경 문제 (3) | 2023.07.07 |
[Spring Boot] 프로메테우스, 그라파나를 이용한 스프링 부트 모니터링 (3) | 2023.07.02 |
[Spring] 영속성 컨텍스트는 어디까지 유지되는가 (4) | 2023.03.12 |