WebSocket은 클라이언트-서버 간 지속적인 연결을 유지하며 실시간 양방향 통신을 제공한다. 그러나 다중 서버 환경에서는 단일 서버 메모리만으로 사용자의 접속 상태(online/offline)를 추적할 수 없다. 이 문제를 해결하기 위해 Redis를 활용한 중앙 집중형 세션 관리가 필요하다.
✅ 핵심 목표: 분산 서버 환경에서도 사용자 접속 상태를 일관되게 추적 및 브로드캐스트 가능하게 설계
┌──────────────┐
│ Web Client │
└──────┬───────┘
│ WebSocket 연결
┌──────▼──────┐ ┌──────────────┐
│ WebSocket │──────────────▶ │
│ Server │ Redis Pub/Sub│ Redis DB │
│ (N개 서버) ◀──────────────│ │
└─────────────┘ └──────────────┘
│
▼
유저별 실시간 브로드캐스트 및 상태 추적
| 목적 | 설명 |
|---|---|
| 접속 유저 상태 공유 | 여러 서버가 Redis를 통해 유저의 접속 여부를 공유 가능 |
| 서버 간 메시지 브로드캐스트 | Pub/Sub 기능으로 WebSocket 메시지를 다른 서버로 전달 |
| 중앙 세션 저장소 | 유저 상태, 마지막 활동 시간 등을 저장 |
conn, _ := upgrader.Upgrade(w, r, nil)
userID := extractUserIDFromJWT(r)
redisClient.SAdd(ctx, "online_users", userID)
redisClient.Publish(ctx, "user_status", fmt.Sprintf("%s:online", userID))
SAdd: Redis Set에 사용자 ID 추가 (현재 접속 중인 사용자 목록)Publish: 상태 변경 이벤트를 모든 서버에 브로드캐스트conn.Close()
redisClient.SRem(ctx, "online_users", userID)
redisClient.Publish(ctx, "user_status", fmt.Sprintf("%s:offline", userID))
모든 서버는 Redis의 user_status 채널을 구독하고, 유저 상태 이벤트를 처리한다.
sub := redisClient.Subscribe(ctx, "user_status")
go func() {
for msg := range sub.Channel() {
handleUserStatus(msg.Payload) // 예: "user123:offline"
}
}()
onlineUsers, _ := redisClient.SMembers(ctx, "online_users")
| 키 이름 | 타입 | 설명 |
|---|---|---|
online_users | Set | 접속 중인 사용자 ID 저장 |
user_status | Pub/Sub | 서버 간 접속 상태 이벤트 공유 |
user:{id}:last_seen | String | 마지막 접속 시간 기록 |
online_users에 TTL을 설정하거나 Ping 메시지를 주기적으로 송신ping 메시지를 전송하고 서버는 이를 Redis에 반영user:{id}:connections)| 항목 | 설명 |
|---|---|
| 통신 | WebSocket을 통한 실시간 연결 |
| 상태 공유 | Redis의 Pub/Sub과 Set을 통해 분산된 서버 간 유저 상태 동기화 |
| 확장성 | 수평 확장 가능하며, 서버 수가 많아져도 Redis를 통해 일관된 상태 관리 가능 |
| 주요 기술 | WebSocket (Go), Redis Set/PubSub, JWT 기반 인증 |
여기서 는 전체 사용자 집합, 는 현재 접속 중인 사용자 집합이다.
WebSocket과 Redis를 결합함으로써, 단일 서버 한계를 넘는 실시간 사용자 추적 시스템을 구현할 수 있다. 본 방식은 실시간 채팅, 알림, 협업 플랫폼 등에서 접속자 추적의 신뢰성을 보장하며, 수평 확장이 가능하다는 점에서 실무적으로 매우 유용하다.