스프링 부트 활용

HttpMessageConverters

   HttpMessageConverters는 스프링 프레임워크에서 제공하는 인터페이스고, 스프링 MVC의 일부분이다. HTTP 요청 본문으로 들어오는 것을 객체로 변환하거나, 객체를 HTTP 본문 응답으로 변경할 때 사용한다.
주로 @RequestBody@ResponseBody와 함께 사용된다.

   어떤 데이터가 요청으로 들어올 때 요청의 본문에 데이터가 들어있고, 그것을 객체로 받고 싶다면 @RequestBody를 사용하여 데이터를 객체로 받을 수 있다. 스프링이 알아서 conversion해준다.

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

이런 경우에 HttpMessageConverter가 사용된다. 이 때 사용하는 HttpMessageConverters는 여러가지가 있고, 그 중에서 어떤 요청을 받았는지 또는 어떤 응답을 보내야 하는지에 따라 사용하는 HttpMessageConverters가 달라진다.

예를 들어 요청이 JSON 요청이고, JSON 본문이 들어왔다고 하자. 요청에는 ContentType이라는 헤더가 있는데, ContentType에 JSON이라고 적혀있고, 본문도 JSON이다. 그러면 JSONMessageConverters가 사용되어서 JSON 메세지를 user라는 객체로 컨버팅 해준다.
return을 할 때, user라는 객체를 return 할 것이다. http는 문자이기 때문에, 객체 자체를 response로 내보낼 수 없다. 이것을 변환하기 위해 HttpMessageConverters가 사용된다.

컴포지션 타입1일 경우에는 기본적으로 JsonMessageConverters가 사용된다. 일반 String 객체나 int 객체인 경우 int는 toString을 사용해서 문자열로 변환할 수 있기 때문에 StringMessageConverter가 사용된다.

참고로, @RestController가 붙어있으면 @ResponseBody를 생략할 수 있다.

@RestController
public class UserController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

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

}

일반 @Controller를 사용할 경우에는 @ResponseBody를 꼭 넣어줘야한다. 그래야 MessageConverter가 적용이 된다.
그러지 않는 경우 View Name Resolver, Bean name View Resolver를 사용해서 이름에 해당하는 뷰를 찾으려고 시도하게 될 것이다.

@Controller
public class UserController {

    @GetMapping("/hello")
    public @ResponseBody String hello() {
        return "hello";
    }

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

}

MessageConverter 활용 예제

   실제로 적용되는 MessageConverter를 활용해 보기 위해서는 컴포지션 객체를 만들어야 한다.. User를 생성하는 Controller를 만들어보자.

먼저 Test 코드를 작성한다.

@Test
public void createUser_JSON() throws Exception {
    String userJson = "{\"username\":\"grace\", \"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("grace"))))
            .andExpect(jsonPath("$.password",
                    is(equalTo("123"))));
}

그리고 이렇게 완성한 코드를 실행하면…짜잔! 당연히 에러가 뜬다. 아직 로직 코어를 구현하지 않았기 때문이다.
에러 코드를 읽어보면 404 에러가 뜬 것을 확인할 수 있는데, 요청을 처리하는 handler를 찾지 못했기 때문에 404에러가 뜬 것이다.

자, 그러면 UserController에 맵핑을 추가하고 다시 테스트 코드를 돌려보자.

package me.gracenam.demospringmvc.user;

import org.springframework.web.bind.annotation.*;

@RestController
public class UserController {

    @PostMapping("/users/create")
    public User create() {
        return null;
    }

}

이렇게 하면…이번에도 역시 에러가 뜬다. 좀 전과는 조금 다른 에러인데 status가 요청이 처리가 되었다는 200인데, 우리가 원하는 응답이 나오지는 않았다는 에러이다.

이제 User 객체를 만들어주는데 자바 Bean 규약에 의해 getter / setter가 있어야 한다.

package me.gracenam.demospringmvc.user;

public class User {

    private Long id;

    private String username;

    private String password;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

요청을 처리하는 handler(UserController)도 완성해주자. @RequestBody를 통해서 user를 가져와서 return해주도록 만든다.

package me.gracenam.demospringmvc.user;

import org.springframework.web.bind.annotation.*;

@RestController
public class UserController {

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

}

이제 테스트 코드를 다시 실행해보면 정상적으로 작동하는 것을 확인할 수 있다.


Reference


  1. 객체 안에 여러 개의 프로퍼티를 가질 수 있는 타입