Restdocs를 사용하여 API 문서 만들기

업데이트: Link

Restdocs를 사용하여 API 문서 만들기

이 가이드는 Spring 애플리케이션에서 HTTP 엔드포인트에 대한 문서를 생성하는 프로세스를 안내합니다.

구축할 내용

API를 노출하는 몇 가지 HTTP 엔드포인트가 있는 간단한 Spring 애플리케이션을 빌드합니다. JUnit과 Spring의 MockMvc를 사용하여 웹 레이어만 테스트합니다. 그런 다음 동일한 테스트를 사용하여 Spring REST Docs를 사용하여 API에 대한 문서를 생성합니다.

필요한 사항

이 가이드를 완료하는 방법

대부분의 Spring 시작하기 가이드와 마찬가지로 처음부터 시작하여 각 단계를 완료할 수도 있고, 이미 익숙한 기본 설정 단계를 건너뛸 수도 있습니다. 어느 쪽이든 작동하는 코드가 생성됩니다.

처음부터 시작하려면 스프링 초기화 시작하기로 이동하세요.

기본 사항을 건너뛰려면 다음을 수행하세요:

  • 다운로드하여 이 가이드의 소스 리포지토리의 압축을 풀거나 Git을 사용하여 복제합니다: git clone [https://github.com/spring-guides/gs-testing-restdocs.git](https://github.com/spring-guides/gs-testing-restdocs.git)

  • cd into gs-testing-restdocs/initial

  • 간단한 애플리케이션 만들기로 이동합니다.

완료되면 gs-testing-restdocs/complete에서 코드와 비교하여 결과를 확인할 수 있습니다.

스프링 이니셜라이저로 시작하기

준비된 프로젝트를 사용할 수 있습니다. pre-initialized project을 클릭하고 생성을 클릭하여 ZIP 파일을 다운로드합니다. 이 프로젝트는 이 튜토리얼의 예제에 맞게 구성되었습니다.

프로젝트를 수동으로 초기화하려면:

  1. https://start.spring.io로 이동합니다. 이 서비스는 애플리케이션에 필요한 모든 종속성을 가져오고 대부분의 설정을 대신 수행합니다.

  2. Gradle 또는 Maven 중 사용하려는 언어를 선택합니다. 이 가이드에서는 Java를 선택했다고 가정합니다.

  3. Dependencies를 클릭하고 Spring Web을 선택합니다.

  4. Click Generate.

  5. 선택한 항목으로 구성된 웹 애플리케이션의 아카이브인 결과 ZIP 파일을 다운로드합니다.

사용 중인 IDE에 Spring Initializr 통합 기능이 있는 경우, 해당 IDE에서 이 프로세스를 완료할 수 있습니다.

Github에서 프로젝트를 포크하여 IDE 또는 다른 편집기에서 열 수도 있습니다.

간단한 애플리케이션 만들기

Spring 애플리케이션을 위한 컨트롤러를 새로 생성합니다. 다음 목록(src/main/java/com/example/testingrestdocs/HomeController.java에서 가져온 것)은 그 방법을 보여줍니다:

package com.example.testingrestdocs;

import java.util.Collections;
import java.util.Map;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HomeController {

	@GetMapping("/")
	public Map<String, Object> greeting() {
		return Collections.singletonMap("message", "Hello, World");
	}

}

애플리케이션 실행

스프링 이니셜라이저는 애플리케이션을 시작하는 데 사용할 수 있는 main 클래스를 생성합니다. 다음 목록(src/main/java/com/example/testingrestdocs/TestingRestdocsApplication.java에서 가져온 것)은 Spring Initializr가 생성한 애플리케이션 클래스를 보여줍니다:

package com.example.testingrestdocs;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestingRestdocsApplication {

	public static void main(String[] args) {
		SpringApplication.run(TestingRestdocsApplication.class, args);
	}
}

스프링 부팅 애플리케이션`은 다음 사항을 모두 추가하는 편의용 어노테이션입니다:

  • @Configuration: 애플리케이션 컨텍스트에 대한 빈 정의의 소스로 클래스에 태그를 지정합니다.

  • @EnableAutoConfiguration: Spring Boot에 클래스 경로 설정, 기타 빈 및 다양한 속성 설정을 기반으로 빈 추가를 시작하도록 지시합니다.

  • @EnableWebMvc: 애플리케이션을 웹 애플리케이션으로 플래그를 지정하고 DispatcherServlet 설정과 같은 주요 동작을 활성화합니다. Spring Boot는 클래스 경로에서 spring-webmvc를 발견하면 자동으로 추가합니다.

  • @ComponentScan: Spring이 com.example.testingrestdocs 패키지에서 다른 구성 요소, 구성 및 서비스를 찾도록 지시하여 HelloController 클래스를 찾도록 합니다.

main() 메서드는 Spring Boot의 SpringApplication.run() 메서드를 사용하여 애플리케이션을 실행합니다. XML이 한 줄도 없다는 것을 눈치채셨나요? web.xml` 파일도 없습니다. 이 웹 애플리케이션은 100% 순수 Java이며 배관이나 인프라 구성을 처리할 필요가 없습니다. Spring Boot가 이 모든 것을 처리합니다.

로깅 출력이 표시됩니다. 몇 초 내에 서비스가 시작되고 실행됩니다.

애플리케이션 테스트

이제 애플리케이션이 실행 중이므로 테스트할 수 있습니다. http://localhost:8080에서 홈 페이지를 로드할 수 있습니다. 그러나 변경 시 애플리케이션이 작동한다는 확신을 주기 위해 테스트를 자동화하고 싶습니다. 또한 HTTP 엔드포인트에 대한 문서를 게시하려고 합니다. Spring REST Docs를 사용하여 해당 테스트의 동적 부분을 테스트의 일부로 생성할 수 있습니다.

가장 먼저 할 수 있는 일은 애플리케이션 컨텍스트가 시작되지 않으면 실패하는 간단한 정상 상태 확인 테스트를 작성하는 것입니다. 이렇게 하려면 테스트 범위에서 Spring Test 및 Spring REST Docs를 프로젝트에 종속성으로 추가하세요. 다음 목록은 Maven을 사용하는 경우 추가해야 할 항목을 보여줍니다:

<dependency>
  <groupId>org.springframework.restdocs</groupId>
  <artifactId>spring-restdocs-mockmvc</artifactId>
  <scope>test</scope>
</dependency>

다음 목록은 완성된 pom.xml 파일을 보여줍니다:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>gs-testing-restdocs</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- tag::test[] -->
		<dependency>
		  <groupId>org.springframework.restdocs</groupId>
		  <artifactId>spring-restdocs-mockmvc</artifactId>
		  <scope>test</scope>
		</dependency>
		<!-- end::test[] -->
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.asciidoctor</groupId>
				<artifactId>asciidoctor-maven-plugin</artifactId>
				<version>1.5.8</version>
				<executions>
					<execution>
						<id>generate-docs</id>
						<phase>prepare-package</phase>
						<goals>
							<goal>process-asciidoc</goal>
						</goals>
						<configuration>
							<backend>html</backend>
							<doctype>book</doctype>
						</configuration>
					</execution>
				</executions>
				<dependencies>
					<dependency>
						<groupId>org.springframework.restdocs</groupId>
						<artifactId>spring-restdocs-asciidoctor</artifactId>
						<version>${spring-restdocs.version}</version>
					</dependency>
				</dependencies>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

다음 예는 Gradle을 사용하는 경우 추가할 내용을 보여줍니다:

testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'

다음 목록은 완성된 build.gradle 파일을 보여줍니다:

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.0.0'
	id 'io.spring.dependency-management' version '1.1.0'
	id 'org.asciidoctor.convert' version '1.5.8'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
	mavenCentral()
}

ext {
	set('snippetsDir', file("build/generated-snippets"))
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	// tag::test[]
	testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
	// end::test[]
}

tasks.named('test') {
	outputs.dir snippetsDir
	useJUnitPlatform()
}

tasks.named('asciidoctor') {
	inputs.dir snippetsDir
	dependsOn test
}

빌드 파일의 주석은 무시해도 됩니다. 이 가이드에 포함할 파일의 일부를 선택하기 위한 것입니다.

Spring MockMvc를 사용하여 HTTP 콘텐츠를 캡처하는 REST Docs의 mockmvc 플레이버를 포함했습니다. 자체 애플리케이션이 Spring MVC를 사용하지 않는 경우 전체 스택 통합 테스트와 함께 작동하는 restassured 플레이버를 사용할 수도 있습니다.

이제 다음 예제(src/test/java/com/example/testingrestdocs/TestingRestdocsApplicationTests.java에서 가져온)에서 볼 수 있듯이 @RunWith@SpringBootTest 어노테이션과 빈 테스트 메서드가 있는 테스트 사례를 생성합니다:

package com.example.testingrestdocs;

import org.junit.jupiter.api.Test;

import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class TestingRestdocsApplicationTests {

	@Test
	public void contextLoads() throws Exception {
	}
}

이 테스트는 IDE 또는 명령줄에서 실행할 수 있습니다(./mvnw test 또는 ./gradlew test를 실행하여).

정상 여부를 확인하는 것도 좋지만 애플리케이션의 동작을 확인하는 테스트도 작성해야 합니다. 유용한 접근 방식은 Spring이 들어오는 HTTP 요청을 처리하여 컨트롤러에 전달하는 MVC 계층만 테스트하는 것입니다. 이를 위해 Spring의 MockMvc를 사용하고 테스트 케이스에 @WebMvcTest 어노테이션을 사용하여 이를 주입하도록 요청할 수 있습니다. 다음 예제(src/test/java/com/example/testingrestdocs/WebLayerTest.java에서 가져옴)는 이를 수행하는 방법을 보여줍니다:

@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class)
public class WebLayerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void shouldReturnDefaultMessage() throws Exception {
        this.mockMvc.perform(get("/"))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("Hello, World")));
    }
}

문서용 스니펫 생성

이전 섹션의 테스트는 HTTP 요청을 (모의) 만들고 응답을 어설트합니다. 생성한 HTTP API에는 (적어도 원칙적으로는) 동적인 콘텐츠가 있으므로, 테스트를 감시하고 문서에 사용할 HTTP 요청을 수집할 수 있으면 정말 좋을 것입니다. Spring REST Docs를 사용하면 “스니펫”을 생성하여 그렇게 할 수 있습니다. 테스트에 주석을 추가하고 “어설션”을 추가하면 이 기능을 사용할 수 있습니다. 다음 예제(src/test/java/com/example/testingrestdocs/WebLayerTest.java에서 가져온 것)는 전체 테스트를 보여줍니다:

package com.example.testingrestdocs;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.hamcrest.Matchers.containsString;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(HomeController.class)
@AutoConfigureRestDocs(outputDir = "target/snippets")
public class WebLayerTest {

	@Autowired
	private MockMvc mockMvc;

	@Test
	public void shouldReturnDefaultMessage() throws Exception {
		this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
				.andExpect(content().string(containsString("Hello, World")))
				.andDo(document("home"));
	}
}

새로운 어노테이션은 생성된 코드조각의 디렉토리 위치에 대한 인수를 받는 @AutoConfigureRestDocs(Spring Boot의)입니다. 그리고 새로운 어설션은 코드조각의 문자열 식별자에 대한 인수를 받는 MockMvcRestDocumentation.document입니다.

Gradle 사용자는 출력 디렉토리로 target 대신 build를 사용하는 것을 선호할 수 있습니다. 하지만 상관없습니다. 원하는 대로 사용하세요.

테스트를 실행한 다음 target/snippets를 살펴봅니다. 다음과 같이 home(식별자)이라는 디렉터리에 Asciidoctor 코드조각이 포함되어 있어야 합니다:

└── target
    └── snippets
        └── home
            └── curl-request.adoc
            └── http-request.adoc
            └── http-response.adoc
            └── httpie-request.adoc
            └── request-body.adoc
            └── response-body.adoc

기본 스니펫은 HTTP 요청 및 응답에 대한 Asciidoctor 형식입니다. 일반적으로 많이 사용되는 두 가지 명령줄 HTTP 클라이언트인 curlhttpie에 대한 명령줄 예시도 있습니다.

테스트에서 document() 어설션에 인수를 추가하여 추가 스니펫을 만들 수 있습니다. 예를 들어, 다음 예제(src/test/java/com/example/testingrestdocs/WebLayerTest.java에서 가져온 것)에서 볼 수 있듯이 PayloadDocumentation.responseFields() 스니펫을 사용하여 JSON 응답의 각 필드를 문서화할 수 있습니다:

this.mockMvc.perform(get("/"))
    ...
    .andDo(document("home", responseFields(
        fieldWithPath("message").description("The welcome message for the user.")
    ));

테스트를 실행하면 ‘응답 필드 adoc’이라는 추가 스니펫 파일을 찾을 수 있습니다. 이 파일에는 필드 이름과 설명의 표가 포함되어 있습니다. 필드를 생략하거나 이름을 잘못 입력하면 테스트가 실패합니다. 이것이 바로 REST 문서의 힘입니다.

사용자 정의 스니펫을 생성하고 스니펫의 형식을 변경하고 호스트 이름과 같은 값을 사용자 정의할 수 있습니다. 자세한 내용은 Spring REST Docs 문서를 참조하세요.

스니펫 사용

생성된 코드 조각을 사용하려면 프로젝트에 일부 Asciidoctor 콘텐츠를 포함시킨 다음 빌드 시점에 코드 조각을 포함시켜야 합니다. 이 작업을 확인하려면 src/main/asciidoc/index.adoc이라는 새 파일을 만들고 원하는 대로 코드 조각을 포함하세요. 다음 예제(src/main/asciidoc/index.adoc에서 가져온)는 그 방법을 보여줍니다:

= Getting Started With Spring REST Docs

This is an example output for a service running at http://localhost:8080:

.request
include::{snippets}/home/http-request.adoc[]

.response
include::{snippets}/home/http-response.adoc[]

As you can see the format is very simple, and in fact you always get the same message.

이 Asciidoc 파일의 주요 특징은 Asciidoctor include 지시문을 사용하여 두 개의 코드조각을 포함한다는 것입니다(콜론과 후행 괄호는 구문 분석기가 해당 줄에서 특별한 작업을 수행하도록 지시합니다). 포함된 코드조각의 경로는 {스니펫}이라는 자리 표시자(Asciidoctor에서는 어트리뷰트)로 표현된다는 점에 유의하세요. 이 간단한 사례에서 다른 마크업은 상단의 =(레벨 1 섹션 제목)와 코드조각의 캡션(“요청” 및 “응답”) 앞에 있는 .뿐입니다. .`는 해당 줄의 텍스트를 캡션으로 바꿉니다.

그런 다음 빌드 구성에서 이 소스 파일을 선택한 문서 형식으로 처리해야 합니다. 예를 들어 Maven을 사용하여 HTML을 생성할 수 있습니다(./mvnw package를 수행하면 target/generated-docs가 생성됩니다). 다음 목록은 pom.xml 파일의 Asciidoc 부분을 보여줍니다:


Gradle을 사용하는 경우, ./gradlew asciidoctor를 실행하면 build/asciidoc이 생성됩니다. 다음 목록은 build.gradle 파일에서 Asciidoctor 관련 부분을 보여줍니다:

plugins {
	...
	id 'org.asciidoctor.convert' version '1.5.6'
}

...

asciidoctor {
    sourceDir 'src/main/asciidoc'
    attributes \
      'snippets': file('target/snippets')
}

Gradle에서 Asciidoctor 소스의 기본 위치는 src/doc/asciidoc입니다. 소스 디렉터리`를 Maven의 기본값과 일치하도록 설정했습니다.

요약

축하합니다! 방금 Spring 애플리케이션을 개발하고 Spring Restdocs를 사용하여 이를 문서화했습니다. 작성한 HTML 문서를 정적 웹사이트에 게시하거나 패키징하여 애플리케이션 자체에서 제공할 수 있습니다. 문서는 항상 최신 상태로 유지되며, 그렇지 않은 경우 테스트에서 빌드에 실패합니다.

참조 항목

다음 가이드도 도움이 될 것입니다:

새 가이드를 작성하거나 기존 가이드에 기여하고 싶으신가요? 기여 가이드라인을 확인하세요.

모든 가이드는 코드에 대해서는 ASLv2 라이선스를, 글에 대해서는 저작자표시-변경금지 크리에이티브 커먼즈 라이선스를 적용하여 공개됩니다.

댓글남기기