-
Spring Rest Docs 적용하기프로젝트/WebRTC 화상통화 프로젝트 2022. 7. 21. 00:01
개요
프런트엔드와 원활하게 소통하기 위해 API 명세서를 만드려고 합니다.
이때 코드를 기반으로 API 명세서를 만들어주는 Swagger와 Spring RestDocs가 있습니다.
이 둘에 대해 비교해보고 RestDocs를 선택하게 되었습니다.
https://junuuu.tistory.com/318
컨트롤러에 작성한 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파일을 생성합니다.
그리고 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 명령어를 수행합니다.
정상적으로 파일이 생성되었습니다.
이후 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