-
Spring Rest Docs 적용하기프로젝트/WebRTC 화상통화 프로젝트 2022. 7. 21. 00:01반응형
개요
프런트엔드와 원활하게 소통하기 위해 API 명세서를 만드려고 합니다.
이때 코드를 기반으로 API 명세서를 만들어주는 Swagger와 Spring RestDocs가 있습니다.
이 둘에 대해 비교해보고 RestDocs를 선택하게 되었습니다.
https://junuuu.tistory.com/318
API 문서화를 위한 Swagger와 Spring Rest Docs 비교
Swagger란? API 문서를 자동으로 만들어주는 라이브러리입니다. REST API를 편리하게 문서화해주고, 이를 통해 편리하게 API를 호출해보고 테스트할 수 있는 프로젝트입니다. 이를 활용하여 협업하는
junuuu.tistory.com
컨트롤러에 작성한 MockMvc 단위 테스트로 Spring Rest Docs를 만드는 법을 알아보겠습니다.
Spring Rest Docs 최소 요구사항
Java 8
Spring Framework 5
Spring Rest Docs 공식문서
Spring Rest Docs 기초지식
API에 대해 읽기 쉬운 문서를 자동으로 생성해주는 라이브러리입니다.
동작 과정을 요약하면 아래와 같습니다
테스트 코드를 돌리면 "스니펫"이라고 하는 문서 조각을 만들어줍니다.
이 조각을 모아서 문서로 만들기 때문에 저희는 스니펫들로 .adoc 파일을 생성해줍니다.
Asciidoctor를 이용하여 HTML을 생성합니다.
html로 변환 된 파일을 static 폴더로 이동시킵니다.
개발자가 해야하는 과정
RestDocs 관련 build.gradle 세팅하기
테스트 코드 작성하기
스니펫으로 .adoc 파일 만들어주기
1단계 build.gradle 세팅하기
plugins { id "org.asciidoctor.jvm.convert" version "3.3.2" } //asciidoctor의 플러그인으로 .aodc 파일을 .html으로 바꾸어서 특정 위치로 파일 이동시키기 위한 플러그인입니다. configurations { asciidoctorExt } dependencies { asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' //mockmvc를 기반으로 작성하기 때문에 spring-restdocs-mockmvc에 대한 의존성을 추가합니다. } ext { snippetsDir = file('build/generated-snippets') } //테스트 후 스니펫들이 들어갈 경로 test { outputs.dir snippetsDir } asciidoctor { inputs.dir snippetsDir configurations 'asciidoctorExt' dependsOn test } bootJar { dependsOn asciidoctor copy { from "${asciidoctor.outputDir}" into 'src/main/resources/static/docs' } } // 생성된 문서를 static/docs로 복사하는 과정입니다.2단계 테스트 코드 작성하기
@AutoConfigureRestDocs테스트할 컨트롤러에 Sring Rest Docs 자동 설정 어노테이션을 달아줍니다.
이후에 .andDo(document("스니펫 파일명")) 을 통해 스니펫들을 생성해줍니다.
@AutoConfigureRestDocs @WebMvcTest(OAuthController.class) class OAuthControllerTest { @Autowired private MockMvc mockMvc; @MockBean private OAuthService oAuthService; @Test void Oauth_로그인() throws Exception { //given DefaultOAuth2User user = makeDefaultOAuth2User(); Authentication authentication = new UsernamePasswordAuthenticationToken(user, "password", user.getAuthorities()); //when ResultActions resultActions = mockMvc.perform(get("/oauth/loginInfo").with(authentication(authentication))); //then resultActions.andExpect(status().isOk()) .andDo(document("login")); } private DefaultOAuth2User makeDefaultOAuth2User() { //{sub=107986715009708108628, provider=google, name=김준우, email=bababrll@naver.com} Map<String, Object> customAttribute = new LinkedHashMap<>(); customAttribute.put("sub", "107986715009708108628"); customAttribute.put("provider", "google"); customAttribute.put("name", "김준우"); customAttribute.put("email", "bababrll@naver.com"); return new DefaultOAuth2User( Collections.singleton(new SimpleGrantedAuthority("USER")), customAttribute, "sub"); } }테스트를 실행하면 login이라는 이름의 스니펫들이 만들어질 것으로 예상됩니다.
테스트 실행 후 build/generated-snippets에 adoc 파일들이 만들어집니다.

3단계 스니펫으로 .adoc 파일 만들기
adoc 파일 작성의 편의를 위해 AsciiDoc 플러그인을 설치합니다.
setting -> plugins -> AsciiDoc 다운로드
src/docs/asciidoc에 index.adoc파일을 생성합니다.

src/docs/asciidoc/index.adoc 그리고 index.adoc에 다음과 같이 작성합니다.
= REST Docs 문서 만들기 (글의 제목) 부제목(부제) :doctype: book :icons: font :source-highlighter: highlightjs // 문서에 표기되는 코드들의 하이라이팅을 highlightjs를 사용 :toc: left // toc (Table Of Contents)를 문서의 좌측에 두기 :toclevels: 2 :sectlinks: [[Member-API]] == Member API [[Member-로그인]] === 로그인 operation::login[snippets='http-request,path-parameters,http-response,response-fields']위에서 AsciiDoc 플러그인을 설치했다면 로컬에서 문서가 어떻게 구성되는지 확인할 수 있습니다.

Intellij의 하단에 Terminal로 이동해서 ./gradlew build 명령어를 수행합니다.

intellij의 하단 terminal으로 이동 
resources/static/docs/index.html 파일 생성 정상적으로 파일이 생성되었습니다.
이후 Application을 실행시키고 localhost:8080/docs/index.html으로 이동하면 다음과 같은 화면을 볼 수 있습니다.

이 정도까지가 튜토리얼정도로 이해하시면 될 것 같습니다.
기능 고도화
1. 응답 헤더 문서화
테스트하는 컨트롤러에서 4가지의 헤더를 반환하는 상황입니다.

컨트롤러에서 반환하는 헤더 4가지를 API 문서화 시킵니다.
.andDo(document("login", responseHeaders( headerWithName("accessToken").description("인증토큰"), headerWithName("refreshToken").description("갱신토큰"), headerWithName("accessTokenExpiration").description("인증토큰 만료일"), headerWithName("refreshTokenExpiration").description("갱신토큰 만료일") ) ));이후에 index.aodc으로 이동해서 response-headers를 추가합니다.
operation::login[snippets='custom,http-response,response-headers,response-fields']결과는 다음과 같습니다.

accessToken, refreshToken, accessTokenExpiration, refreshTokenExpiration의 헤더값의 정보가 HTTP 응답 메시지에 포함됩니다.
2. 응답 필드 문서화
응답되는 RESPONSE-BODY는 다음과 같습니다.
{ "message":"success", "data":{"name":"김준우","email":"bababrll@naver.com","provider":"naver","nickname":null} }컨트롤러가 반환하는 body를 문서화 시킵니다.
responseFields( fieldWithPath("message").description("응답 메시지"), fieldWithPath("data.name").description("사용자 이름"), fieldWithPath("data.email").description("사용자 이메일"), fieldWithPath("data.provider").description("로그인 제공사"), fieldWithPath("data.nickname").description("사용자 닉네임") )결과는 다음과 같습니다

3. 출력 예쁘게 하기
HTTP 응답 메시지에서 출력되는 데이터는 다음과 같이 일자로 나옵니다.

조금 더 보기 편하게 하기위해서 다음과 같은 코드를 입력하면 됩니다.
preprocessResponse(prettyPrint())이제 출력이 보기좋게 나옵니다.

4. 요청 필드 문서화
.andDo(document("set-nickname", preprocessRequest(prettyPrint()), requestFields( fieldWithPath("memberId").description("회원 아이디"), fieldWithPath("nickname").description("설정할 닉네임") ) ));
5. 요청 PathVariable 문서화
resultActions.andExpect(status().isOk()) .andDo(document("get-icons", pathParameters( parameterWithName("member-id").description("회원 아이디") ) ));'프로젝트 > WebRTC 화상통화 프로젝트' 카테고리의 다른 글
Controller 단위테스트 하기 (0) 2022.07.26 스프링 부트 + Mysql 도커로 띄우기 (0) 2022.07.21 Spring Security Oauth2.0 로그인 단위테스트 (0) 2022.07.20 Spring Security와 Oauth 2.0으로 로그인 구현하기(SpringBoot + React) (13) 2022.07.19 ERD 설계하기 (0) 2022.07.17