[RestDocs]API 문서화 본문

백앤드 개발일지/스프링부트

[RestDocs]API 문서화

giron 2022. 6. 3. 20:20
728x90

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 이상부터 설정 방법이 바뀌었으니 해당 블로그를 참고하면 된다!

https://velog.io/@max9106/Spring-Spring-rest-docs%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%AC%B8%EC%84%9C%ED%99%94

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
}

 

ext

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파일을 작성해줍니다.

station.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

추가로 index.html을 만들어서 관련 API 링크를 걸어주면 프론트분들이 보기 더 편하겠죠~!😁😁

fieldWithPath 사용법

json이 아래와 같은 형식일 때,

{
	"a":{
		"b":[
			{
				"c":"one"
			},
			{
				"c":"two"
			},
			{
				"d":"three"
			}
		],
		"e.dot" : "four"
	}
}

fieldsWithPath

dto안에 dto면 . 으로 연결하고, 배열이면 []을 이용한다고 생각하면 편하다.

 

https://docs.spring.io/spring-restdocs/docs/current/reference/html5/

 

Spring REST Docs

Document RESTful services by combining hand-written documentation with auto-generated snippets produced with Spring MVC Test, WebTestClient, or REST Assured.

docs.spring.io

https://techblog.woowahan.com/2597/

 

Spring Rest Docs 적용 | 우아한형제들 기술블로그

{{item.name}} 안녕하세요? 우아한형제들에서 정산시스템을 개발하고 있는 이호진입니다. 지금부터 정산시스템 API 문서를 wiki 에서 Spring Rest Docs 로 전환한 이야기를 해보려고 합니다. 1. 전환하는

techblog.woowahan.com

 

728x90
Comments