mentor says
"수고 많으셨습니다! Session에 대해서 학습해보시는 걸 추천합니다. "
provides a way to identify a user across mmore than one page request or visit to web site and to store information about the user. <tomcat 공식 문서>
두 개 이상의 페이지에서의 요청이나 웹사이트 방문에서 사용자를 식별하고 해당 사용자에 대한 정보를 저장하는 방법을 제공합니다.
서블릿 컨테이너는 이 인터페이스를 사용하여 HTTP 클라이언트와 HTTP 서버 간의 세션을 생성한다. 세션은 사용자의 두 개 이상의 연결 또는 페이지 요청에 대해 지정된 기간 동안 지속된다. 세션은 일반적으로 사이트를 여러 번 방문할 수 있는 한 사용자에 해당하고, 서버는 쿠키를 사용하거나 URL을 다시 작성하는 등 다양한 방법으로 세션을 유지할 수 있다.
세션 정보는 현재 웹 응용프로그램(ServletContext)으로만 범위가 지정되므로, 한 컨텍스트에 저장된 정보는 다른 컨텍스트에서 직접 볼 수 없습니다.
HttpSession 사용하기
Controller에서 HttpSession을 사용하려면 아래의 두 가지 방법 중 한 가지를 이용하면 된다.
@PostMappint("/signup")
public String sign(@RequestBody signUpDto: SignUpDTO, httpSession: HttpSession) {}
위와 같은 방식을 사용하는 것이 첫 번째 방식이다. 이렇게 사용하게 되면 항상 HttpSession을 생성하게 된다.
@PostMapping("/signup")
public String sign(@RequestBody signUpDTO: SignUpDTO, req: HttpServletRequest) {
val session = req.session
}
이것이 두번째 방식이다. 이렇게 사용하면 필요한 시점에만 HttpSession을 생성할 수 있다.
세션만 보더라도 Spring MVC 내부에서 엄청 복잡한 일들이 일어나고 있다는 것을 알 수 있다. Spring에서 Session을 구현한다면 HttpSession을 사용하게 될 것이다.
httpSession.setAttribute("user", user)
httpSession.getAttribute("user")
httpSession.invalidate()
위와 같이 setAttribute
를 사용하여 user
를 세션에 저장하고 getAttribute
를 사용해서 다시 user
를 꺼내올 수 있다. 그리고 invalidate
을 통해 세션을 제거할 수 있다.
그렇다면 의문점
HttpSession.getAttribute("user");
사용자 A가 접속해도 "user" Key로 값을 가져오고,
사용자 B가 접속해도 "user" Key로 가져온다.
같은 Key를 쓰는데 어떻게 유저 A와 B를 구분해서 값을 가져오나?
이것이 가능한 이유를 알기 위해서는 JSESSIONID라는 것을 알아야 합니다.
JSESSIONID ?
The name to be used for all session cookies created for this context. If set, this overrides any name set by the web application. If not set, the value specified by the web application, if any, will be used, or the name JSESSIONID if the web application does not explicitly set one.
요약 : 이름을 지정하지 않으면 JSESSIONID 이름이 사용됩니다.
기존 세션을 사용하고 있는 URL을 Postman을로 API 호출해보았습니다.
그러면 Response Cookie에 JSESSIONID가 포함되어 응답으로 오는 것을 볼 수 있습니다. 즉, 세션이 서버에 등록되면 서버에서 응답값으로 클라이언트에게 JSESSIONID를 RESPONSE HEADER에 담아 보내주는 것을 볼 수가 있습니다.
즉, 전체적인 흐름을 정리하면 아래와 같습니다.
서블릿 컨테이너
는 새로운 HttpSession 객체를 생성하고 길고 unique한 ID를 생성 후, 서버의 메모리에 저장합니다.JSESSIONID
란 이름을 key로, 생성한 session ID를 value로 하여 HTTP 응답의 Set-Cookie header에 cookie로 설정합니다Request Headers
에 JSESSIONID
를 담아서 서버로 요청을 보냅니다.서블릿 컨테이너
는 들어오는 모든 HTTP request의 cookie header에서 JSESSIONID
라는 이름의 cookie가 있는지 확인하고 해당 값 (session ID)을 사용하여 서버의 메모리에 저장된 HttpSession을 가져옵니다그러면 지금까지 위에서 정리한 내용을 바탕으로 같은 Key를 쓰는데 어떻게 유저 A와 B를 구분해서 값을 가져오나요?
라는 질문은 요청이 올 때의 Request Header에 존재하는 JSESSIONID로 구분할 수 있다.
라고 답 할 수 있겠습니다.
setAttribute
할 때의 key
가 중요한 것이 아니라 클라이언트(브라우저)에서 요청을 보낼 떄 Request Header
에 JESSIONID
가 있냐 없냐가 유저를 구분하는데 중요한 역할을 한다는 것을 알 수 있습니다.
그럼 누가 만드는건데 ?
우리가 웹어플리케이션을 개발할 때 사용한 HttpSession도 Servlet Container가 생성한 인스턴스입니다. Spring Web MVC를 이용해서 구현한다 할지라도 Spring은 Servlet Container가 만든 HttpSession을 주입할 뿐, HttpSession을 생성하는 주체는 Servlet Container입니다.
(참고로 SpringSession을 이용한다면 Servlet Container가 생성한 구현체가 아니라 SpringSession이 생성한 구현체가 될 것입니다.
SpringSession은 Servlet Container가 아닌 Redis나 Mongodb같은 데이터베이스에 세션을 저장/관리하기 위해 API를 제공합니다. )
그럼 언제 만들어 지는데?
HttpSession을 Servlet Container가 만드는건 알겠는데, 그럼 언제 만들어질까요? 모든 요청에 대해 Session을 만들까요? 아닙니다. Session을 만드는 것 자체도 부담이 가는 일이기 때문에, Session을 사용할 때만 생성합니다. Spring Web MVC에서는 HttpSession을 주입해야 할 때, 내부적으로 Servlet Container에게 Session을 달라고 합니다.
httpServletRequest.getSession()
그리고 그 때 Servlet Container가 HttpSession을 생성해줍니다. Spring이 Servlet Container에게 Session을 달라고 하는 시점은 HttpSession을 사용하는 방법에 따라 약간씩 다릅니다.
우리가 구현한 메소드에서 매개변수를 통해 주입받는 방식으로 구현한다면 선언시에 Servlet Container에게 Session을 달라고 요청합니다. 따라서 위의 예제에서는 login 메소드를 호출하는 즉시 Session이 요청/생성됩니다.