ViewResolver, @ResponseBody, MessageConverter
웹 페이지(HTML) 반환과 웹 데이터(JSON) 반환
Spring MVC에서 Controller는 사용자의 요청을 처리한 뒤 ,그 결과를 반환한다. 이때 반환 타입에 따라 DispatcherServlet은 HTML 페이지를 렌더링하거나, 데이터를 JSON 형식으로 반환하는 등 다른 방식으로 응답을 처리할 수 있다.
- Spring에 위임되어 Controller 에서 처리한 모든 응답은 Front Controller에게 반환 타입을 고려해 반환
- Front Controller인 DistpatcherServlet은 앞선 Controller가 반환한 타입에 따라 View 생성
- DispatcherServlet은 HandlerAdapter의 응답으로 String, ModelAndView, Object 중 하나를 받는다.
- DispatcherServlet은 @ResponseBody 여부에 따라 ViewResolver 혹은 MessageConver를 실행한다.
1. 웹 페이지(HTML)를 반환 = View 반환 = ViewTemplate + Model: ViewResolver 사용
2. 웹 데이터(JSON)를 반환 = Model 반환 (@ResponseBody 명시): MessageConverter 사용
웹 페이지(HTML) 반환: View 반환
사용자에게 웹 페이지를 반환하는 방식은 주로 View를 반환하는 것이다. Spring에서는 ViewTemplate을 통해 HTML 파일을 렌더링하고, 이를 사용자에게 전달한다. 이 과정에서 DispatcherServlet은 ViewResolver를 통해 적절한 ViewTemplate을 찾아 렌더링 한다.
String 반환: 정적 페이지(ViewTemplate)
@Controller
public class SampleController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
return "hello"; // resources/templates/hello.html 파일을 반환
}
}
- String 반환 방식은 간단하게 ViewTemplate의 이름을 반환하는 것이다. 위 코드에서 hello는 resources/templates 폴더 내에 있는 hello.html 파일을 가르킨다.
- Spring의 ViewResolver는 이 ViewTemplate을 찾아 사용자의 요청에 맞게 HTML 페이지로 변환해 사용자에게 반환한다.
ModelAndView 반환: 동적 페이지(Viewtemplate + Model)
ModelAndView는 ViewTemplate와 함께 Model 데이터를 반환할 때 사용된다. 이 방식은 동적 웹페이지를 생성할 때 유용하다.
동적 페이지를 반환하는 방법은 총 3가지가 있다.
- ModelAndView (구현 클래스): ViewTemplate + Model의 역할
- ModelMap(구현 클래스): ViewTemplate 과 Model의 역할 분리
- Model(인터페이스): 다양한 구현 클래스 적용가능
이 총 3개의 반환 방법을 실습 코드를 보며 한개씩 파악해보자.
1. ModelAndView 사용 예제
ModelAndView는 Model 데이터와 ViewTemplate을 함께 관리할 수 있는 클래스다. 이 방식은 동적 데이터를 담아 페이지에 전달하기 유용하다.
package com.example.demo.controller;
import com.example.demo.service.User;
import com.example.demo.service.UserServiceInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
@RequestMapping("/users")
public class UserController {
@Autowired
private UserServiceInterface AUserService;
@GetMapping("")
public ModelAndView userPage(ModelAndView modelAndView) {
List<User> users = AUserService.findAll();
modelAndView.addObject("users", users); // Model에 유저 목록 추가
modelAndView.setViewName("/users/list"); // ViewTemplate 설정
return modelAndView;
}
@GetMapping("/1/detail")
public ModelAndView detailPage(ModelAndView modelAndView) {
User user = AUserService.findById(1);
modelAndView.addObject("id", user.getId());
modelAndView.addObject("name", user.getName());
modelAndView.addObject("age", user.getAge());
modelAndView.addObject("job", user.getJob());
modelAndView.addObject("specialty", user.getSpecialty());
modelAndView.setViewName("/users/detail");
return modelAndView;
}
}
- modelAndView.addObject("key,value): Model에 데이터를 추가하여 View에서 사용할 수 있게한다.
- modelAndView.setViewName("/users/list"): 반환할 ViewTemplate의 경로를 지정한다.
2. ModelMap 사용 예제
ModelMap은 Model 데이터를 View와 분리하여 처리할 수 있는 방식이다. 이 방식은 ModelAndView보다 코드가 간결하고, 데이터와 뷰를 명확하게 분리할 수 있다.
package com.example.demo.controller;
import com.example.demo.service.User;
import com.example.demo.service.UserServiceInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/users")
public class UserController {
@Autowired
private UserServiceInterface AUserService;
@GetMapping("")
public String userPage(ModelMap model) {
List<User> users = AUserService.findAll();
model.addAttribute("users", users); // Model에 유저 목록 추가
return "/users/list"; // ViewTemplate 반환
}
@GetMapping("/1/detail")
public String detailPage(ModelMap model) {
User user = AUserService.findById(1);
model.addAttribute("id", user.getId());
model.addAttribute("name", user.getName());
model.addAttribute("age", user.getAge());
model.addAttribute("job", user.getJob());
model.addAttribute("specialty", user.getSpecialty());
return "/users/detail"; // ViewTemplate 반환
}
}
- ModelMap은 Model 데이터를 처리하고, View를 단순 String으로 반환할 수 있는 방식이다.
- model.addAttribute("key",value): Model에 데이터를 추가한다
- return "/users/list": ViewTemplate 경로를 지정하여 반환한다.
3. Model 사용 예제
Model은 인터페이스로, ModelMap과 유사하게 Model 데이터를 처리하지만, 다양한 구현체를 지원해 더 유연하게 사용할 수 있다.
package com.example.demo.controller;
import com.example.demo.service.User;
import com.example.demo.service.UserServiceInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/users")
public class UserController {
@Autowired
private UserServiceInterface AUserService;
@GetMapping("")
public String userPage(Model model) {
List<User> users = AUserService.findAll();
model.addAttribute("users", users); // Model에 유저 목록 추가
return "/users/list"; // ViewTemplate 반환
}
@GetMapping("/1/detail")
public String detailPage(Model model) {
User user = AUserService.findById(1);
model.addAttribute("id", user.getId());
model.addAttribute("name", user.getName());
model.addAttribute("age", user.getAge());
model.addAttribute("job", user.getJob());
model.addAttribute("specialty", user.getSpecialty());
return "/users/detail"; // ViewTemplate 반환
}
}
- Model은 ModelMap과 유사하지만, 인터페이스로 정의되어 있다 다양한 상황에서 사용할 수 있다.
- model.addAttribute("key", value): Model에 데이터를 추가하여 ViewTemplate에서 사용할 수 있다.
- return "/users/list": ViewTemplate 경로를 반환한다.
세 가지 방식 비교 정리표
특징 | ModelAndView | ModelMap | Model |
Model 데이터 처리 | Model과 View를 함께 처리 | Model과 View를 분리 | Model과 View를 분리 |
View 반환 방식 | ViewTemplate 이름을 객체 내에서 설정 | ViewTemplate이름을 String으로 반환 | ViewTemplate 이름을 String으로 반환 |
사용 용도 | 동적 페이지 생성, 복잡한 데이터 처리시 | 간단한 데이터 및 페이지 처리 | 다양한 구현체 사용, 더 유현한 데이터 처리 |
반환 타입 | ModelAandView 객체 | String (ViewTemplate 이름) | String (ViewTemplate 이름) |
장점 | Model과 View를 한꺼번에 처리 가능 | 코드가 간결하고 가독성이 좋음 | 다양한 상황에서 유연하게 사용 가능 |
단점 | 객체 생성으로 코드가 길어짐 | 복잡한 데이터를 다루기엔 적합하지 않음 | - |
ViewResolver 역할
Spring에서 DistpatcherServlet이 String 또는 ModelAndView를 반환받으면, ViewResolver를 통해 적절한 ViewTemplate를 찾는다. ViewResolver는 일반적으로 Thymelefat, JSP 등 템플릿 엔진과 연동되어 HTML 파일을 렌더링한다.
- 만약 적절한 ViewTemplate을 찾지 못할 경우, 404 Not Fount 에러가 발생하게 된다.
- ViewResolver는 템플릿 경로와 파일 형식을 설정해 resources/templates 폴더 내에서 HTML 파일을 찾는다.
웹 데이터(JSON) 반환: @RequestBody와 MessageConverter
웹 애플리케이션에서 중요한 부분은 JSON 형식의 데이터를 반환하는 것이다. Spring에서는 @ResponseBody를 사용하여 Controller에서 반환된 데이터를 JSON 형식으로 변환해 클라이언트에게 전달 할 수 있다.
@ResponseBody 사용: JSON 데이터 반환
@Controller
public class SampleController {
@ResponseBody
@RequestMapping(value = "/user", method = RequestMethod.GET)
public User getUser() {
return new User("John Doe", 25);
}
}
- @ResponseBody 애노테이션을 사용하면 반환되는 User 객체가 ViewTemplate이 아닌 JSON 데이터로 변환된다.
- 이때 DispatcherServlet은 VIewResolver를 사용하지 않고, MessageConverter를 사용하여 Java 객체를 JSON으로 변환한다.
MessageConverter의 역할
Srping은 내부적으로 MessageConverrter를 사용해 Java 객체를 JSON, XML 등의 포맷을 변환한다.
MappingJackson2HttpMessageConverter는 주로 JSON 형식의 데이터를 처리하며, Spring에서 기본적으로 제공된다.
- MessageConverter는 객체를 직렬화(Serialization)하여, 네트워크를 통해 데이터를 전송할 수 있는 포맷으로 변환한다.
- 반대로, 클라이언트로부터 JSON 데이터를 Java 객체로 역직렬화(Deserialization)하는 역할도 수행한다.
@RestController 사용
@ResponseBody를 메서드마다 붙이는 대신, @RestController 애노테이션을 클래스에 붙이면 해당 클래스 내 모든 메서드는 자동으로 JSON 데이터를 반환하게 된다. RestController에서 Rest를 보면 알겠지만 REST API로써, REST API를 개발할 때 사용한다.
@RestController
public class UserController {
@RequestMapping(value = "/user", method = RequestMethod.GET)
public User getUser() {
return new User("John Doe", 25);
}
}
- RestController는 @Controller와 @ResponseBody의 결합형으로, 모든 메서드에서 JSON 데이터를 반환한다.
- 위 코드에서 /user 경로로 요청을 보내면 JSON 형식으로 유저 데이터를 반환하게 된다.
직렬화와 역직렬화의 개념
직렬화(Serialization)는 Java 객체르 JSON, XML 같은 문자열 포맷으로 변환하는 과정이고, 역직렬화(Deserialization)는 그 반대로 문자열 데이터를 Java 객체로 변환하는 과정이다. 이는 Spring에서 데이트럴 주고받을 때 중요한 개념이다.
public class User {
private String name;
private int age;
// getters and setters
}
- Response: Java 객체 → JSON 변환(직렬화)
- Request: JSON → Java 객체 변환(역직렬화)
ℹ️ 참고
[ASAC 6기 강의자료]
'💻DEV-STUDY > Spring' 카테고리의 다른 글
Spring Bean 원리과 의존성 주입 (1) | 2024.10.06 |
---|---|
[Spring]HttpMessageConverter (0) | 2024.10.06 |
[Spring] Spring Controller의 기본 동작 (0) | 2024.10.04 |
[Spring] Spring MVC 원리 (0) | 2024.10.04 |
[Spring] 페이지 반환 (0) | 2024.10.04 |