docs : MMORPG 아키텍처 적합성 분석 보고서 추가
현재 서버 구조의 MMORPG 적합성 평가 (2.1/10) 게임 루프, AOI, 존 관리, 엔티티 시스템 등 미구현 영역 분석 Phase 1~5 전환 로드맵 포함 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
330
MMORPG_ARCHITECTURE_ANALYSIS.md
Normal file
330
MMORPG_ARCHITECTURE_ANALYSIS.md
Normal file
@@ -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<long, NetPeer> sessions (hashKey → peer)
|
||||
Dictionary<int, NetPeer> 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<ushort, Action<NetPeer, long, byte[]>>` 핸들러 매핑 테이블
|
||||
- 패킷 타입별 핸들러 자동 등록 (어트리뷰트 또는 초기화 시 등록)
|
||||
|
||||
```
|
||||
예시 구조:
|
||||
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+ 대응
|
||||
```
|
||||
Reference in New Issue
Block a user