api서버에 rest docs를 추가하여 사용되는 api들을 호출하고 응답하는 값을 설정해주고 test code와 exception 처리까지 해보자! 우선 지금 포스트에서는 Rest Docs 적용까지 해보려고 한다.
rest docs 적용기 이전에 내가 직접 작성한 글인데 해당 글을 다시 참고하며 진행했다.
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
// Rest Docs (1) asciidoctor 추가
id "org.asciidoctor.jvm.convert" version "3.3.2"
}
group = 'com.juno'
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-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
// Querydsl
implementation 'com.querydsl:querydsl-jpa'
// Querydsl JPAAnnotationProcessor 사용 지정
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
// Querydsl java.lang.NoClassDefFoundError(javax.annotation.Entity) 발생 대응
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
// Querydsl java.lang.NoClassDefFoundError(javax.annotation.Generated) 발생 대응
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
// Rest Docs (2) mockMvc에서 restdocs를 사용할 수 있도록 추가
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
// Rest Docs (3) 빌드시 snippets 파일들이 저장될 저장소
ext {
snippetsDir = file('build/generated-snippets')
}
tasks.named('test') {
useJUnitPlatform()
// Rest Docs (4) test 실행 시 파일을 (3)에서 설정한 저장소에 출력하도록 설정
outputs.dir snippetsDir
}
// Rest Docs (5) asccidoctor 설정
asciidoctor {
dependsOn test
inputs.dir snippetsDir
}
// Rest Docs (6) asciidoctor가 실행될 때 docs 하위 파일 삭제
asciidoctor.doFirst {
delete file('src/main/resources/static/docs')
}
// Rest Docs (7) bootJar 시 asciidoctor 종속되고 build하위 스니펫츠 파일을 classes 하위로 복사
bootJar {
dependsOn asciidoctor
copy {
from "${asciidoctor.outputDir}"
into 'BOOT-INF/classes/static/docs'
}
}
// Rest Docs (8) from의 파일을 into로 복사
task copyDocument(type: Copy) {
dependsOn asciidoctor
from file("build/docs/asciidoc")
into file("src/main/resources/static/docs")
}
// Rest Docs (9) build 시 copyDocument 실행
build {
dependsOn copyDocument
}
clean {
delete file('src/main/generated') // 인텔리제이 Annotation processor 생성물 생성 위치
}
Rest Docs의 장점(?)은 api 문서에 작성되는 특징이 있어서 test code를 꼭 작성해야한다. 간단한 테스트를 위한 간단한 테스트 코드를 먼저 작성한 뒤 추후에 제대로 작성해보자.
@SpringBootTest
@AutoConfigureMockMvc //mock test를 위한 설정
@AutoConfigureRestDocs(uriHost = "jayeonapple.com") //rest docs 설정
@Transactional(readOnly = true)
class ItemControllerTest {
@Autowired
private MockMvc mock;
@Test
@DisplayName("임시 테스트")
void test() throws Exception{
//given
//when
ResultActions act = mock.perform(MockMvcRequestBuilders.get("/v1/items").contentType(MediaType.APPLICATION_JSON));
//then
act.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("200"));
//docs
act.andDo(document("test",
preprocessRequest(prettyPrint()), //request json 형식으로 이쁘게
preprocessResponse(prettyPrint()), //response json 형식으로 이쁘게
/*requestFields( //request param
),*/
responseFields( //response param
fieldWithPath("code").type(JsonFieldType.STRING).description("결과 코드"),
fieldWithPath("msg").type(JsonFieldType.STRING).description("메세지"),
fieldWithPath("data.[].idx").type(JsonFieldType.NUMBER).description("상품 IDX"),
fieldWithPath("data.[].name").type(JsonFieldType.STRING).description("상품 이름"),
fieldWithPath("data.[].price").type(JsonFieldType.NUMBER).description("상품 가격"),
fieldWithPath("data.[].options.[].idx").type(JsonFieldType.NUMBER).description("상품 옵션 IDX"),
fieldWithPath("data.[].options.[].kg").type(JsonFieldType.NUMBER).description("상품 무게 KG 값"),
fieldWithPath("data.[].options.[].name").type(JsonFieldType.STRING).description("상품 옵션 이름"),
fieldWithPath("data.[].options.[].price").type(JsonFieldType.NUMBER).description("상품 옵션 가격")
)
));
}
}
테스트 코드를 실행에 성공하면 다음과 같이 adoc 파일이 생성된다.
ifndef::snippets[]
:snippets: ./build/generated-snippets
endif::[]
= JAYEON API
:toc: left
:toclevels: 4
:toc-title: eats api
== API TEST
=== REQUEST
include::{snippets}/test/http-request.adoc[]
=== REQEUST FIELD
include::{snippets}/test/request-fields.adoc[]
=== RESPONSE
include::{snippets}/test/http-response.adoc[]
=== RESPONSE FIELD
include::{snippets}/test/response-fields.adoc[]
다음과 같이 파일을 작성해준다. 작성법은 구글에 검색하면 많이 나오니까 검색해서 사용해보면 좋다.
그 후 build를 통해 build를 하면
설정된 곳에 html 파일이 떨어진다. build를 하면 자동 생성되니 git으로 등록하는 일이 없도록 하자!
완료된 화면을 확인할 수 있다!