Redis를 포함한 3-Tier DB 구조로 채팅 서버 확장하기

shjk·2025년 3월 26일

이전 글에서는 Go 언어 기반 채팅 서버에 PostgreSQL과 MongoDB를 함께 사용하는 이유를 설명하였다. 이번 글에서는 여기에 Redis를 추가하여 성능과 실시간성을 극대화한 3-Tier Persistence 구조를 소개한다.


1. Redis의 특징과 역할

✅ Redis란?

Redis는 메모리 기반의 고속 키-값 저장소로, Pub/Sub, 캐싱, 세션 저장소 등 다양한 실시간 기능을 제공한다.

✅ Redis가 필요한 이유

  • 채팅 서버는 낮은 지연 시간, 빠른 응답성이 핵심이다.
  • 특히 메시지 브로드캐스트, 읽음 처리, 유저 온라인 상태 등은 DB 접근 없이 빠르게 처리되어야 한다.
  • 이를 위해 Redis를 실시간 처리 전용 계층으로 도입한다.

2. 3-Tier 구조의 개요

각 DB가 담당하는 역할은 다음과 같다:

계층저장소주 역할
실시간 계층Redis실시간 메시지 중계, 읽음 상태, 유저 presence
반정형 계층MongoDB메시지 로그 저장 (비정형, 대량)
정형 계층PostgreSQL사용자, 채팅방, 메타 정보 저장 (정형, 관계형)

이 구조는 다음과 같은 장점을 갖는다:

  • Redis: 빠른 읽기/쓰기 + Pub/Sub → 실시간성 확보
  • MongoDB: 유연한 메시지 저장 구조 → 확장성 확보
  • PostgreSQL: 복잡한 관계형 쿼리 처리 → 정합성 확보

3. Redis의 활용 방식

3.1. 메시지 중계 (Pub/Sub)

// 발신자 메시지를 Redis Pub 채널로 전송
redisClient.Publish(ctx, "chatroom:{id}", serializedMessage)
// 수신자는 해당 채널을 구독하여 메시지를 수신
sub := redisClient.Subscribe(ctx, "chatroom:{id}")
msg, _ := sub.ReceiveMessage(ctx)

메시지는 MongoDB에 영속 저장하면서, Redis를 통해 실시간 브로드캐스트됨.


3.2. 사용자 Presence 및 세션 캐싱

  • Redis Set/Hash를 사용하여 접속 중인 유저 목록, 마지막 활동 시간을 관리함.
  • 웹소켓 연결이 끊기면 Redis에서 해당 유저의 presence를 제거함.
redisClient.HSet(ctx, "user:{id}:session", "online", true)
redisClient.Expire(ctx, "user:{id}:session", time.Minute*5)

3.3. 읽음 처리 캐싱

  • 읽음 상태는 고빈도 접근이 발생하므로, PostgreSQL이 아닌 Redis에서 관리함.
  • 예: 유저가 메시지를 마지막으로 읽은 시간 → ZADD로 관리
redisClient.ZAdd(ctx, "chatroom:{id}:read_status", redis.Z{
    Score:  float64(time.Now().Unix()),
    Member: userID,
})

4. 전체 흐름 아키텍처

[ Client ]
    ↓ WebSocket
[ Go 채팅 서버 ]
    ┣ Redis (실시간 중계 및 상태)
    ┣ MongoDB (메시지 저장)
    ┗ PostgreSQL (유저/채팅방 관리)

메시지 발신 시 흐름:

  1. Redis를 통해 모든 참여자에게 실시간 중계
  2. MongoDB에 메시지 본문 저장
  3. PostgreSQL에는 채팅방 메타 정보 갱신 (예: 마지막 메시지 시간)

5. 결론: 3-Tier 저장 전략의 효과

항목효과
응답 속도Redis 덕분에 실시간 메시지 처리 가능
확장성MongoDB로 메시지 저장 구조의 유연성 확보
정합성PostgreSQL로 사용자 및 권한 관계 정확히 관리
유지보수역할별 분리된 DB 구조로 책임 명확화

결과적으로 Redis, MongoDB, PostgreSQL의 3-Tier 구조는 실시간성과 정합성, 확장성을 동시에 충족하는 채팅 서버 아키텍처의 이상적인 구현이다.


참고: Redis TTL 전략

읽음 상태, presence 등의 임시 데이터는 TTL(Time To Live)을 활용하여 주기적으로 만료 처리한다. 이를 통해 메모리 점유율을 최소화하고, 별도의 정리 작업 없이 상태 유지가 가능하다.

redisClient.Set(ctx, "user:{id}:online", true, time.Minute*3)

마무리

3-Tier Persistence 아키텍처는 다음과 같은 환경에 특히 유효하다.

  • 실시간 웹소켓 기반 채팅 시스템
  • 대량 메시지 저장과 빠른 조회가 필요한 서비스
  • 접속자 상태 및 동시성 처리가 중요한 환경

추후에는 Kafka를 통한 메시지 스트리밍 처리 구조와 연결하여, 메시지 저장 및 분석 로직까지 비동기적으로 분리하는 구조도 고려할 수 있다.

profile
백엔드 개발자

0개의 댓글