여러분, 혹시 이렇게 생각해 본 적 있으신가요? "우리 회사 서버는 하나일까, 여러 개일까?" 사실, 대부분의 회사는 이미 다중 서버 환경을 사용하고 있을 가능성이 높습니다. 서버 하나로 모든 걸 처리하기엔 너무 많은 사용자와 데이터가 있습니다. 그렇다면 다중 서버 환경에서는 어떤 문제가 생길까요?
바로, 세션 데이터 관리입니다. 서버가 여러 대라면, 사용자의 세션 데이터가 특정 서버에만 저장된다면 큰 문제가 될 수 있습니다. 만약 그 서버가 다운된다면? 사용자는 로그아웃되거나 데이터를 잃을 수 있습니다. 그래서 등장하는 해결책이 바로 Redis입니다. Redis는 빠르고 유연하며, 이러한 세션 관리 문제를 깔끔하게 해결할 수 있습니다.
세션 객체와 Key-Value 데이터베이스
세션 데이터가 어떻게 생성되고 저장소에서 조회되는지 한눈에 파악할 수 있도록 시퀀스 다이어그램으로 정리해 보았습니다. 이 흐름을 이해하면 Redis를 활용한 세션 관리가 얼마나 간단하고 효율적인지 감이 올 겁니다!
세션 객체가 어떻게 구성되어 있는지 한번 살펴볼까요? 세션 객체는 간단히 말해서 Key랑 Value로 이루어져 있습니다.
- Key는 세션 ID처럼 세션을 고유하게 식별하는 값입니다.
- Value는 세션과 관련된 여러 정보를 담고 있습니다.
예를 들면, 세션이 언제 생성됐는지, 마지막으로 접근한 시간, 사용자 이름이나 권한 같은 것들이 있습니다.
세션 데이터 예시
세션 ID | d1kssudgktpdy2 |
생성 시간 | 2024-12-23T10:00:00Z |
마지막 접근 시간 | 2024-12-23T10:30:00Z |
사용자 이름 | 렘 |
사용자 권한 | 관리자 |
이런 데이터는 보통 Map처럼 Key-Value 형태로 저장됩니다. 이런 구조에 잘 맞는 데이터 저장 방식이 바로 Key-Value 데이터베이스입니다.
Key-Value 데이터베이스는 아주 단순한 구조라 관계형 데이터베이스처럼 복잡한 조회는 어렵지만, 단일 키 기반 작업에서는 매우 빠르고 효율적입니다. 그래서 Redis 같은 Key-Value 데이터베이스가 세션 저장소로 많이 활용됩니다. 세션 데이터를 읽고 쓰는 데는 이만한 친구가 없습니다!
Redis로 세션 관리하는 이유는?
현재 메모리 기반 세션 관리를 사용하고 있다면 몇 가지 한계점을 경험했을 수 있습니다.
- 서버 재시작 시 데이터 손실 : 세션 데이터가 메모리에만 저장되기 때문에 서버가 재시작되면 데이터가 날아가 버립니다.
- 다중 서버 환경에서의 한계 : 서버가 여러 대라면 세션 데이터를 공유하기 어려워 확장성이 떨어집니다.
- 증가하는 사용자 수 대응 문제 : 사용자가 많아질수록 특정 서버에 부하가 집중될 수 있습니다. 특히 세션 데이터가 특정 서버에 종속되면 장애가 발생했을 때 사용자 경험이 크게 저하됩니다.
"세션 관리에 Redis가 왜 좋을까?"라는 질문이 떠오르셨다면, 이렇게 생각해 보세요. 세션 데이터는 실시간으로 읽고 써야 하고, 트래픽이 몰릴 때도 빠르고 안정적으로 처리할 수 있어야 하니까요! Redis는 바로 이 점에서 최적화된 선택입니다.
Redis는 데이터를 메모리에 저장하기 때문에 읽기와 쓰기 속도가 매우 빠릅니다. 기존 방식처럼 즉각적인 응답 속도를 제공하면서도, 필요한 경우 데이터를 디스크로 동기화해 서버를 재시작해도 데이터를 안전하게 복구할 수 있습니다. 이런 안정성은 Redis가 기존 메모리 기반 방식과 차별화되는 중요한 이유 중 하나입니다.
또한 Redis는 네트워크를 통해 데이터를 공유할 수 있기 때문에, 다중 서버 환경에서도 문제없이 세션 데이터를 관리할 수 있습니다. 즉, 특정 서버에만 데이터를 의존하지 않으니 확장성과 부하 분산이 훨씬 용이해집니다.
Redis의 TTL(Time-to-Live) 기능은 세션 관리의 효율성을 극대화합니다. 세션 데이터를 저장할 때 만료 시간을 설정하면, 시간이 지나면 자동으로 데이터가 삭제됩니다. 덕분에 비활성 데이터를 별도로 정리하지 않아도 되고, 메모리 자원을 더 효율적으로 사용할 수 있습니다. 예를 들어, 아래와 같이 간단히 TTL을 설정할 수 있습니다.
SET session:abc123 "user_data" EX 3600
이 명령은 세션 데이터를 3600초(1시간) 동안만 유지하도록 설정합니다. 시간이 지나면 Redis가 알아서 데이터를 삭제해 관리 부담을 줄여줍니다.
Redis가 제공하는 해시(Hash) 구조는 세션 데이터를 필드별로 나누어 저장하고 관리할 수 있어 매우 효율적입니다. 예를 들어, 한 사용자의 세션 정보를 다음과 같이 저장할 수 있습니다.
# 세션 데이터 저장
HSET session:abc123 user_name "rem"
HSET session:abc123 role "admin"
HSET session:abc123 last_access "2024-12-23T12:00:00"
# 세션 데이터 조회
HGET session:abc123 user_name
# 결과: "rem"
필요한 데이터만 빠르게 조회할 수 있어 메모리를 절약하면서도 조회 성능이 뛰어납니다. 특히 해시 외에도 Redis는 리스트, 셋, 정렬된 셋 등 다양한 데이터 구조를 지원해 세션 데이터를 유연하게 관리할 수 있습니다.
확장성과 안정성도 Redis의 큰 장점입니다. Redis는 클러스터링과 샤딩 기능을 통해 대규모 트래픽 증가에도 안정적으로 대응할 수 있습니다.
- 클러스터링은 데이터를 복제해 장애 발생 시에도 빠르게 복구할 수 있게 해줍니다.
- 샤딩은 데이터를 여러 노드에 분산 저장하여 부하를 분산하고 성능을 향상시킵니다.
예를 들어, Redis 클러스터 환경에서 데이터를 다음과 같이 분산할 수 있습니다.

- Redis 클러스터: 여러 Redis 노드를 하나의 클러스터로 묶어서 데이터를 분산 저장합니다. 클러스터 내에서 데이터를 분할하고 복제하여 높은 가용성과 확장성을 제공합니다.
- 샤딩: 클러스터 내 각 Redis 노드는 데이터를 샤딩(분할)하여 저장합니다. 예를 들어, Key 1은 Node 1에, Key 2는 Node 2에 저장되는 식으로 분산됩니다. 이를 통해 부하를 분산시키고 성능을 향상시킵니다.
- 클라이언트는 Redis 클러스터에 연결하여 데이터를 요청하고, 요청된 데이터는 해당하는 노드에서 처리됩니다. 만약 하나의 노드에 장애가 발생하더라도 다른 노드가 데이터를 처리할 수 있어 안정성도 보장됩니다.
이런 구조는 특히 사용자 수가 폭발적으로 증가하거나 트래픽이 몰릴 때 큰 효과를 발휘합니다.
유저 정보 수정 시 Redis와 RDB 간 싱크 방법
Redis는 빠르고 유연한 세션 관리 솔루션이지만, 서비스 운영 중에는 데이터 일관성을 유지해야 하는 과제가 발생할 수 있습니다. 예를 들어, 사용자가 자신의 이름을 변경한 경우 RDB에는 업데이트된 정보가 저장되지만, Redis에는 이전 정보가 남아 있을 수 있습니다. 이런 상황에서 Redis와 RDB 간의 데이터 싱크를 어떻게 해결할 수 있을까요?
RDB와 Redis 간의 데이터 불일치를 해결하기 위해 몇 가지 대표적인 전략을 사용할 수 있습니다. 아래 예시로 살펴볼까요?
1. 실시간 업데이트 트리거
"정보가 바뀌면 바로바로 반영해야 한다!"
- 이 방법은 사용자가 이름을 수정하거나 정보를 변경했을 때, RDB에 저장한 데이터를 Redis에도 즉시 업데이트하는 방식입니다. 데이터가 바로 동기화되기 때문에, Redis와 RDB가 따로 노는 일이 없습니다.
// RDB에 사용자 정보 업데이트
async function updateUserInDB(userId: string, newName: string): Promise<void> {
// RDB 업데이트 로직 (예: SQL 쿼리)
await db.query('UPDATE users SET name = ? WHERE id = ?', [newName, userId]);
}
// Redis의 세션 데이터 동기화
async function updateUserInRedis(sessionId: string, newName: string): Promise<void> {
await redisClient.hSet(`session:${sessionId}`, 'user_name', newName);
}
// 통합 실행
async function updateUser(userId: string, sessionId: string, newName: string): Promise<void> {
await updateUserInDB(userId, newName);
await updateUserInRedis(sessionId, newName);
}
어떻게 동작하나요?
- 사용자가 정보를 수정하면 RDB에 먼저 저장됩니다.
- 저장된 정보가 Redis 세션 데이터에도 바로 업데이트됩니다.
- 결과적으로 최신 정보를 Redis에서도 바로 사용할 수 있습니다.
장점과 단점
- 장점: 항상 최신 데이터를 Redis에 유지할 수 있어서 깔끔합니다.
- 단점: 매번 RDB와 Redis를 둘 다 업데이트하니까 트랜잭션 비용이 살짝 늘어날 수 있습니다.
2. TTL(Time-to-Live) 활용
"한 번 저장하고 시간이 지나면 알아서 갱신되게 하자!"
- TTL은 Redis 데이터를 자동으로 만료시켜서 오래된 정보가 남아 있지 않도록 관리하는 방법입니다.
만료된 데이터는 다음 요청 때 RDB에서 최신 정보를 가져와 다시 Redis에 저장합니다.
// Redis에 세션 데이터 설정 및 TTL 적용
async function setSessionWithTTL(sessionId: string, userData: string, ttl: number): Promise<void> {
await redisClient.set(`session:${sessionId}`, userData, { EX: ttl });
}
// 예시 사용
setSessionWithTTL('abc123', JSON.stringify({ user_name: 'rem', role: 'admin' }), 3600);
어떻게 동작하나요?
- 데이터를 저장할 때 TTL을 설정합니다. 예를 들어, 3600초(1시간) 동안 유효하도록 설정합니다.
- TTL이 만료되면 Redis 데이터는 자동 삭제됩니다.
- 삭제 후, 다음 요청 때 RDB에서 최신 데이터를 다시 가져옵니다.
장점과 단점
- 장점: 자동으로 오래된 데이터를 삭제하니 관리 부담이 줄게 됩니다.
- 단점: TTL 기간 동안에는 Redis와 RDB 간에 데이터가 다를 수 있습니다.
3. 이벤트 기반 동기화
"변경될 때만 동기화하면 효율적이지 않을까?"
- 이 방법은 RDB에서 데이터가 바뀔 때마다 이벤트를 발행해서 Redis가 업데이트를 받는 방식입니다.
Kafka나 RabbitMQ 같은 메시지 큐를 사용해서 동기화를 처리합니다.
// 이벤트 발행 함수 (Kafka나 RabbitMQ 사용 가능)
async function publishUserUpdateEvent(userId: string, newName: string): Promise<void> {
const eventPayload = JSON.stringify({ user_id: userId, new_name: newName });
await messageQueue.publish('user_update', eventPayload);
}
// Redis 동기화 함수
async function onUserUpdate(event: { user_id: string; new_name: string }): Promise<void> {
await redisClient.hSet(`session:${event.user_id}`, 'user_name', event.new_name);
}
// 통합 실행
async function updateUserAndPublishEvent(userId: string, newName: string): Promise<void> {
await updateUserInDB(userId, newName); // RDB 업데이트
await publishUserUpdateEvent(userId, newName); // 이벤트 발행
}
어떻게 동작하나요?
- RDB에서 사용자 정보가 바뀌면, 이벤트가 발행됩니다.
- 메시지 큐를 통해 Redis로 이벤트가 전달됩니다.
- Redis가 이벤트를 수신해서 세션 데이터를 업데이트합니다.
장점과 단점
- 장점: 대규모 트래픽 환경에서도 확장성이 좋습니다.
- 단점: 이벤트 처리에 지연이 발생하면 잠깐 동안 Redis와 RDB가 달라질 수 있습니다.
4. Lazy Loading (지연 로딩)
"요청이 들어올 때만 최신 상태로 갱신하자!"
- 이 방법은 클라이언트 요청이 있을 때만 Redis 데이터를 RDB와 비교해 필요할 때만 동기화하는 방식이에요.
// Redis 데이터 조회 및 비교
async function syncRedisWithDBIfStale(sessionId: string, userId: string): Promise<void> {
const redisData = await redisClient.hGetAll(`session:${sessionId}`);
const rdbData = await db.query('SELECT name, role FROM users WHERE id = ?', [userId]);
// 데이터 비교 후 Redis 갱신
if (redisData.user_name !== rdbData[0].name) {
await redisClient.hSet(`session:${sessionId}`, {
user_name: rdbData[0].name,
role: rdbData[0].role,
});
}
}
어떻게 동작하나요?
- 클라이언트 요청이 들어오면 Redis 데이터를 조회합니다.
- Redis 데이터와 RDB 데이터를 비교합니다.
- 다를 경우, Redis 데이터를 RDB 데이터로 갱신합니다.
장점과 단점
- 장점: 불일치가 발생할 때만 동기화하니 비용이 줄어듭니다.
- 단점: 요청이 많아지면 RDB와 Redis 비교 작업이 많아져 성능에 영향을 줄 수 있습니다.
포인트!
- 실시간 업데이트 트리거: 데이터 변경 시 즉시 동기화 → 최신성 보장
- TTL 활용: 자동 만료로 관리 부담 감소 → 단순한 관리 방식
- 이벤트 기반 동기화: 이벤트로 효율적인 동기화 → 확장성 탁월
- Lazy Loading: 요청 시 갱신 → 비용 절감
5. 수정 시 데이터 동기화 흐름

1. 클라이언트 → RDB
- 클라이언트가 사용자 정보를 수정하기 위한 요청을 서버로 보냅니다. 이 요청은 RDB에 데이터를 저장하거나 업데이트하는 작업을 포함합니다.
- 예시 : 사용자가 이름 변경 요청을 보냈을 때, 새로운 이름 데이터를 서버에서 처리합니다.
"클라이언트의 요청에 따라 RDB에 사용자 데이터를 저장하거나 수정하는 단계입니다."
2. RDB → Redis
- RDB에서 사용자 정보가 성공적으로 저장된 후, 해당 데이터를 Redis에 업데이트합니다. Redis는 세션 데이터를 관리하므로 최신 정보로 동기화가 필요합니다.
- 이 단계는 데이터 일관성을 유지하기 위해 수행됩니다.
- 예시 : RDB에서 업데이트된 사용자 이름이 Redis 세션 데이터의 user_name 필드에 저장됩니다.
"RDB에 저장된 최신 데이터를 Redis 세션 데이터와 동기화하는 단계입니다."
3. Redis → 클라이언트
- Redis가 동기화 작업을 완료한 뒤, 클라이언트에 성공 응답을 반환합니다. 이를 통해 클라이언트는 데이터가 정상적으로 수정되었음을 확인할 수 있습니다.
- 클라이언트 입장에서는 데이터가 실시간으로 수정된 것처럼 보입니다.
"Redis에서 데이터를 성공적으로 동기화한 뒤, 클라이언트에게 작업 완료 응답을 반환합니다."
추천 전략
- 실시간 최신 데이터가 중요할 때: 실시간 업데이트 트리거 또는 이벤트 기반 동기화가 좋습니다.
- 트래픽이 많고 비용 효율성이 필요할 때: TTL 설정이나 Lazy Loading가 좋습니다.
왜 Redis가 최선일까요?
세션 관리에서 Redis가 최선인 이유를 이해하려면, 다른 대안들과 비교해 보는 것이 가장 빠릅니다. Redis는 단순히 빠르다는 것만으로 선택받는 게 아닙니다. 이 친구가 얼마나 세션 관리에 특화되어 있는지, 다른 대안들과 어떻게 차별화되는지 살펴볼까요?
Redis vs RDBMS (MySQL, PostgreSQL 등)
관계형 데이터베이스는 데이터 무결성과 복잡한 쿼리를 잘 처리하는 데 강점이 있지만, 세션 관리에는 다소 무겁게 느껴질 수 있습니다. 간단한 작업을 할 때는 NoSQL이나 인메모리 데이터베이스가 훨씬 더 효율적일 수 있습니다.
- 세션 데이터는 단순히 키-값으로 저장되고 빠르게 읽고 쓰는 게 핵심인데, RDBMS는 이런 단순 작업에 비효율적일 수 있습니다.
- 쿼리 처리에 걸리는 시간과 디스크 기반 저장은 Redis의 메모리 기반 속도와 비교가 안 됩니다!
결론: 세션 관리에서는 빠른 응답 시간과 단순 저장이 중요하기 때문에 Redis가 훨씬 더 적합합니다. RDBMS는 세션 관리보다는 복잡한 데이터 관계를 다룰 때 좋습니다.
Redis vs Memcached
Memcached도 메모리 기반 데이터베이스라 속도 면에서는 Redis와 비슷해 보일 수 있습니다. 하지만 깊게 들여다보면 큰 차이가 있습니다.
- TTL 외 기능 부족: Memcached는 TTL(Time-to-Live)을 지원하지만, 세션 관리에 필요한 다양한 데이터 구조(예: 해시, 리스트 등)는 지원하지 않습니다.
- Redis는 해시(Hash) 구조로 세션 데이터를 깔끔하게 관리할 수 있습니다. 예를 들어, 사용자 이름, 권한, 마지막 접근 시간을 필드별로 저장하고 조회할 수 있습니다.
- 확장성 차이: Memcached는 클러스터링이나 샤딩 기능이 상대적으로 제한적입니다. 반면 Redis는 클러스터링과 샤딩을 통해 대규모 트래픽에도 안정적으로 대응할 수 있습니다.
- 데이터 지속성: Memcached는 데이터를 메모리에만 저장하고 휘발성이 강합니다. Redis는 필요에 따라 데이터를 디스크로 동기화할 수 있어 서버 재시작 후에도 복구가 가능합니다.
결론: Memcached는 단순 캐싱 작업에는 적합하지만, 세션 관리처럼 데이터 구조와 안정성이 중요한 작업에서는 Redis가 더 유리합니다.
다른 대안들과 비교했을 때 Redis가 세션 관리에 최적인 이유를 정리하자면
- 빠른 속도: 메모리 기반 데이터 저장으로 실시간 처리에 최적화
- 다양한 데이터 구조 지원: 해시, 리스트, 셋 등으로 유연한 세션 데이터 관리 가능
- 확장성: 클러스터링과 샤딩을 통해 대규모 트래픽에도 안정적
- 자동화된 관리: TTL 기능으로 비활성 데이터를 자동으로 정리
- 데이터 지속성: 필요 시 데이터를 디스크에 저장해 안정성 제공
Redis는 단순히 "빠르다"는 이유만으로 선택되는 게 아닙니다. 세션 관리에 필요한 모든 특성을 갖추고 있고, 다른 대안과 비교해도 압도적인 장점을 보여줍니다.
세션 관리에서는 Redis를 선택하는 것이 단순한 옵션이 아니고, 가장 합리적이고 효율적인 선택입니다.
결론
Redis는 정말 간단하면서도 강력한 친구입니다. 빠른 처리 속도는 기본이고, TTL 설정부터 클러스터링, 다양한 데이터 구조 지원까지... 세션 관리에 필요한 거의 모든 걸 갖췄다고 해도 과언이 아닙니다. 특히, 시스템이 확장되거나 다중 서버 환경에서 문제가 생길까 봐 걱정하던 분들에게는 완전 구원투수 같은 존재입니다. 기존 세션 관리의 복잡함 때문에 골치 아팠다면, Redis가 딱 그 해답이 될 수 있습니다. 속도도 빠르고, 안정적이고, 심지어 관리도 쉽다니! Redis를 도입하면 여러분의 시스템이 더 깔끔하고 안정적으로 세션을 관리할 수 있는 방식으로 변화하지 않을까요? 한번 써보면 "왜 진작 안 썼지?" 하고 감탄하게 될 겁니다! 😉
Reference
https://hyuntaeknote.tistory.com/8
다중 서버 환경에서 Session은 어떻게 공유하고 관리할까? - 4편(Redis vs Memcached)
개요 지난 시간 세션 저장소로 In-Memory Database를 사용하기로 하였습니다. 하지만, In-Memory Database에는 다양한 데이터베이스들이 존재합니다. 각각의 데이터베이스는 저장하는 데이터의 형태 및 특
hyuntaeknote.tistory.com
https://goodgid.github.io/Redis/
Redis 개념과 특징
goodgid.github.io
https://f-lab.kr/insight/session-management-with-redis
레디스를 활용한 세션 관리 방법
레디스를 활용한 세션 관리 방법, 장점 및 구현 방법에 대해 설명합니다.
f-lab.kr
대용량 트래픽 처리를 위한 로그인 설계 : Redis
지난번 Sacle out 방식을 선택하고 세션 불일치 문제점을 해결할 수 있는 방안으로 Session Clustering 기술을 살펴보았습니다.그러나 Session Clustering은 늘어나는 서버의 갯수만큼 세션 정보를 복제 해야
velog.io
https://curiousjinan.tistory.com/entry/redis-to-rdb-data-sync
[Spring] Redis에서 RDB로 조회수 동기화하기
일정 시간마다 Redis의 데이터를 RDB에 저장해 보자 📌 서론 내가 만든 요리 SNS어플인 "레시피아"에서는 유저가 레시피를 조회하면 조회수 데이터를 Redis에 계속 누적시킨다. 만약 유저가 레시피
curiousjinan.tistory.com
https://velog.io/@jinmin2216/Redis-5.-Redis-Caching-%EC%A0%84%EB%9E%B5
[Redis] 5. Redis Caching 전략
레디스에서 캐시를 어떤 방식으로 사용할까?
velog.io
https://pompitzz.github.io/blog/Redis/LocalCacheSyncWithRedisPubSub.html#redis-pub-sub
Redis(Pub/Sub)로 로컬 캐시 동기화하기 | BLOG
Redis(Pub/Sub)로 로컬 캐시 동기화하기 작성일: 2021-12-30 21:00 대규모 트래픽이 발생하는 Stateless 애플리케이션 웹 서버를 운영할 때 낮은 레이턴시와 데이터베이스 부하 감소를 위해 캐시는 필수적
pompitzz.github.io
https://dgjinsu.tistory.com/32
[Redis] Spring boot 에서 Redis로 성능 개선하기
Cache란? 나중에 할 요청 결과를 미리 저장 해 두었다가 빠르게 서비스 해 주는 것 Look aside Cache(일반적으로 많이 쓰는 방식) DB를 가기 전에 Cache에 먼저 데이터가 있는지 확인 해본다. 만약 Cache에
dgjinsu.tistory.com
'DB' 카테고리의 다른 글
트랜잭션이란? (0) | 2024.07.23 |
---|---|
DB 파티셔닝(Partitioning)이란 (2) | 2024.07.22 |
[DB] DDL, DML, DCL, TCL 이란? (0) | 2024.07.21 |
관계형 DB와 비관계형 DB의 차이(RDBMS vs NoSQL) (4) | 2024.07.11 |
[DB] 관계형 데이터베이스의 1:1, 1:N, N:M 관계 (4) | 2024.07.10 |