반응형

[Spring Boot #13] 스프링 웹 MVC : HttpMessageConverter, ViewResolver

반응형

| 스프링 부트에서 스프링 웹 MVC 컨트롤러 구현


프로젝트 구조

| pom.xml
+---src
| +---main
| | +---java
| | | \---com
| | | \---tutorial
| | | \---sptringbootmvc
| | | | SptringBootMvcApplication.java
| | | |
| | | \---user
| | | UserController.java
| | |
| | \---resources
| | application.properties
| |
| \---test
| \---java
| \---com
| \---tutorial
| \---sptringbootmvc
| \---user
| UserControllerTest.java


테스트 코드

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

@Autowired
MockMvc mockMvc;

@Test
public void hello() throws Exception {
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("hello saelobi"));
}
}

참조글 : [Spring/Spring Boot] - [Spring Boot #12] 스프링부트에서 테스트 작성하기( Spring Boot Test )


소스 코드

@SpringBootApplication
public class SptringBootMvcApplication {

public static void main(String[] args) {
SpringApplication.run(SptringBootMvcApplication.class, args);
}

}
@RestController
public class UserController {

@GetMapping("/hello")
// @ResponseBody 가 생략이 되어 있음
public String hello(){
// @RestController가 아닌 @Controller가 어노테이션으로 붙어 있을 경우
// 아래 return되는 문자열은 ViewResolver를 통해 jsp 파일을 찾게 된다.
// 하지만 @RestController가 붙어있으면 자동적으로 StringMessageConverter가 사용되어
// HTTP 응답 본문에 들어가게 된다.
return "hello saelobi";
}
}
  • @RestController : 스프링 4부터 기존 특정한 JSP를 만들어 내는 뷰를 반환하는 방식이 아닌 데이터 자체를 서비스 하는 것을 지원하는 어노테이션. @ResponseBody을 생략해도 HttpMessageConverter 인터페이스를 통해 자동적으로 반환 객체를 HTTP 응답 본문으로 변환한다. 위 예시에서는 StringMessageConverter 구현체가 사용되어 String 객체를 HTTP 응답 본문으로 변환해준다.
  • @GetMapping : @RequestMapping(method=RequestMethod.GET)의 축약형


| @RequestBody 어노테이션을 통한 HTTP 메세지와 객체 매핑


프로젝트 구조

|   pom.xml
+---src
| +---main
| | +---java
| | | \---com
| | | \---tutorial
| | | \---sptringbootmvc
| | | | SptringBootMvcApplication.java
| | | |
| | | \---user
| | | User.java
| | | UserController.java
| | |
| | \---resources
| | application.properties
| |
| \---test
| \---java
| \---com
| \---tutorial
| \---sptringbootmvc
| \---user
| UserControllerTest.java


테스트 코드

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

@Autowired
MockMvc mockMvc;

@Test
public void hello() throws Exception {
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("hello saelobi"));
}

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

  • MediaType.[데이터_형식] : 스프링 3부터 지원. HTTP 명세에 정의된 데이터 전달 형식을 나타내는 HTTP Response의 Content-type 리스트
  • jsonPath : https://github.com/json-path/JsonPath 의 경로 규칙을 따라서 response-body의 json 포맷에 접근할 수 있게 지원하는 메서드


소스 코드

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;
}
}

@RestController
public class UserController {

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

// HttpMessageConverters는 스프링 프레임워크에서 제공하는 인터페이스며
// 스프링 MVC의 일부분
// HTTP 응답 본문을 객체로 변환하거나 그 반대로 변환
// 만일 HTTP에 content-type으로 JSON이 들어오면 (또한 본문도 JSON) JsonConverter로 바뀜
// content-type이 문자열이면 StringMessageConverter가 사용됨
@PostMapping("/users/create")
public User create(@RequestBody User user){
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) 형식이 다를 때도 잘 동작함.


테스트 코드

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

@Autowired
MockMvc mockMvc;

@Test
public void hello() throws Exception {
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("hello saelobi"));
}

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

@Test
public void createUser_XML() throws Exception {
String userJson = "{\"username\":\"saelobi\", \"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("saelobi"))
.andExpect(xpath("/User/password").string("123"));
}
}


참고자료 : https://www.inflearn.com/course/스프링부트


반응형

이 글을 공유하기

댓글

Designed by JB FACTORY