일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 스프링 부트
- Docker
- AOP
- REDIS
- Level2
- mock
- 레벨2
- JUnit5
- 우아한세미나
- Spring Batch
- 트랜잭션
- 세션
- 프로그래머스
- 미션
- 자바
- 의존성
- HTTP
- 백준
- Paging
- 우아한테크코스
- yml
- MSA
- CircuitBreaker
- 서블릿
- AWS
- 코드리뷰
- JPA
- 프리코스
- 스프링부트
- 우테코
- Today
- Total
늘
[RestDocs]API 문서화 본문
Spring REST Docs는 테스트가 통과해야 문서화가 된다.
API문서 자동화에는 대표적으로 swagger와 Rest docs가 있는데 개인적으로 RestDocs를 선호한다.
장점
- Spring REST Docs는 테스트가 성공하지 않으면 문서를 만들 수 없습니다. 즉, 반 강제로 테스트를 해야 하고 이를 통해 신뢰도 높은 api문서가 나온다.
- 실제 코드에 추가되는 코드가 없습니다. - swagger를 보면 프로덕션 코드에 어노테이션이 덕지덕지 붙어있는데, 이는 관심사의 분리가 실패된 케이스가 아닌가.. 라는 생각이 들어서 RestDocs를 선호한다.
- 커스터마이징 가능
문서화에 사용할 테스트 도구
여기서 API문서화 할때는 Controller테스트를 통해서 생성하므로 RestAssured보다는 MockMvc를 사용하는 것을 개인적으로 추천한다. 왜냐하면
- MockMvc를 사용하면 @SpringBootTest를 사용하지 않으므로 모든 컨텍스트를 띄우지 않고, @WebMvcTest를 통해 Presentation Layer Bean들만 불러온다. 그리고 그 외 Bean은 Mock 객체 설정을 해주어 순수한 Controller 로직을 테스트하므로 속도가 빠르다.
- 반면에 RestAssured는 REST 웹 서비스를 검증하기 위한 라이브러리이며 대부분 End-to-End Test(전 구간 테스트)에 사용된다고 생각한다.
바로 사용법
해당 docs적용은 gradle 7.0 아래의 버전이다. gradle 7.0 이상부터 설정 방법이 바뀌었으니 해당 블로그를 참고하면 된다!
build.gradle
//plugins에 추가해준다.
plugins{
...
id "org.asciidoctor.convert" version "1.5.9.2"
}
//dependencies에 추가해준다.
dependencies {
...
asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' //mockmvc사용
...
//만약 restAssuered를 사용한다면 아래 의존성을 주입해준다.
testImplementation 'org.springframework.restdocs:spring-restdocs-restassured' //restassured사용
}
//build/generated-snippets에 파일을 생성한다.
//테스트 결과를 1차적으로 snippet으로 생성
ext {
snippetsDir = file('build/generated-snippets')
}
//test
test {
outputs.dir snippetsDir
useJUnitPlatform()
}
// 이후 asciidoc을 통하여, snippet을 마크 언어로 구성된 파일로 변환
asciidoctor {
inputs.dir snippetsDir
dependsOn test
}
//jar파일 패키징
bootJar {
dependsOn asciidoctor
from ("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
// from 위치에서 into 위치로 복사하고 싶으면 아래 설정 추가
task copyDocument(type: Copy) {
dependsOn asciidoctor
from file("build/asciidoc/html5/")
into file("src/main/resources/static/docs")
}
build {
dependsOn copyDocument
}
RestDocsConfig
@TestConfiguration
public class RestDocsConfig {
@Bean
public RestDocsMockMvcConfigurationCustomizer restDocsMockMvcConfigurationCustomizer() {
return configurer -> configurer.operationPreprocessors()
.withRequestDefaults(prettyPrint())
.withResponseDefaults(prettyPrint());
}
}
메서드 이름에서 보이는 것처럼 커스터마이징이 가능하다.
~ControllerTest
@DisplayName("지하철 역 API 문서화")
@AutoConfigureRestDocs
@WebMvcTest(StationController.class)
@Import(RestDocsConfig.class)
class StationControllerTest {
@MockBean
private StationService stationService;
@Autowired
private MockMvc mvc;
@Autowired
private ObjectMapper objectMapper;
@Test
@DisplayName("역 생성 문서화")
void createStation() throws Exception {
Station 신설역 = new Station(1L, "신설역");
StationResponse stationResponse = new StationResponse(신설역);
StationRequest request = new StationRequest("신설역");
given(stationService.save(any())).willReturn(stationResponse);
String content = objectMapper.writeValueAsString(request);
ResultActions results = mvc.perform(post("/stations")
.content(content)
.contentType(MediaType.APPLICATION_JSON)
.characterEncoding("UTF-8"));
results.andExpect(status().isCreated())
.andDo(print())
.andDo(document("station-create",
responseFields(
fieldWithPath("id").type(JsonFieldType.NUMBER).description("식별자"),
fieldWithPath("name").type(JsonFieldType.STRING).description("역 이름")
)
));
}
}
post의 경우 content를 넣어줘야하는데 content()가 string값을 받으므로
import com.fasterxml.jackson.databind.ObjectMapper;
jackson.databind의 ObjectMapper를 사용해서 String으로 바꿔준다.
테스트가 성공하면 build/generated-snippets에 dcoument()에 넣은 이름으로 .adoc파일이 생성이 된다.
그런 후 src/docs/asciidoc와 같이 디렉토리를 만들고 *.adoc파일을 작성해줍니다.
= 기론의 애플리케이션 API 문서 (Station)
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:sectlinks:
:snippets: ../../../build/generated-snippets
= Station
---
== 기본 API
=== 역 생성
`POST /stations`
요청 HTTP
include::{snippets}/station-create/http-request.adoc[]
성공 응답 HTTP
include::{snippets}/station-create/http-response.adoc[]
include::{snippets}/station-create/response-fields.adoc[]
위와 같이 작성해주면 모든게 끝난다.
이후, 다시 빌드를 해주고 localhost:8080/docs/station.html로 접속하면 된다.
결과
추가로 index.html을 만들어서 관련 API 링크를 걸어주면 프론트분들이 보기 더 편하겠죠~!😁😁
fieldWithPath 사용법
json이 아래와 같은 형식일 때,
{
"a":{
"b":[
{
"c":"one"
},
{
"c":"two"
},
{
"d":"three"
}
],
"e.dot" : "four"
}
}
dto안에 dto면 . 으로 연결하고, 배열이면 []을 이용한다고 생각하면 편하다.
https://docs.spring.io/spring-restdocs/docs/current/reference/html5/
https://techblog.woowahan.com/2597/
'백앤드 개발일지 > 스프링부트' 카테고리의 다른 글
[Transaction] commit된 트랜잭션에 롤백된 트랜잭션이 참여하면 어떻게 될까? TranscationalEventListener와 함께 알아보자 (0) | 2022.12.02 |
---|---|
[프록시] 스프링에서 사용되는 proxy전략 (2) | 2022.07.23 |
@Mock vs @MockBean vs @InjectMocks (1) | 2022.05.31 |
[Spring bean lifecycle, hook]빈 생명주기 (0) | 2022.05.23 |
@RestController와 @ResponseBody없이 json으로 통신하는 방법 (2) | 2022.04.30 |