아키텍처 개요
gochat 프로젝트는 분산형 인스턴트 메시징 시스템으로, 마이크로서비스 아키텍처 패턴을 기반으로 설계되었습니다. 이 시스템은 API 게이트웨이, 비즈니스 로직 처리, 메시지 큐잉, WebSocket 연결 관리 등 명확히 분리된 계층으로 구성됩니다. 핵심 설계 목표는 높은 동시성 처리, 수평적 확장성, 그리고 안정적인 메시지 전달 보장입니다.
전체 아키텍처 구조
gochat 시스템은 네 개의 핵심 계층으로 구성된 마이크로서비스 아키텍처를 채택하고 있습니다. 각 계층은 독립적으로 배포 및 확장 가능하며, RPC(원격 프로시저 호출)를 통해 상호 통신합니다. 전체 아키텍처 다이어그램과 서비스 디스커버리 구조는 readme.md:64-68에서 확인할 수 있으며, 이 구조는 서비스 간의 느슨한 결합과 동적 서비스 발견을 가능하게 합니다.
正在加载图表渲染器...
아키텍처 구성 요소 설명
위 다이어그램은 gochat 시스템의 핵심 계층과 데이터 흐름을 보여줍니다. 각 화살표는 동기적 RPC 호출 또는 비동기적 메시지 큐 통신을 나타냅니다.
계층 간 통신 특성:
- API → Logic: 동기 RPC 호출로, 사용자 요청에 대한 즉각적인 응답이 필요한 작업(로그인, 메시지 발송 요청 등)을 처리합니다. readme.md:64-72
- Logic → Queue: 비동기 메시지 푸시로, 메시지 발송 작업을 큐에 적재하여 발송자에게 즉시 응답을 반환합니다.
- Task → Connect: 동기 RPC 호출로, 큐에서 소비한 메시지를 실제 WebSocket 연결로 전달합니다.
서비스 디스커버리: 각 Connect 계층 인스턴스는 고유한 serverId를 할당받으며, Logic 계층은 사용자의 현재 연결 위치를 serverId로 추적합니다. 이를 통해 Task 계층이 메시지를 정확한 Connect 인스턴스로 라우팅할 수 있습니다.
핵심 모듈 상세 분석
API 계층 (API Layer)
职责边界 (책임 경계):
- HTTP/REST 인터페이스 제공
- 사용자 인증 및 세션 토큰 발급
- 요청 유효성 검증 및 기본 보안 처리
- Logic 계층으로의 RPC 호출 라우팅
진입점 및 주요 API:
POST /login: 사용자 로그인 처리, Logic 계층에 사용자 접속 정보 기록 요청POST /send: 메시지 발송 요청 접수, Logic 계층으로 메시지 전달
처리 흐름:
- 사용자 인증 요청 수신
- Logic 계층 RPC 호출을 통한 비즈니스 로직 실행
- 응답 반환 (로그인 성공 시 세션 정보 포함)
책임하지 않는 영역:
- WebSocket 연결 관리 (Connect 계층 담당)
- 메시지 큐잉 (Logic 계층 담당)
- 실제 메시지 전달 (Task 계층 담당)
Logic 계층 (Logic Layer)
职责边界 (책임 경계):
- 사용자 접속 상태 관리 (어느 Connect 서버에 연결되어 있는지 추적)
- 메시지 발송 요청 처리 및 큐 적재
- 방(Room) 멤버십 관리
- 서비스 간 조율 및 라우팅 정보 제공
주요 데이터 구조:
- 사용자 접속 맵:
{userId: {serverId, roomId, status}} - 방 정보:
{roomId: [userId1, userId2, ...]}
핵심 기능:
- 접속 정보 기록: API 계층으로부터 로그인 알림을 받으면 사용자의
serverId를 저장합니다. readme.md:74-79 - 메시지 큐잉: 발송 요청된 메시지를 메시지 큐에 푸시합니다. readme.md:81-82
- 라우팅 정보 조회: Task 계층이 메시지 전달 시 필요한 사용자 위치 정보를 제공합니다.
에러 처리:
- 큐 적재 실패 시 재시도 로직 적용
- 사용자 오프라인 시 오프라인 메시지 저장 (구현 확인 필요)
Task 계층 (Task Layer)
职责边界 (책임 경계):
- 메시지 큐 소비 (Logic 계층이 적재한 메시지)
- 메시지 수신자 위치 파악 (
userId,roomId,serverId기반) - Connect 계층으로의 RPC 호출 수행
핵심 로직: Task 계층은 메시지 큐를 구독하며, 메시지를 소비한 후 다음과 같은 결정을 내립니다:
- 방송 메시지:
roomId기준으로 해당 방의 모든 사용자에게 전달 - 개인 메시지:
userId기준으로 특정 사용자의serverId를 조회하여 해당 Connect 인스턴스로 RPC 호출 readme.md:91-92
메시지 처리 흐름:
메시지 수신 → 수신자 타입 판별 (방/개인) →
대상 serverId 조회 → Connect 계층 RPC 호출
확장성 고려사항:
- Task 계층은 상태를 가지지 않으므로 수평적 확장이 용이
- 메시지 큐 파티셔닝을 통한 처리량 증대 가능
Connect 계층 (Connect Layer)
职责边界 (책임 경계):
- WebSocket 연결 수립 및 유지
- 사용자 세션(Channel) 관리
- 방(Room) 단위 브로드캐스트
- 실제 메시지 푸시 수행
핵심 데이터 구조 - 버킷 구조: Connect 계층은 락 경쟁을 최소화하기 위해 버킷(Bucket) 기반 구조를 사용합니다. readme.md:94-100
Connect 계층:
Bucket[]: (락 경쟁 최소화를 위한 분할)
Room: (방 단위 그룹화)
Channel: (개별 사용자 세션)
버킷 구조의 이점:
- 세밀한 락킹: 전역 락 대신 버킷 단위 락 사용으로 동시성 향상
- 메모리 효율성: 방 단위 그룹화로 빠른 브로드캐스트 가능
- 확장성: 버킷 수를 조정하여 성능 튜닝 가능
주요 기능:
- 인증 핸드셰이크: WebSocket 연결 시 사용자 인증 수행
- 방 입장/퇴장: 사용자를 적절한 방에 할당
- 메시지 푸시: Task 계층으로부터 RPC 호출을 받으면 해당 방/사용자에게 메시지 전달 readme.md:88-89
메시지 전달 흐름
gochat의 메시지 전달은 비동기 큐잉 패턴을 사용하여 높은 처리량과 안정성을 확보합니다. 사용자 A가 사용자 B에게 메시지를 보내는 전체 과정은 readme.md:71-89에 상세히 설명되어 있습니다.
正在加载图表渲染器...
메시지 전달 단계별 상세 분석
1단계 및 2단계 - 사용자 로그인 및 연결 수립:
사용자는 먼저 API 계층을 통해 로그인합니다. 로그인 성공 시 Logic 계층은 사용자가 연결된 Connect 서버의 serverId를 기록하고, 기본적으로 1번 방에 입장시킵니다. readme.md:74-79 그 후 사용자는 Connect 계층과 WebSocket 연결을 수립합니다.
3단계 - 메시지 발송 요청: 사용자 A가 메시지 발송 API를 호출하면, API 계층은 이를 Logic 계층으로 전달합니다. Logic 계층은 메시지를 즉시 큐에 푸시하고 발송자에게 성공 응답을 반환합니다. 이 비동기 패턴은 발송자의 대기 시간을 최소화합니다. readme.md:81-82
4단계 - 메시지 전달 처리:
Task 계층이 큐에서 메시지를 소비한 후, 메시지 내용에 포함된 userId, roomId, serverId 정보를 기반으로 수신자의 위치를 파악합니다. readme.md:84-86 이후 해당 Connect 인스턴스로 RPC 호출을 수행하고, Connect 계층은 메시지를 적절한 방 또는 개별 사용자에게 전달합니다. readme.md:88-89
방송 vs 개인 메시지 구분:
- 방 메시지: Task 계층이 방 ID를 기반으로 Connect 계층을 호출하면, Connect 계층은 해당 방의 모든 사용자에게 브로드캐스트
- 개인 메시지: Task 계층이
userId로serverId를 조회한 후, 해당 Connect 인스턴스로 직접 RPC 호출하여 특정 사용자에게만 전달 readme.md:91-92
모듈 간 의존 관계
正在加载图表渲染器...
의존 관계 분석
강한 의존성 (실선):
- API → Logic: 모든 비즈니스 요청이 Logic 계층을 거침
- Task → Logic: 사용자 위치 조회를 위한 필수 호출
- Task → Connect: 메시지 전달의 최종 단계
- Logic → Queue: 메시지 발송의 핵심 경로
약한 의존성 (점선):
- 모든 계층 → Service Discovery: 서비스 인스턴스 발견을 위한 참조용 의존성
확장성 영향:
- Logic 계층과 Connect 계층은 상태를 가지므로 신중한 확장 전략 필요
- API 계층과 Task 계층은 무상태로 쉽게 수평 확장 가능
핵심 설계 결정사항
1. 비동기 메시지 큐 도입
결정: 메시지 발송 요청과 실제 전달 사이에 메시지 큐를 배치
이유:
- 발송자에게 즉각적인 응답 제공 (사용자 경험 향상)
- 트래픽 스파이크 완충 (큐가 버퍼 역할)
- 발송/전달 계층의 독립적 확장 가능
트레이드오프:
- 메시지 전달 지연 증가 (큐 대기 시간)
- 시스템 복잡도 증가 (큐 운영 필요)
증거: readme.md:81-82에서 Logic 계층이 메시지를 큐에 푸시하는 것을 확인
2. Connect 계층의 버킷 구조
결정: Connect 계층 내부를 Bucket → Room → Channel 계층 구조로 조직화
이유:
- 락 경쟁 최소화: 전역 락 대신 버킷 단위 락 사용
- 빠른 브로드캐스트: 방 단위 그룹화로 O(1) 방 조회
- 메모리 지역성 향상: 관련 데이터의 물리적 근접 배치
트레이드오프:
- 구현 복잡도 증가
- 버킷 수 튜닝 필요
증거: readme.md:94-100에서 버킷 구조 설명 확인
3. RPC 기반 서비스 간 통신
결정: 서비스 간 통신에 REST 대신 RPC 사용
이유:
- 타입 안전성: IDL(Interface Definition Language) 기반 계약
- 성능: 바이너리 프로토콜로 오버헤드 감소 (gRPC 등 사용 시)
- 코드 생성: 클라이언트/서버 스텁 자동 생성
트레이드오프:
- 디버깅 어려움 (바이너리 프로토콜)
- HTTP 친화적이지 않음 (브라우저 직접 호출 불가)
4. serverId 기반 라우팅
결정: 각 Connect 인스턴스에 고유 serverId 할당 및 사용자-서버 매핑 유지
이유:
- 정확한 메시지 라우팅: 사용자가 연결된 서버로 직접 전달
- 상태 관리 단순화: 사용자 위치를 단일 키로 표현
증거: readme.md:76에서 serverId 기록 언급
5. 기본 방 입장 전략
결정: 로그인 시 기본적으로 1번 방에 자동 입장
이유:
- 사용자 경험 단순화: 별도 방 입장 절차 없이 즉시 메시지 수신 가능
- 테스트 및 데모 용이성
트레이드오프:
- 1번 방에 과도한 트래픽 집중 가능성
- 유연성 저하 (사용자가 방 선택 권한 제한)
증거: readme.md:76에서 "기본적으로 1번 방 입장" 언급
기술 스택 선정
| 기술 | 용도 | 선정 이유 | 대안 |
|---|---|---|---|
| Go | 주요 개발 언어 | 높은 동시성 처리(goroutine), 낮은 메모리 footprint, 빠른 컴파일 | Rust, Java |
| WebSocket | 실시간 양방향 통신 | HTTP 폴링 대비 낮은 지연, 서버 푸시 가능 | Server-Sent Events, Long Polling |
| RPC (gRPC 등) | 서비스 간 통신 | 타입 안전성, 바이너리 직렬화, 스트리밍 지원 | REST API, GraphQL |
| Message Queue (Kafka/RabbitMQ) | 비동기 메시징 | 높은 처리량, 내구성, 재시도 메커니즘 | Redis Pub/Sub, NATS |
| Service Discovery (etcd/Consul) | 서비스 등록 및 발견 | 동적 인스턴스 관리, 헬스체크 통합 | Kubernetes Service, 정적 설정 |
| Bucket 구조 | 세션 관리 최적화 | 락 경쟁 감소, 빠른 방 조회 | 단일 맵, 분산 캐시 |
| Room/Channel 추상화 | 사용자 그룹화 | 브로드캐스트 효율성, 논리적 그룹핑 | 사용자별 직접 관리 |
주요 설정 및 시작 프로세스
필수 구성 요소
메시지 큐 설정:
- Logic 계층이 메시지를 푸시할 대상 큐/토픽 구성
- Task 계층이 구독할 컨슈머 그룹 설정
- 재시도 정책 및 데드레터 큐 구성 (구현 확인 필요)
서비스 디스커버리 설정:
- Connect 인스턴스의 serverId 자동 생성 또는 할당 메커니즘
- Logic 계층의 접속 정보 저장소 (Redis 등 사용 가능, 구현 확인 필요)
- 헬스체크 및 인스턴스 등록/해제 로직
RPC 통신 설정:
- 서비스 간 타임아웃 및 재시도 정책
- 로드 밸런싱 전략 (라운드 로빈, 최소 연결 등)
- 서킷 브레이커 설정 (장애 전파 방지)
시작 순서 권장사항
- 인프라 계층: 메시지 큐, 서비스 디스커버리 시작
- 상태 관리 계층: Logic, Connect 계층 시작 (순서 무관)
- 무상태 계층: API, Task 계층 시작 (순서 무관)
Connect 계층 버킷 튜닝
버킷 수는 예상 동시 접속자 수와 서버 하드웨어 스펙에 따라 조정해야 합니다. 일반적인 권장사항:
- 버킷 수: CPU 코어 수 × 2~4배
- 방 분산: 해시 기반 방-버킷 매핑으로 고른 분산 유지
- 모니터링: 버킷별 락 대기 시간 모니터링으로 병목 식별
증거: readme.md:94-100에서 버킷 구조와 락 최적화 목적 명시
알려진 제약사항
-
메시지 순서 보장: 현재 아키텍처에서는 단일 사용자의 메시지 순서 보장이 명시되지 않음 (큐 파티셔닝 전략에 따라 달라질 수 있음)
-
오프라인 메시지: 소스에서 오프라인 메시지 저장 및 전달 메커니즘이 명시되지 않음 (추가 확인 필요)
-
메시지 영속성: 메시지 큐의 보존 기간 이후 메시지는 복구 불가능할 수 있음
-
수평 확장 시 고려사항: Connect 계층 확장 시 기존 연결의 마이그레이션 전략 필요 (Graceful Shutdown/Restart)
-
단일 지역 가정: 현재 설명은 단일 데이터센터/지역 내 배포를 가정하는 것으로 보임 (다중 지역 복제 전략은 별도 설계 필요)
