스프링 세션의 이해:등장 배경과 그 원리
🔍️ 왜 세션이 필요한가?
웹에서 데이터를 전송하는 데 기본적으로 사용되는 HTTP는 본질적으로 상태가 저장되지 않는다. 즉, 동일한 사용자로부터 여러 요청이 들어와도 이를 인식하지 못한다. 그러나 웹 애플리케이션에서 사용자 인증, 장바구니 등과 같은, 상태
를 유지하는 것이 중요한 시나리오가 있다. 세션은 웹 애플리케이션이 특정 기간 동안 사용자를 인식하고 구분할 수 있게 함으로써 이러한 한계를 극복하는 데 중요한 역할을 한다.
🔍 세션은 언제 생성되는가?
세션은 다음과 같은 경우에 생성된다.
- HttpServletRequest의 request.getSession()을 명시적으로 호출할 때 생성된다.
- Spring Security를 사용하는 경우
SessionCreationPolicy
에 따라 생성된다.- SessionCreationPolicy.IF_REQUIRED: 필요한 경우에만 생성 (기본값)
🔍전통적인 세션의 문제점
전통적인 HttpSession은 컨테이너(ex. Tomcat) 에 의해 생성되고 종속되어 사용된다. 즉 컨테이너가 세션을 생성하고 애플리케이션에 세션 ID를 전달한다. 이는 컨테이너가 자체 HTTPSession을 관리한다는 것을 의미하며, 분산 환경에서 실행되는 경우 다른 서버 간에 세션 공유를 불가능하게 하는 원인이 된다.
✏️ 고정 세션
이를 해결하기 위해 고정 세션 접근 방식을 사용할 수 있다.
- 로드밸런서 또는 웹 서버가 클라이언트와 앱 서버 간에 매칭을 한다.
- 이 작업은 로드 밸런서가 식별을 위해 클라이언트에 쿠키를 할당함으로써 수행된다.
- 로드 밸런서는 세션이 진행되는 동안 클라이언트의 각 요청을 동일한 애플리케이션 서버로 라우팅 한다.
고정 세션 방식은 다음과 같은 문제점을 가지고 있다.
- 특정 서버에 과부하가 걸릴 수 있다.
- A서버가 다운되면 B서버는 A서버의 세션에 대한 정보를 얻지 못한다.
🔍 Spring Session
이러한 문제를 해결하기 위해 Spring에서는 HTTP Session을 중앙에서 관리하도록 도와준다.
Session Storage 방식을 사용하면 다음과 같은 장점을 가질 수 있다.
- 애플리케이션 재시작 시 세션 복원이 가능하다.
- 로드 밸런서에서 세션을 고정할 필요가 없다.
-> 특정 서버에 과부하를 주지 않고 독점적으로 분산할 수 있다.
단점
- Session Storage에 장애가 발생하면 모든 세션을 잃어버려 세션을 사용하는 모든 서버에 영향을 미친다. 이를 해결하기 위해 마스터-슬레이브 복제 방법을 사용할 수 있다.
- 네트워크 I/O가 발생하기 때문에 로컬 메모리보다는 성능적인 면에서 떨어진다.
🔍️Spring Session의 원리
Spring Session은 애플리케이션과 Session management간의 추상화 계층을 제공함으로써 NoSQL, RDBS 등과 같은 영구 저장소에 저장할 수 있도록 도와준다. 따라서 어떤 데이터베이스를 사용하든 Spring Session이 제공하는 API를 통해 똑같은 방법으로 관리가 가능하다. 사용할 수 있는 저장소로는 다음 JDBC
, Redis
, MongoDB
등이 있다.
Spring Session은 HttpSession을 필터와 Wrapper로 감쌈으로써 기능을 제공한다.
- SessionRepositoryRequestWrapper를 이용하여 기본 HttpSession을 커스텀 세션으로 바꾼다.
1 2 3 4 5 6
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest { public HttpServletRequestWrapper(HttpServletRequest request) { super(request); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
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 요청을 세션 리포지토리 요청 래퍼로 대체한다.
1 2 3 4 5 6 7 8 9 10 11 12
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파일에 설정해주는 것만으로 위의 작업을 자동화해준다.
1
implementation("org.springframework.boot:spring-boot-starter-data-redis")
1
spring.session.store-type=redis # Session store type.
이렇게 하면 Filter를 구현하는 springSessionRepositoryFilter라는 이름의 Spring 빈이 생성되며, 이 필터는 스프링 세션이 지원하도록 HttpSession 구현을 대체하는 역할을 담당한다.
🔍 결론
- 웹 애플리케이션의 세션 관리는 사용자 인증 및 쇼핑 카트와 같은
상태
유지가 필요한 경우에 매우 중요하다. - 전통적인 HttpSession은 분산 환경에서의 한계를 가지고 있으며, 이를 해결하기 위해 스프링 세션은 중앙집중식으로 세션을 관리한다.
- 세션 저장소 다운으로 인한 전체 세션 손실의 가능성과 네트워크 I/O에 의한 성능 저하와 같은 단점도 고려해야 한다.