앞선 포스팅의 @RequestParam과 model 개념을 배울 때 잠깐 request scope와 model scope, session scope에 대해 언급했었다. 이번 포스팅에서는 해당 3가지 개념의 scope를 비교해보고, 그 중 세션을 사용하는 방법인 @SessionAttributes에 대해 정리해보겠다.
request에 있는 모든 값들은 오직 그 request URL에서만 유효하다.
예를 들어, login 페이지의 request에서 유효했던 name과 password 값들은, welcome 페이지로 가면 name과 password 값이 존재하지 않음을 볼 수 있었다. (개발자 도구 탭, network, payload data에)
즉, login.jsp에서 사용가능했던 name과 password값들은 welcome.jsp에서는 존재하기 않아서 사용할 수 없었다.
model scope는 by default로, request scope와 동일하게 작동한다. 즉, 컨트롤러 코드에서 넣은 model 값 또한 해당 URL의 뷰에서만 사용가능하고, 다른 뷰에서는 모델값을 사용할 수 없었다.
그렇기에 더 오래 값들을 기억하고 사용하고 싶다면, 보다 더 넓은 범위인 session에 값을 집어넣으면 된다.
결론부터 말하자면, session의 범위는 다수의 request URL에서 유지된다.
Session scope에서는 세부정보(값)들이 다수의 request URL에 걸쳐 저장된다. request가 login -> welcome으로 가는 것과 같이, 다수의 request URL에서 session값인 name이 유지되는 것을 확인할 수 있었다! (이때, name은 session scope에 저장된 것임!)
세션에 값을 넣으려면 @SessionAttributes 어노테이션을 사용하면 된다.
해당 model 값은, 해당 request URL에서만 값이 유지된다.
// listTodos.jsp
<html>
<head>
<title>List Todos Page</title>
</head>
<body>
<div>Welcome ${name}</div>
<div>Your todos are : ${todos}</div>
</body>
</html>
// TodoController.java
@Controller
public class TodoController {
private TodoService todoService;
@Autowired
public TodoController(TodoService todoService) {
super();
this.todoService = todoService;
}
// list-todos
@RequestMapping("/list-todos")
public String listAllTodos(ModelMap model) {
List<Todo> todos = todoService.findByUsername("minjiki2");
model.addAttribute("todos", todos);
return "listTodos";
}
}
즉, 위 코드에서 model에 todos만 넣었고, 그 todos는 "/list-todos"에서만 값이 유지된다. 그러니 listTodos.jsp에서는 유효한 모델 값은 todos만 되니, name값은 없어서 공백으로 나온다.
왜냐하면, model.put("name", name); 은 /login + POST method와 RequestMapping된 LoginController의 gotoWelcomePage메서드에 추가되어 있기 때문이다.
그렇기에 name값을 오래도록 기억할 수 있도록, LoginController에서 @SessionAttributes를 사용해 세션에 name값을 넣자!
핵심을 말하자면, LoginController에서 model.put("name", name);의 "name속성값"이 /login request URL 뿐만 아니라 다른 URL인 /list-todos request URL에서도 유지되길 원했다. 그래서 @SessionAttributes를 이용해 model.put("name", name"); 을 넣을 때, model scope가 아닌 session scope으로 속성값의 범위를 넓혔다.
이렇게 하면, "name" 속성값이 세션에서 관리되므로 다수의 여러 request URL에서 공유되어 유지 가능하다!
컨트롤러 메서드의 모델에 넣은 (model.addAttribute("name", value)나 model.put("name", value)를 통해) 정보 중에서, @SessionAttributes에서 지정한 이름과 동일한 이름이 있다면, 이를 "model에 추가될 때" session scope에 저장해준다.
// @SessionAttributes 예제코드
@Controller
@SessionAttributes("event")
public class SampleController {
@RequestMapping("/events/form")
public String getEvent(Model model){
Event event = new Event();
event.setLimit(50);
event.setName("event1");
model.addAttribute("event", event); // 속성명 - 속성값
return "/events/form";
}
}
// LoginController.java
@Controller
@SessionAttributes("name")
public class LoginController {
private AuthenticationService authenticationService;
@Autowired
public LoginController(AuthenticationService authenticationService) {
super();
this.authenticationService = authenticationService;
}
@RequestMapping(method = RequestMethod.GET, value = "/login")
public String gotoLoginPage() {
return "login";
}
@RequestMapping(method = RequestMethod.POST, value = "/login")
public String gotoWelcomePage(@RequestParam String name, @RequestParam String password, ModelMap model) {
if (authenticationService.authenticate(name, password)) {
model.put("name", name);
return "welcome";
}
String errorMsg = "admin login is failed! Please try again";
model.put("error", errorMsg);
return "login";
}
}
@Controller
@SessionAttributes("name")
public class TodoController {
private TodoService todoService;
@Autowired
public TodoController(TodoService todoService) {
super();
this.todoService = todoService;
}
// list-todos
@RequestMapping("/list-todos")
public String listAllTodos(ModelMap model) {
List<Todo> todos = todoService.findByUsername("minjiki2");
model.addAttribute("todos", todos);
return "listTodos";
}
}
name속성값을 model scope가 아닌 보다 넓은 범위의 session scope으로 추가하고 나서야 앞선 실행결과와 달리, Welcome "admin" 값이 나왔다.
Request Scope -> 해당 request가 발생한 URL에서만 값이 유지된다.
Session Scope -> 세부정보가 다수의 request URL에 걸쳐 저장된다.
Model Scope
이 시리즈는 Udemy 강의의 내용을 정리한 것입니다.
https://www.udemy.com/course/spring-boot-and-spring-framework-korean/