본문 바로가기
springboot로 API만들어보기

[스프링부트로 API 만들기] Spring Rest Docs를 사용해서 API Docs 자동으로 생성하기

by alwaysone 2021. 1. 5.

이제 API를 만들고 검증까지 했다. 이제 테스 트롤 완료한 API들은 사용할 수 있는 상태이다.  하지만 아직 API를 사용할 사람들에게 어떻게 써야 하는지 알려줄 방법이 없다.

API Documents.

API Documents는 API 사용법을 알려주는 문서이다. 쉽게 말하면 매뉴얼이다. 공개된 API는 이런 문서들이 제공이 되어야 사용자들이 쓸 수 있기 때문에 필수로 작성해야 하는 문서이다. 

자동화 도구

하지만, API 스펙을 문서로 관리하는것은 상당히 귀찮다. API에 변경이 생겼을 때마다 찾아서 고쳐야 하며, 모두 실제로 한 번씩 확인해보며 작성을 해야 하기 때문이다. 그렇기 때문에 이런 문서화를 자동화할 수 있는 도구들이 생겼다. 그중 주로 사용되는 도구들은 SwaggerSpring Rest Docs 가 있다.

Swagger vs Spring Rest Docs

둘 다 문서를 작성할 때 매력적인 포인트가 있지만 비교해 보면 아래와 같다.

Swagger

  • 적용하기 쉽다
  • API를 테스트해볼 수 있다.
  • 코드에 어노테이션을 추가해야 한다.
  • 실제 코드와 맞지 않을 수 있다.

Spring Rest Docs

  • 코드를 건드리지 않아도 된다.
  • 테스트를 통과해야만 문서가 작성된다.
  • 테스트 코드에 작성할 것이 많다.

필자는 API Docs는 신뢰성이 매우 중요하다고 생각해, 테스트를 통과해야만 문서가 작성되는 Spring Rest Docs를 사용하는 편이다. 이번 게시글에서는 Spring Rest Docs를 사용해서 문서를 작성하는 법을 알아보자.

구현해보기

의존성 추가

plugins {
	id 'org.springframework.boot' version '2.4.1'
	id 'io.spring.dependency-management' version '1.0.10.RELEASE'
	id "org.asciidoctor.convert" version "1.5.9.2"  //  plugin 추가
	id 'java'
}

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

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

	//API Documents
	asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor'
	testCompile 'org.springframework.restdocs:spring-restdocs-mockmvc'
}

// test가 진행된 이후 문서 작성
asciidoctor {
	dependsOn test
}

// 작성된 문서를 서버에서 접근할 수 있도록 이동
bootJar {
	dependsOn asciidoctor
	from ("${asciidoctor.outputDir}/html5") {
		into 'static/docs'
	}
}

test {
	useJUnitPlatform()
}

주석이 달려있는 부분을 추가하면 된다.

dependsOn은 지정된 테스트가 수행되고 작업이 진행되도록 하는 설정이고,

from into는 파일을 복사하는 설정이다. asciidoctor가 생성해준 문서를 서버의 static디렉터리로 이동시켜 서버에서 해당 API Docs를 접속 가능하게 하는 설정이다.

RestDocs 환경설정하기

> common/RestDocsConfiguration.java

@TestConfiguration
public class RestDocsConfiguration {

  @Bean
  public RestDocsMockMvcConfigurationCustomizer restDocsMockMvcConfigurationCustomizer() {
    return configurer -> configurer.operationPreprocessors()
        .withRequestDefaults(prettyPrint())
        .withResponseDefaults(prettyPrint());
  }
}

문서에서 json의 가독성을 높이도록 하는 설정 파일이다.

basecontrollerTest에 추가하기

@Disabled
@Transactional
@SpringBootTest
@AutoConfigureMockMvc
@Import(RestDocsConfiguration.class)
@AutoConfigureRestDocs(uriScheme = "https", uriHost = "templet.restapi.com", uriPort = 443)
public class BaseControllerTest {

  @Autowired
  protected MockMvc mockMvc;

  @Autowired
  protected ObjectMapper objectMapper;

}

위의 설정 파일을 import 시켜주고, 문서에 표시될 baseURL을 지정해준다.

테스트 코드에 적용하기

    @Test
    @DisplayName("포스트 저장(성공)")
    void insertPostSuccess() throws Exception {
        // Given
        UpdatePostRequest updatePostRequest = new UpdatePostRequest("title", "body");
        // When
        ResultActions resultActions = this.mockMvc.perform(post("/posts/")
                .contentType(MediaType.APPLICATION_JSON)
                .content(this.objectMapper.writeValueAsString(updatePostRequest))
        );
        // Then
        resultActions.andExpect(status().isCreated())
                .andExpect(jsonPath("title").value("title"))
                .andExpect(jsonPath("body").value("body"))
                .andExpect(jsonPath("views").value(0))
                .andDo(document("insert-post",
                        requestFields(
                                fieldWithPath("title").type(JsonFieldType.STRING).description("포스트 제목"),
                                fieldWithPath("body").type(JsonFieldType.STRING).description("포스트 본문")
                        ),
                        responseFields(
                                fieldWithPath("title").type(JsonFieldType.STRING).description("포스트 제목"),
                                fieldWithPath("body").type(JsonFieldType.STRING).description("포스트 본문"),
                                fieldWithPath("views").type(JsonFieldType.NUMBER).description("조회수"))
                        )
                );
    }

테스트 코드의 Then 이후에 document를 위한 설정을 한다.

document의 첫 번째 매개변수는 문서화를 위해 작성되는 폴더의 이름을 지정해주는 것이다.

requestFields, responseFields는 request, response의 변수를 설명하는 옵션이다. 

테스트가 수행되었을 때 생성되는 파일

테스트가 완료되면 /build/generated-snippets에 위에서 지정한 document의 이름으로 위와 같은 파일이 생긴다. 이런 adoc문서들을 가지고 API문서를 매핑시키는 작업을 진행을 하면, 문서를 완성할 수 있다.

Asciidoc 작성하기

> src/docs/asciidoc/index.adoc 

= Post REST API Guide
Always0ne;
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 4
:sectlinks:
:operation-curl-request-title: Example request
:operation-http-response-title: Example response


[[insertPost]]
=== 게시글 작성
`Post` 요청을 사용해서 게시글을 작성 할 수 있다.

operation::insert-post[snippets='http-request,request-fields,http-response,response-fields']

위와 같이 adoc 문법으로 파일을 작성해주면 빌드를 할 때 문서가 생성하도록 할 수 있다. 위 부분은 메타데이터이며, 하단의 operation::{document에서 지정한 이름}[snippets='클라이언트에게 보여주고 싶은 문서']로 작성을 하면 된다.

빌드가 된 후 생긴 문서

현재까지의 패키지 상태

마무리

지금까지 API 문서를 만들었다. 게시글에서 보여준 것 이외에 많은 옵션들이 있으니 찾아서 시도해보는 걸 추천한다. 이제 배포를 위한 작업만 남았다. 다음장에서는 배포 준비를 위한 작업을 알아보자.

 

 

댓글