똑같은 삽질은 2번 하지 말자

Spring Boot 개념다지기 No.9(Spring-Boot-Devtools,Spring Web MVC) 본문

Spring/Spring Boot

Spring Boot 개념다지기 No.9(Spring-Boot-Devtools,Spring Web MVC)

곽빵 2020. 4. 29. 15:40

Spring-Boot-Devtools

  • 캐시 설정을 개발 환경에 맞게 변경.(캐시를 끔 보통)
  • 클래스패스에 있는 파일이 변경 될 때마다 자동으로 재시작.
    • 직접 껐다 켜는거 (cold starts)보다 빠르다. 왜?  
    • 릴로딩 보다는 느리다. (JRebel 같은건 아님)
    • 리스타트 하고 싶지 않은 리소스는? spring.devtools.restart.exclude
    • 리스타트 기능 끄려면? spring.devtools.restart.enabled = false
  • 라이브 릴로드? 리스타트 했을 때 브라우저 자동 리프레시 하는 기능
    • 브라우저 플러그인 설치해야 함. 
    • 라이브 릴로드 서버 끄려면? spring.devtools.liveload.enabled = false
  • 글로벌 설정
    • ~/.spring-boot-devtools.properties
  • 리모트 애플리케이션 -> 이건 쓰지말자..

 

Spring Web MVC

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

 

Web on Servlet Stack

This part of the reference documentation covers support for Servlet stack, WebSocket messaging that includes raw WebSocket interactions, WebSocket emulation via SockJS, and pub-sub messaging via STOMP as a sub-protocol over WebSocket. 4.1. Introduction The

docs.spring.io

음..정말 많은 기능들이 있다.

일단 한번 Maven을 살펴보면

이렇게 많은 패키지들이 있고 그 안에 들어가면 내용이 너무나도 방대하다..

Spring MVC로 프로젝트를 한번 한적있는데, 음 그때도 뭐 딱히 내가 설정해준것들은 없지만

DispatcherServlet 커스텀하고 viewresolver건들고 이정도?

이렇게 많은 내용을 알아보기엔 무리고 차근차근 하나씩 기능들을 사용해보면서 배워보자

 

HttpMessageConverters

Interface이며 Spring Web MVC의 일부분이다.

HTTP요청 본문으로 들어오는걸 객체객체HTTP응답 본문으로 하는 역할을 한다.

 

요청본문의 contentType을 보고 타입이 JSON이면 JSONConverter을 사용한다.

그럼 테스트를 통해 한번 해보자.

 

우선 UserController

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 // RestController 일땐 반환형식 @ResponseBody가 자동으로 붙어
				// HTTP응답본문 (문자열)로 반환이 되는데,
// @Controller  // 여기처럼 Controller일때는 해당 문자열의 view 파일을 찾아 글로 간다. 
public class UserController {
	@GetMapping("/hello")
	public String hello() {
		return "hello heewon";
	}
	@PostMapping("/users/create")
	public User create(@RequestBody User user) {
		return user;
	}
}

RestController 는 @ResponseBody 쓸 필요없는거 인지하자 (주석 참조)

 

다음은 User Class

public class User { // JavaBean 규약에 따라서 Binding하는데 Getter Setter
					// 가 있어야 Binding 된다.
	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;
	}
}

 

그리고 테스트 코드

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

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 com.example.gwak.controller.UserController;

@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {
	@Autowired
	MockMvc mockMvc;

	@Test
	public void createUser_JSON() throws Exception{
		String userJson = "{\"username\":\"heewon\", \"password\":\"1234\"}";
		mockMvc.perform(post("/users/create") // 나는 Post방식 /users/create 요청한다.
						.contentType(MediaType.APPLICATION_JSON_UTF8) // 이건 JSON 타입이야
						.accept(MediaType.APPLICATION_JSON_UTF8) // JSON으로 받고 싶어
						.content(userJson)) // 요청 내용
					.andExpect(status().isOk())
					.andExpect(jsonPath("$.username", is(equalTo("heewon"))))
					.andExpect(jsonPath("$.password", is(equalTo("1234"))));
	}
}

 

ViewResolver

messageConverter와 더불어 사용자가 요청한 이러한 타입(Accept Header)으로 받고싶어~~

했을때 ViewResolver의 contents negotiation으로 조정해준다.

밑의 예는 위에와 다르게 xml으로 받길 원하는 테스트 코드

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath;

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 com.example.gwak.controller.UserController;

@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {
	@Autowired
	MockMvc mockMvc;

	@Test
	public void createUser_XML() throws Exception{
		String userJson = "{\"username\":\"heewon\", \"password\":\"1234\"}";
		mockMvc.perform(post("/users/create") // 나는 Post방식 /users/create 요청한다.
							.contentType(MediaType.APPLICATION_JSON_UTF8) // 이건 JSON 타입이야
							.accept(MediaType.APPLICATION_XML) // JSON으로 받고 싶어
							.content(userJson)) // 요청 내용
						.andExpect(status().isOk())
						.andExpect(xpath("/User/username")
									.string("heewon"))
						.andExpect(xpath("/User/password")
									.string("1234"));
	}
}

 

하지만 이런 에러가 뜰것인데 이건, 이런 타입을 찾을수 없다는건데

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

이 XML 메시지 컨버터를 추가하면 된다.

Comments