본문으로 건너뛰기

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>

파라미터 설명

파라미터설명기본값
hostRedis 서버 주소localhost
portRedis 포트6379
passwordRedis 패스워드(없음)
databaseRedis 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) 대비 무시할 수 있는 수준입니다.

다음 페이지에서는 세션 직렬화의 원리와 성능 최적화 방법을 알아봅니다.