## 왜 ### 완주 후 공유 수단이 링크 하나 - 토너먼트 생성 시점에 6자리 코드(`ABC123`) 발급. 이후 불변 - 그 코드는 대기실 합류에만 쓰임 - **원인:** `checkJoinable` 이 `isPending()` 을 요구 - **결과:** 완주 후 코드가 죽어 공유 수단이 URL 하나로 좁아짐 - **결과:** 오프라인 구두 전달·손 입력 지원 없음 ### 링크 주소의 추측 가능성 - `/play/{tournamentId}` 의 주소가 순번 id - **결과:** 공유를 켠 토너먼트에 번호만 바꿔 진입 가능 - 6자리 코드는 약 1,750만 조합 - **결과:** 이 축으로는 코드가 나음 - **범위:** 링크 자체의 추측 가능성은 이 작업 밖 ## 무엇을 ### 완주한 토너먼트에 코드로 입장 - `POST /api/v1/tournaments/from-play-code` 신설. body 는 `{ "code": "ABC123" }` - 기존 코드에 "완주 상태에서는 플레이 입장 주소" 의미를 하나 더 얹음 - 새 컬럼·새 코드 발급 없음 - **조건:** `uk_tournaments_active_invite_code` 가 활성 토너먼트 전체에서 코드 유일성 보장 - 삭제 행은 generated 컬럼이 `NULL` 이 되어 코드 재활용 - **결과:** 코드 하나가 활성 토너먼트 하나를 가리키는 것이 이미 성립 ### 상태가 정하는 게이트 | 대상 상태 | 코드로 열리는 문 | 게이트 | |---|---|---| | 대기실 | 참여자로 합류 (기존) | 초대 유효 · 30분 | | 주최자 완주 | 플레이 생성 (신규) | 공유 링크 유효 · 14일 | - 코드는 주소. 열리는 문은 상태가 결정 - 공유를 켜지 않았으면 `playLinkExpiresAt` 이 비어 코드로도 막힘 - 주최자 탈퇴로 링크가 무효화된 경우도 같은 게이트를 타 함께 막힘 ### 결정 | 항목 | 결정 | |---|---| | 미리보기 | `by-invite-code` 에 상태별 분기. 완주면 409 대신 플레이 가능 정보 | | 정원 | 8명. 초대·링크·코드 동일. 초과 시 `TOURNAMENT-030` | | 알림 | ROOT 주최자 (`TournamentPlayedFromLink` 유지) | | 만료 | `playLinkExpiresAt` 을 그대로 게이트로 사용. 링크와 자동 동일 | - **참조:** [미결정 항목 정리](https://github.com/TeamPiKi/core/issues/1013#issuecomment-5676671676) ### 검증 | 시나리오 | 기대 | |---|---| | 완주한 토너먼트 코드로 입장 | 플레이 생성. 원본 아이템 그대로 | | 공유를 켜지 않은 완주 토너먼트 코드 | 거부 | | 만료된 플레이 링크의 코드 | 거부. 재발급(#980) 후 다시 입장 가능 | | 대기실 토너먼트 코드 | 종전대로 합류 (회귀 가드) | | 주최자 탈퇴로 무효화된 토너먼트 코드 | 거부 | | 이미 참여 중인 사람의 재입장 | 기존 참여 행 (멱등) | | 정원 초과 | `TOURNAMENT-030` | ## 범위 ### 클론 재공유와 source 평탄화 소멸 - **이전:** 링크로 들어온 사람마다 자기 판(클론)이 생김. `TOURNAMENT-024` 가 그 클론의 재공유를 차단 - **이전:** 이 이슈의 본래 두 번째 축은 그 차단을 걷고 새 클론의 `sourceTournamentId` 를 ROOT 로 평탄화 - #1027 Phase 1-3(#1044)으로 클론 개념 소멸 - **원인:** 진행 상태가 `tournaments.status` 에서 `tournament_users.status` 로 이동 - **결과:** 누가 어느 주소로 들어오든 그 토너먼트의 참여 행 하나. 새 클론 생성 없음 - **결과:** 평탄화할 `sourceTournamentId` 가 새로 생기지 않아 질문 자체가 소멸 - **후속:** 남은 클론 행은 리다이렉트 껍데기. 제거는 #1027 Phase 4 ## 참고 ### 관련 이슈 | 이슈 | 관계 | |---|---| | #980 | 플레이 링크 멱등화·만료 후 재발급 | | #1044 | 클론 제거 (#1027 Phase 1-3) |
Activity
sevineleven commentedon Sep 15, 2026
미결정 항목 정리 - 착수 가능
본문의 "함께 정할 것" 을 정했습니다.
by-invite-code에 상태별 분기를 둔다. COMPLETED 면 409 대신 플레이 가능 정보를 내린다TOURNAMENT-030TournamentPlayedFromLink유지)playLinkExpiresAt을 그대로 게이트로 쓴다 → 링크와 자동으로 같아진다"링크 경로도 클론을 받게 할지" 는 사라졌습니다
#1027 Phase 1-3(#1044)으로 클론 개념 자체가 없어져서 이 질문이 성립하지 않습니다. 진행 상태가 참여 행(
tournament_users.status)으로 옮겨졌고, 누가 어느 주소로 들어오든 그 토너먼트의 참여 행이 하나 생깁니다.같은 이유로 본문의 "source 를 ROOT 로 평탄화" 도 필요 없어졌습니다. 이 이슈는 코드로 입장하는 경로 하나만 남습니다.
정원을 링크에도 거는 이유
지금은 초대 합류만 8명 제한이고 플레이 링크는 무제한입니다. 코드 입장만 막으면 "코드로는 막히는데 링크 달라고 하면 들어가지는" 상태가 되어 제한이 이름뿐이 됩니다. 같은 동작이므로 주소와 무관하게 같은 제한을 건다.
기존 사용은 안 깨집니다. prod 실측상 공유된 토너먼트 37개의 입장자 분포가 이렇습니다.
8명에 근접한 사례가 없어, 제한을 걸어도 막히는 사용자가 없습니다.
곁가지 - 문서가 코드와 어긋나 있습니다
#370 본문엔 "소유자 포함 최대 9명" 으로 적혀 있는데 실제 코드는 이렇습니다.
참여 행 기준이라 주최자 포함 8명입니다. 이 작업에서 API 문서도 8로 맞춥니다.
만료가 자동으로 맞는 이유
코드는 주소일 뿐이고 무엇이 열려 있는지는 상태가 정합니다.
isPending()+isInviteValid()(30분)isPlayLinkValid()(14일)playLinkExpiresAt이 비어 있으면(주최자가 공유를 안 켰으면) 코드로도 막힙니다. 주최자가 나가 링크가 무효화된 경우도 같은 게이트를 타므로 함께 막히고, #980 으로 만료 후 재발급하면 코드도 같이 살아납니다.feat: 완료된 토너먼트에 코드로 입장 (#1013)
[-]완료된 토너먼트를 코드로도 입장 + 클론 재공유 (source 는 ROOT 로 평탄화)[/-][+]플레이 링크를 코드로도 공유[/+]feat: 완료된 토너먼트에 코드로 입장 (#1013) (#1101)