diff --git a/MMORPG_ARCHITECTURE_ANALYSIS.md b/MMORPG_ARCHITECTURE_ANALYSIS.md new file mode 100644 index 0000000..55cc1bb --- /dev/null +++ b/MMORPG_ARCHITECTURE_ANALYSIS.md @@ -0,0 +1,330 @@ +# MMORPG 아키텍처 적합성 분석 보고서 + +## 1. 분석 목적 + +현재 서버 구조가 **다수의 플레이어가 동시 접속하여 실시간으로 상호작용하는 MMORPG**에 적합한지 평가하고, 부족한 부분과 개선 방향을 제시한다. + +--- + +## 2. MMORPG 서버의 핵심 요구사항 + +| 요구사항 | 설명 | +|----------|------| +| 대규모 동시접속 (CCU) | 수백~수천 명이 같은 월드에서 실시간 플레이 | +| 저지연 패킷 처리 | 위치/액션 동기화는 50ms 이내 응답 필요 | +| 관심 영역 관리 (AOI) | 내 주변 플레이어만 패킷을 주고받아야 대역폭 절약 | +| 게임 루프 (Tick) | 고정 주기(예: 20~60Hz)로 월드 상태를 업데이트 | +| 존/채널 분리 | 지역별 부하 분산 및 인스턴스 관리 | +| 엔티티 관리 | 플레이어, NPC, 몬스터 등의 상태를 중앙에서 관리 | +| 서버 간 통신 | 로그인/로비/게임/채팅 등 역할별 서버 분리 | +| 장애 복구 | 서버 크래시 시 세션 복구, 데이터 무결성 보장 | + +--- + +## 3. 현재 구조 평가 + +### 3.1 네트워크 계층 + +**현재 구조:** +``` +단일 ServerBase → 단일 NetManager → 단일 스레드 PollEvents() +``` + +| 항목 | 현재 상태 | MMORPG 요구 | 평가 | +|------|-----------|-------------|------| +| 프로토콜 | UDP (LiteNetLib) | UDP 기반 필수 | **적합** | +| 직렬화 | ProtoBuf | 고성능 바이너리 직렬화 | **적합** | +| 폴링 모델 | 단일 스레드 `while + Task.Delay(1)` | 멀티스레드 or 고성능 이벤트 루프 | **부적합** | +| 패킷 라우팅 | type 기반 if 분기 | 핸들러 매핑 테이블 + 디스패처 | **부적합** | + +**문제점:** + +- **단일 스레드 병목**: `PollEvents()`가 단일 스레드에서 모든 패킷을 처리한다. CCU 500 이상에서 패킷 큐가 쌓이면 처리 지연이 발생한다. MMORPG에서 플레이어 100명이 초당 10패킷을 보내면 초당 1,000패킷인데, `HandlePacket` 내부에서 DB 조회 같은 블로킹 작업이 끼면 전체 루프가 멈춘다. + +- **패킷 디스패처 없음**: `HandlePacket(peer, hashKey, type, payload)` 하나로 모든 게임 패킷을 처리해야 한다. 패킷 종류가 17개인 현재도 거대한 switch문이 필요하며, 패킷이 늘어날수록 유지보수가 어려워진다. + +- **`Task.Delay(1)` 해상도**: Windows에서 실제로 ~15ms 간격으로 폴링된다. 60Hz 게임 루프가 필요한 MMORPG에서는 16.6ms마다 정확한 틱이 필요한데, 현재 구조로는 불가능하다. + +--- + +### 3.2 세션 관리 + +**현재 구조:** +``` +Dictionary sessions (hashKey → peer) +Dictionary pendingPeers (peerId → peer) +``` + +| 항목 | 현재 상태 | MMORPG 요구 | 평가 | +|------|-----------|-------------|------| +| 자료구조 | 일반 Dictionary | ConcurrentDictionary 또는 락 기반 | **위험** | +| 인증 | 8byte hashKey raw 전송 | JWT/OAuth + 토큰 갱신 | **부적합** | +| 세션 정보 | hashKey + NetPeer만 | 플레이어 ID, 존, 위치, 상태 등 | **부족** | +| 재연결 | hashKey 기반 피어 교체 | 세션 상태 전체 복구 | **부분 구현** | + +**문제점:** + +- **인증 보안 취약**: 8바이트 hashKey를 평문으로 보내면, 패킷 스니핑으로 다른 사람의 세션을 탈취할 수 있다. MMORPG는 JWT 같은 시간 제한 토큰 + 서버 측 검증이 필수다. + +- **세션에 게임 상태 없음**: `Session` 클래스에 `HashKey`와 `Peer`만 있다. MMORPG에서는 세션이 플레이어의 현재 존, 위치, 파티, 인벤토리 캐시 등을 들고 있어야 패킷 처리 시 매번 DB를 조회하지 않는다. + +- **재연결 시 상태 미복구**: WiFi→LTE 전환 시 피어만 교체하고 게임 상태(존, 위치, 버프 등)를 클라이언트에 재전송하는 로직이 없다. + +--- + +### 3.3 게임 루프 & 월드 관리 + +**현재 구조:** +``` +없음. GameServer 클래스는 빈 껍데기. +``` + +| 항목 | 현재 상태 | MMORPG 요구 | 평가 | +|------|-----------|-------------|------| +| 게임 루프 | 없음 | 고정 틱 (20~60Hz) | **미구현** | +| 존/맵 관리 | 없음 | Zone/Channel/Instance 분리 | **미구현** | +| AOI (관심 영역) | 없음 | Grid/Quadtree 기반 영역 관리 | **미구현** | +| 엔티티 시스템 | 없음 | Player/NPC/Monster 상태 관리 | **미구현** | +| 물리/충돌 | 없음 | 서버 권위 이동 검증 | **미구현** | + +**문제점:** + +이것이 가장 큰 갭이다. MMORPG 서버의 핵심인 **게임 루프**가 존재하지 않는다. + +- **게임 루프 없음**: 현재는 "클라이언트가 패킷을 보내면 서버가 반응"하는 순수 리액티브 구조다. MMORPG는 클라이언트 입력이 없어도 서버가 자체적으로 NPC AI, 몬스터 이동, 버프 타이머, 리스폰 등을 주기적으로 처리해야 한다. + +- **AOI 없음**: 현재 `Broadcast()`는 **모든 접속자**에게 패킷을 보낸다. 플레이어 100명이 모두 움직이면 초당 `100 × 100 = 10,000`패킷이 발생한다. 1,000명이면 `1,000,000`패킷이다. AOI 없이는 CCU 50만 넘어도 대역폭이 폭발한다. + +- **존/채널 없음**: 모든 플레이어가 하나의 `sessions` Dictionary에 평면적으로 존재한다. "마을에 있는 플레이어"와 "던전에 있는 플레이어"를 구분할 방법이 없다. + +--- + +### 3.4 데이터베이스 계층 + +**현재 구조:** +``` +Handler → Service → Repository(Dapper) → MySQL +매 요청마다 새 커넥션 (풀링은 MySQL 드라이버에 위임) +``` + +| 항목 | 현재 상태 | MMORPG 요구 | 평가 | +|------|-----------|-------------|------| +| ORM | Dapper (경량) | 경량 ORM 적합 | **적합** | +| 커넥션 풀링 | MySQL 드라이버 위임 | 명시적 풀 관리 | **보통** | +| 캐싱 | 없음 | Redis/인메모리 캐시 필수 | **부적합** | +| 비동기 | async/await | 논블로킹 필수 | **적합** | + +**문제점:** + +- **캐시 계층 없음**: 플레이어가 스킬을 쓸 때마다 DB에서 스탯을 읽으면 게임이 안 된다. MMORPG는 로그인 시 플레이어 데이터를 메모리에 올리고, 주기적으로 DB에 저장(Write-Back)하는 구조가 필수다. + +- **게임 루프에서 DB 호출 위험**: 현재는 패킷 핸들러에서 직접 DB를 호출하는 구조인데, 게임 루프 스레드에서 DB await를 하면 틱 지연이 발생한다. 게임 로직과 DB I/O는 반드시 분리해야 한다. + +--- + +### 3.5 서버 구조 (스케일링) + +**현재 구조:** +``` +단일 프로세스, 단일 서버 +``` + +| 항목 | 현재 상태 | MMORPG 요구 | 평가 | +|------|-----------|-------------|------| +| 서버 분리 | 모놀리식 단일 서버 | 로그인/로비/게임/채팅 분리 | **부적합** | +| 수평 확장 | 불가 | 존별 서버 분산 | **부적합** | +| 메시지 큐 | 없음 | Redis Pub/Sub, RabbitMQ 등 | **미구현** | +| 로드밸런싱 | 없음 | 게이트웨이 + 존 서버 라우팅 | **미구현** | + +**문제점:** + +- **단일 서버 한계**: 하나의 `ServerBase` 인스턴스가 인증, 에코, 게임 패킷을 모두 처리한다. CCU가 늘어나면 서버를 추가할 방법이 없다. + +- **서버 간 통신 없음**: "A 서버의 플레이어가 B 서버의 플레이어에게 귓속말"이 불가능하다. 파티, 길드, 거래 등 크로스 존 기능을 구현할 수 없다. + +--- + +## 4. 종합 평가 + +### 적합성 점수 + +| 영역 | 점수 (10점) | 비고 | +|------|:-----------:|------| +| 네트워크 프로토콜 | 8 | UDP + ProtoBuf 조합 우수 | +| 패킷 처리 파이프라인 | 3 | 단일 스레드, 디스패처 없음 | +| 세션 / 인증 | 3 | 보안 취약, 게임 상태 없음 | +| 게임 루프 | 0 | 미구현 | +| 월드 / 존 관리 | 0 | 미구현 | +| AOI (관심 영역) | 0 | 미구현 | +| 엔티티 시스템 | 0 | 미구현 | +| DB / 캐시 | 4 | Dapper 좋으나 캐시 없음 | +| 서버 확장성 | 1 | 단일 프로세스 모놀리식 | +| **종합** | **2.1 / 10** | | + +### 결론 + +> 현재 구조는 **에코 서버 / 소규모 매칭 게임 (10~20명)** 수준에 적합하며, +> **MMORPG로 사용하기에는 핵심 시스템 대부분이 미구현** 상태이다. +> +> 네트워크 기반(LiteNetLib + ProtoBuf)과 DB 레이어(Dapper + Repository 패턴)는 +> 좋은 출발점이지만, 그 위에 올라가야 할 게임 서버 계층이 빠져 있다. + +--- + +## 5. MMORPG 전환을 위한 로드맵 + +### Phase 1: 게임 루프 & 엔티티 시스템 (최우선) + +``` +현재: 클라이언트 패킷 → ServerBase → GameServer(빈 껍데기) +목표: 클라이언트 패킷 → 입력 큐 → GameLoop(고정 틱) → 월드 상태 업데이트 → 브로드캐스트 +``` + +**구현 항목:** + +1. **고정 틱 게임 루프** + - `Stopwatch` 기반 정밀 타이머로 50ms(20Hz) 또는 33ms(30Hz) 간격 틱 + - 패킷 수신은 입력 큐에 적재, 게임 루프에서 꺼내서 처리 + - 네트워크 I/O와 게임 로직 스레드 분리 + +2. **엔티티 관리자** + - `GamePlayer`, `GameNpc`, `GameMonster` 등 인게임 엔티티 클래스 + - 위치, 상태, 스탯을 서버 메모리에서 관리 + - 로그인 시 DB → 메모리 로드, 주기적으로 메모리 → DB 저장 + +3. **패킷 디스패처** + - `Dictionary>` 핸들러 매핑 테이블 + - 패킷 타입별 핸들러 자동 등록 (어트리뷰트 또는 초기화 시 등록) + +``` +예시 구조: +GameLoop +├── ProcessInputQueue() // 클라이언트 패킷 처리 +├── UpdateNpcAI() // NPC 행동 업데이트 +├── UpdateBuffTimers() // 버프/디버프 타이머 +├── UpdateRespawns() // 몬스터 리스폰 +├── BroadcastWorldState() // 변경된 상태만 클라이언트에 전송 +└── SaveDirtyEntities() // 변경된 엔티티 DB 저장 (N틱마다) +``` + +### Phase 2: 존/채널 & AOI + +**구현 항목:** + +1. **존 시스템** + - `Zone` 클래스: 해당 존의 플레이어/NPC/몬스터 목록 관리 + - 존 진입/퇴장 시 엔티티 이동 + - 인스턴스 던전은 동적 Zone 생성/파괴 + +2. **AOI (Area of Interest)** + - 그리드 기반 관심 영역: 맵을 NxN 셀로 나누고, 플레이어가 속한 셀 + 인접 셀만 패킷 전송 + - `Broadcast()` → `BroadcastToNearby(position, radius, data)`로 교체 + - CCU 1,000명 기준 패킷 수: 1,000,000 → ~50,000 (95% 감소) + +``` +예시: +┌───┬───┬───┐ +│ │ B │ │ B는 A의 AOI 범위 안 → 패킷 수신 +├───┼───┼───┤ +│ │ A │ │ A가 이동 패킷 발신 +├───┼───┼───┤ +│ │ │ C │ C는 AOI 범위 밖 → 패킷 안 받음 +└───┴───┴───┘ +``` + +### Phase 3: 인증 강화 & 세션 확장 + +**구현 항목:** + +1. **JWT 기반 인증** + - 로그인 서버에서 JWT 발급 → 게임 서버에서 검증 + - hashKey 대신 토큰 기반 인증, 만료/갱신 처리 + +2. **세션 확장** + ```csharp + public class GameSession + { + public long AccountId { get; init; } + public NetPeer Peer { get; set; } + public GamePlayer? Player { get; set; } // 인게임 엔티티 참조 + public Zone? CurrentZone { get; set; } // 현재 존 + public DateTime LastActivity { get; set; } // AFK 감지 + } + ``` + +3. **재연결 상태 복구**: 재연결 시 현재 존, 위치, 파티 상태를 클라이언트에 재전송 + +### Phase 4: 캐시 & DB 최적화 + +**구현 항목:** + +1. **인메모리 캐시 도입** + - 접속 중인 플레이어 데이터는 메모리에 상주 + - Write-Back 패턴: 변경 사항을 큐에 쌓고 N초마다 일괄 DB 저장 + - 아이템/스킬 테이블 등 정적 데이터는 서버 시작 시 로드 + +2. **DB I/O 분리** + - 게임 루프 스레드에서 직접 DB 호출 금지 + - 별도 DB Worker 스레드에서 저장 큐 처리 + - 긴급 저장(거래, 결제)만 즉시 처리 + +### Phase 5: 서버 분산 아키텍처 + +**구현 항목:** + +1. **서버 역할 분리** + ``` + 클라이언트 + ↓ + 게이트웨이 서버 (인증 + 라우팅) + ├── 로비 서버 (매칭, 파티, 채팅) + ├── 게임 서버 A (마을 존) + ├── 게임 서버 B (필드 존) + ├── 게임 서버 C (던전 인스턴스) + └── DB/캐시 서버 (Redis + MySQL) + ``` + +2. **서버 간 메시지 큐** + - Redis Pub/Sub 또는 RabbitMQ로 서버 간 통신 + - 크로스 존 기능: 귓속말, 길드, 파티 초대, 거래 + +3. **수평 확장** + - 존 단위로 게임 서버 인스턴스 추가 + - 게이트웨이가 플레이어를 적절한 존 서버로 라우팅 + +--- + +## 6. 현재 코드에서 재사용 가능한 부분 + +현재 코드가 완전히 쓸모없는 것은 아니다. 아래 부분은 그대로 또는 약간의 수정으로 재사용 가능하다. + +| 컴포넌트 | 재사용성 | 비고 | +|----------|:--------:|------| +| LiteNetLib 기반 네트워크 | **높음** | UDP 전송 계층으로 그대로 사용 | +| ProtoBuf 패킷 정의 | **높음** | PacketBody.cs의 17개 패킷 구조 재사용 | +| PacketSerializer | **높음** | 직렬화/역직렬화 로직 그대로 사용 | +| ARepository + Dapper | **높음** | DB 계층 그대로 사용 | +| ServerBase 인증 흐름 | **보통** | JWT로 교체하되 pending → auth 흐름은 유지 | +| Session/재연결 로직 | **보통** | 확장 후 재사용 | +| Docker/compose 설정 | **높음** | 배포 파이프라인 그대로 사용 | + +--- + +## 7. 권장 우선순위 요약 + +``` +[즉시] Phase 1: 게임 루프 + 엔티티 시스템 + 패킷 디스패처 + → 이것 없이는 어떤 게임 로직도 구현 불가 + +[단기] Phase 2: 존/채널 + AOI + → CCU 50명만 넘어도 Broadcast 폭발 + +[중기] Phase 3: 인증 강화 + 세션 확장 + → 보안과 재연결 안정성 + +[중기] Phase 4: 캐시 + DB 분리 + → 성능 병목 해소 + +[장기] Phase 5: 서버 분산 + → CCU 1,000+ 대응 +```