개발 공부 기록하기/04. Spring & Spring Boot

Spring REST DOCS를 Spring Boot에 적용하기 (2)

lannstark 2019. 7. 25. 23:23

본 포스팅에서는 Spring Boot에 적용된 Spring REST Docs를 보다 잘 활용하는 방법에 대해 기술합니다!

적용하는 방법은 여기를 참고해 주세요!

request, response 변경하기

기본적으로 나오는 http-request.adochttp-response.adoc에서 한 두가지 정도를 변경해보자

 

JSON을 더 보기 좋게

기본적으로 나오는 json은 한 줄로 취급된다. 하지만 이것을 조금 더 이쁘게 출력하고 싶다면 prettyPrint()를 사용하면 된다.

mockMvc.perform(get("/api/v1/data"))
  .andDo(print())
  .andExpect(status().isOk())
  .andDo(document("signup"),
    preprocessRequest(prettyPrint()),
    preprocessResponse(prettyPrint()));

BEFORE

AFTER

* 주의 : 스프링 부트가 static 파일을 자체 캐싱해두기 때문에 *.adoc 파일을 변경하지 않으면 코드를 바꿨다 하더라도 바뀐 스니펫이 적용되지 않을 수 있다!

 

URI 변경

modifyUris를 사용하면 request 및 response에 있는 URI를 변경할 수 있다.

mockMvc.perform(get("/api/v1/data"))
  .andDo(print())
  .andExpect(status().isOk())
  .andDo(document("signup"),
    preprocessRequest(modifyUris()
      .scheme("https")
      .host("www.lannstark.com")
      .removePort(), prettyPrint()),
    preprocessResponse(prettyPrint()));

BEFORE

AFTER

Host가 바꼈고, port가 더이상 보이지 않는다.
하지만 https가 적용되지는 않았다! (뇌피셜: 실제 SSL 설정이 끝나야 적용될 수도 있을 듯 하다)

보다 자세한 내용은 여기를 참고하면 좋다!

 

추가 스니펫

다양한 형태의 API 인풋과 아웃풋에 대한 docu 작성 방법을 알아보자

쿼리 파라미터

쿼리 파라미터 http://localhost/api/v1?name=이름

아래와 같이 작성하면 된다

mockMvc.perform(get("/api/v1/owner?name=" + name))
  ...
  .andDo(document("somewhere",
  requestParameters(
    parameterWithName("name").description("조회될 이름")
  )));

path 파리미터

path 파리미터 http://localhost/api/이름

아래와 같이 작성하면 된다

mockMvc.perfrom(RestDocumentationRequestBuilders.get("/api/v1/owner/{namne}", name))
  ...
  .andDo(document("somewhere",
  pathParameters(
    parameterWithName("name").description("조회될 이름")
  )));

* 특이사항 : RestDocumentationRequestBuilders의 method를 사용해야 한다.

JSON request

HTTP body에 JSON 형식으로 데이터를 넣는
아래와 같이 작성하면 된다

mockMvc.perform(post("/"))
  ...
  .andDo(document("somewhere", 
    requestFields(
    fieldWithPath("email").type(JsonFieldType.STRING).description("이메일"),
    fieldWithPath("password").type(JsonFieldType.STRING).description("비밀번호")
  )));

JSON response 설명

아래와 같이 작성하면 된다

mockMvc.perform(get("/api/v1/owner?name=" + name))
  ...
  .andDo(document("somewhere",
  requestParameters(
    parameterWithName("name").description("조회될 이름")
  ),
  responseFields(
    fieldWithPath("score").type(JsonFieldType.NUMBER).description("점수")
  )));

 

특이사항

만약 배열을 반환하고 있다면 이와 같은 식으로 작성하면 된다.

{
  [
    {
      score: 150
    },
    {
      score: 100
    }
  ]
}
fieldWithPath("[].score").type(JsonFieldType.NUMBER).description("각각의 점수")

배열에 이름이 있다면

{
  scoreArray: [
    {
      score: 150
    },
    {
      score: 100
    }
  ]
}
fieldWithPath("scoreArray.[].score").type(JsonFieldType.NUMBER).description("각각의 점수")

로 처리하면 된다.

 

스니펫 커스터마이징

이렇게 생긴 스니펫 포맷을 커스터마이징 할 수도 있다. 이전 포스팅과 이번 포스팅의 여기까지를 잘 이해하고 있으면 너무나 잘 설명하고 있는 이 글을 보며 금방 따라할 수 있을 것이다. 따로 포스팅을 작성하지는 않을 계획이다.

 

주의할 점

Rest docs와 관련하여 삽질하다가 알아낸건데, 만약 API가 객체를 반환하지 않는데 responseFields를 사용한다면 ClassCastException가 계속 나올 것이다!

예를 들어

@RequestMappin(value = "", method = RequestMethod.GET)
public boolean amISuccess() {
  return true;
}

이런 API가 있고 이 API를 대상으로

mockMvc.perform(get("/"))
  .andDo(print())
  .andExpect(status().isOk())
  .andDo(document("test", responseFields(
    fieldWithPath("success").type(JsonFieldType.STRING).description("성공 실패 여부")
  )));

와 같은 코드를 작성한다면 ClassCastException만 보게 될 것이다... (2019년 7월 24일 최신 버전 기준)

해결 방법은 API가 객체를 반환하게 해야 한다

 

* 추가로, String 객체도 안된다

String 객체를 반환하는 API에 responseFields를 설정하려고 하면
content as it could not be parsed as JSON or XML만 보게 될 것이다.