🔍 HttpSession
✏️ 세션이 왜 필요한가요?
웹에서 데이터를 전송하는 데 기본적으로 사용되는 HTTP는 본질적으로 상태가 저장되지 않는다. 즉, 동일한 사용자로부터 여러 요청이 들어와도 이를 인식하지 못한다. 그러나 웹 애플리케이션에서 사용자 인증, 장바구니 등과 같은 상태를 유지하는 것이 중요한 시나리오가 있다. 세션은 웹 애플리케이션이 특정 기간 동안 사용자를 인식하고 구분할 수 있게 함으로써 이러한 한계를 극복하는 데 중요한 역할을 한다.
✏️기존 세션의 문제점
전통적인 HttpSession은 컨테이너(ex. Tomcat) 에 종속되어 사용된다. 즉 컨테이너가 세션을 생성하고 애플리케이션에 세션 ID를 제공한다. 이런 방식은 기본적으로 클러스터링 환경에서 실행되는 경우 각 컨테이너가 자체 HTTPSession을 관리한다는 것을 의미한다. 즉, 세션 정보는 다른 서버 간에 공유 되지 않는다는 문제가 있다.
고정 세션
이를 해결하기 위해 고정 세션 접근 방식을 사용할 수 있다.
- 로드밸런서 또는 웹 서버가 클라이언트와 앱 서버 간에 매칭을 한다.
- 이 작업은 로드 밸런서가 식별을 위해 클라이언트에 쿠키를 할당함으로써 수행된다.
- 로드 밸런서는 세션이 진행되는 동안 클라이언트의 각 요청을 동일한 애플리케이션 서버로 라우팅 한다.
고정 세션 방식은 다음과 같은 문제점을 가지고 있다.
- 특정 서버에 과부하가 걸릴 수 있다.
- A서버가 다운되면 B서버는 A서버의 세션에 대한 정보를 얻지 못한다.
이러한 문제를 해결하기 위해 Spring에서는 HTTP Session을 중앙에서 관리하도록 도와준다.
🔍 Spring Session
이러한 문제를 해결하기 위해 Spring에서는 HTTP Session을 중앙에서 관리하도록 도와준다.
Session Storage 방식을 사용하면 다음과 같은 장점을 가질 수 있다.
- 애플리케이션 재시작 시 세션 복원이 가능하다.
- 로드 밸런서에서 세션을 고정할 필요가 없다. -> 특정 서버에 과부하를 주지 않고 독점적으로 분산할 수 있다.
단점
- Session Storage에 장애가 발생하면 모든 세션을 잃어버려 세션을 사용하는 모든 서버에 영향을 미친다. 이를 해결하기 위해 마스터-슬레이브 복제 방법을 사용할 수 있다.
- 네트워크 I/O가 발생하기 때문에 로컬 메모리보다는 성능적인 면에서 떨어진다.
✏️ 원리
Spring Session은 애플리케이션과 Session management간의 추상화 계층을 제공함으로써 NoSQL, RDBS 등과 같은 영구 저장소에 저장할 수 있도록 도와준다. 따라서 어떤 데이터베이스를 사용하든 Spring Session이 제공하는 API를 통해 똑같은 방법으로 관리가 가능하다. 사용할 수 있는 저장소로는 다음 JDBC
, Redis
, MongoDB
등이 있다.
Spring Session은 HttpSession을 필터와 Wrapper로 감쌈으로써 기능을 제공한다.
- SessionRepositoryRequestWrapper를 이용하여 기본 HttpSession을 커스텀 세션으로 바꾼다.
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest {
public HttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
}
public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
public SessionRepositoryRequestWrapper(HttpServletRequest original) {
super(original);
}
public HttpSession getSession() {
return getSession(true);
}
public HttpSession getSession(boolean createNew) {
// create an HttpSession implementation from Spring Session
}
// ... other methods delegate to the original HttpServletRequest ...
}
- HttpServlet 요청을 세션 리포지토리 요청 래퍼로 대체한다.
public class SessionRepositoryFilter implements Filter {
public doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
SessionRepositoryRequestWrapper customRequest =
new SessionRepositoryRequestWrapper(httpRequest);
chain.doFilter(customRequest, response, chain);
}
// ...
}
놀라운 점은 스프링 부트를 사용하면 Auto-Configuration 기능 덕분에 다음과 같은 의존성을 추가하고 yml파일에 설정해주는 것만으로 위의 작업을 자동화해준다.
implementation("org.springframework.boot:spring-boot-starter-data-redis")
spring.session.store-type=redis # Session store type.
이렇게 하면 Filter를 구현하는 springSessionRepositoryFilter라는 이름의 Spring 빈이 생성되며, 이 필터는 스프링 세션이 지원하도록 HttpSession 구현을 대체하는 역할을 담당한다.
#스프링세션 #springsession #웹개발 #httpsession #세션관리 #세션스토리지
https://docs.spring.io/spring-session/reference/guides/boot-redis.html
https://docs.spring.io/spring-session/reference/guides/java-redis.html
https://docs.spring.io/spring-session/reference/configuration/redis.html