아키텍처 목표부터 정하자
MMO 서버의 핵심 목표
- 정합성: 같은 입력이면 같은 월드 상태가 나와야 함
- 지연 제어: 피크 상황에서도 틱 지연이 폭증하지 않아야 함
- 확장성: 동접 증가 시 구조를 갈아엎지 않고 확장 가능해야 함
설계 원칙
- 네트워크 처리와 게임 로직 실행을 분리한다.
- 상태의 단일 소유자(Single Writer)를 명확히 둔다.
- 공유 메모리보다 메시지/큐 전달을 우선한다.
세션/유저/월드 책임 경계
책임 분리
| 컴포넌트 | 책임 | 하지 말아야 할 일 |
|---|
Session | 소켓, 송수신 버퍼, 연결 상태 | 게임 규칙 판정 |
User/Player | 계정/캐릭터 식별, 로그인 상태 | 소켓 I/O 직접 처리 |
World/Zone | 위치, 전투, AI, 오브젝트 상태 | 저수준 네트워크 API 호출 |
매니저의 역할
SessionManager: 세션 생성/조회/종료, 브로드캐스트 대상 추출
UserManager: 계정/캐릭터 맵핑, 로그인 중복 제어
- 핵심은 "중앙 집중"이 아니라 명확한 소유권과 조회 경로입니다.
실수 방지 규칙
Session*를 전역 어디서나 수정 가능하게 두지 마세요.
- 쓰기 권한은 담당 스레드/도메인으로 제한하세요.
스레드 분배와 데이터 흐름
권장 기본 구성
| 역할 | 담당 스레드 | 출력 |
|---|
| Accept/Connect | Accept 전담 | 신규 Session |
| I/O 완료 처리 | IOCP Worker | Packet/Event Job |
| 게임 로직 | Logic Thread(들) | 상태 갱신 + Send 요청 |
| DB 작업 | Async DB Worker | 저장 결과 Job |
데이터 흐름
flowchart LR
N[IOCP Worker\n네트워크 완료 처리] --> J[Job Queue]
J --> L[Logic Thread\n월드 상태 갱신]
L --> S[Send Queue]
S --> N
L --> DQ[DB Queue]
DQ --> D[DB Worker]
D --> J
핵심 경계
- IOCP 워커는 "파싱/검증/Job 생성"까지, 게임 규칙 실행은 로직 스레드에서 수행
- DB 응답도 직접 상태 변경하지 말고 Job으로 되돌려 처리
Job Queue 패턴 (락 폭발 방지)
왜 필요한가
- 워커가 곧바로 게임 상태를 수정하면 공유 락이 급증합니다.
- Job Queue로 넘기면 로직 스레드가 순차 처리하여 정합성과 단순성을 얻습니다.
최소 흐름
Packet p = DecodePacket(session, bytes);
logicQueue.Push(MakeJob(sessionId, std::move(p)));
while (logicQueue.TryPop(job)) {
ExecuteGameRule(job);
}
운영 포인트
- 큐 길이 상한(Backpressure) 없으면 피크 시 메모리 급증
- 큐 지연 시간(enqueue -> execute)을 지표로 반드시 수집
존/샤드/심리스(Seamless) 설계
Zone ownership
- 존마다 로직 스레드 하나를 두고 해당 존 상태를 단일 소유하게 하면 락이 크게 줄어듭니다.
- 플레이어/몬스터는 "현재 존 소유자"가 결정합니다.
경계 이동(핸드오프)
- 원존이 이동 이벤트 생성
- 대상 존 큐로 전달
- 대상 존이 상태 인수 후 소유권 전환
- 클라이언트에게 전환 결과 통지
심리스의 난제
- 경계 부근에서 동시 전투/충돌 처리 책임이 모호해집니다.
- 실무에서는 포탈/채널 분리로 단순화하거나, 액터 모델·이중버퍼로 복잡도를 감당합니다.
DB와 외부 시스템 연동
금지 패턴
- 로직 스레드에서 동기 DB 호출 금지(틱 스톨 발생)
권장 패턴
- 로직 -> DB 요청 큐 -> DB 워커 -> 결과 Job 반환
- 타임아웃/재시도/중복 요청 방지 키(idempotency key) 설계를 함께 둡니다.
저장 전략
- 중요 데이터(인벤토리, 재화)는 즉시성/내구성을 우선
- 부가 데이터(통계, 로그)는 배치/비동기 적재로 분리
규모별 권장 로드맵
| 단계 | 권장 구조 | 전환 신호 |
|---|
| 1단계(프로토타입) | 단일 로직 스레드 + IOCP + Job Queue | 큐 지연/틱 지연이 피크에서 급증 |
| 2단계(성장기) | 존 단위 로직 스레드 + DB 워커 확장 | 특정 존 과부하, 핫스팟 지속 |
| 3단계(대규모) | 샤드/채널 분산 + 메시지 버스 + 운영 자동화 | 단일 프로세스 수직 확장 한계 |
강의 시 유의사항
강조 포인트
- 아키텍처 품질은 "락 개수"가 아니라 "소유권 경계 명확성"으로 판단하세요.
- IOCP 워커에서 게임 로직을 실행하지 않는 이유를 반드시 사례로 설명하세요.
- 심리스 월드는 기술 이슈와 기획 이슈가 동시에 존재합니다.
자주 하는 오해
| 오해 | 바로잡기 |
|---|
| 락만 잘 걸면 확장된다 | 공유 상태가 많으면 락 자체가 병목이 된다 |
| IOCP 쓰면 아키텍처는 끝났다 | 로직 분리/큐 설계/운영 지표가 더 중요하다 |
| 존 분할은 기술만으로 해결 가능 | 콘텐츠 동선/기획과 함께 설계해야 한다 |
체크 질문 (스스로 답해보기)
- 현재 프로젝트에서
Session, Player, World의 쓰기 소유권은 어디에 있는가?
- 로직 스레드가 멈추지 않도록 DB 연동을 어떤 큐 구조로 분리할 것인가?
- 존 경계 이동 시 소유권 이전 순서를 코드로 어떻게 강제할 것인가?