Session에 대해서 - 컨트롤러에서 HttpSession 사용하기
Spring MVC - HttpSession
HttpSession은 서블릿이 제공하는 일종의 쿠키로,
`Cookie : JSESSIONID=3LKSAJFKJ20298349028SD9F0`과 같이 랜덤값으로 설정되며 주로 로그인 처리를 위해 사용한다.
세션에 로그인한 사용자의 정보를 넣어두고 사용하는 것이다.
컨트롤러에서 세션 사용하기
1. 세션에 값 담기
컨트롤러의 파라미터로 `HttpServletRequest`를 받아 `request.getSession()`으로 세션을 생성할 수 있다.
@PostMapping("/login")
public String login(@ModelAttribute User user, HttpServletRequest request) {
(검증 로직 생략)
HttpSession session = request.getSession();
session.setAttribute("user", user);
return "redirect:/";
}
- `request.getSession(true)`
- 세션이 존재하면 기존 세션 반환
- 세션이 존재하지 않으면 새로운 세션 생성하여 반환
- `request.getSession()`과 동일(디폴트 값이 true)
- `request.getSession(false)`
- 세션이 존재하면 기존 세션 반환
- 세션이 존재하지 않으면 null 반환(새로운 세션 생성 X)
`HttpSession session = request.getSession();`으로 세션을 생성하였으면
`session.setAttribute(String name, Object value);`로 값을 저장할 수 있다.
이후 로그아웃 처리를 하는 경우에는 더이상 세션에 값을 유지할 필요가 없으므로
`session.invalidate();`로 세션을 제거한다.
@PostMapping("/logout")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/";
}
이 때 `request.getSession(false)`를 사용했는데
여기서 세션이 새로 만들어져도 의미가 없으므로 생성하지 않도록 했다.
참고로 대부분의 사용자는 로그아웃보단 웹을 그냥 종료하기 때문에
`invalidate()`로 세션을 삭제하는 것 뿐만 아니라 세션의 종료시점도 설정해야 한다.
// 글로벌 설정(Spring Boot): application/properties 파일에 추가
// 분 단위로 설정해야 함
server.servlet.session.timeout=60 // 60초
// 특정 세션 단위로 설정: 세션을 설정하는 컨트롤러에서 작성
session.setMaxInactiveInterval(1800); // 1800초
마지막 사용 시점(사용자의 최근 요청 시간) 이후로 설정한 시간만큼 계속 생존 시간이 늘어난다.
2. 세션에 있는 값 가져오기
`session.getAttribute(String name)`으로 넣어놨던 값을 가져올 수 있다.
@GetMapping("/")
public String home(HttpServletRequest request, Model model) {
HttpSession session = request.getSession(false);
// 세션이 없는 경우
if (session == null) {
return "home"; //홈으로 이동
}
User loginUser = session.getAttribute("user");
// 세션은 있으나 세션에 사용자 데이터가 없는 경우
if (loginUser == null) {
return "home";
}
// 세션이 있는 경우
if (loginUser != null) {
model.addAttribute("user", loginUser); // 세션값을 따로 모델에 담아야 html에서 이용 가능
return "loginhome"; //로그인한 사용자 홈으로 이동
}
return "home";
}
HttpSession vs request.getSession()
그런데 한가지 의문이 들 수 있다. (내가 들었음)
`request.getSession()`의 반환 타입이 `HttpSession`인데
컨트롤러의 파라미터를 `HttpServletRequest`가 아니라 `HttpSession`을 바로 받아서 사용해도 되지 않을까?
@PostMapping("/login")
public String login(@ModelAttribute User user, HttpSession session) {
(검증 로직 생략)
session.setAttribute("user", user);
return "redirect:/";
}
결론적으로 둘 다 사용 가능하다.
두가지 방식의 차이점은
`HttpSession`을 사용하면 반드시 세션을 생성하게 되고
`HttpServletRequest`를 사용하면 위에서 보았듯 세션 생성 여부를 선택할 수 있다.
따라서 필요할 때만 세션을 생성하여 사용할 수 있는 것이다.
- `HttpSession`
- 컨트롤러 호출 시 세션이 존재하면 존재하는 세션 전달
- 없으면 새로운 세션 생성
- `request.getSession(Boolean create)`
- 컨트롤러 호출 시 세션이 존재하면 존재하는 세션 전달
- 없으면 create 값에 따라 새로 생성 또는 null 반환
@SessionAttribute
스프링은 세션을 더 편리하게 사용할 수 있는 `@SessionAttribute`를 지원한다.
이 애너테이션은 세션을 새로 생성하지는 않지만 등록되어 있는 세션을 찾을 때 유용하게 사용할 수 있다.
@GetMapping("/")
public String home(@SessionAttribute(name = "user", required = false) User user, Model model) {
// 세션에 데이터가 없으면 홈으로 이동
if (user == null) {
return "home";
}
// 세션에 데이터가 있으면 로그인된 사용자 홈으로 이동
model.addAttribute("user", user);
return "loginhome";
}
세션 존재 여부, 세션의 사용자 데이터 존재 여부를 따로 확인하지 않고 간단하게 사용 가능하다.
직접 애너테이션 만들어 사용하기
`@SessionAttribute`보다 좀 더 간단하게 사용하려면 직접 애너테이션을 만들어 쓰는 방법도 있다.
@GetMapping("/")
public String home(@Login User user, Model model) { // @Login 애너테이션 사용
if (user == null) {
return "home";
}
model.addAttribute("user", user);
return "loginHome";
}
`@Login` 애너테이션이 세션에 있는 로그인 회원 데이터를 찾아주고, 세션에 데이터가 없다면 null을 반환하도록 할 것이다.
1. 인터페이스 생성
@Target(ElementType.PARAMETER) // 파라미터에만 사용
@Retention(RetentionPolicy.RUNTIME) // 런타임까지 애너테이션 정보가 남아있음
public @interface Login {}
2. `HandlerMethodArgumentResolver`를 구현한 클래스 생성
public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hasLoginAnnotation = parameter.hasParameterAnnotation(Login.class); // 파라미터: step1에서 생성한 인터페이스
boolean hasUserType = User.class.isAssignableFrom(parameter.getParameterType());
return hasLoginAnnotation && hasUserType;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
return session.getAttribute("user");
}
}
- `supportsParameter()`: `@Login`이 있으면서 `User` 타입이면 해당 `Resolver`가 실행됨
- `resolveArgument()`: 컨트롤러 호출 직전에 호출되어 필요한 파라미터 정보를 생성함. 세션의 `User` 객체를 찾아 반환하도록 함
3. `WebConfig`에 설정 추가
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginUserArgumentResolver());
}
}
`WebMvcConfigurer`를 구현한 클래스인 `WebConfig`를 만들어 `Resolver`를 등록한다.
이제 확인해보면 `@Login`이 제대로 동작하는 것을 볼 수 있을 것이다.
참고
'Programming > Spring' 카테고리의 다른 글
[JPA] 상속관계 매핑하기 (0) | 2024.02.09 |
---|---|
Spring Annotation 정리 (0) | 2024.01.23 |
[Spring Boot] MessageSource - 오류 메시지 처리 방식 (0) | 2024.01.20 |
[Spring Boot] Embeded Mode로 테스트 하기 (feat. H2 Database) (0) | 2024.01.16 |
[Spring Boot] Bean Validation - 검증 기능 사용 방법 (0) | 2024.01.05 |
Liked this Posting!