스프링 부트 웹 MVC 컨트롤러 구현
인텔리제이에서 Spring Initializr를 통해 Web을 선택하고 프로젝트를 생성하면 pom.xml에 다음과 같은 의존성들이 자동으로 추가되어 있을 것이다.
| 1 | <dependencies> | 
- 테스트 코드위의 get에서 빨간줄이 생긴다면 아래의 두 방법으로 대체할 수 있다.1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14(SpringRunner.class) 
 @WebMvcTest(UserController.class)
 public class UserControllerTest {
 
 MockMvc mockMvc; // 웹 MVC테스트를 만들 때 주로 사용, @WebMvcTest 어노테이션 사 자동으로 빈으로 만들어줌
 
 public void hello() throws Exception {
 mockMvc.perform(get("/hello"))
 .andExpect(status().isOk())
 .andExpect(content().string("hello"));
 }
1.
| 1 | mockMvc.perform(MockMvcRequestBuilders.get("/hello")) | 
2.
| 1 | MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/hello"); | 
메인 패키지 아래에 user패키지에 UserController를 생성
- 소스 코드 - 1 
 2
 3
 4
 5
 6
 7
 8
 public class DemospringmvcApplication {
 public static void main(String[] args) {
 SpringApplication.run(DemospringmvcApplication.class, args);
 }
 }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 public class UserController {
 // @ResponseBody가 생략되어 있음
 ("/hello")
 public String hello(){
 // 그냥 @Controller라면 @ResponseBody를 꼭 붙여주어야 함
 // 그래야 MessageConverter가 적용이 된다.
 // 그냥 @Controller인 경우에는 ViewResolver를 통 hello라는 이름에 해당하는 뷰를 찾으려 시도한다.
 // 하지만 @RestController가 붙어있으면 자동적으로 StringMessageConverter가 사용되어
 // HTTP 응답 본문에 들어가게 된다.
 return "hello";
 }
 }- 위와 같이 특별한 설정 없이 스프링 부트 MVC개발을 바로 시작할 수 있다. 
 스프링 부트에서 제공해주는 기본 설정 덕분 - (WebMvcAutoConfiguration)
- @RestController - @RestController 어노테이션은 스프링 4점대 버전부터 지원하는 어노테이션으로, 컨트롤러 클래스에 @RestController 만 붙이면 메서드에 @ResponseBody 어노테이션을 붙이지 않아도 문자열과 JSON 등을 전송할 수 있다. 뷰를 리턴하는 메서드들을 가지고 있는 @Controller와는 다르게 @RestController는 문자열, 객체 등을 리턴하는 메서드들을 가지고 있다.
 
HttpMessageConverters
- HTTP 요청 본문을 객체로 변경하거나, 객체를 HTTP 응답 본문으로 변경할 때 사용.- {“username”:”keesun”, “password”:”123”} <-> User- @ReuqestBody
- @ResponseBody
 
 
- {“username”:”keesun”, “password”:”123”} <-> User
- HttpMessageConverters는 스프링 프레임워크에서 제공하는 인터페이스며 스프링 MVC의 일부분
- 만일 HTTP에 content-type으로 JSON이 들어오면 (또한 본문도 JSON) JsonConverter로 바뀜
- content-type이 문자열이면 StringMessageConverter가 사용됨
- 테스트 코드 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28- (SpringRunner.class) 
 @WebMvcTest(UserController.class)
 public class UserControllerTest {
 
 MockMvc mockMvc;
 
 public void hello() throws Exception {
 mockMvc.perform(MockMvcRequestBuilders.get("/hello"))
 .andExpect(status().isOk())
 .andExpect(content().string("hello"));
 }
 // user를 생성하는 테스트
 
 public void createUser_JSON() throws Exception {
 String userJson = "{\"username\":\"kwon\", \"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("kwon"))))
 .andExpect(jsonPath("$.password", is(equalTo("123"))));
 }
 }
- 소스 코드 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31- 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;
 }
 }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 public class UserController {
 // @ResponseBody가 생략되어 있음
 ("/hello")
 public String hello(){
 // 그냥 @Controller라면 @ResponseBody를 꼭 붙여주어야 함
 // 그래야 MessageConverter가 적용이 된다.
 // 그냥 @Controller인 경우에는 ViewResolver를 통 hello라는 이름에 해당하는 뷰를 찾으려 시도한다.
 // 하지만 @RestController가 붙어있으면 자동적으로 StringMessageConverter가 사용되어
 // HTTP 응답 본문에 들어가게 된다.
 return "hello";
 }
 ("/users/create")
 public User create(@RequestBody User user){
 return user;
 }
 }
ViewResolver
- ViewResolver : 스프링에서 Controller에서 반환한 값(ModelAndView 혹은 Model)을 통해 뷰를 만드는 역할
- ContentNegotiatingViewResolver : 동일한 URI에서 HTTP Request에 있는 Content-type 및 Accept 헤더를 기준으로 다양한 Content-type으로 응답할 수 있게 하는 ViewResolver
응답을 XML로 바꾸어 보아도 정상적으로 동작한다. 단, XML로 내보내고 싶은 경우에는 다음의 의존성을 추가해야 한다.
| 1 | <dependency> | 
- 테스트 코드1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40(SpringRunner.class) 
 @WebMvcTest(UserController.class)
 public class UserControllerTest {
 
 MockMvc mockMvc;
 
 public void hello() throws Exception {
 mockMvc.perform(MockMvcRequestBuilders.get("/hello"))
 .andExpect(status().isOk())
 .andExpect(content().string("hello"));
 }
 // user를 생성하는 테스트
 
 public void createUser_JSON() throws Exception {
 String userJson = "{\"username\":\"kwon\", \"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("kwon"))))
 .andExpect(jsonPath("$.password", is(equalTo("123"))));
 }
 
 public void createUser_XML() throws Exception {
 String userJson = "{\"username\":\"kwon\", \"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("kwon"))
 .andExpect(xpath("/User/password").string("123"));
 }
 }
참조
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard
https://webcoding.tistory.com/entry/Spring-REST-API-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
