본문 바로가기

더 나은 엔지니어가 되기 위해/지금은 안쓰는 자바

[스프링 부트 개념과 활용] 웹 MVC 설정 1. MessageConverters

인프런에서 백기선님의 스프링부트 개념과 활용 강의를 듣고, 개인적으로 공부하며 핵심만 정리한 글입니다.

스프링 웹 MVC 설정

스프링부트는 자동설정을 통해 스프링 웹 MVC 설정들을 기본적으로 제공해준다.
그런데 이 중 일부를 수정하거나, 아니면 내가 원하는대로 완전히 바꾸고 싶을 땐 어떻게 해야할까.

다음과 같은 방법들이 있다.

  • 자동설정된 스프링 MVC를 확장하는 경우
    • @Configuration + WebMvcConfigurer
  • 아예 스프링 MVC를 원하는 대로 재정의 하는 경우
    • @Configuration + @EnableWebMvc

HttpMessageConverters

(HTTP 요청 -> 객체) 로 매핑 또는 (객체 -> HTTP 응답) 하는데 사용
예를 들면 다음과 같은 요청이

{
  "userName": "heumsi",
  "password": 123
}

다음과 같은 객체로 매핑이 되는 과정이다.

class User {
  private Long id;
  private String userName;
  private String password;
}

이를 스프링에서 사용하기 위해서는 @RequestBody , @ResponseBody 와 같은 어노테이션을 활용한다.
예를 들면 다음과 같다.

@Controller
public class UserController {

  @PostMapping("/user")
  public @ResponseBody User create(@RequestBody User user) {
    return user;
  }
}

@Controller 대신 @RestController 를 사용하면, @ResponseBody 를 붙이지 않아도, 이런 HttpMessageConvert 를 자동으로 해준다.
예를 들어 위 코드는 다음과 같다.

@RestController
public class UserController {

  @PostMapping("/user")
  public User create(@RequestBody User user) {
    return user;
  }
}

만약 @Controller 를 사용하는데 매핑 리턴 값으로 "heumsi" 와 같은 String 타입을 리턴하면, 이는 ViewNameResolver 가 heumsi 에 해당하는 View 를 찾으라는 의미가 된다.

@RestController 와 @Controller 의 차이

  • HTTP Response Body가 생성되는 방식이 다르다.
  • @Controller 는 View Page 를 반환하지만
  • @RestController 는 객체를 반환하기만 하면 application/json 타입의 HTTP Response 에 객체를 담아 반환한다.

출처 : https://wondongho.tistory.com/76

위 코드를 테스팅 코드로 작성하면 다음과 같다.

@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {

  @Autowired
  MockMvc mockMvc;

  @Test
  public void createUser_JSON() throws Exception{
    String userJson = "{\"username\":\"heumsi\", \"password\":\"123\"}";
    mockMvc.perform(post("/users/create")
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .accept(MediaType.APPLICATION_JSON_UTF8)
                    .content(userJson))
      .andExpect(status().isOk())
      .andExpect(jsonPath("$.username", is(equalTo("heumsi"))))
      .andExpect(jsonPath("$.password", is(equalTo("123"))));
  }
}

ContentNegotiatingViewResolver

서버로 들어오는 요청의 헤더에는 accept 라는 키가 있다.
이 값에는 클라이언트가 원하는 응답의 형태가 값으로 담기는데, 위 테스팅 코드를 살펴보면 APPLICATION_JSON_UTF8 가 보인다.

ContentNegotiatingViewResolver 는 이렇게 요청의 accept 에 맞춰 자동으로 응답을 변환시켜준다. 즉 위에서 application/json 타입으로 변환하는 작업을 한게 바로 이 ContentNegotiatingViewResolver 라는 것이다.

json 의 경우 클래스패스에 내장되어있는 기본 라이브러리를 통해 가능하지만, xml 의 경우, pom.xml 에 별도의 라이브러리를 추가해줘야 한다.

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-xml</artifactId>
  <version>2.9.6</version>
</dependency>

xmlaccept 로 두고, 테스트 코드를 작성하면 다음과 같다.

@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {

  @Autowired
  MockMvc mockMvc;

  @Test
  public void createUser_XML() throws Exception{
    String userJson = "{\"username\":\"heumsi\", \"password\":\"123\"}";
    mockMvc.perform(post("/users/create")
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .accept(MediaType.APPLICATION_XML)
                    .content(userJson))
      .andExpect(status().isOk())
      .andExpect(xpath("/User/username").string("heumsi"))
      .andExpect(xpath("/User/password").string("123"));
  }
}