Redis 기반 세션 공유
Redis는 초당 수십만 건의 읽기/쓰기를 처리할 수 있는 인메모리 데이터 저장소입니다. 모든 Tomcat 인스턴스가 하나의 Redis를 공유 세션 저장소로 사용하면, 어느 서버에서든 동일한 세션에 접근할 수 있습니다.
Redis 세션 공유 아키텍처
[클라이언트]
│
[Load Balancer]
├── [App1: Tomcat] ─▶ ┌──────────────┐
├── [App2: Tomcat] ─▶ │ Redis │
└── [App3: Tomcat] ─▶ │ 세션 저장소 │
└──────────────┘
모든 Tomcat이 Redis에 세션을 저장하고 조회합니다. 어느 서버로 요청이 가든 동일한 Redis에서 세션을 읽으므로 불일치가 발생하지 않습니다.
Redis 설치 및 기본 설정
# Ubuntu
sudo apt-get install redis-server
# CentOS
sudo yum install redis
# 시작
sudo systemctl enable --now redis
# 연결 확인
redis-cli ping
# PONG
# 기본 설정 확인
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy
Redis 설정 최적화 (세션용)
# /etc/redis/redis.conf
# 외부 접속 허용 (필요한 경우)
bind 0.0.0.0 # 또는 특정 IP: bind 10.0.0.10
# 패스워드 설정 (필수)
requirepass your-strong-password-here
# 세션 만료 시 메모리 자동 정리
maxmemory 2gb
maxmemory-policy allkeys-lru # 메모리 부족 시 LRU 방식으로 키 삭제
# 지속성 비활성화 (세션 전용이면 RDB/AOF 불필요)
save ""
appendonly no
방법 1: Tomcat Redis Session Manager
tomcat-redis-session-manager 라이브러리를 사용해 Tomcat의 기본 세션 매니저를 Redis 기반으로 교체합니다.
의존성 추가
# JAR 다운로드 후 Tomcat lib 폴더에 복사
wget https://github.com/jcoleman/tomcat-redis-session-manager/releases/download/2.0.0/tomcat-redis-session-manager-2.0.0.jar
cp tomcat-redis-session-manager-2.0.0.jar /opt/tomcat/lib/
# Jedis 클라이언트도 필요
wget https://repo1.maven.org/maven2/redis/clients/jedis/4.4.3/jedis-4.4.3.jar
cp jedis-4.4.3.jar /opt/tomcat/lib/
# Commons Pool2 (Jedis 의존성)
wget https://repo1.maven.org/maven2/org/apache/commons/commons-pool2/2.11.1/commons-pool2-2.11.1.jar
cp commons-pool2-2.11.1.jar /opt/tomcat/lib/
context.xml 설정
<!-- /opt/tomcat/conf/context.xml -->
<Context>
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve"/>
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="10.0.0.10"
port="6379"
password="your-strong-password-here"
database="0"
maxInactiveInterval="1800"
sessionPersistPolicies="SAVE_ON_CHANGE"
/>
</Context>
파라미터 설명
| 파라미터 | 설명 | 기본값 |
|---|---|---|
host | Redis 서버 주소 | localhost |
port | Redis 포트 | 6379 |
password | Redis 패스워드 | (없음) |
database | Redis DB 번호 (0~15) | 0 |
maxInactiveInterval | 세션 만료 시간(초) | 1800 |
sessionPersistPolicies | 저장 정책 | SAVE_ON_CHANGE |
방법 2: Spring Session + Redis (권장)
Spring Boot 애플리케이션에서 가장 쉽고 안정적인 방법입니다. @EnableRedisHttpSession 하나로 세션 저장소가 Redis로 전환됩니다.
의존성 추가 (build.gradle)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.session:spring-session-data-redis'
}
application.yml 설정
spring:
data:
redis:
host: 10.0.0.10
port: 6379
password: your-strong-password-here
timeout: 2000ms
lettuce:
pool:
max-active: 10
max-idle: 5
min-idle: 1
max-wait: 1000ms
session:
store-type: redis
timeout: 30m # 세션 만료 시간
redis:
namespace: myapp # Redis 키 접두사 (여러 앱 분리 가능)
flush-mode: on-save # on-save: 변경 시에만 저장, immediate: 즉시 저장
Spring Boot 설정 클래스
// src/main/java/com/example/config/SessionConfig.java
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@Configuration
@EnableRedisHttpSession(
maxInactiveIntervalInSeconds = 1800,
redisNamespace = "myapp:session"
)
public class SessionConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 키: 문자열 직렬화
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 값: JSON 직렬화 (가독성 + 호환성)
GenericJackson2JsonRedisSerializer jsonSerializer =
new GenericJackson2JsonRedisSerializer();
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
template.afterPropertiesSet();
return template;
}
}
세션 사용 예시 (Controller)
@RestController
public class AuthController {
@PostMapping("/login")
public ResponseEntity<String> login(
@RequestBody LoginRequest request,
HttpSession session) {
// 인증 로직...
User user = authService.authenticate(request.getUsername(), request.getPassword());
// 세션에 저장 → Redis에 자동 저장됨
session.setAttribute("userId", user.getId());
session.setAttribute("username", user.getUsername());
session.setAttribute("role", user.getRole());
session.setMaxInactiveInterval(1800); // 30분
return ResponseEntity.ok("Login successful");
}
@GetMapping("/profile")
public ResponseEntity<UserProfile> getProfile(HttpSession session) {
Long userId = (Long) session.getAttribute("userId");
if (userId == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
// 어느 서버에서 처리해도 Redis에서 동일한 세션 조회
UserProfile profile = userService.findById(userId);
return ResponseEntity.ok(profile);
}
@PostMapping("/logout")
public ResponseEntity<Void> logout(HttpSession session) {
session.invalidate(); // Redis에서 세션 삭제
return ResponseEntity.ok().build();
}
}
Redis 세션 데이터 확인
# Redis에 저장된 세션 키 확인
redis-cli -h 10.0.0.10 -a your-password KEYS "myapp:session*"
# myapp:session:sessions:1a2b3c4d-...
# myapp:session:sessions:expires:1a2b3c4d-...
# 세션 내용 확인
redis-cli -h 10.0.0.10 -a your-password HGETALL "myapp:session:sessions:1a2b3c4d"
# 세션 TTL 확인 (남은 만료 시간)
redis-cli -h 10.0.0.10 -a your-password TTL "myapp:session:sessions:1a2b3c4d"
# 1623 (초)
# 특정 세션 강제 삭제 (관리자 기능)
redis-cli -h 10.0.0.10 -a your-password DEL "myapp:session:sessions:1a2b3c4d"
Redis 고가용성: Redis Sentinel과 Redis Cluster
Redis 자체가 단일 장애점이 되지 않도록 이중화합니다.
Redis Sentinel (자동 페일오버)
# application.yml (Spring Boot + Sentinel)
spring:
data:
redis:
sentinel:
master: mymaster
nodes:
- 10.0.0.10:26379
- 10.0.0.11:26379
- 10.0.0.12:26379
password: sentinel-password
password: redis-password
Redis Cluster (수평 확장)
# application.yml (Spring Boot + Cluster)
spring:
data:
redis:
cluster:
nodes:
- 10.0.0.10:7001
- 10.0.0.10:7002
- 10.0.0.11:7003
- 10.0.0.11:7004
- 10.0.0.12:7005
- 10.0.0.12:7006
max-redirects: 3
password: redis-cluster-password
성능 비교: 세션 전략별 응답 시간
로컬 세션 (단일 서버) : ~0.1ms (메모리 직접 접근)
Tomcat 클러스터링 : ~1~5ms (네트워크 복제 포함)
Redis 세션 (로컬) : ~0.5ms (Redis 루프백)
Redis 세션 (원격) : ~1~2ms (네트워크 1회 왕복)
Redis Cluster : ~2~5ms (클러스터 라우팅 포함)
일반 웹 서비스에서 Redis 세션 조회로 인한 추가 지연은 1~2ms 수준으로, 비즈니스 로직 처리 시간(수십~수백ms) 대비 무시할 수 있는 수준입니다.
다음 페이지에서는 세션 직렬화의 원리와 성능 최적화 방법을 알아봅니다.