일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 참조키
- 포트
- AuthenticationEntryPoint
- subquery
- MySQL
- appspec
- 메세지수정
- foreignkey
- Query
- EC2
- appspec.yml
- 예약
- 외부키
- 메소드명
- ubuntu
- 테스트
- 검색
- querydsl
- 서브쿼리
- 네이티브쿼리
- ㅔㄴ션
- application.yml
- docker명령어
- WeNews
- 적용우선순위
- 추후정리
- 컨테이너실행
- 커밋메세지수정
- 2 > /dev/null
- 테스트메소드
Archives
- Today
- Total
제뉴어리의 모든것
[Section 3] [Spring MVC] API 문서화 본문
전체 항목
- Spring Rest Docs란?
- Spring Rest Docs 적용하기
Spring Rest Docs란?
작성된 Test의 내용을 토대로 API 문서를 자동으로 만들어 주는 Spring의 모듈 중 하나이다.
Spring Rest Docs 는 문서 작성 도구로 기본적으로 Asciidoctor 를 사용하며, 이것을 사용해 HTML 을 생성한다. 필요한 경우 Markdown 을 사용하도록 변경할 수 있다
위와 같은 흐름으로 진행된다.
- 테스트 코드를 실행하여 스니핏(snippets) 이라는 조각 문서(.adoc)를 만든다
- 스니핏을 기반으로 API 문서를 생성한다 (.adoc)
- API 문서를 HTML 로 변환한다.
레퍼런스 사이트:
https://spring.io/projects/spring-restdocs#learn
https://docs.spring.io/spring-restdocs/docs/current/reference/html5/
https://docs.spring.io/spring-restdocs/docs/current/api/
Spring Rest Docs 적용하기
- build.gradle 적용
plugins {
id 'org.springframework.boot' version '2.7.4'
id 'io.spring.dependency-management' version '1.0.14.RELEASE'
id 'java'
id "org.asciidoctor.jvm.convert" version "3.3.2" .adoc 파일 확장자를 가지는 AsciiDoc 문서를 생성해주는 Asciidoctor를 사용하기 위한 플러그인을 추가
}
group = 'kyh.toy'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
configurations {
asciidoctorExt
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//Auditing
implementation'org.springframework.boot:spring-boot-starter-web'
implementation'org.projectlombok:lombok'
implementation'org.springframework.boot:spring-boot-starter-data-jpa'
//h2
runtimeOnly 'com.h2database:h2'
//유효성 어노테이션
implementation 'org.springframework.boot:spring-boot-starter-validation'
//Json 맵퍼
implementation 'com.google.code.gson:gson:2.9.0'
//rest docs
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
//MapStruct
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}
//rest docs
ext {
snippetsDir = file('build/generated-snippets') //스니핏이 생성될 경로를 변수에 할당
}
tasks.named('test') { //spring boot test로 인해 원래 있는 test task
outputs.dir snippetsDir //rest docs, test task 작동할때 생성되는 스니핏의 저장 경로를 snippetsDir 에 저장된 경로로 설정하겠다
useJUnitPlatform()
}
asciidoctor { //실제 스니핏 asciidoc 문서를 만드는 task
configurations 'asciidoctorExt' //asciidoctor 작의 구성으로 configurations 을 사용하겠다.
inputs.dir snippetsDir //해당 작업의 input 디렉토리를 snippetsDir로 하겠다. 즉, test task의 결과물이 asciidoctor task의 인풋이다
dependsOn test //test task가 먼저 시작되고 해당 task가 돌아간다 (즉, 의존하겠다)
}
task copyDocument(type: Copy) {
dependsOn asciidoctor
println "asciidoctor output: ${asciidoctor.outputDir}"
from file("${asciidoctor.outputDir}")
into file("src/main/resources/static/docs")
}
bootJar {
dependsOn copyDocument //asciidoctor task가 먼저 시작되고 해당 task가 돌아간다 (즉, 의존하겠다)
from ("${asciidoctor.outputDir}") { //asciidoctor의 결과물을 jar 파일 안에 static/docs 경로에 복사한다.
into 'static/docs'
}
}
build {
dependsOn copyDocument
}
- 실제 문서를 생성하는 Test 코드
package kyh.toy.wisesaying.member;
import com.google.gson.Gson;
import kyh.toy.wisesaying.ControllerTestHelper;
import kyh.toy.wisesaying.member.controller.MemberController;
import kyh.toy.wisesaying.member.dto.MemberDto;
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.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import java.util.List;
import static kyh.toy.wisesaying.utils.apiDocumentUtils.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
//@ExtendWith({RestDocumentationExtension.class, SpringExtension.class}) //RestDocumentationExtension.class - 출력 디렉토리 자동 구성
@WebMvcTest(MemberController.class) // api 계층의 테스트에 필요한 빈들만 등록, 통합테스트 보다 가벼움
@MockBean(JpaMetamodelMappingContext.class) //Jpa 관련 빈들을 등록, 해당 애노테이션 없으면 현재 @EnableJpaAuditing 때문에 에러남. @EnableJpaAuditing이 @SpringBootApplication 와 같이 쓰여서 테스트시에도 활성화되는데, 그러면서 Jpa 관련 빈들을 찾기 때문.
@AutoConfigureRestDocs
public class MemberControllerRestDocsTests {
@Autowired
MockMvc mockMvc;
@Autowired
Gson gson;
String baseURI = "/api_v1/members";
@Test
void postMemberTest() throws Exception {
//given
MemberDto.Post post = new MemberDto.Post();
post.setEmail("january@gmail.com");
post.setName("멋쟁이");
post.setPhone("010-2222-3333");
post.setPassword("1234");
String content = gson.toJson(post);
//when
// 단순히 request 보내는 부분
ResultActions actions = mockMvc.perform(post(baseURI)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(content));
//~ 단순히 request 보내는 부분
//then
MvcResult mvcResult = actions.andExpect(status().isCreated())
.andExpect(jsonPath("$.data.email").value(post.getEmail()))
.andExpect(jsonPath("$.data.name").value(post.getName()))
.andExpect(jsonPath("$.data.phone").value(post.getPhone()))
.andDo(document("post-member", //실질적으로 문서를만드는 구문, document()
//request 관련 문서를 만들기전, response 과련 무서를 만들기 전 전처리기 추가
getRequestPreProcessor(), getResponsePreProcessor(),
requestFields( //문서에 입력되는 실제 필드 내용 정의
List.of(
fieldWithPath("email").type(JsonFieldType.STRING).description("이메일"),
fieldWithPath("name").type(JsonFieldType.STRING).description("이름"),
fieldWithPath("phone").type(JsonFieldType.STRING).description("휴대폰 번호"),
fieldWithPath("password").type(JsonFieldType.STRING).description("비밀 번호")
)
),
responseFields( //문서에 입력되는 실제 필드 내용 정의
List.of(
fieldWithPath("data").type(JsonFieldType.OBJECT).description("결과 데이터"),
fieldWithPath("data.memberId").type(JsonFieldType.NUMBER).description("회원 식별자"),
fieldWithPath("data.email").type(JsonFieldType.STRING).description("이메일"),
fieldWithPath("data.name").type(JsonFieldType.STRING).description("이름"),
fieldWithPath("data.phone").type(JsonFieldType.STRING).description("휴대폰 번호"),
fieldWithPath("data.cardList").type(JsonFieldType.ARRAY).description("카드 리스트").ignored()
)
))).andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
}
@Test
void patchMemberTest() {
}
}
'코드스테이츠 > 정리 블로깅' 카테고리의 다른 글
[Section4] [Cloud] 배포 자동화 - 1 (0) | 2022.10.18 |
---|---|
[Section3] section3 회고 (0) | 2022.10.18 |
[Section 3] [Spring MVC] 서비스 (비즈니스) 계층 (0) | 2022.10.17 |
[Section2] section2 회고 (0) | 2022.10.17 |
[Section2] 기술면접 (0) | 2022.10.17 |