arena/context/server.md

79 lines
7.2 KiB
Markdown

# Context: Server & API
## 1. 모듈별 상세 역할
- **`server/index.js`**:
- Fastify 서버 진입점입니다.
- 개발 모드에서는 `@fastify/middie`로 Vite 미들웨어를 붙이고, `/api/*` 요청은 Vite SPA fallback이 가로채지 않도록 Fastify 라우트로 통과시킵니다.
- 운영 모드(`npm start`)에서는 `@fastify/static`으로 `dist/`를 서빙하고, HTML 요청은 `index.html`로 fallback합니다.
- **`server/config.js`**:
- `config.json`을 읽어 서버 포트, MongoDB host/port/db/user/pass, 쿠키 보안 옵션을 정규화합니다.
- `MONGODB_URI`가 직접 있으면 우선 사용하고, 없으면 `MONGODB_HOST`/`MONGODB_PORT` 기반으로 URI를 조립합니다.
- 전투 사망 통계 컬렉션(`MONGODB_DAILY_DEATH_COLLECTION`)과 집계 기준 타임존(`DEATH_STATS_TIME_ZONE`) 기본값을 제공합니다.
- About 콘텐츠 컬렉션은 `MONGODB_ABOUT_COLLECTION`으로 조정하며 기본값은 `about_content`입니다.
- **`server/visitorCookie.js`**:
- `arena_visitor_id` 쿠키 읽기/쓰기와 UUID 형식 검증을 담당합니다.
- 방문자 쿠키 처리를 방문자 API와 일일 지표 API가 공유하도록 분리합니다.
- **`server/db.js`**:
- `MongoClient`를 한 번 생성한 뒤 재사용하여 MongoDB 커넥션 풀을 유지합니다.
- 종료 시 `closeMongoConnection()`으로 커넥션을 닫습니다.
- **`server/about.js`**:
- About 개발자정보와 개인정보처리방침 Markdown을 DB 기본 문서로 시드하고, 서버 메모리에 캐시합니다.
- **`server/dailyMetrics.js`**:
- `GET /api/daily-metrics/today`: `ANALYTICS_TIME_ZONE` 기준 오늘의 운영 지표를 반환합니다.
- `POST /api/daily-metrics/match-started`: 사용자가 실제 전투를 시작했을 때 `totalMatchStarts`를 누적합니다.
- `POST /api/daily-metrics/match-finished`: 실제 전투가 끝났을 때 `totalMatchFinishes`를 누적합니다.
- `POST /api/daily-metrics/donation-clicked`: 후원 버튼 클릭 수를 누적하기 위한 예약 API입니다.
- 날짜별 합산 컬렉션(`daily_metrics`)과 날짜+방문자 해시 기준 임시 카운터 컬렉션(`daily_visitor_activity`)을 사용합니다.
- 임시 카운터는 `expireAt` TTL 인덱스로 기본 60일 뒤 자동 삭제됩니다.
- **`server/deathStats.js`**:
- `GET /api/death-stats/today`: `DEATH_STATS_TIME_ZONE` 기준 오늘 일자의 종족별 사망 집계와 총 사망 수를 반환합니다.
- `POST /api/death-stats/today`: 전투 종료 시 전달된 `deathsBySpecies`를 오늘 일자별 누적 문서의 `deathsBySpecies`, `totalDeaths`, `battles`에 바로 더합니다.
- 집계 대상 종족은 `human`, `orc`, `skeleton`, `slime`, `wolf`, `bear`로 제한합니다.
- **`server/visitors.js`**:
- `POST /api/visitors/check`: `arena_visitor_id` `HttpOnly` 쿠키를 확인하고 없으면 UUID를 발급합니다.
- MongoDB `visitors` 컬렉션에 `_id = visitorId`로 upsert해 방문자 1명당 1개 문서를 유지합니다.
- `GET /api/visitors/stats`: 전체 유니크 방문자 수를 반환합니다.
## 2. 주요 로직 구현 세부 사항
### 유니크 방문자 체크
브라우저가 직접 MongoDB에 연결하지 않고, Fastify API가 MongoDB 커넥션 풀을 유지합니다.
1. 프론트엔드가 앱 로드 시 `POST /api/visitors/check`를 호출합니다.
2. 서버가 `arena_visitor_id` 쿠키를 검사합니다.
3. 쿠키가 없거나 유효하지 않으면 `crypto.randomUUID()`로 새 방문자 ID를 만들고 `HttpOnly` 쿠키로 내려줍니다.
4. MongoDB에는 `_id`, `firstSeenAt`, `lastSeenAt`, `visits`, `firstUserAgent`, `lastUserAgent`를 저장합니다.
5. `countDocuments()`로 전체 유니크 방문자 수를 계산해 반환합니다.
6. 같은 요청에서 일일 지표의 `totalVisits`를 1 증가시키고, 해당 날짜에 처음 확인된 방문자면 `uniqueVisitors`도 1 증가시킵니다.
### 일일 운영 지표
수익화 판단에 필요한 최소 지표만 저장하며, 입력 닉네임이나 매치 상세 로그는 저장하지 않습니다.
1. 앱 로드 시 기존 `POST /api/visitors/check` 흐름에서 `daily_metrics.totalVisits``daily_metrics.uniqueVisitors`를 갱신합니다.
2. 사용자가 직접 시작한 전투만 `POST /api/daily-metrics/match-started``totalMatchStarts`에 누적합니다.
3. 프리뷰 전투는 제외하고, 실제 전투가 승리/무승부로 끝난 경우만 `POST /api/daily-metrics/match-finished``totalMatchFinishes`에 누적합니다.
4. `daily_visitor_activity`는 날짜와 방문자 UUID를 함께 해시한 `_id`를 사용해 당일 방문자별 `visits`, `matchStarts`, `matchFinishes`, `donationClicks`만 임시 저장합니다.
5. 방문자의 당일 `matchStarts`가 1에서 2로 넘어가는 순간에만 `daily_metrics.visitorsWithTwoOrMoreMatches`를 1 증가시킵니다.
6. `daily_visitor_activity``DAILY_ACTIVITY_RETENTION_DAYS` 설정값에 따라 TTL로 자동 삭제하고, 장기 보관 대상은 날짜별 합산 문서인 `daily_metrics`입니다.
### 전투 사망 통계
프리뷰 전투는 통계에서 제외하고, 사용자가 시작한 실제 전투만 저장합니다.
1. `fighterManifest.js`의 모든 스킨은 `species`를 가집니다.
2. `combat.js`의 처치 흐름이 `ArenaScene.recordKill()`을 호출하면, `ArenaScene`은 피처치자의 `skin.species`를 현재 전투의 `battleDeathCounts`에 누적합니다.
3. 전투가 5초 이상 이어지면 `GET /api/death-stats/today`로 가져온 오늘 집계와 현재 전투의 사망 수를 합산해 `#battle-notice`에 표시합니다.
4. 전투 종료 시 `POST /api/death-stats/today``deathsBySpecies`만 보냅니다.
5. 서버는 `daily_death_stats` 컬렉션에서 오늘 일자의 `battles`, `totalDeaths`, `deathsBySpecies.*` 값을 `$inc`로 갱신합니다.
## 3. 설정 규칙
- **서버 설정**: `.env` 대신 `config.json`을 사용합니다. 로컬 전용 파일이며, 저장소에는 `config.json.sample`만 공유합니다.
- **MongoDB 연결**: 접속 정보는 `config.json``MONGODB_HOST`, `MONGODB_PORT`, `MONGODB_DB` 등으로 관리합니다.
- **일일 지표 설정**: `MONGODB_DAILY_METRICS_COLLECTION`, `MONGODB_DAILY_VISITOR_ACTIVITY_COLLECTION`, `ANALYTICS_TIME_ZONE`, `DAILY_ACTIVITY_RETENTION_DAYS`로 집계 컬렉션과 임시 카운터 보관 기간을 조정합니다.
- **API 변경**: `/api/*` 경로는 Fastify 라우트가 담당하며, 개발 모드에서 Vite 미들웨어보다 우선순위를 가집니다.
## 4. About 콘텐츠
- **`server/about.js`**:
- 서버 시작 시 `MONGODB_ABOUT_COLLECTION` 컬렉션(기본값 `about_content`)에 `developer-info`, `privacy-policy` 기본 문서를 upsert합니다.
- 개발자 정보 기본값은 `alias: horoli`, `email: sunha321@gmail.com`, `github: https://github.com/Horoli`입니다.
- 개인정보처리방침은 `privacy-policy.markdown` 문자열 필드에 Markdown 원문으로 저장합니다. 기본값은 빈 문자열이며, 운영자가 DB에서 직접 작성/수정합니다.
- 서버가 MongoDB 연결에 성공하면 About 콘텐츠를 메모리에 캐시합니다. 브라우저 표시를 위해 `GET /api/about` 읽기 전용 API만 제공하며, 수정 API는 만들지 않습니다.