feat: initialize sunrise with 100-player shared rooms
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# 관악산 공동 관측 서버
|
||||
|
||||
Node.js 22.18 이상. 단일 프로세스에서 `gwanaksan` 장소의 `gwanaksan-001` 방을 운영한다. 입장한 참여자는 최대 100명이며 자신과 다른 탭도 각각 포함한다. Socket.IO 클라이언트는 HTTP long-polling으로 연결 후 WebSocket으로 업그레이드할 수 있다.
|
||||
|
||||
## 실행
|
||||
|
||||
```powershell
|
||||
npm ci
|
||||
# 인증이 필요하거나 주소/포트를 바꿀 때 .env.example을 .env로 복사해 편집
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
기본 접속: `http://127.0.0.1:3000`. 기본 DB: `mongodb://172.16.0.7:27017`, DB 이름 `sunrise`, 컬렉션 `visitSessions`. 인증이 필요하면 `MONGODB_URI`에 계정과 `authSource` 등 해당 서버 설정을 지정한다. URI를 브라우저용 `VITE_*` 변수에 넣지 않는다. `.env`는 버전 관리에서 제외한다.
|
||||
|
||||
개발 시 별도 터미널에서 `npm run dev:server`와 `npm run dev`를 실행한다. Vite의 `/socket.io`, `/health`, `/api` 요청은 3000 포트 서버로 전달된다. `npm run preview`는 프런트 미리보기이며 공동 방 서버를 대신하지 않는다.
|
||||
|
||||
`HOST` 기본값은 `127.0.0.1`이라 현재 컴퓨터에서만 접속할 수 있다. LAN/운영 배포 시 실제 바인딩 주소, `PORT`, `ALLOWED_ORIGINS`를 지정하고 TLS를 제공하는 프록시와 방화벽을 구성한다. 이번 구현은 외부 호스팅 배포까지 수행하지 않는다. 동일 방/정원을 여러 Node 프로세스에서 따로 실행하지 않는다. 프로세스별 메모리는 공유되지 않으며 현재는 단일 인스턴스 구성이다.
|
||||
|
||||
## 동작
|
||||
|
||||
- 공동 관측은 서버 UTC 시각을 기준으로 진행한다. 날짜 입력과 시간 슬라이더는 잠기며, 혼자 보기에서 기존 LIVE/CUSTOM 기능을 사용할 수 있다.
|
||||
- 위치·방향·인사/기도·우산·우비·후드를 서버 메모리에 저장한다. 초당 최대 10회 입력을 검증하고, 변경이 있으면 같은 방에 모아서 보낸다. 신규 입장에는 전체 참여자 상태를 전달한다. 5초마다 전체 상태도 다시 보낸다.
|
||||
- 현재는 100명 상한에서 단순성을 위해 변경이 있을 때 방 전체 스냅샷을 보낸다. 여러 방/대규모 확장 시 측정 결과에 따라 변경분 전송을 검토한다.
|
||||
- 보행 검증은 기존 관악산 경로·암반 영역과 이동 속도를 재사용한다. 위치 갱신 거리를 시간 기반 예산으로 제한하고 잘못된 좌표는 승인된 위치로 되돌린다. 완전한 서버 물리 시뮬레이션은 아니다.
|
||||
- 같은 장소의 날씨는 서버가 기존 조회·캐시 로직으로 읽어서 공유한다. 장비 착용 UI는 기존 비·눈 조건을 유지한다. 장비 자체는 영구 저장하지 않는다.
|
||||
- 같은 브라우저의 익명 `visitorId`를 localStorage에 보존하되, 로그인 인증이나 실제 사람의 신원으로 취급하지 않는다. 각 입장에는 별도의 서버 발급 방문 ID를 사용한다. 재접속 시 시작점과 장비 기본값으로 돌아온다. 시작점 복귀 버튼은 장비를 유지한다.
|
||||
- 연결이 끊기면 원격 캐릭터를 정리하고 재접속을 시도한다. heartbeat로 단절을 감지하기 전에는 자리가 잠시 유지될 수 있다. 혼자 보기로 전환하면 연결을 종료한다.
|
||||
- 101번째 참여자는 입장을 거절하고 재시도/혼자 보기를 제공한다. MongoDB 기록을 저장하는 동안에도 자리를 예약해 동시 입장으로 정원을 초과하지 않는다.
|
||||
|
||||
## 접속 기록
|
||||
|
||||
저장 필드: `_id`, `visitorId`, `nickname`, `locationId`, `roomId`, `joinedAt`, `lastSeenAt`, `leftAt`, `endReason`, `serverInstanceId`.
|
||||
|
||||
입장 기록 저장 성공 후 입장을 확정하고, 접속 중에는 60초마다 `lastSeenAt`을 갱신한다. 연결 종료와 정상 서버 종료에서 종료 정보를 기록한다. 위치·장비·감정표현은 DB에 쓰지 않는다.
|
||||
|
||||
서버 시작 시 과거 기록을 자동으로 변경하지 않는다. 강제 종료 또는 DB 장애로 퇴장 기록을 남기지 못하면 `leftAt`이 null로 남을 수 있다. 이는 현재 접속 중이라는 증거가 아니다. 실제 정원은 메모리에서 계산하며 `lastSeenAt`은 마지막 확인 시각일 뿐 정확한 퇴장 시각이 아니다. 로그 무손실 보장을 위한 디스크 큐와 자동 복구 작업은 포함하지 않는다.
|
||||
|
||||
DB 연결 실패 시 서버 시작을 중단한다. 실행 중 기록 저장 실패 시 `/health`는 503이고 신규 입장은 막으며 기존 참여자의 이동은 계속 처리한다. 원인을 해결한 후 서버를 재시작한다. 임의로 메모리 저장소로 전환하지 않는다. 개발·테스트에서만 `VISIT_STORE=memory`를 명시하면 기록을 영구 저장하지 않는 모드로 실행한다.
|
||||
|
||||
## 검증
|
||||
|
||||
```powershell
|
||||
npm test
|
||||
npm run build
|
||||
npm run verify:mongo
|
||||
npm run test:load
|
||||
```
|
||||
|
||||
`verify:mongo`는 매 실행마다 UUID가 붙은 임시 DB를 생성해 insert/heartbeat/finish/read 및 연결 시 기존 기록 보존을 확인한 뒤 그 DB만 삭제한다. `sunrise` 운영 데이터는 사용하지 않는다.
|
||||
|
||||
`test:load`는 메모리 저장소의 독립 로컬 서버와 100개 클라이언트를 한 프로세스에서 실행한다. 기본 300초이며 PowerShell에서 `$env:LOAD_SECONDS='10'`으로 짧은 실행도 가능하다. 보고서는 `artifacts/multiplayer/load-report.json`에 저장한다. 101번째 입장 거절·퇴장 후 자리 재사용·10Hz 이동을 검사한다.
|
||||
|
||||
2026-10-30 로컬 5분 결과: 오류 0, 이동 보정 0, 예상하지 않은 연결 종료 0. 방송 이후 수신 지연 p95 6ms, 최대 61ms. 이 값은 입력부터 방송까지의 대기 시간을 제외하며 실제 인터넷 지연·서버 단독 CPU 사용률·MongoDB 부하·모바일 GPU 성능을 보장하지 않는다. 두 브라우저에서 상호 캐릭터 표시/이동, 인원 표시, 혼자 보기와 재입장을 확인했다. 별도 메모리 검증 서버에서 99개 가상 참여자에 실제 브라우저 한 개가 입장해 100/100명 표시 및 양·우산·우비 렌더링을 확인했다. 해당 장면의 정량 프레임 시간과 모바일 성능은 측정하지 않았다.
|
||||
|
||||
## 주요 파일
|
||||
|
||||
- `server/app.ts`: HTTP/Socket.IO 서버, 정적 파일, 상태 확인
|
||||
- `server/room.ts`: 정원 예약, 입퇴장, 상태 검증과 방송
|
||||
- `server/visits.ts`: MongoDB 방문 기록과 명시적 메모리 테스트 저장소
|
||||
- `src/multiplayer/protocol.ts`: 클라이언트/서버 공통 이벤트 타입
|
||||
- `src/multiplayer/client.ts`: 입장·재접속·현재 상태·시각 동기화
|
||||
- `src/multiplayer/remotePlayers.ts`: 다른 참여자의 양 캐릭터 표시와 보간
|
||||
- `src/constants/multiplayer.ts`, `server/constants/multiplayer.ts`: 통신·정원·서버 제한
|
||||
Reference in New Issue
Block a user