우리 팀은 프로젝트의 API 문서를 노션으로 관리하고 있다.
API 설계도 보러가기
이 방식은 여러 단점들을 가지고 있다.
따라서, 신뢰할 수 있고 API를 자동으로 문서화해주는 도구들을 사용해보려 한다.
여러 도구들을 찾아보던 찰나, Spring Rest Docs
와 Swagger
를 알게 되었고 그 둘을 비교하면 아래와 같다.
Spring Rest Docs | Swagger | |
---|---|---|
장점 | 제품 코드에 영향이 없다. | API를 테스트해볼 수 있는 화면을 제공한다. |
테스트가 성공해야 문서가 작성된다. | 적용하기 쉽다. | |
단점 | 적용하기 어렵다. | 제품 코드에 어노테이션이 침범한다. |
테스트가 성공해야 문서가 작성된다. | 제품 코드와 동기화가 안될 수 있다. |
우리에게 적합한 방식은 무엇일까?
우리의 목적은 API 문서를 제공하는 것이였다.
물론 테스트를 작성하고 성공해야만 문서가 작성되는 Spring Rest Docs이 신뢰성이 높을 순 있지만,
어차피 Rest Docs도 단점은 존재한다.
따라서 당장 적용해보고 싶은 마음이 컸기에 적용하기 쉬운 Swagger를 적용해보았다.
그 외 결정해야 할 것들
- Junit vs Spock
- MockMvc(
@WebMvcTest
) vs RestAssured(@SpringBootTest
)- AsciiDoc vs Markdown
우리 팀은.. 당장 적용해보고 싶은 마음이 컸기에 평소에 쓰던 방식을 따라가기로 했다.
→ JUnit & RestAssured & Markdown!
아래 블로그를 참고했다.
SpringBoot SpringDoc(OpenAPI)을 이용한 Swagger 적용
위 사진에서 주황색 박스 부분이 Swagger
를 도입하며, 코드에 들어간 부분들이다.
컨트롤러 선언부에 @Tag
는 어쩔 수 없다지만, @Operation
과 @ApiResponses
의 중복 코드가 매우 많다.
공통된 부분들을 찾아보자.
@Operation
@ApiResponses
와 내부에 Error에 해당하는@ApiResponse
API 별로 달라야 하는 부분들을 찾아보자.
@Operation
의 summary 속성@ApiResponses
와 내부에 성공 시에 해당하는@ApiResponse
의 implementation 속성
처음에 원했던 사용방식은 아래와 같다.
@CustomSwaggerAPI(summary = "회원 로그인", implementation = TestExample.class)
//성공 시에 implementation에는 TestExample.class가 들어가고,
//실패 시에 implementation에는 Error.class가 자동으로 들어가길 원했다.
다만 위 방식처럼 한 어노테이션 내에서 implementation
에 두 클래스를 각각 경우를 따져 넣는건 불가능하다.
→ 그래서 성공 시에 적용되는 어노테이션과 실패 시에 적용되는 어노테이션을 구분하기로 했다!
성공 시에 사용되는 애너테이션
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Operation
@ApiResponses(value = {
@ApiResponse(responseCode = "200")
})
public @interface SwaggerApi {
String summary() default "";
Class<?> implementation() default Void.class;
}
이 애너테이션을 잘 들여보면, @Operation
을 내장하도록 하여 summary 속성을 지정하도록 하였다.
또한 implementation 속성에 들어갈 클래스를 받도록 필드를 선언해놓았다.
실패 시에 사용되는 애너테이션
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@ApiResponses(value = {
@ApiResponse(
responseCode = "400",
content = @Content(schema = @Schema(implementation = Error.class))),
@ApiResponse(
responseCode = "401",
content = @Content(schema = @Schema(implementation = Error.class))),
@ApiResponse(
responseCode = "403",
content = @Content(schema = @Schema(implementation = Error.class)))
})
public @interface SwaggerApiFailWithAuth {
}
각각의 에러 종류에 따라 implentation에 Error.class
가 기본으로 들어가도록 선언해놓았다.
커스텀 애너테이션 사용 결과
이전 코드와 비교했을 때 훨씬 중복 범위를 줄이고, 가독성은 그대로 가져간 것을 확인할 수 있다!
여러 사람들이 Spring Rest Docs와 Swagger의 장단점을 비교하여 둘 중 하나만 사용하는 것이 아닌,
각각의 장점들만 가져와 같이 사용하는 경우들도 있다고 한다.
시간이 난다면 아래 블로그 등을 참고하여도 좋을 것 같다.