Today I Learned
소프트웨어를 구성하는 요소들을 Model, View, Controller로 구분하여 관심사 분리
사용자 인터페이스로부터 비즈니스 로직을 분리하여 애플리케이션의 시각적 요소나 그 이면에서 실행되는 비즈니스 로직을 서로 영향 없이 쉽게 유지보수 가능
1. 사용자가 Client(브라우저)를 통해 서버에 HTTP Request
2. 요청을 받은 Servlet 컨테이너가 HttpServletRequest, HttpServletResponse 객체(약속된 HTTP의 규격을 맞추면서 쉽게 HTTP에 담긴 데이터를 사용하기 위한 객체) 생성
3. 설정된 정보를 통해 어떠한 Servlet에 대한 요청인지 분석
4. 해당 Servlet에서 service 메서드 호출 -> 브라우저의 요청 Method에 따라 doGet 혹은 doPost 등의 메서드 호출
5. 호출한 메서드들의 결과를 그대로 반환 or 동적 페이지 생성 뒤 HttpServletResponse 객체에 응답을 담아 Client(브라우저)에 반환
6. 응답이 완료시 생성된 HttpServletRequest, HttpServletResponse 객체 소멸
Front Controller 사용 전과 후
코드의 중복과 누락을 막기 위해 적용
Spring은 DispatcherServlet을 사용하여 Front Controller 패턴 방식으로 API 요청을 효율적으로 처리
Front Controller 구조
1. Client(브라우저)에서 HTTP 요청이 들어오면 DispatcherServlet 객체가 요청을 분석
2. ispatcherServlet 객체가 분석한 데이터를 토대로 Handler mapping을 통해 Controller를 찾아 요청을 전달
3. Controller는 요청에 대한 처리를 완료 후 데이터('Model')와 'View' 정보 전달
4. ViewResolver 통해 View에 Model을 적용하여 View를 Client에게 응답으로 전달
해당 클래스가 Controller의 역할을 수행할 수 있도록 등록
@Controller
public class HelloController {
@GetMapping("/api/hello")
@ResponseBody
public String hello() {
return "Hello World!";
}
}
각각의 HTTP method에 해당하는 Mapping
@GetMapping("/api/get")
@ResponseBody
public String get() {
return "GET Method Request";
}
@PostMapping("/api/post")
@ResponseBody
public String post() {
return "POST Method Request";
}
@PutMapping("/api/put")
@ResponseBody
public String put() {
return "PUT Method Request";
}
@DeleteMapping("/api/delete")
@ResponseBody
public String delete() {
return "DELETE Method Request";
}
중복되는 URL 단축
@Controller
@RequestMapping("/api")
public class HelloController {
@GetMapping("/hello")
@ResponseBody
public String hello() {
return "Hello World!";
}
@GetMapping("/get")
@ResponseBody
public String get() {
return "GET Method 요청";
}
@PostMapping("/post")
@ResponseBody
public String post() {
return "POST Method 요청";
}
@PutMapping("/put")
@ResponseBody
public String put() {
return "PUT Method 요청";
}
@DeleteMapping("/delete")
@ResponseBody
public String delete() {
return "DELETE Method 요청";
}
}
thymeleaf를 주석처리 해줘야 Controller 이용 가능
1. Client 의 요청을 Controller에서 Model로 처리
(DB 조회가 필요하다면 DB 작업 후 처리한 데이터를 Model에 저장)
2. Template engine(Thymeleaf) 에게 View, Model 전달
3. Template engine이 View에 Model을 적용 → 동적 웹페이지 생성
4. Client(브라우저)에게 View(동적 웹 페이지, HTML)를 전달
thymeleaf
동적 페이지 처리를 위한 템플릿 엔진. 추가하면 자동으로 Controller에서 html 파일 찾는 경로를/resources/templates 로 설정.
Template engine 종류
타임리프(Thymeleaf), Groovy, FreeMarker, Jade, JSP 등
웹 생태계가 고도화 되는 과정중에 상대적으로 프론트엔드와 백엔드가 각각 따로 발전하게 되면서, 느슨하게 결합하는 방식을 더 많이 채택, 따라서 서버가 직접 뷰(html/css/js)를 반환하기 보다는 요청에 맞는 특정한 정보만 반환하는 것을 선호하게 됨으로써 데이터 교환 포맷 중 주로 JSON 형태로 데이터를 반환
// 반환값 String
@GetMapping("/response/json/string")
@ResponseBody
public String helloStringJson() {
return "{\"name\":\"Robbie\",\"age\":95}";
}
// 반환값 Java Class
@GetMapping("/response/json/class")
@ResponseBody
public Star helloClassJson() {
return new Star("Robbie", 95);
}
@RestController = @Controller + @ResponseBody
package com.sparta.springmvc.response;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/response/rest")
public class ResponseRestController {
// [Response header]
// Content-Type: text/html
// [Response body]
// {"name":"Robbie","age":95}
@GetMapping("/json/string")
public String helloStringJson() {
return "{\"name\":\"Robbie\",\"age\":95}";
}
// [Response header]
// Content-Type: application/json
// [Response body]
// {"name":"Robbie","age":95}
@GetMapping("/json/class")
public Star helloClassJson() {
return new Star("Robbie", 95);
}
}
@Test
@DisplayName("Object To JSON : get Method 필요")
void test1() throws JsonProcessingException {
Star star = new Star("Robbie", 95);
ObjectMapper objectMapper = new ObjectMapper(); // Jackson 라이브러리의 ObjectMapper
String json = objectMapper.writeValueAsString(star);
System.out.println("json = " + json);
}
@Test
@DisplayName("JSON To Object : 기본 생성자 & (get OR set) Method 필요")
void test2() throws JsonProcessingException {
String json = "{\"name\":\"Robbie\",\"age\":95}"; // JSON 타입의 String
ObjectMapper objectMapper = new ObjectMapper(); // Jackson 라이브러리의 ObjectMapper
Star star = objectMapper.readValue(json, Star.class);
System.out.println("star.getName() = " + star.getName());
}
GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95
// [Request sample]
// POST http://localhost:8080/hello/request/form/model
// Header
// Content type: application/x-www-form-urlencoded
// Body
// name=Robbie&age=95
@PostMapping("/form/model")
@ResponseBody
public String helloRequestBodyForm(@ModelAttribute Star star) {
return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
}
GET http://localhost:8080/hello/request/form/param/model?name=Robbie&age=95
// [Request sample]
// GET http://localhost:8080/hello/request/form/param/model?name=Robbie&age=95
@GetMapping("/form/param/model")
@ResponseBody
public String helloRequestParam(@ModelAttribute Star star) {
return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
}
// [Request sample]
// POST http://localhost:8080/hello/request/form/json
// Header
// Content type: application/json
// Body
// {"name":"Robbie","age":"95"}
@PostMapping("/form/json")
@ResponseBody
public String helloPostRequestJson(@RequestBody Star star) {
return String.format("Hello, @RequestBody.<br> (name = %s, age = %d) ", star.name, star.age);
}
@ModelAttribute와 @RequestBody의 차이
https://tecoble.techcourse.co.kr/post/2021-05-11-requestbody-modelattribute/
Spring에서 @RequestParam과 @ModelAttribute 구분하는 방법
Spring은 해당 파라미터(매개변수)가 SimpleValueType이라면 @RequestParam으로 간주하고 아니라면 @ModelAttribute가 생략되어있다 판단