[Spring Boot #11] 스프링 웹 MVC - HttpMessageConverters

 

 

참고 URL : https://docs.spring.io/spring/docs/5.0.7.RELEASE/spring-framework-reference/web.html#spring-web

| 웹 MVC 컨트롤러 구현

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @GetMapping
    // @ResponseBody가 생략되어 있는 것이다.
    public String hello(){
        // @RestController가 아닌 @Controller 어노테이션을 사용할 때는 
        // 아래 return되는 문자열은 ViewResolver를 통해 jsp파일을 찾는다.
        // 하지만 @RestController 어노테이션을 사용하면 자동적으로 StringMessageConverter가
        // 사용되어 HTTP응답 본문에 들어가게 된다.
        return "hello";
    }
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.run(args);
        //SpringApplication.run(Application.class, args);
    }
}

테스트코드

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

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

    @Autowired
    MockMvc mockMvc;

    @Test
    public void hello() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/hello"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("hello"));
    }
}
  • @RestController : 스프링4부터 기존 특정한 JSP를 만들어 내는 뷰를 반환하는 방식이 아닌 데이터 자체를 서비스하는 것을 지원하는 어노테이션이다. @RestController을 사용할 때는 @ResponseBody를 생략해도 HttpMessageConverter인터페이스를 통해 자동적으로 반환 객체를 HTTP 응답 본문으로 변환한다. 위의 코드는 StringMessageConverter가 사용되어 String 객체를 HTTP응답 본문으로 변환해주는 것이다.
  • @GetMapping : @RequestMapping(method = RequestMethod.GET)의 축약형 이다.

 

| HttpMessageConverters - @RequestBody 를 통한 HTTP 메세지와 객체 매핑

HTTP 요청 본문을 객체로 변환하거나, 객체를 HTTP 응답 본문으로 변경할 때 사용한다.

  • @RequestBody
  • @ResponseBody

테스트 코드

import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

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

    @Autowired
    MockMvc mockMvc;

    @Test
    public void hello() throws Exception {

        mockMvc.perform(MockMvcRequestBuilders.get("/hello"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("hello"));
    }

    //JSON 활용한 테스트
    @Test
    public void createUser_JSON() throws Exception {
        String userJson = "{\"username\":\"kyh\", \"password\":\"123\"}";
        mockMvc.perform(MockMvcRequestBuilders.post("/users/create")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .accept(MediaType.APPLICATION_JSON_UTF8)
                .content(userJson))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.username",
                        Matchers.is(Matchers.equalTo("kyh"))))
                .andExpect(MockMvcResultMatchers.jsonPath("$.password",
                        Matchers.is(Matchers.equalTo("123"))));

    }

    //XML활용한 테스트
    @Test
    public void createUser_XML() throws Exception {
        String userJson = "{\"username\":\"kyh\", \"password\":\"123\"}";
        mockMvc.perform(MockMvcRequestBuilders.post("/users/create")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .accept(MediaType.APPLICATION_XML)
                .content(userJson))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.xpath("/User/username").
                        string("kyh"))
                .andExpect(MockMvcResultMatchers.xpath("/User/password").string("123"));

    }
}
  • MediaType.[데이터형식] : 스프링3부터 지원한다. HTTP에 정의된 데이터 전달 형식을 나타내는 HTTP Response의 Content-type 리스트

  • jsonPath : https://github.com/json-path/JsonPath의 경로 규칙을 따라서 response-body의 json포맷에 접근할 수 있게 지원하는 메서드 이다.

소스코드

package com.kyhslam.spring.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;
    }
}
package com.kyhslam.spring.user;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {


    @GetMapping
    public String hello(){
        // @ResponseBody가 생략되어 있는 것이다.
        // @RestController가 아닌 @Controller 어노테이션을 사용할 때는 
        // 아래 return되는 문자열은 ViewResolver를 통해 jsp파일을 찾는다.
        // 하지만 @RestController 어노테이션을 사용하면 자동적으로 StringMessageConverter가
        // 사용되어 HTTP응답 본문에 들어가게 된다.
        return "hello";
    }

     @PostMapping("/users/create")
    public User create(@RequestBody User user){
        // HttpMessageConverter는 스프링 프레임워크에서 제공하는 인터페이스이다.


         return user;
     }

}
    • @PostMapping : @GetMapping과 유사하게 @RequestMapping(method = RequestMethod.POST) 의 축약형이다.

    • Controller 단에서 따로 json타입에 대한 정보를 명시하지 않아도 아래에서 설명할 ContentNegotiatingViewResolver를 통해 자동적으로 json형식으로 데이터를 반환하도록 스프링부트에서 제공을 한다. 이 ViewResolver는 Converter와 연관되어있어 Content-type을 기준으로 어떤 Converter를 쓸지 결정한다.

 

| ViewResolver : ContentNegotiatingViewResolver

  • ViewResolver : 스프링에서 Controller에서 반환한 값(ModelAndView 혹은 Model)을 통해 뷰를 만드느 역할을 한다.

  • ContentNegotiatingViewResolver : 동일한 URI에서 HTTP Request에 있는 Content-type 및 Accept 헤더를 기준으로 다양한 Content-type으로 응답할 수 있게하는 ViewResolver 이다.

아래를 보면 JSON 및 XML 형식의 다른 요청일 때도 잘 동작하는 걸 볼 수 있다.

import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

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

    @Autowired
    MockMvc mockMvc;

    @Test
    public void hello() throws Exception {

        mockMvc.perform(MockMvcRequestBuilders.get("/hello"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("hello"));
    }

    //JSON 활용한 테스트
    @Test
    public void createUser_JSON() throws Exception {
        String userJson = "{\"username\":\"kyh\", \"password\":\"123\"}";
        mockMvc.perform(MockMvcRequestBuilders.post("/users/create")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .accept(MediaType.APPLICATION_JSON_UTF8)
                .content(userJson))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.username",
                        Matchers.is(Matchers.equalTo("kyh"))))
                .andExpect(MockMvcResultMatchers.jsonPath("$.password",
                        Matchers.is(Matchers.equalTo("123"))));

    }

    //XML활용한 테스트
    @Test
    public void createUser_XML() throws Exception {
        String userJson = "{\"username\":\"kyh\", \"password\":\"123\"}";
        mockMvc.perform(MockMvcRequestBuilders.post("/users/create")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .accept(MediaType.APPLICATION_XML)
                .content(userJson))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.xpath("/User/username").
                        string("kyh"))
                .andExpect(MockMvcResultMatchers.xpath("/User/password").string("123"));

    }
}

댓글

Designed by JB FACTORY