API 문서화(Documentation)
API 문서화는 애플리케이션 프로그래밍 인터페이스(API)를 개발자들이 쉽게 이해하고 사용할 수 있도록 설명하고 문서화하는 과정입니다. 이는 개발자들이 API를 올바르게 활용하고 문제를 해결하는데 도움이 되며, 개발 프로세스에서 매우 중요한 부분입니다.
백엔드 애플리케이션, REST API 방식의 애플리케이션에서 의미하는 API 문서화란 클라이언트가 REST API 백엔드 애플리케이션에 요청을 전송하기 위해서 알아야 되는 요청 정보(요청 URL(또는 URI), request body, query parmeter 등)를 문서로 잘 정리한 것을 의미합니다.
이렇게 작성된 API 문서는 API의 기능과 목적을 간략하게 설명하여 개발자가 API의 사용 시나리오를 파악할 수 있도록 돕습니다. 또한, API의 엔트포인트(URI)와 지원하는 HTTP 메서드를 나열하여 개발자가 API에 대한 요청을 어떻게 보낼 수 있는지 알 수 있도록 합니다.
API 문서 생성의 자동화
API 문서생성의 자동화가 필요한 5가지 이유를 알아보겠습니다.
1. 효율성
- 수작업으로 API 문서를 작성하면 많은 시간과 노력이 소요됩니다.
- API의 기능이나 매개변수 등이 변경될 때마다 수동으로 문서를 업데이트해야 합니다.
- 자동화된 문서 생성은 API의 구조와 정보를 추출하여 자동으로 문서를 생성하기 때문에 작업 시간을 절약하고 개발자들이 업데이트된 정보에 접근할 수 있도록 합니다.
2. 일관성
- 수작업으로 작성한 문서는 사람마다 스타일과 구성이 달라질 수 있습니다.
- 자동화된 문서 생성은 일관된 형식과 구조를 유지할 수 있으며, 개발자들이 일관성 있는 정보를 얻을 수 있습니다.
3. 정확성
- 수작업으로 작성한 문서는 인간의 실수로 인해 오류가 발생할 수 있습니다.
- 자동화된 문서 생성은 API에서 직접 정보를 추출하므로 정확성을 보장할 수 있습니다.
4. 실시간 업데이트
- API 문서는 변화하는 API와 항상 동기화되어야 합니다.
- 자동화된 문서 생성은 API의 변경 사항을 실시간으로 반영할 수 있으며, 개발자들이 항상 최신 정보에 액세스할 수 있습니다.
5. 개발자 경험 향상
- 자동화된 문서 생성은 개발자들이 API를 쉽게 이해하고 사용할 수 있도록 돕습니다.
- 문서의 일관성과 정확성은 개발자들이 API를 더 빠르게 통합하고 문제를 해결하는 데 도움이 됩니다.
이처럼 자동화된 API 문서 생성은 개발자들의 생산성과 협업을 향상시키는 데 큰 도움을 줍니다. 업데이트와 유지보수가 용이하며, 정확하고 일관된 정보를 제공하여 개발자들이 API를 보다 효과적으로 활용할 수 있도록 합니다.
Spring Rest Docs vs Swagger
Spring Rest Docs와 Swagger는 둘 다 API 문서화를 지원하는 도구라는 공통점을 가지고있다.
API문서화를 지원하는 도구지만 서로 다른 문서화 방식과 특징을 가지고 있다.
1. Spring Rest Docs
- Spring Rest Docs는 테스트 기반의 API 문서화 도구이다.
- 애플리케이션 코드에 문서화를 위한 정보들이 포함되지 않는다.
- 테스트 케이스의 실행이 “passed”여야 API 문서가 생성된다.
- 테스트 케이스를 반드시 작성해야 된다.
- API 툴로써의 기능은 제공하지 않는다.
2. Swagger
- Swagger는 애터네이션 기반의 API 문서화 도구이다.
- 애플리케이션 코드에 문서화를 위한 애너테이션들이 포함된다.
- 가독성 및 유지 보수성이 떨어진다.
- API 문서와 API 코드 간의 정보 불일치 문제가 발생할 수 있다.
- API 툴로써의 기능을 활용할 수 있다.
간단히 말하면, Spring Rest Docs는 테스트 주도의 방식으로 API 문서를 생성하고 Swagger는 API 설계와 문서 작성, 인터랙티브한 문서 제공 등을 통합적으로 지원하는 프레임워크입니다. 선택은 프로젝트 요구사항과 개발자 선호도에 따라 달라집니다.
Spring Rest Docs
Spring Rest Docs는 REST API 문서를 자동으로 생성해 주는 Spring 하위 프로젝트입니다.
Spring Rest Docs는 테스트 코드를 기반으로 API 문서를 생성하여 정확하고 일관성 있는 문서를 유지할 수 있게 해주는 도구입니다.
Spring Rest Docs의 가장 큰 특징은 Controller의 슬라이스 테스트를 통해 테스트가 통과되어야지만 API 문서가 정상적으로 만들어진다는 것입니다.
아래 그림을 통해 Spring Rest Docs의 API 문서 생성 흐름을 알아보겠습니다.
- 테스트 코드 작성
- 슬라이스 테스트 코드 작성
ⅰ. Controller에 대한 슬라이스 테스트 코드를 먼저 작성 - API 스펙 정보 코드 작성
ⅰ. 슬라이스 테스트 코드 다음에 Controller에 정의 되어 있는 API 스펙 정보(Request Body, Response Body, Query Parameter 등)를 코드로 작성.
- 슬라이스 테스트 코드 작성
- test 태스크(task) 실행
- 작성된 슬라이스 테스트 코드를 실행
ⅰ. 일반적으로 Gradle의 빌드 태스크(task)중 하나인 test task를 실행 시켜서 API 문서 스니핏(snippet)을 일괄 생성 - 테스트 실행 결과가 “passed”이면 다음 작업을 진행하고, “failed”이면 문제를 해결하기 위해 테스트 케이스를 수정한 후, 다시 테스트를 진행해야 한다.
- 작성된 슬라이스 테스트 코드를 실행
- API 문서 스니핏(.adoc 파일) 생성
- 테스트 케이스의 테스트 실행 결과가 “passed”이면 테스트 코드에 포함된 API 스펙 정보 코드를 기반으로 API 문서 스니핏이 .adoc 확장자를 가진 파일로 생성됩니다.
스니핏(snippet)은 일반적으로 코드의 일부 조각을 의미한다.
테스트 케이스 하나 당 하나의 스니핏이 생성되며, 여러개의 스니핏을 모아서 하나의 API 문서를 생성한다.
- API 문서 생성
- API 문서를 HTML로 변환
- HTML로 변환된 API 문서는 HTML 파일 자체를 공유할 수도 있고, URL을 통해 해당 HTML에 접속해서 확인가능
Spring Rest Docs 설정
Spring Rest Docs가 API 문서 생성 작업을 정상적으로 수행할 수 있도록 기본적인 설정 작업을 먼저 해 주어야 합니다.
✔ build.gradle 설정
plugins {
id 'org.springframework.boot' version '2.7.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id "org.asciidoctor.jvm.convert" version "3.3.2" // (1)
id 'java'
}
group = 'com.codestates'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
// (2)
ext {
set('snippetsDir', file("build/generated-snippets"))
}
// (3)
configurations {
asciidoctorExtensions
}
dependencies {
// (4)
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
// (5)
asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.mapstruct:mapstruct:1.5.1.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'com.google.code.gson:gson'
}
// (6)
tasks.named('test') {
outputs.dir snippetsDir
useJUnitPlatform()
}
// (7)
tasks.named('asciidoctor') {
configurations "asciidoctorExtensions"
inputs.dir snippetsDir
dependsOn test
}
// (8)
task copyDocument(type: Copy) {
dependsOn asciidoctor // (8-1)
from file("${asciidoctor.outputDir}") // (8-2)
into file("src/main/resources/static/docs") // (8-3)
}
build {
dependsOn copyDocument // (9)
}
// (10)
bootJar {
dependsOn copyDocument // (10-1)
from ("${asciidoctor.outputDir}") { // (10-2)
into 'static/docs' // (10-3)
}
}
- (1)에서는 .adoc 파일 확장자를 가지는 AsciiDoc 문서를 생성해 주는 Asciidoctor를 사용하기 위한 플러그인을 추가합니다.
- (2)에서는 ext 변수의 set() 메서드를 이용해서 API 문서 스니펫이 생성될 경로를 지정합니다.
- (3)에서는 AsciiDoctor에서 사용되는 의존 그룹을 지정하고 있습니다. :asciidoctor task가 실행되면 내부적으로 (3)에서 지정한 ‘asciidoctorExtensions’라는 그룹을 지정합니다.
- (4)에서 'org.springframework.restdocs:spring-restdocs-mockmvc'를 추가함으로써 spring-restdocs-core와 spring-restdocs-mockmvc 의존 라이브러리가 추가됩니다.
- (5)에서 spring-restdocs-asciidoctor 의존 라이브러리를 추가합니다. (3)에서 지정한 asciidoctorExtensions 그룹에 의존 라이브러리가 포함이 됩니다.
- (6)에서는 :test task 실행 시, API 문서 생성 스니펫 디렉토리 경로를 설정합니다.
- (7)에서는 :asciidoctor task 실행 시, Asciidoctor 기능을 사용하기 위해 :asciidoctor task에 asciidoctorExtensions을 설정합니다.
- (8)은 :build task 실행 전에 실행되는 task입니다. :copyDocument task가 수행되면 index.html 파일이 src/main/resources/static/docs 에 copy 되며, copy 된 index.html 파일은 API 문서를 파일 형태로 외부에 제공하기 위한 용도로 사용할 수 있습니다.
- (8-1)에서는 :asciidoctor task가 실행된 후에 task가 실행되도록 의존성을 설정합니다.
- (8-2)에서는 "build/docs/asciidoc/" 경로에 생성되는 index.html을 copy한 후,
- (8-3)의 "src/main/resources/static/docs" 경로로 index.html을 추가해 줍니다.
- (9)에서는 :build task가 실행되기 전에 :copyDocument task가 먼저 수행되도록 합니다.
- (10)에서는 애플리케이션 실행 파일이 생성하는 :bootJar task 설정입니다.
- (10-1)에서는 :bootJar task 실행 전에 :copyDocument task가 실행되도록 의존성을 설정합니다.
- (10-2)와 (10-3)에서는 Asciidoctor 실행으로 생성되는 index.html 파일을 jar 파일 안에 추가해 줍니다. jar 파일에 index.html을 추가해 줌으로써 웹 브라우저에서 접속(http://localhost:8080/docs/index.html) 후, API 문서를 확인할 수 있습니다.
Controller 테스트 케이스에 Spring RestDocs 적용하기
이제 Controller에 대한 테스트 케이스를 작성하고, API 문서화를 위한 API 스펙 정보를 테스트 케이스에 추가해 봅시다.
✔ API 문서 생성을 위한 테스트 케이스 기본 구조
package com.codestates.restdocs.member;
import com.codestates.member.controller.MemberController;
import com.codestates.member.mapper.MemberMapper;
import com.codestates.member.service.MemberService;
import com.google.gson.Gson;
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.boot.test.mock.mockito.MockBean;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
@WebMvcTest(MemberController.class) // (1)
@MockBean(JpaMetamodelMappingContext.class) // (2)
@AutoConfigureRestDocs // (3)
public class MemberControllerRestDocsTest {
@Autowired
private MockMvc mockMvc; // (4)
@MockBean
// (5) 테스트 대상 Controller 클래스가 의존하는 객체를 Mock Bean 객체로 주입받기
@Test
public void postMemberTest() throws Exception {
// given
// (6) 테스트 데이터
// (7) Mock 객체를 이용한 Stubbing
// when
ResultActions actions =
mockMvc.perform(
// (8) request 전송
);
// then
actions
.andExpect(// (9) response에 대한 기대 값 검증)
.andDo(document(
// (10) API 문서 스펙 정보 추가
));
}
}
위 예시 코드는 Spring Rest Docs를 이용해 API 문서를 생성하기 위한 테스트 케이스의 기본 구조입니다.
- (1)에서는 @SpringBootTest 애너테이션을 사용하지 않고, @WebMvcTest 애너테이션을 사용했습니다.
- @WebMvcTest 애너테이션은 Controller를 테스트하기 위한 전용 애너테이션입니다.
- @WebMvcTest 애너테이션의 괄호 안에는 테스트 대상 Controller 클래스를 지정합니다.
- (2)는 JPA에서 사용하는 Bean 들을 Mock 객체로 주입해 주는 설정입니다.
- Spring Boot 기반의 테스트는 항상 최상위 패키지 경로에 있는 xxxxxxxApplication 클래스를 찾아서 실행합니다.
- (3)에서는 Spring Rest Docs에 대한 자동 구성을 위해 @AutoConfigureRestDocs를 추가해 줍니다.
- (4)에서 MockMvc 객체를 주입받습니다.
- (5)에서는 Controller 클래스가 의존하는 객체(주로 서비스 클래스, Mapper)의 의존성을 제거하기 위해 @MockBean 애너테이션을 사용해서 Mock 객체를 주입받습니다.
- (6)에서는 HTTP request에 필요한 request body나 query parmeter, path variable 등의 데이터를 추가합니다.
- (7)에서는 (5)에서 주입받은 Mock 객체가 동작하도록 Mockito에서 지원하는 given() 등의 메서드로 Stubbing 해 줍니다.
- (8)에서는 MockMvc의 perform() 메서드로 request를 전송합니다. MockMvc의 perform() 메서드는 슬라이스 테스트에서 여러분이 사용했던 방법과 동일합니다.
- (9)에서는 response를 검증합니다. ResultActions의 .andExpect() 역시 슬라이스 테스트에서 사용했던 방법과 동일하게 검증을 진행하면 됩니다.
- (10)에서 테스트 수행 이후, API 문서를 자동 생성하기 위한 해당 Controller 핸들러 메서드의 API 스펙 정보를 document(…)에 추가해 줍니다..andDo(…) 메서드는 andExpect()처럼 어떤 검증 작업을 하는 것이 아니라 일반적인 동작을 정의하고자 할 때 사용됩니다. document(…) 메서드는 API 문서를 생성하기 위해 Spring Rest Docs에서 지원하는 메서드입니다.