# 06. 로비와 방 ## 1. 목표 - 방 만들기: 첫 화면에서 **게임 카드 누르기 → "방 만들기"** 두 번이면 대기실, 코드와 링크가 바로 보인다. - 방 참여: 링크를 누르면 바로 대기실(처음이면 닉네임 한 칸). 코드를 받았으면 첫 화면 "코드로 참여"에 숫자 6개. - 글을 몰라도 쓸 수 있게 큰 아이콘 + 짧은 글자. ## 2. 화면과 주소 | 주소 | 화면 | |---|---| | `/` | 홈: 위쪽에 큰 버튼 두 개 **[방 만들기] [코드로 참여]**, 그 아래 "진행 중인 게임으로 돌아가기"(있을 때만), 그 아래 게임 카드 목록 | | `/g/:gameId` | 게임 소개: 한 줄 설명, 인원, 시간, 난이도, **[방 만들기] [빠른 시작] [규칙 보기]**, 열린 공개 방 목록 | | `/r/:code` | 방(대기실 ↔ 게임 화면 ↔ 결과 화면이 같은 주소에서 전환) | | `/j` | 코드 입력 전용 화면(숫자 키패드) | | `/me` | 계정 | | `/rules/:gameId` | 규칙 설명(그림 포함) | 초대 링크는 `https://<도메인>/r/123456`. 짧고 읽기 쉬워서 말로 불러 줘도 된다. ## 3. 방 코드 - 6자리 숫자(000000 제외, 같은 숫자 6개·연속 숫자 제외). 숫자만 쓰는 이유: 어르신·어린이가 키패드로 입력하기 가장 쉽고, 말로 전달하기 쉽다. - 화면에는 `123 456`처럼 3자리씩 띄어 보여 준다. 입력은 띄어쓰기·하이픈 무시. - 사용 중인 코드와 겹치지 않게 무작위 발급. 방이 끝나고 24시간 동안은 같은 코드 재사용 금지(옛 링크로 엉뚱한 방에 들어가는 것 방지). - 코드 입력 화면: 6칸 큰 숫자 칸, 휴대폰 숫자 키패드(`inputmode="numeric"`), 6자리가 채워지면 자동으로 입장 시도. 클립보드에 6자리 숫자나 초대 링크가 있으면 "붙여넣기" 버튼을 크게 표시. ## 4. 방 만들기 1. 게임 선택(홈 카드 또는 홈의 [방 만들기] → 게임 고르기 화면). 2. 바로 대기실 생성. 모든 옵션은 **기본값으로 이미 정해져 있다**(인원은 게임 기본 인원, 공개 여부는 "친구만(비공개)"). 3. 대기실에서 방장이 바꿀 수 있는 것: 게임 모드/옵션(시간 제한 포함 — 게임마다 다름), 최대 인원(게임의 최소~최대 범위 안), 공개/비공개, 관전 허용, 채팅 허용. 4. 옵션은 "기본 설정" 몇 개만 보이고 나머지는 "자세한 설정" 접기 안에 둔다. ## 5. 대기실 화면 (PC는 두 칸 배치 — `07-ui-ux.md` 3.4. 아래는 내용 순서이며 좁은 화면에서는 이 순서로 한 칸에 쌓인다) 1. **초대 카드**(가장 눈에 띄게): 큰 글씨 코드 `123 456` - [코드 복사] [링크 복사] [공유하기](모바일: 공유 시트 → 카카오톡 등) [QR 보기](같은 공간의 가족이 폰으로 찍어서 입장) - 복사하면 버튼이 1.5초간 "복사했어요 ✓"로 바뀜 + 진동(지원 기기). - 공유 문구: "OO님이 오목 방에 초대했어요! 코드 123 456 · https://<도메인>/r/123456" 2. **자리 목록**: 게임 인원만큼 자리 칸. 각 칸: 프로필, 닉네임, 방장 왕관, 준비 체크, 연결 상태 점(초록/노랑/회색). 빈자리는 점선 "+ 초대하기"(누르면 초대 카드로 스크롤). 3. **게임 설정 요약**: "오목 · 렌주룰 · 한 수 30초" + 방장에게만 [설정 바꾸기]. 4. **아래 고정 버튼**(엄지 닿는 곳): - 방장: [게임 시작] — 최소 인원이 안 되면 비활성 + "1명 더 필요해요" 안내. 모두 준비가 아니어도 시작 가능하되 "준비 안 된 사람이 있어요. 시작할까요?" 확인. - 나머지: [준비하기] / [준비 취소]. 5. 채팅(접을 수 있음), 관전자 수. 방장 기능: 내보내기, 방장 넘기기, 자리 순서 섞기(선 정하기), 게임 바꾸기(같은 방 유지 — 친구들끼리 여러 게임 이어서 하기). 방장이 나가면 가장 먼저 들어온 사람이 방장. ## 6. 링크로 들어오기 1. `/r/123456` 접속. 2. 로그인 안 됨 → 같은 화면 위에 작은 창: 방 정보("오목 · 방장 OO · 1/2명") + 닉네임 칸(추천 이름 채워짐) + [이 이름으로 입장] + 작게 [디스코드로 로그인]. 3. 입장 → 빈자리가 있으면 **자동으로 자리에 앉는다**. 없으면 관전자로 들어가고 "자리가 나면 앉을 수 있어요". 4. 방이 없거나 끝났으면: "이 방은 끝났어요" + [같은 게임 새 방 만들기] [홈으로]. ## 7. 빠른 시작 - 게임 상세의 [빠른 시작]: 같은 게임의 공개 대기실 중 사람이 가장 많은(가득 차지 않은) 방에 들어간다. 없으면 공개 방을 새로 만들고 기다린다. - 기다리는 동안 "다른 사람을 기다리는 중… (공개 방)" + [친구 초대하기] 버튼. ## 8. 게임 중 - 위쪽 상태 줄: 지금 할 일 문장("내 차례예요" / "OO님 차례예요"), 남은 시간 링. - 메뉴(⋯): 규칙 보기, 소리, 글자 크기, 무르기 요청(지원 게임), 무승부 제안(지원 게임), 기권, 나가기. - 나가기를 누르면: "게임 중에 나가면 OO 처리돼요. 정말 나갈까요?"(게임별: 2인 대전은 기권패, 카드 게임은 다이 후 자리 비움 등). - 관전자는 [자리 나면 참가] 대기 가능(다음 판부터). ## 9. 게임 끝 - 결과 화면: 순위, 점수, 핵심 장면 요약(예: "흑 5목 완성"), [한 판 더] [게임 바꾸기] [나가기]. - [한 판 더]는 투표: 자리에 앉은 사람 모두 누르면 같은 설정으로 즉시 새 게임(선/딜러는 규칙에 따라 교대). 안 누르는 사람이 있으면 그 사람이 [관전하기]로 일어나거나 방장이 [게임 시작]으로 바로 시작할 수 있다. - 기록: 끝난 게임은 `game_results`에 저장, 방 안에서 "이번 방 전적"(예: OO 3승 2패) 표시. ## 10. 공개 방 목록 - 게임 상세 화면에서만 보인다(홈을 복잡하게 하지 않음). 정렬: 곧 시작할 방(빈자리 1개) 우선. - 각 줄: 방 이름(기본 "OO님의 방"), 인원, 모드, [참여]. ## 11. 서버 방 모델 ```ts type RoomStatus = 'lobby' | 'playing' | 'finished' | 'paused' | 'broken'; interface Room { code: string; // 6자리 id: string; // 내부 ULID hostId: UserId; gameId: GameId; options: unknown; // 게임 optionsSchema로 검증된 값 visibility: 'private' | 'public'; maxPlayers: number; // 게임 min~max 범위 graceSec: number; // 연결 끊김 유예(기본 60초). 턴 시간은 게임 옵션 allowSpectators: boolean; chat: { enabled: boolean; filter: boolean }; seats: Array<{ userId: UserId; ready: boolean } | null>; spectators: Set; presence: Map; status: RoomStatus; seq: number; game?: { state: unknown; rng: RngState; deadline: number | null; startedAt: number; seedHash: string }; rematchVotes: Set; sessionStats: Map; createdAt: number; lastActiveAt: number; } ``` - 한 사용자는 동시에 한 방에만 플레이어로 있을 수 있다(다른 방에 들어가면 이전 방 대기실 자리에서 빠짐. 이전 방이 게임 중이면 "진행 중인 게임이 있어요. 그래도 이동할까요?"). - 제한: 서버 전체 방 2,000개, 사용자당 동시에 만든 방 3개.