My Melody Is Cute Spring MVC Spring Rest Docs
본문 바로가기

개발공부🌷/Spring MVC

Spring MVC Spring Rest Docs

API 문서화(Documentation)

클라이언트가 REST API 백엔드 어플리케이션에

요청을 전송하기 위해서 알아야 되는 요청정보를 문서로 잘 정리 하는 것

 

(개발자가 직접 작성하거나 애플리케이션 빌드로 API 문서 자동생성 가능)

-> 수기로 직접 작성은 비효율적임

 

더보기

REST API 

HTTP 프로토콜을 통해 API를 설계하기 위한 아키텍처 스타일

CRUD 기능을 가짐

 


Spring Rest Docs VS Swagger

 

Swagger의 API 문서화 방식

애너테이션을 클래스마다 일일히 추가해야함

 가독성 및 유지 보수성이 떨어짐

API 스펙정보를 문자열로 입력하는경우가 많아서 API문서와 API 코드간의 불일치 문제가 발생할 수 있음

포스트맨처럼 API요청 툴의 기능을 사용할 수 있음

 

Spring Rest Docs의 API 문서화 방식

애플리케이션의 API 스펙정보와 API 문서정보의 불일치 문제 방지 가능

( 테스트 케이스에서 전송하는 API 문서정보와

Controller에서 구현한 Request Body,Response Body, Query Parameter 등의 정보가 하나라도 일치하지 않으면 

테스트 케이스의 실행이 되지않고 문서가 생성되지 않음 )

-> 테스트 케이스를 일일히 작성해주고 테스트가 passed 되어야 한다

 


Spring Rest Docs란??

REST API 문서를 자동으로 생성해 주는 Spring 하위 프로젝트

Controller의 슬라이스 테스트를 통해 테스트가 통과 되어야지 API문서가 정상적으로 만들어짐

 

🍀Spring Rest Docs의 API 문서 생성 흐름

 

 

 

1. 테스트 코드 작성

 

- 슬라이스 테스트 코드 작성

 

- API 스펙 정보 코드 작성 슬라이스 테스트 코드 다음에

Controller에 정의되어 있는 API 스펙정보를 코드로 작성

 

2. test 테스크(task) 실행

 

- 작성된 슬라이스 테스트 코드를 실행

(Gradle의 빌드 태스크(task)중 하나인 test task를 실행시켜서 스니펫을 일괄생성)

 

- 테스트 실행결과가 passed이면 다음작업 진행, failed이면 다시 테스트케이스 수정

 

3. API 문서 스니펫(.adoc 파일) 생성

 

- 테스트 케이스의 테스트 실행 결과가 passed이면 테스트 코드에 포함된 API 스펙 정보 코드를 기반으로

API 문서 스니펫이 .adoc 확장자를 가진 파일로 생성됨

 

더보기

✨스니펫(snippet)은 코드의 일부조각을 의미하는 경우가 많지만 여기서는 문서의 일부 조각을 의미

테스트 케이스 하나 당 하나의 스니핏이 생성

여러개의 스니핏을 모아서 하나의 API문서 생성 가능

 

4. API문서생성

 

5.API문서를 HTML로 변환

- HTML로 변환된 API문서는 HTML파일자체 공유 가능, URL을 통해 해당 HTML에 접속해서

확인할 수 있음

 


🌟Spring Rest Docs 기본 설정

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) .adoc 파일 확장자를 가지는 AsciiDoc 문서를 생성해주는 Asciidoctor 사용하기 위한 플러그인 추가
	id 'java'
}

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

repositories {
	mavenCentral()
}

// (2) ext 변수의 set() 메서드를 이용해서, API 문서 스니핏이 생성될 경로 지정
ext {
	set('snippetsDir', file("build/generated-snippets"))
}

// (3) AsciiDoctor에서 사용되는 의존 그룹 지정
// (:asciidoctor task가 실행되면 내부적으로 아래에서 지정한 ‘asciidoctorExtensions’라는 그룹을 지정)
configurations {
	asciidoctorExtensions
}

dependencies {
       // (4) spring-restdocs-core / spring-restdocs-mockmvc 의존 라이브러리가 추가
	testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
  
  // (5) spring-restdocs-asciidoctor 의존 라이브러리 추가
  // ((3)에서 지정한 asciidoctorExtensions 그룹에 의존 라이브러리가 포함됩)
	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) test task 실행 시, API 문서 생성 스니핏 디렉토리 경로 설정
tasks.named('test') {
	outputs.dir snippetsDir
	useJUnitPlatform()
}

// (7)  :asciidoctor task 실행 시, Asciidoctor 기능 사용을 위해 :asciidoctor task에 asciidoctorExtensions 설정
tasks.named('asciidoctor') {
	configurations "asciidoctorExtensions"
	inputs.dir snippetsDir
	dependsOn test
}

// (8) :build task 실행 전에 실행되는 task
// :copyDocument task가 수행되면 index.html 파일이 "src/main/resources/static/docs" 에 copy됨
// copy된 index.html 파일은 API 문서를 파일 형태로 **외부에 제공하기 위한 용도**로 사용
task copyDocument(type: Copy) {
	dependsOn asciidoctor
    // (8-1) :asciidoctor task가 실행된 후에 task가 실행 되도록 의존성 설정
	from file("${asciidoctor.outputDir}")
    // (8-2) "build/docs/asciidoc/" 경로에 생성되는 index.html을 copy
	into file("src/main/resources/static/docs")
    // (8-3) "src/main/resources/static/docs" 경로로 index.html을 추가
}

build {
	dependsOn copyDocument
    // (9) :build task가 실행되기 전에 :copyDocument task가 먼저 수행 되도록 함
}

// (10) 애플리케이션 실행 파일이 생성하는 :bootJar task 설정
// ( jar 파일에 포함해서 웹 브라우저에서 API 문서를 확인하기 위한 용도 )
bootJar {
	dependsOn copyDocument
    // (10-1) :bootJar task 실행 전에 :copyDocument task가 실행 되도록 의존성 설정
	from ("${asciidoctor.outputDir}") {
    // (10-2) Asciidoctor 실행으로 생성되는 index.html 파일을 jar 파일 안에 추가
		into 'static/docs'
        // (10-3) jar 파일에 index.html을 추가해 줌으로써 웹 브라우저에서 접속(http://localhost:8080/docs/index.html) 후, API 문서를 확인 가능
	}
}

 

🌟API 문서 스니펫을 사용하기 위한 템플릿(source파일) 생성

API 문서 스니핏이 생성 되었을때 이것을 사용해서 최종 API문서로 만들어주는

템플릿 문서(index.adoc)을 생성하는 것

 

✅ Gradle 기반 프로젝트에서는 src/docs/asciidoc/ 경로에 해당하는 디렉토리 생성

src/docs/asciidoc/ 디렉토리 내에 비어있는 템플릿 문서(index.adoc) 생성하기

(스피닛을 모아서 html로 변환할 최종 API문서를 작성할 용도)

 

🚩Controller를 테스트 할 테스트 케이스 작성하고 해당 API 스펙 정보를 테스트 케이스에 추가해주면 API 문서 스니핏을 생성할 수 있다

 


 

🌟API 문서생성을 위한 테스트 케이스 기본 구조

@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 문서 스펙 정보 추가
                 ));
    }
}

.andDo(document(...)); 이후가 API문서에 대한 정보

(1) @WebMvpTest(MemberController.claa)

Controller테스트를 위한 애너테이션 (@SpringBootTest는 사용하지 않음)

괄호 안에는 테스트 대상 Controller클래스 지정

 

(2) @MockBean(JpaMetamodelMappingContext.class)

JPA에서 사용하는 Bean들을 Mock객체로 주입해주는 설정

 

@EnableJpaAuditing // 추가됨
@SpringBootApplication
public class Section3Week3RestDocsApplication {
	public static void main(String[] args) {
		SpringApplication.run(Section3Week3RestDocsApplication.class, args);
	}
}

Spring Boot 기반 테스트는 최상위 패키지 경로의
~~Application 클래스에서 실행되는데 이 클래스에 @EnableJpaAuditing이 추가됨
(JPA Bean관련 생성일자,수정일자등 감사기능 활성화)

🌟추가하게 되면 JPA 웹계층과 관련된 Bean들만 필요로 해서
@WebMvcTest애너테이션을 추가해서 테스트를 진행할때
JpaMetamodelMappingContextMock객체로 대체해서 주입해주어야 함

 

(3)@AutoConfigureRestDocs를 추가

Spring Rest Docs에 대한 자동 구성을 위한 애너테이션

 

(10) API 문서를 자동 생성하기위한 Controller 핸들러 메서드의 API 스펙정보를 document(...)에 추가해줌

 

document(...) 메서드 : 스펙정보를 받아서 API문서를 생성하기 위해 실질적인 문서화 작업을 수행하는 RestDocumentationResultHandler클래스에서 핵심기능을 하는 메서드( Spring Rest Docs에서 지원하는 메서드)

andDo(...) 메서드 : andExpect()처럼 어떤 검증작업을 하는것이 아님 일반적인 동작을 정의하고자 할 때 사용

 

 

🌟 @SpringBootTest vs @WebMvcTest

@SpringBootTest 애너테이션

@AutoConfigureMockMvc와 함께 사용

프로젝트에서 사용하는 전체 Bean을 ApplicationContext에 등록해서 사용

- 편리하지만 실행속도가 느림

🚩데이터 베이스까지 요청 프로세스가 이어지는 통합 테스트에 사용

 

@WebMvcTest 애너테이션

Controller 테스트에 필요한 Bean만 ApplicationContext에 등록하여 사용

- 실행속도가 빠르지만 Controller에 의존하고 있는 객체가 있다면

해당 객체를 Mock객체를 사용해서 의존성을 일일이 제거해 줘야 함

🚩Controller를 위한 슬라이스 테스트에 주로 사용

 

 

requestFields(...) : 문서로 표현될 request body, 거기에 포함된 데이터는  List<FieldDescriptor>의 원소인 FieldDescriptor 의 객체

 

responseFields(...) : 문서로 표현될 response body, 거기에 포함된 데이터는 List<FieldDescriptor>의 원소인 FieldDescriptor 의 객체

'개발공부🌷 > Spring MVC' 카테고리의 다른 글

Spring MVC 테스팅(Testing)3  (0) 2023.12.07
Spring MVC 테스팅(Testing) 2  (0) 2023.12.06
Spring MVC 테스팅(Testing)  (0) 2023.12.06
Spring MVC 트랜잭션(Transaction)  (0) 2023.12.05
Spring MVC JPA 2  (0) 2023.11.28