2 분 소요


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편 - 백엔드 웹 개발 활용 기술(김영한)>

댓글남기기