[Spring MVC] 서블릿 HTTP 세션
HttpSession
소개
서블릿이 제공하는 HttpSession
도 직접 만든 SessionManager
와 같은 방식으로 동작한다.
서블릿을 통해 HttpSession
을 생성하면 쿠키 이름은 JSESSIONID
, 값은 추정 불가능한 랜덤 값으로 쿠키가 생성된다.
Cookie: JSESSIONID=5B78E23B513F50164D6FDD8C97B0AD05
사용
SessionConst
public class SessionConst {
public static final String LOGIN_MEMBER = "loginMember";
}
LoginController
@PostMapping("/login")
public String loginV3(@Valid @ModelAttribute LoginForm form,
BindingResult bindingResult, HttpServletRequest request) {
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
// ...
//로그인 성공 처리
//세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
HttpSession session = request.getSession();
//세션에 로그인 회원 정보 보관
session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
return "redirect:/";
}
@PostMapping("/logout")
public String logoutV3(HttpServletRequest request) {
//세션을 삭제한다.
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/";
}
세션 생성과 조회
세션을 생성하려면 request.getSession(true)
를 사용하면 된다.
public HttpSession getSession(boolean create);
request.getSession(true)
(default)- 세션이 있으면 기존 세션을 반환한다.
- 세션이 없으면 새로운 세션을 생성해서 반환한다.
request.getSession(false)
- 세션이 있으면 기존 세션을 반환한다.
- 세션이 없으면
null
을 반환한다.
HomeController
@GetMapping("/")
public String homeLoginV3(HttpServletRequest request, Model model) {
//세션이 없으면 home
HttpSession session = request.getSession(false);
if (session == null) {
return "home";
}
Member loginMember = (Member)session.getAttribute(SessionConst.LOGIN_MEMBER);
//세션에 회원 데이터가 없으면 home
if (loginMember == null) {
return "home";
}
//세션이 유지되면 로그인으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
session.getAttribute(SessionConst.LOGIN_MEMBER)
: 로그인 시점에 세션에 보관한 회원 객체를 찾는다.
@SessionAttribute
스프링은 세션을 더 편리하게 사용할 수 있도록 @SessionAttribute
을 지원한다.
HomeController
@GetMapping("/")
public String homeLoginV3Spring(
@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false)
Member loginMember, Model model) {
//세션에 회원 데이터가 없으면 home
if (loginMember == null) {
return "home";
}
//세션이 유지되면 로그인으로 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
TrackingModes
로그인을 처음 시도하면 URL이 jsessionid
를 포함하고 있다.
http://localhost:8080/;jsessionid=F59911518B921DF62D09F0DF8F83F872
이는 웹 브라우저가 쿠키를 지원하지 않을 때 쿠키 대신 URL을 통해서 세션을 유지하는 방법이다. 이 방법을 사용하려면 URL에 이 값을 계속 포함해서 전달해야 한다. 서버 입장에서 웹 브라우저가 쿠키를 지원하는지 최초에는 판단하지 못하므로, 쿠키 값도 전달하고 URL에 jsessionid
도 함께 전달한다.
URL 전달 방식을 끄고 항상 쿠키를 통해서만 세션을 유지하고 싶다면 다음 옵션을 넣으면 된다.
application.properties
server.servlet.session.tracking-modes=cookie
세션 정보 확인
//세션 데이터 출력
session.getAttributeNames().asIterator()
.forEachRemaining(name -> log.info("session name={}, value={}", name, session.getAttribute(name)));
log.info("sessionId={}", session.getId());
log.info("maxInactiveInterval={}", session.getMaxInactiveInterval());
log.info("creationTime={}", new Date(session.getCreationTime()));
log.info("lastAccessedTime={}", new
Date(session.getLastAccessedTime()));
log.info("isNew={}", session.isNew());
return "세션 출력";
sessionId
: 세션Id,JSESSIONID
의 값이다. 예)34B14F008AA3527C9F8ED620EFD7A4E1
maxInactiveInterval
: 세션의 유효 시간, 예) 1800초, (30분)creationTime
: 세션 생성일시lastAccessedTime
: 세션과 연결된 사용자가 최근에 서버에 접근한 시간, 클라이언트에서 서버로sessionId
(JSESSIONID
)를 요청한 경우에 갱신된다.isNew
: 새로 생성된 세션인지, 아니면 이미 과거에 만들어졌고, 클라이언트에서 서버로sessionId
(JSESSIONID
)를 요청해서 조회된 세션인지 여부
타임아웃 설정
세션은 사용자가 로그아웃을 직접 호출해서 session.invalidate()
가 호출되는 경우에 삭제된다.
하지만 사용자가 로그아웃을 선택하지 않고 웹 브라우저를 종료해버리면 HTTP가 비연결성이므로 서버 입장에서는 사용자가 웹 브라우저를 종료한 것인지 아닌지 인식할 수 없다.
이 경우 남아있는 세션을 무한정 보관하면 다음과 같은 문제가 발생한다.
- 세션과 관련된 쿠키(
JSESSIONID
)를 탈취 당했을 경우 오랜 시간이 지나도 해당 쿠키로 악의적인 요청을 할 수 있다. - 세션은 기본적으로 메모리에 생성되고, 메모리의 크기가 무한하지 않기 때문에 필요한 경우만 생성해서 사용해야 한다.
설정 방법
사용자가 서버에 최근 요청한 시간을 기준으로 30분 정도를 유지하도록 설정.
글로벌 설정
application.properties
server.servlet.session.timeout=60
: 60초, 기본은 1800(30분)
(글로벌 설정은 분 단위로 설정해야 한다. 60, 120..)
특정 세션 단위로 시간 설정
session.setMaxInactiveInterval(1800);
세션 타임아웃 발생
세션의 타임아웃 시간은 해당 세션과 관련된 JSESSIONID
를 전달하는 HTTP 요청이 있으면 현재 시간으로 다시 초기화 된다.
session.getLastAccessedTime()
: 최근 세션 접근 시간
LastAccessedTime
이후로 timeout 시간이 지나면, WAS가 내부에서 해당 세션을 제거한다.
[참고]
주의할 점은 최소한의 데이터만 보관해야 한다는 점이다.
보관한 데이터 용량 * 사용자 수로 세션의 메모리 사용량이 급격하게 늘어나서 장애로 이어질 수 있기 때문이다.
또한, 세션의 시간을 너무 길게 가져가면 메모리 사용이 계속 누적될 수 있으므로 적당한 시간을 선택하는 것이 필요하다.
<출처 : 인프런 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술(김영한)>
댓글남기기