Sticky Session 없이 세션 공유? Tomcat에서 진짜 구현한 방법
실제 업무 중 톰캣 2대에 클러스터 구성을 하면서 로드밸런싱을 걸었는데, 로그인 유지가 안 되는 문제가 생겼다. 원인을 분석해보니 기존엔 Sticky Session에 의존해서 세션을 유지하고 있었던 것. 즉, 로그인한 사용자는 항상 같은 톰캣 인스턴스에 붙기 때문에 세션 공유가 필요 없었다.
그런데 이번에는 Sticky Session 없이 진짜로 톰캣 간 세션을 공유해야 했다. 로드밸런서에서 완전한 round-robin 방식으로 분산되기 때문에, 어느 서버든 같은 세션으로 동작해야 했다.
이 글에서는 Sticky Session 없이 Tomcat 간 세션을 공유하는 방법을 실무 기준으로 단계별 설명하고, 중간에 겪었던 삽질과 설정 팁까지 모두 정리해 본다.
목표: Tomcat 2대 간의 완전한 세션 공유
환경 요약
- Tomcat 9.x
- Java Servlet 기반 웹앱
- 로드밸런서: Nginx / AWS ALB 등 Sticky Session 없음
- 목표: 서버 A에서 로그인 → 서버 B에서도 로그인 유지
이걸 만족시키려면 결국 세션 복제(Session Replication)이 필요하다.
1단계: server.xml에서 클러스터 설정 활성화
Tomcat의 기본 클러스터링 기능을 사용하면, DeltaManager를 통해 세션을 메모리에서 복제할 수 있다. 서버 간 통신은 <Cluster> 태그로 구성된다.
<Engine name="Catalina" defaultHost="localhost">
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=""/>
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
</Cluster>
...
</Engine>
DeltaManager는 모든 세션 변경을 클러스터에 복제한다. 즉시 반영되므로 Sticky Session 없이도 세션이 유지된다.
2단계: web.xml에서 distributable 설정
세션 복제를 하려면 해당 웹 애플리케이션이 복제가 가능하다는 걸 명시해줘야 한다. web.xml에 아래 태그를 추가한다.
<web-app ...>
<distributable/>
</web-app>
이게 없으면 세션 복제 대상에서 제외되며, 클러스터링이 동작하지 않는다.
3단계: 세션에 복제 불가능한 객체가 있으면 안 됨
Tomcat 클러스터링은 세션 객체 전체를 직렬화(Serializable)해서 전송한다. 그러므로 세션에 저장하는 모든 객체는 반드시 implements Serializable이 되어야 한다.
public class LoginUser implements Serializable {
private String id;
private String name;
...
}
만약 Serializable이 아닌 객체를 세션에 담으면 replication 시 에러가 나거나 무시된다.
4단계: 멀티 서버 간 네트워크 통신 확인
세션 복제는 서버 간 UDP/Multicast 또는 TCP로 통신하기 때문에, 방화벽이나 보안그룹에서 포트를 허용해야 한다.
기본 포트 확인
- 클러스터 멤버십 포트:
228.0.0.4:45564(멀티캐스트) - Replication 수신 포트:
4000(TCP)
A서버 ↔ B서버 간 이 포트들이 열려 있어야 하고, 로컬 테스트 시에는 hosts 파일로 가상 IP 설정도 가능하다.
5단계: 로그 확인으로 정상 동작 여부 검증
Tomcat을 실행하면 아래와 같은 로그가 나오는지 확인해보자:
[INFO] org.apache.catalina.ha.tcp.SimpleTcpCluster - Cluster started with DeltaManager
[INFO] Replicating session for node: node1 to node2
또한 JSESSIONID가 서버 간 동일하게 유지되는지, 로그인 후 서버를 바꿔도 세션이 유지되는지를 테스트로 검증한다.
비교: Sticky Session vs 세션 복제
| 구분 | Sticky Session | 세션 복제 |
|---|---|---|
| 구현 난이도 | 낮음 | 중간 |
| 장애 대응 | 약함 (특정 서버 다운 시 로그아웃) | 강함 (다른 서버로도 로그인 유지) |
| 로드밸런싱 유연성 | 제한적 | 유연함 |
주의할 점
- 세션 객체는 직렬화 가능한 최소 단위로 유지할 것
- 세션 크기가 커지면 네트워크 부하와 레이턴시가 증가함
- 서버 수가 많아질수록 DeltaManager보단
BackupManager또는 Redis 도입 검토
마무리: Sticky Session 없이도 세션 공유는 가능하다
Tomcat의 클러스터링 기능만 잘 활용하면 굳이 Sticky Session에 의존하지 않아도 세션 공유가 가능하다. 물론 설정도 꼼꼼히 해줘야 하고, 세션에 들어가는 객체 구조도 신경 써야 한다. 하지만 일단 한번 잘 세팅해두면 장애 대응력과 확장성이 훨씬 좋아진다.
이번 실무 사례가 Sticky Session 없는 로드밸런싱 환경에서 세션 문제를 겪고 있는 분들에게 도움이 되길 바란다. 필요하다면 Redis 세션 클러스터링으로 확장하는 포스팅도 이어서 다룰 예정!
'웹개발 > 서버 운영 & 인프라' 카테고리의 다른 글
| Nginx와 Apache 차이점 총정리: 어떤 웹서버를 써야 할까? (1) | 2025.08.02 |
|---|---|
| 리눅스에서 crontab으로 서비스 자동 재시작 설정하는 방법 (실무 예제 포함) (5) | 2025.08.01 |
| AWS EC2에 WAR 파일 배포, 실무에서는 이렇게 합니다 (Tomcat 환경 기준) (1) | 2025.07.19 |
| Apache + Tomcat 로드밸런싱 구성 방법 (mod_jk vs mod_proxy 실무 설정 예제) (0) | 2025.06.26 |
| Tomcat 세션 클러스터링, Redis 없이 DeltaManager로 구현하는 방법 (1) | 2025.06.26 |