Java/Spring으로 API를 개발하는 사람들에게 API 문서 자동화라고 한다면 보통 두개를 말한다. Swagger 와 Spring Rest Docs 이다. 둘은 아래와 같은 특징들을 가진다.
Swagger : API 를 테스트 해볼 수 있는 화면을 제공하고, 적용하기 쉽다.
Spring Rest Docs : 제품코드에 영향이 없고, 테스트를 통과해야만 문서가 제대로 나온다.
글에서 다뤄볼 내용은 Spring Rest Docs 이다. Spring Rest Docs 란 테스트 코드를 기반으로 자동으로 API 문서를 작성할 수 있게 도와주는 프레임워크이다. 따라서 테스트 코드를 강제한다. 또한 해당 테스트를 통과하지 못하면 문서는 정상적으로 생성되지 않는다.
그렇다면 프로젝트를 생성해서 간단하게 한번 만들어보자.
우선 프로젝트를 하나 생성했다.
plugins {
id 'org.springframework.boot' version '2.6.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
// AsciiDoc 파일을 컨버팅 후 Build 폴더에 복사하기 위한 플러그인
id "org.asciidoctor.jvm.convert" version "3.3.2"
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
ext {
set('snippetsDir', file("build/generated-snippets"))
}
dependencies {
implementation 'org.modelmapper:modelmapper:2.1.1'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
annotationProcessor 'org.projectlombok:lombok'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// Spring-Rest-Docs를 사용하기 위해 라이브러리 추가
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
test {
outputs.dir snippetsDir
useJUnitPlatform()
}
// gradle 빌드시 test -> asciidoctor 순으로 실행된다.
asciidoctor {
inputs.dir snippetsDir
dependsOn test
}
// gradle 빌드시 asciidoctor -> copyDocument 순으로 실행된다.
task copyDocument(type: Copy) {
dependsOn asciidoctor
from file("build/docs/asciidoc")
into file("src/main/resources/static/docs")
}
// gradle 빌드시 copyDocument를 실행한다.
build {
dependsOn copyDocument
}
테스트를 위해 아래와 같이 간단한 컨트롤러를 생성했다.
@RequestMapping(value = {"/posts"}, produces = MediaType.APPLICATION_JSON_VALUE)
@RestController
public class PostsController {
@GetMapping(value = "")
public ResponseEntity<List<PostsDto>> getPosts() {
List<PostsDto> response = Arrays.asList(
new PostsDto((long)1, "TEST TITLE(1)", "TEST CONTENT(1)", "TEST AUTHOR(1)"),
new PostsDto((long)1, "TEST TITLE(2)", "TEST CONTENT(2)", "TEST AUTHOR(2)"),
new PostsDto((long)1, "TEST TITLE(3)", "TEST CONTENT(3)", "TEST AUTHOR(3)"),
new PostsDto((long)1, "TEST TITLE(4)", "TEST CONTENT(4)", "TEST AUTHOR(4)")
);
return ResponseEntity.ok()
.body(response);
}
}
테스트 코드를 작성해보자
@SpringBootTest
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
@AutoConfigureMockMvc
class SpringRestDocsExampleApplicationTests {
@Autowired
private MockMvc mockMvc;
@BeforeEach
public void before(
WebApplicationContext context,
RestDocumentationContextProvider provider) {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(documentationConfiguration(provider))
.addFilter(new CharacterEncodingFilter("UTF-8", true))
.alwaysDo(print())
.build();
}
@Test
void 컨트롤러_호출_후_Asciidoc_생성_테스트() throws Exception{
ResultActions result = this.mockMvc
.perform(get("/posts").accept(MediaType.APPLICATION_JSON))
.andDo(print());
result
.andExpect(status().isOk())
.andDo(document("posts",
responseFields(
fieldWithPath("[].id").description("글 ID"),
fieldWithPath("[].title").description("글 제목"),
fieldWithPath("[].content").description("글 본문"),
fieldWithPath("[].author").description("글 작성자")
)
));
}
}