diff --git a/agent.md b/agent.md index 10ac94d..8bf9e7d 100644 --- a/agent.md +++ b/agent.md @@ -25,6 +25,7 @@ │ ├── config.js # config.json 로드 및 MongoDB URI 조립 │ ├── db.js # MongoClient 커넥션 풀 생성/재사용/종료 │ ├── deathStats.js # 전투 종료 시 오늘 일자별 종족 사망 통계 누적 API +│ ├── about.js # About 개발자정보/개인정보처리방침 기본값 시드 및 조회 API │ └── visitors.js # 유니크 방문자 체크 및 통계 API ├── public/ # 정적 리소스 (게임 에셋) │ └── assets/ @@ -58,6 +59,7 @@ ├── battleDeathNotice.js # [New] 상단 사망 공지 메시지 및 UI 관리 ├── victoryCelebration.js # [New] 승리 축하 연출 (DOM/Audio) 모듈 ├── matchForm.js # 설정 폼 제어 및 localStorage 유지 + ├── aboutDialog.js # About 다이얼로그, 개발자정보/개인정보처리방침 표시 ├── deathStats.js # 사망 통계 API 호출 래퍼 └── visitorCounter.js # 방문자 체크 API 호출 및 표시 ``` @@ -89,6 +91,7 @@ - `GET /api/health`: 서버 및 MongoDB 설정 여부 확인. - `POST /api/visitors/check`: 현재 브라우저 방문자를 체크하고 유니크 방문자 수를 반환. - `GET /api/visitors/stats`: 전체 유니크 방문자 수 조회. + - `GET /api/about`: 서버 시작 시 캐시한 개발자정보와 개인정보처리방침 Markdown 조회. - `GET /api/death-stats/today`: 오늘의 종족별 전투 사망 통계 조회. - `POST /api/death-stats/today`: 종료된 전투의 종족별 사망 수를 오늘 집계에 누적. diff --git a/config.json.sample b/config.json.sample index 9b7d0a4..c920a5f 100644 --- a/config.json.sample +++ b/config.json.sample @@ -7,9 +7,14 @@ "MONGODB_USER": "", "MONGODB_PASS": "", "MONGODB_VISITOR_COLLECTION": "visitors", + "MONGODB_ABOUT_COLLECTION": "about_content", "MONGODB_DAILY_DEATH_COLLECTION": "daily_death_stats", + "MONGODB_DAILY_METRICS_COLLECTION": "daily_metrics", + "MONGODB_DAILY_VISITOR_ACTIVITY_COLLECTION": "daily_visitor_activity", "MONGODB_MAX_POOL_SIZE": 10, "MONGODB_SERVER_SELECTION_TIMEOUT_MS": 5000, "DEATH_STATS_TIME_ZONE": "Asia/Seoul", + "ANALYTICS_TIME_ZONE": "Asia/Seoul", + "DAILY_ACTIVITY_RETENTION_DAYS": 60, "COOKIE_SECURE": false } diff --git a/context/match-ui.md b/context/match-ui.md index b7ae910..b071b60 100644 --- a/context/match-ui.md +++ b/context/match-ui.md @@ -8,6 +8,7 @@ ### UI 컴포넌트 (`src/ui/`) - **`matchForm.js`**: 설정 폼 제어 및 `localStorage` 설정 유지. +- **`aboutDialog.js`**: About 버튼/다이얼로그, 개발자정보, 개인정보처리방침 Markdown 표시. - **`arenaScoreboard.js`**: 좌측 HUD 레일의 팀 badge 업데이트 및 관전 시점 전환. - **`arenaKillLog.js`**: 좌측 하단 킬로그 표시 및 관리. - **`battleDeathNotice.js`**: 상단 사망 통계 공지 UI. @@ -23,8 +24,18 @@ ### 전투 화면 레이아웃 (HUD) - **팀 Badge**: 좌측 HUD 레일에 배치되며, 클릭 시 해당 팀의 생존 유닛 중 무작위 1명으로 시점을 고정합니다. - **킬로그**: 처치자와 피처치자를 좌우로 배치하고, 피처치자 아이콘에 빨간 X를 겹쳐 사망 관계를 명확히 표시합니다. -- **승리 연출**: 승리 시 Web Audio 기반 팡파르와 CSS 애니메이션(광선, 컨페티)을 결합해 화려하게 연출합니다. 무승부는 더 차분한 톤을 사용합니다. +- **모바일 레이아웃**: 실제 전투 시작 시 모바일에서는 옵션 drawer를 자동으로 접고, 상단 팀 HUD는 옵션 버튼 폭을 제외한 영역에 두 줄 4열로 맞춰 4개 이후 팀도 잘리지 않게 합니다. 모바일 팀 카드 선택 표시는 내부 테두리로 처리해 외곽선이 잘려 보이지 않게 합니다. 킬로그는 전투 캔버스 바로 아래에 배치하되 방문자 카운터 안전 여백을 남겨 하단 카운터와 충돌하지 않게 합니다. +- **모바일 옵션 drawer**: 전투 중 펼친 옵션 drawer는 닉네임 입력 높이와 컨트롤 간격을 줄여 전투 시작/재시작/일시정지 버튼이 작은 화면에서도 한 번에 보이도록 합니다. +- **승리 연출**: 승리 시 Web Audio 기반 팡파르와 CSS 애니메이션(광선, 컨페티)을 결합해 화려하게 연출합니다. 전투 종료 시 옵션 drawer를 접어 결과 배너가 설정 폼과 충돌하지 않게 하며, 결과 배너는 일정 시간 후 자동으로 사라지거나 클릭 시 즉시 닫힙니다. 무승부는 더 차분한 톤을 사용합니다. ## 3. UI 개발 규칙 - **DOM 접근 최소화**: 성능 최적화를 위해 필요한 시점에만 최소한으로 DOM을 업데이트합니다. -- **반응형 상태**: `#app`의 클래스(`match-live`, `options-open`, `drawer-collapsed`, `match-paused`)를 통해 전반적인 UI 상태를 제어합니다. +- **반응형 상태**: `#app`의 클래스(`match-live`, `options-open`, `drawer-collapsed`, `match-paused`, `match-ended`)를 통해 전반적인 UI 상태를 제어합니다. + +## 4. About 다이얼로그 + +- **`src/ui/aboutDialog.js`**: + - 메인 대기 화면과 전투 화면에서 공통으로 노출되는 `#about-button`을 제어합니다. + - About 다이얼로그는 기본으로 개발자정보 탭을 보여주고, 개인정보처리방침 탭에서 DB에 저장된 Markdown 원문을 안전한 DOM 노드로 렌더링합니다. + - 브라우저는 `GET /api/about` 읽기 전용 API로 서버 시작 시 캐시된 About 콘텐츠를 가져오며, API 실패 시 개발자 기본값과 빈 정책 안내 문구로 폴백합니다. + - 전투 화면 모바일 레이아웃에서는 About 버튼과 방문자 카운터가 하단에서 겹치지 않도록 kill log 여백 계산에 포함합니다. diff --git a/context/server.md b/context/server.md index 1a77158..371fdf0 100644 --- a/context/server.md +++ b/context/server.md @@ -10,9 +10,22 @@ - `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`에 바로 더합니다. @@ -31,6 +44,16 @@ 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`입니다. ### 전투 사망 통계 프리뷰 전투는 통계에서 제외하고, 사용자가 시작한 실제 전투만 저장합니다. @@ -43,4 +66,13 @@ ## 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는 만들지 않습니다. diff --git a/index.html b/index.html index 161aacc..7eddb30 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ Arena Picker +