CODEGATE 행사 부스에서 운영하는 체험형 웹 방탈출 게임입니다. 플레이어는 제한 시간 안에 보안 콘셉트의 스테이지를 클리어하고, 점수는 공개 스코어보드에 기록됩니다.
- 운영 제한 시간: 5분
- 현재 총 스테이지: 5개
- 랭킹 기준: 클리어 스테이지 수, 점수, 마지막 클리어 시점의 남은 시간 순
- 닉네임 정책: 중복 닉네임 사용 불가
- 게임 종료 시 제출 순위가 2~7위면 굿즈 추첨이 자동으로 진행됩니다.
- stage4는 OpenAI API를 사용하는 프롬프트 인젝션 스테이지입니다.
- stage5는 게이트 제어 웹사이트의 관리자 로그인을 SQL injection으로 우회하는 스테이지입니다.
| Stage | 이름 | 핵심 동작 |
|---|---|---|
| 1 | ACCESS LOCK | 락픽 UI를 조작해 첫 격벽을 엽니다. |
| 2 | REMOTE TERMINAL | 터미널/소스 화면을 보고 게이트를 엽니다. |
| 3 | CCTV COOKIE | 개발자도구로 cctv_enabled 쿠키 값을 바꿔 CCTV 문을 엽니다. 60초 후 힌트 버튼이 표시됩니다. |
| 4 | AI GUARD | AI 경비원과 대화해 프롬프트 인젝션으로 키패드 코드를 얻습니다. 대화는 10회 제한입니다. |
| 5 | SQL INJECTION | 출구 게이트 앞 컴퓨터에서 게이트 제어 사이트를 열고 관리자 로그인을 SQLi로 우회합니다. 열린 게이트를 통과하면 탈출합니다. |
- 스테이지당 점수 범위: 1,000점 ~ 2,000점
- 스테이지 시작 후 1초마다 10점씩 감점됩니다.
- 스테이지별 점수는 1,000점 아래로 내려가지 않습니다.
- 총점은 클리어한 스테이지 점수의 합입니다.
- 랭킹 정렬 순서:
- 클리어 스테이지 수가 많은 기록
- 총점이 높은 기록
- 마지막 클리어 시점의 남은 시간이 많은 기록
- 먼저 제출된 기록
- 닉네임은 1~16자입니다.
- 앞뒤 공백과 연속 공백은 정리됩니다.
- 제어 문자는 사용할 수 없습니다.
- 대소문자만 다른 닉네임도 같은 닉네임으로 취급합니다.
- 이미 등록된 닉네임은 게임 시작 전에 차단됩니다.
- 동시에 제출되는 경우를 대비해 서버 제출 시점에도 중복 닉네임을
409 duplicate_nickname으로 차단합니다. - 기존 점수 파일에 중복 닉네임이 있으면 서버 로드 시 최고 기록 하나만 남깁니다.
- 점수 제출 시점 순위가 2~7위인 플레이어만 추첨 대상입니다.
- 종료 화면에서 순위·점수·남은 시간 정보를 유지한 채 굿즈 추첨 패널이 함께 표시됩니다.
- 참가자가
뽑기 시작버튼을 누르면 추첨 애니메이션과 당첨 상품이 표시됩니다. - 최초 재고는 간식 100개, 키링 50개, 클리커 50개입니다.
- 재고가 있는 상품 중 하나를 같은 확률로 무작위 추첨하며 재고가 없는 상품은 추첨에서 제외됩니다.
- 추첨 결과와 남은 재고는 점수 파일에 함께 저장되므로 서버를 재시작해도 유지됩니다.
- 관리자 화면에서 남은 재고와 기록별 당첨 상품을 확인하고 재고만 초기화할 수 있습니다.
- 점수 기록을 삭제해도 이미 지급된 상품 재고는 복구되지 않습니다.
docker compose up -d --build접속 주소:
http://localhost:8080/운영 화면:
http://localhost:8080/scoreboard
http://localhost:8080/admin상태 확인:
curl http://localhost:8080/api/health중지:
docker compose down점수 기록까지 초기화해야 할 때만 볼륨을 함께 삭제합니다.
docker compose down -vnpm install
npm run dev개발 서버:
http://localhost:5173/프로덕션 빌드:
npm run build빌드 결과 미리보기:
npm run previewDocker Compose는 프로젝트 루트의 .env를 읽습니다.
SCORE_ADMIN_TOKEN=change-this-admin-token
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4.1-mini
STAGE_FOUR_PASSWORD=7391
STAGE_FOUR_MAX_GUARD_REQUESTS=10| 변수 | 기본값 | 설명 |
|---|---|---|
SCORE_ADMIN_TOKEN |
codegate-admin |
/admin 관리자 인증 토큰 |
OPENAI_API_KEY |
없음 | stage4 AI 경비원 호출에 필요 |
OPENAI_MODEL |
gpt-4.1-mini |
stage4에서 사용할 OpenAI 모델 |
STAGE_FOUR_PASSWORD |
7391 |
stage4 키패드 정답 |
STAGE_FOUR_MAX_GUARD_REQUESTS |
10 |
stage4 AI 대화 횟수 제한 |
OPENAI_API_KEY가 없으면 stage4의 일반 AI 대화는 사용할 수 없습니다. 다만 공식 기록
맥락, 수정 없는 원문 요청, 무결성 보존 이유를 모두 갖춘 요청은 서버에서 처리됩니다.
GET /api/health
GET /api/scores?limit=10
GET /api/scores/nickname?nickname=PLAYER
POST /api/scores
POST /api/stage4/guard
POST /api/stage4/verify
POST /api/stage5/login점수 제출 예시:
{
"nickname": "PLAYER",
"clearedStages": 4,
"remainingTimeMs": 42123,
"score": 6730
}중복 닉네임이면 POST /api/scores는 409를 반환합니다.
관리자 API는 Authorization: Bearer <SCORE_ADMIN_TOKEN> 헤더가 필요합니다.
GET /api/admin/scores?limit=500
DELETE /api/admin/scores
DELETE /api/admin/scores/:id
POST /api/admin/prizes/reset시작 화면 닉네임 입력란에 debug를 입력하면 스테이지 선택 화면으로 들어갑니다.
현재 선택 가능한 디버그 항목은 1~6입니다. 6을 선택하면 실제 점수 제출과
재고 차감 없이 2위 종료 결과와 굿즈 추첨 화면을 바로 확인할 수 있습니다.
디버그 런은 스코어보드에 기록되지 않습니다.
src/
├─ config/
│ └─ gameConfig.ts
├─ constants/
│ ├─ stageLayout.ts
│ └─ world.ts
├─ objects/
│ └─ createPlayerTexture.ts
├─ pages/
│ ├─ adminPage.ts
│ └─ scoreboardPage.ts
├─ scenes/
│ └─ DungeonScene.ts
├─ services/
│ ├─ scoreboard.ts
│ ├─ scoring.ts
│ └─ stageFour.ts
├─ ui/
│ ├─ createAiGuardDialog.ts
│ ├─ createBofTerminalScreen.ts
│ ├─ createGameFlowScreen.ts
│ ├─ createGameHud.ts
│ ├─ createLockpickScreen.ts
│ └─ createStageFourKeypad.ts
├─ world/
│ ├─ createAiGuardStage.ts
│ ├─ createBofStage.ts
│ ├─ createCctvStage.ts
│ ├─ createDungeonBackground.ts
│ ├─ createDungeonPlatforms.ts
│ └─ createStageTransport.ts
├─ main.ts
└─ style.css
server/
└─ scoreServer.mjs- 점수와 굿즈 재고 데이터는 Docker 볼륨
score-data의/data/scores.json에 저장됩니다. - 웹 컨테이너는 Nginx로 정적 파일을 제공하고
/api/*요청을 score API 컨테이너로 프록시합니다. - stage4의 AI 응답 비용은 실제 대화 요청 수에 비례합니다. 서버를 켜둔 것만으로는 OpenAI 비용이 발생하지 않습니다.
- 관리자 토큰과 OpenAI API 키는 저장소에 커밋하지 않습니다.