HttpSession 직접 사용하기
트래픽이 작거나 단일 서버에서 동작하는 웹 어플리케이션의 경우 서블릿의 HttpSession을 이용해서 사용자 로그인 상태를 유지하는 경우가 많다. HttpSession을 사용하는 가장 손쉬운 방법은 HttpSession을 컨트롤러 메서드의 파라미터로 지정하는 것이다. 다음 코드를 보자.
@Controller
@RequestMapping("/auth/login")
public class LoginController {
@RequestMapping(method = RequestMethod.POST)
public String login(@Valid LoginCommand loginCommand, Errors errors, HttpSession session) {
if (errors.hasErrors()) {
return LOGIN_FORM;
}
try {
Auth auth = authenticator.authenticate(loginCommand.getEmail(), loginCommand.getPassword());
session.setAttribute("auth", auth);
return "redirect:/index.jsp";
} catch (AuthenticationException ex) {
errors.reject("invalidIdOrPassword");
return LOGIN_FORM;
}
}
// 이하 생략
...
}
HttpSession 타입의 파라미터가 존재하면 스프링 MVC는 HttpSession을 생성해서 파라미터 값으로 전달한다. 기존에 세션이 존재하면 그 세션을 전달하고, 세션이 존재하지 않으면 새로운 세션을 생성해서 전달한다.
만약 상황에 따라 세션을 생성하고 싶다면, HttpSession을 파라미터로 받으면 안 된다. 다음과 같이 HttpServletRequest를 받아서 상황에 따라 세션을 직접 생성해주어야 한다.
@Controller
@RequestMapping("/auth/login")
public class LoginController {
@RequestMapping(method = RequestMethod.POST)
public String login(@Valid LoginCommand loginCommand, Errors errors, HttpServletRequest request) {
if (errors.hasErrors()) {
return LOGIN_FORM;
}
try {
Auth auth = authenticator.authenticate(loginCommand.getEmail(), loginCommand.getPassword());
HttpSession session = request.getSession();
session.setAttribute("auth", auth);
return "redirect:/index.jsp";
} catch (AuthenticationException ex) {
errors.reject("invalidIdOrPassword");
return LOGIN_FORM;
}
}
// 이하 생략
...
}
@SessionAttributes 어노테이션을 이용한 모델과 세션 연동
스프링은 임시 용도로 사용될 데이터를 세션에 보관할 때 사용할 수 있는 @SessionAttributes 어노테이션을 제공하고 있다. @SessionAttributes 어노테이션을 이용해서 각 화면 간 모델 데이터를 공유하는 방법은 다음과 같다.
- 클래스에 @SessionAttributes를 적용하고, 세션으로 공유할 객체의 모델 이름을 지정한다.
- 컨트롤러 메서드에서 객체를 모델에 추가한다.
- 공유한 모델의 사용이 끝나면 SessionStatus를 사용해서 세션에서 객체를 제거한다.
@SessionAttributes가 제대로 동작하려면 모델에 같은 이름을 갖는 객체를 추가해야 한다. 다음과 같이 보통 첫 번째 단계를 처리하는 컨트롤러 메서드에서 모델에 객체를 추가한다.
@Controller
@SessionAttributes("eventForm")
public class EventCreationController {
@RequestMapping("/newevent/step1")
public String step1(Model model) {
// 모델에 객체 추가
model.addAttribute("eventForm", new EventForm());
return EVENT_CREATION_STEP1;
}
@RequestMapping(value = "/newevent/step2", method = RequestMethod.POST)
public String step2(@ModelAttribute("eventForm") EventForm formData, BindingResult result) {
new EventFormStep1Validator().validate(formData, result);
if (result.hasErrors()) {
return EVENT_CREATION_STEP1;
}
return EVENT_CREATION_STEP2;
}
// 이하 생략
...
}
세션에 보관할 객체를 생성하는 또 다른 방법은 @ModelAttribute 메서드를 사용하는 것이다. 다음 코드를 보자.
@Controller
@SessionAttributes("eventForm")
public class EventCreationController {
@ModelAttribute("eventForm")
public EventForm formData() {
return new EventForm();
}
@RequestMapping("/newevent/step1")
public String step1() {
return EVENT_CREATION_STEP1;
}
@RequestMapping(value = "/newevent/step2", method = RequestMethod.POST)
public String step2(@ModelAttribute("eventForm") EventForm formData, BindingResult result) {
new EventFormStep1Validator().validate(formData, result);
if (result.hasErrors()) {
return EVENT_CREATION_STEP1;
}
return EVENT_CREATION_STEP2;
}
}
위 코드처럼 @ModelAttribute 어노테이션이 적용된 메서드에서 모델 객체를 생성하면 @RequestMapping 메서드에서 Model에 객체를 추가할 필요가 없다. 그리고 스프링은 @ModelAttribute가 적용된 메서드를 실행하기 전에 세션에 동일한 이름을 갖는 객체가 존재하면 그 객체를 모델로 사용하기 때문에 매번 새로운 객체가 생성되지 않는다.
세션을 이용한 객체 공유가 끝나면 다음 코드처럼 컨트롤러 메서드에서 SessionStatus의 setComplete() 메서드를 호출하면 된다.
@Controller
@SessionAttributes("eventForm")
public class EventCreationController {
// 이하 생략
...
@RequestMapping(value = "/newevent/done", method = RequestMethod.POST)
public String done(@ModelAttribute("eventForm") EventForm formData, SessionStatus sessionStatus) {
sessionStatus.setComplete();
return EVENT_CREATION_DONE;
}
}
SessionStatus.setComplete() 메서드를 실행하면 세션에서 객체를 제거할 뿐 모델에서 제거하지는 않는다. 따라서 뷰 코드에서는 모델의 값을 사용할 수 있다.
여담으로 임시 데이터를 보관하기 위해 세션을 사용하면 사용자마다 세션을 생성하게 되고, 접속자가 늘어날수록 세션 객체가 차지하는 메모리 비중이 높아진다. 따라서 트래픽이 높을 것으로 예상되는 곳에서 세션을 사용할 때는 메모리가 부족하지 않을지 따져봐야 하며, 메모리가 부족하면 데이터베이스나 외부의 캐시 서버 등에 임시 데이터를 보관할 것을 고려해야 한다.
Reference
- 웹 개발자를 위한 Spring 4.0 프로그래밍 (최범균 저)
'Spring' 카테고리의 다른 글
| [Spring] 스프링 MVC(12) - 컨트롤러의 파라미터와 리턴 타입 (0) | 2022.03.16 |
|---|---|
| [Spring] 스프링 MVC(11) - @ExceptionHandler, @ControllerAdvice, @ResponseStatus 어노테이션 (0) | 2022.03.16 |
| [Spring] 스프링 MVC(9) - @Valid와 @InitBinder 어노테이션 (0) | 2022.03.15 |
| [Spring] 스프링 MVC(8) - Validator와 Errors/BindingResult (0) | 2022.03.14 |
| [Spring] 스프링 MVC(7) - 커맨드 객체와 @ModelAttribute 어노테이션 (0) | 2022.03.13 |