리다이렉트 할 때 기본적으로 Model에 들어있는 primitive type 데이터는 URI 쿼리 매개변수에 추가된다.
원하는 값만 리다이렉트 할 때 전달하고 싶다면 RedirectAttributes에 명시적으로 추가할 수 있다.
리다이렉트 요청을 처리하는 곳에서 쿼리 매개변수를 @RequestParam 또는 @ModelAttribute로 받을 수 있다.
리다이렉트 할 때 기본적으로 Model에 들어있는 primitive type 데이터는 URI 쿼리 매개변수에 추가된다.
@Controller
@SessionAttributes("event")
public class SampleController {
@GetMapping("/events/form/name")
public String eventsFormName(Model model) {
model.addAttribute("event", new Event());
// 자동으로 세션에 넣어줌
return "events/form-name";
}
@PostMapping("/events/form/name")
public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form-name";
}
return "redirect:/events/form/limit";
}
@GetMapping("/events/form/limit")
public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
model.addAttribute("event", event);
// 자동으로 세션에 넣어줌
return "events/form-limit";
}
@PostMapping("/events/form/limit")
public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, Model model){
if(bindingResult.hasErrors()){
return "/events/form-limit";
}
sessionStatus.setComplete();
// 데이터베이스에서 save 했다고 가정
model.addAttribute("name", event.getname());
model.addAttribute("limit", event.getLimit());
// String과 Integer는 redirect할 때 쿼리 파라미터에 붙는다.
// 스프링 MVC는 이 기능을 지원하나 스프링 부트는 이 기능을 지원하지 않는다.
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(Model model, @SessionAttriubte LocalDateTime visitTime){
System.out.println(visitTime);
Event event = new Event();
event.setName("spring");
event.setLimit(10);
// 데이터베이스에서 읽어왔다고 가정한다.
List<Event> eventList = new ArrayList<>();
eventList.add(event);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
스프링 부트는 이 기능을 기본으로 지원하지 않는다.
WebMvcAutoConfiguration.java
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
conversionService, validator);
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
isIgnoreDefaultModelOnRedirect()는 기본 값이 True이다. 이를 해제하고 싶으면 application.properties에서 다음과 같이 지정하면 된다.
spring.mvc.Ignore-default-model-on-redirect=false
그러나 Model에 있는 모든 정보를 다 보내고 싶지 않고 일부만 보내고 싶다면 Model 대신에 RedirectAttributes를 사용한다.
@Controller
@SessionAttributes("event")
public class SampleController {
@GetMapping("/events/form/name")
public String eventsFormName(Model model) {
model.addAttribute("event", new Event());
// 자동으로 세션에 넣어줌
return "events/form-name";
}
@PostMapping("/events/form/name")
public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form-name";
}
return "redirect:/events/form/limit";
}
@GetMapping("/events/form/limit")
public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
model.addAttribute("event", event);
// 자동으로 세션에 넣어줌
return "events/form-limit";
}
@PostMapping("/events/form/limit")
public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
if(bindingResult.hasErrors()){
return "/events/form-limit";
}
sessionStatus.setComplete();
// 데이터베이스에서 save 했다고 가정
attributes.addAttribute("name", event.getName());
attributes.addAttribute("limit", event.getLimit());
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(Model model, @SessionAttriubte LocalDateTime visitTime){
System.out.println(visitTime);
Event event = new Event();
event.setName("spring");
event.setLimit(10);
// 데이터베이스에서 읽어왔다고 가정한다.
List<Event> eventList = new ArrayList<>();
eventList.add(event);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
이렇게 하면 RedirectAttributes에 명시한 것들만 URI 쿼리 파라미터에 들어온다.
그럼 이를 받는 쪽에서 다음과 같이 쓸 수 있다.
@Controller
@SessionAttributes("event")
public class SampleController {
@GetMapping("/events/form/name")
public String eventsFormName(Model model) {
model.addAttribute("event", new Event());
// 자동으로 세션에 넣어줌
return "events/form-name";
}
@PostMapping("/events/form/name")
public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form-name";
}
return "redirect:/events/form/limit";
}
@GetMapping("/events/form/limit")
public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
model.addAttribute("event", event);
// 자동으로 세션에 넣어줌
return "events/form-limit";
}
@PostMapping("/events/form/limit")
public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
if(bindingResult.hasErrors()){
return "/events/form-limit";
}
sessionStatus.setComplete();
// 데이터베이스에서 save 했다고 가정
attributes.addAttribute("name", event.getName());
attributes.addAttribute("limit", event.getLimit());
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(@RequestParam String name,
@RequestParam Integer limit,
Model model,
@SessionAttriubte LocalDateTime visitTime){
System.out.println(visitTime);
Event newEvent = new Event();
newEvent.setName(name);
newEvent.setLimit(limit);
Event event = new Event();
event.setName("spring");
event.setLimit(10);
// 데이터베이스에서 읽어왔다고 가정한다.
List<Event> eventList = new ArrayList<>();
eventList.add(event);
eventList.add(newEvent);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
혹은 ModelAttribute로 받아올 수도 있는데 유의해야 한다.
@Controller
@SessionAttributes("event")
public class SampleController {
@GetMapping("/events/form/name")
public String eventsFormName(Model model) {
model.addAttribute("event", new Event());
// 자동으로 세션에 넣어줌
return "events/form-name";
}
@PostMapping("/events/form/name")
public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form-name";
}
return "redirect:/events/form/limit";
}
@GetMapping("/events/form/limit")
public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
model.addAttribute("event", event);
// 자동으로 세션에 넣어줌
return "events/form-limit";
}
@PostMapping("/events/form/limit")
public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
if(bindingResult.hasErrors()){
return "/events/form-limit";
}
sessionStatus.setComplete();
// 데이터베이스에서 save 했다고 가정
attributes.addAttribute("name", event.getName());
attributes.addAttribute("limit", event.getLimit());
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(@ModelAttribute("event") Event event,
Model model,
@SessionAttriubte LocalDateTime visitTime){
System.out.println(visitTime);
Event spring = new Event();
spring.setName("spring");
spring.setLimit(10);
// 데이터베이스에서 읽어왔다고 가정한다.
List<Event> eventList = new ArrayList<>();
eventList.add(spring);
eventList.add(event);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
@SessionAttributes("name")과 같은 이름으로 @ModelAttribute("name")을 사용해서는 안된다. 같은 이름이 있을 때에는 세션에서 찾아보기 때문에 에러가 발생한다.
ModelFactory.java
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
container.mergeAttributes(sessionAttributes);
invokeModelAttributeMethods(request, container);
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!container.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
container.addAttribute(name, value);
}
}
}
sessionStatus.setComplete(); 에서 이미 세션을 제거했기 때문에 에러가 발생하는 것이다. sessionStatus.setComplete();를 @GetMapping("/events/list")에서 수행하고 싶지 않다면, 다음과 같이 @SessionAttributes("name")와 다르게 @ModelAttribute("newName")으로 이름을 바꿔줘야 한다.
@Controller
@SessionAttributes("event")
public class SampleController {
@GetMapping("/events/form/name")
public String eventsFormName(Model model) {
model.addAttribute("event", new Event());
// 자동으로 세션에 넣어줌
return "events/form-name";
}
@PostMapping("/events/form/name")
public String eventsFormNameSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "/events/form-name";
}
return "redirect:/events/form/limit";
}
@GetMapping("/events/form/limit")
public String eventsFormLimit(@ModelAttribute @Valid Event event, Model model) {
model.addAttribute("event", event);
// 자동으로 세션에 넣어줌
return "events/form-limit";
}
@PostMapping("/events/form/limit")
public String eventsFormLimitSubmit(@ModelAttribute @Valid Event event, BindingResult bindingResult, SessionStatus sessionStatus, RedirectAttributes attributes){
if(bindingResult.hasErrors()){
return "/events/form-limit";
}
sessionStatus.setComplete();
// 데이터베이스에서 save 했다고 가정
attributes.addAttribute("name", event.getName());
attributes.addAttribute("limit", event.getLimit());
return "redirect:/events/list";
}
@GetMapping("/events/list")
public String getEvents(@ModelAttribute("newEvent") Event event,
Model model,
@SessionAttriubte LocalDateTime visitTime){
System.out.println(visitTime);
Event spring = new Event();
spring.setName("spring");
spring.setLimit(10);
// 데이터베이스에서 읽어왔다고 가정한다.
List<Event> eventList = new ArrayList<>();
eventList.add(spring);
eventList.add(event);
model.addAttribute("eventList", eventList);
return "/events/list";
}
}
이렇게 하면 세션에서 데이터를 가져오는 것이 아니라 쿼리 파라미터에서 데이터를 가져와 바인딩하게 된다.
참고