[Spring] Spring Rest Docs

Juwon Park·2022년 1월 24일
0
post-thumbnail

1. Spring Rest Docs 란?

Spring Rest Docs는 RESTful 서비스에 대한 정확하고 알기 쉬운 문서를 생성하는 것을 돕는 툴이다.즉, API 문서 작성 자동화 툴이다. Spring Rest Docs는 테스트 코드를 기반으로 생성된 snippets와 직접 손으로 작성한 문서를 결합하여 문서화한다.

Spring Rest Docs는 기본적으로 Asciidoctor를 사용하여 문서화한다.Asciidoctor는 일반 텍스트를 처리하고 필요에 맞게 스타일이 지정되어 배치된 HTML을 만든다. 필요의 경우 Markdown을 사용하여 문서를 구성할 수도 있다. Spring Rest Docs는 Spring MVC의 Test 프레임워크, Spring Webflux의 WebTestClient, Rest Assured 3로 작성된 테스트에서 생성된 snippets를 사용한다. 이러한 테스트 중심 접근 방식은 API 문서의 정확성과 신뢰성을 보장한다는 장점이 있다. 또한, 프로덕션 코드를 건드릴 필요가 없기 때문에 프로덕션 코드와 API 문서를 완벽하게 분리할 수 있다는 장점이 있다.

2. Spring Rest Docs vs Swagger

Spring Rest Docs와 Swagger는 API 문서 자동화 툴로 많이 사용되고 있다. Swagger는 적용하기 쉽고 API를 테스트 해볼 수 있는 UI를 제공한다는 장점이 있다. 하지만 프로덕션 코드에 Annotation을 추가해야하기 때문에 프로덕션 코드의 가독성을 저하시킨다. 또한, 코드와 문서가 동기화가 안될 수도 있다는 단점이 있다.

반면에 Spring Rest Docs는 테스트 코드를 기반으로 문서를 작성하기 때문에 코드와의 동기화 이슈가 없고, 정확하고 신뢰성 있는 API 문서를 제공해준다는 장점이 있다. 또한, 테스트 코드에 작성을 하기 때문에 프로덕션 코드와 문서의 완벽한 분리가 가능하다는 장점이 있다. 테스트 코드는 개발할 때 최소한의 테스트이기 때문에 거의 필수(?)로 작성되어야 한다는 관점에서 보면 Spring Rest Docs를 API 문서 자동화 툴로 채택하는 것이 좋을 듯 하다. Swagger의 장점 중 하나인 API 테스트는 Postman이나 Intellij의 .http로 충분히 대체가 되기 때문이다.

3. Spring Rest Docs 작성 방법

Gradle 7.0.2, Junit5, MockMvc, Asciidoc

Build Configuration

build.gradle

plugins {
	id "org.asciidotor.jvm.convert" version "3.3.2"
}

configurations {
	asciidoctorExtensions
}

dependencies {
	asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
    	testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}

ext {
	snippetsDir = file('build/generated-snippets')
}

test {
	outputs.dir snippetsDir
}

asciidoctor {
	configurations 'asciidoctorExtensions'
    	inputs.dir snippetsDir
        dependsOn test
}

Packaging the Documentation

build.gradle

bootJar {
	dependsOn asciidoctor
    	copy {
        	from"${asciidoctor.outputDir}"
            	into 'src/main/resources/static/docs'
        }
}

Customizing Requests and Responsed

test/**/ApiDocumentUtils.java

public interface ApiDocumentUtils {
	static OperationRequestPreprocessor getDocumentRequest() {
    		return preprocessRequest(prettyPrint());
    	}
        static OperationResponsePreprocessor getDocumentResponse() {
    		return preprocessResponse(prettyPrint());
    	}
}

Generating Documentation Snippets

test/**/OrderControllerTest.java

@AutoConfigureMockMvc
@AutoConfigureRestDocs
@WebMvcTest(OrderController.class)
public class OrderControllerTest {
	@Autowired
    	private MockMvc mockMvc;
        
        @Autowired
        private ObjectMapper objectMapper;
        
        @MockBean
        private OrderService orderService;
        
        @Test
	@DisplayName("cancelOrder")
	void cancelOrder() throws Exception {

		//given
		String orderId = "50bcc33f-2b1d-4bbb-abda-7410b8889852";
		OrderCancelResponseDto expectedResponse = new OrderCancelResponseDto("CANCELED", "200", "주문이 취소되었습니다.");
		given(orderInteractor.cancelOrder(any())).willReturn(expectedResponse);

		//when
		ResultActions result = this.mvc.perform(
			RestDocumentationRequestBuilders.post("/orders/{orderId}/cancel", orderId)
				.contentType(MediaType.APPLICATION_JSON)
				.characterEncoding("utf-8")
		);
        
        	//then
		result.andExpect(status().isOk())
			.andExpect(jsonPath("status").value("CANCELED"))
			.andDo(document("cancelOrder",
				getDocumentRequest(),
				getDocumentResponse(),
				pathParameters(
					parameterWithName("orderId").description("주문 고유번호")
				),
				responseFields(
					fieldWithPath("status").description("API 호출에 따른 성공 상태"),
					fieldWithPath("code").description("성공 코드"),
					fieldWithPath("message").description("응답에 대한 설명")
				)));
	}
}

Reference

Spring REST Docs
우아한 형제들 기술 블로그

0개의 댓글