diff --git a/specs/005-room-domain/checklists/requirements.md b/specs/005-room-domain/checklists/requirements.md new file mode 100644 index 00000000..df9119ee --- /dev/null +++ b/specs/005-room-domain/checklists/requirements.md @@ -0,0 +1,35 @@ +# Specification Quality Checklist: 방(Room) 기능 + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-05-17 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) — BCrypt/JPA/스케줄러 등 구현 용어 본문 미포함 +- [x] Focused on user value and business needs — 우선순위(P1~P3)로 사용자 가치 정렬 +- [x] Written for non-technical stakeholders — 한국어 비기술자 친화 서술 +- [x] All mandatory sections completed — User Scenarios / Requirements / Success Criteria 모두 작성 + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain — 2개 마커 해소 (Q1: memberCount 내림차순·개인화 없음 / Q2: 현재 호스트 이탈 전면 불가·자동 만료는 시간 기반 잡, 향후 양도+호스트 단독 방 삭제 도입 예정) +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded — 책·게시글·알림·신고는 명시적 범위 외 +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- 2026-05-17 1차 검증: 마커 해소, 모든 항목 통과. `/speckit-clarify`(선택) 또는 `/speckit-plan`으로 진행 가능. +- 향후 트리거: 호스트 양도 + 호스트 단독 방 삭제 기능 도입 시 FR-016, FR-017, Edge Cases 업데이트 필요(Assumptions에 명시됨). diff --git a/specs/005-room-domain/spec.md b/specs/005-room-domain/spec.md new file mode 100644 index 00000000..6f6acb9f --- /dev/null +++ b/specs/005-room-domain/spec.md @@ -0,0 +1,239 @@ +# Feature Specification: 방(Room) 기능 + +**Feature Branch**: `005-room-domain` + +**Created**: 2026-05-17 + +**Status**: Reviewed (clarifications resolved 2026-05-17) + +**Input**: User description: "room 관련 기능" + +> 본 문서는 신규 기능 정의가 아닌, 이미 운영 중인 THIP 서비스의 "방(Room)" 도메인을 사용자 관점에서 역설계해 정형화한 PRD다. 구현 상세(스택·해시 알고리즘·스케줄러)는 의도적으로 배제하고, 사용자가 무엇을(WHAT) 왜(WHY) 할 수 있어야 하는지에 집중한다. + +## User Scenarios & Testing *(mandatory)* + +THIP의 **방(Room)**은 *같은 책을 함께 읽는 사용자 모임*이다. 호스트가 책을 정해 방을 만들고, 다른 사용자들이 모집 기간 안에 참여한다. 일정 시점에 모집이 마감되면 방은 *진행 중* 상태로 전환되어 기록·투표·오늘의 한마디 등 방 내부 활동이 이루어지고, 종료일 이후에는 *만료* 상태가 된다. + +### User Story 1 - 방 만들고 모집 시작하기 (Priority: P1) + +호스트가 함께 읽을 책·기간·모집 정원·공개 여부·카테고리를 정해 방을 만들고, 다른 사용자에게 노출되어 모집이 시작된다. + +**Why this priority**: 방이 없으면 THIP의 모임 가치는 존재하지 않는다. 모든 후속 활동의 출발점. + +**Independent Test**: 어떤 사용자가 책·시작일·종료일·모집 정원·공개 여부·카테고리를 지정해 방을 만들면 새 방이 *모집 중* 상태로 생성되고, 만든 사용자가 호스트로 자동 참여한 1인 상태로 시작된다. + +**Acceptance Scenarios**: + +1. **Given** 사용자가 정상적인 입력(책·시작일·종료일·모집 정원·카테고리·공개 여부)으로 방 생성을 요청할 때, **When** 생성이 성공하면, **Then** 새 방이 *모집 중* 상태로 만들어지고, 만든 사용자가 *호스트* 역할로 자동 참여한 상태(memberCount=1, roomPercentage=0)가 된다. +2. **Given** 사용자가 *비공개* 방을 비밀번호 없이 만들려 하거나, *공개* 방에 비밀번호를 지정하려 하면, **Then** 작업이 거부된다(공개 여부와 비밀번호 존재 여부는 정확히 짝을 이뤄야 한다). +3. **Given** 사용자가 시작일을 *오늘 또는 과거 날짜*로 지정하거나, 시작일을 종료일과 같거나 이후로 지정하면, **Then** 작업이 거부된다(시작일은 오늘 이후, 종료일은 시작일 이후). +4. **Given** 카테고리 값이 시스템이 인정하는 5종(과학·IT, 문학, 예술, 사회과학, 인문학) 중 어느 하나도 아닐 때, **Then** 작업이 거부된다. + +--- + +### User Story 2 - 모집 중인 방 둘러보고 참여하기 (Priority: P1) + +사용자는 키워드·카테고리로 모집 중인 방을 찾고, 정렬 옵션으로 마감 임박 또는 신청 인원순으로 둘러본다. 공개 방은 곧장 참여할 수 있고, 비공개 방은 비밀번호 검증을 거친 후 참여한다. + +**Why this priority**: 모집된 방이 발견되지 않으면 호스트의 입장에서 방을 만든 보람이 사라진다. P1으로 함께 묶임. + +**Independent Test**: 모집 중인 방이 다수 존재할 때 (a) 키워드로 검색해 결과를 받고, (b) 정렬 옵션(마감 임박/신청 인원)이 적용되며, (c) 공개/비공개 방 모두 검색 결과에 노출되되 비공개 방은 비밀번호 검증을 통과해야 참여할 수 있다. + +**Acceptance Scenarios**: + +1. **Given** 사용자가 키워드(책 이름 또는 방 이름)와 카테고리를 지정해 모집 중인 방을 검색할 때, **When** 검색이 성공하면, **Then** 매칭된 모집 중인 방의 커서 페이지 결과가 반환된다. 공개/비공개 방 모두 포함된다. +2. **Given** 사용자가 카테고리 대신 *전체 검색* 모드(`isAllCategory=true`)를 선택할 때, **Then** 모든 카테고리에 걸친 결과가 반환된다. +3. **Given** 사용자가 정렬 옵션을 *마감 임박*(`deadline`) 또는 *신청 인원*(`memberCount`)으로 지정할 때, **Then** 결과는 그 기준에 따라 정렬된다. +4. **Given** 사용자가 검색 입력을 *확정*(`isFinalized=true`)했을 때, **Then** 시스템은 그 키워드를 사용자별 *최근 검색어*로 기록한다. 입력 중(`isFinalized=false`)에는 기록하지 않는다(책 검색의 정책과 일관). +5. **Given** 사용자가 모집 중인 방 상세보기를 요청할 때, **Then** 방의 기본 정보·정원 대비 현재 참여자 수·진행 상태·책 정보 등이 함께 반환된다. +6. **Given** 사용자가 *공개* 방에 *참여*를 요청할 때, **When** 정원 미만이고 사용자가 아직 참여하지 않았다면, **Then** 사용자는 멤버로 참여하고 방의 memberCount가 1 증가한다. +7. **Given** 사용자가 *비공개* 방에 참여하기 위해 비밀번호 검증을 요청할 때, **When** 비밀번호가 일치하면, **Then** 검증 성공 응답이 반환된다(이후 별도의 참여 요청을 통해 실제 참여가 이루어진다). +8. **Given** 사용자가 *공개* 방에 비밀번호 검증을 요청할 때, **Then** 비밀번호가 필요 없다는 오류로 응답된다. +9. **Given** 사용자가 *모집 기간이 만료된* 방에 참여 또는 비밀번호 검증을 요청할 때, **Then** 작업이 거부된다. +10. **Given** 정원이 가득 찬 방에 참여를 요청할 때, **Then** 정원 초과 오류로 거부된다. + +--- + +### User Story 3 - 내가 참여한 방 관리하기 (Priority: P1) + +사용자는 자신이 참여한 방을 한 곳에서 관리한다. *홈 화면*에서는 활성(모집 중·진행 중) 방만 보고, *전용 목록*에서는 상태 필터(모집 중/진행 중/만료/혼합)로 필터링해 본다. 호스트가 아닌 멤버는 언제든 방을 나갈 수 있다. + +**Why this priority**: 가입한 사용자에게는 "내가 어디에 속해 있나"가 매일 보는 정보. 활성 방의 발견과 떠나기 모두 핵심. + +**Independent Test**: 임의 사용자가 (a) 홈에서 자신이 참여한 활성 방 목록을 보고, (b) 전용 목록에서 상태별로 필터링하며, (c) 호스트가 아닌 방은 나갈 수 있다. + +**Acceptance Scenarios**: + +1. **Given** 사용자가 다수의 방에 참여 중일 때, **When** 사용자가 홈의 *내 참여 방* 목록을 요청하면, **Then** 모집 중·진행 중인 방만 커서 페이지로 반환된다(만료된 방은 제외). +2. **Given** 사용자가 *내 모임방* 전용 목록을 요청할 때, **When** 상태 필터를 지정(`playingAndRecruiting`, `recruiting`, `playing`, `expired`)하면, **Then** 그 상태에 해당하는 자신의 방만 반환된다. 필터 미지정 시 기본값은 *모집중 + 진행중*이다. +3. **Given** 사용자가 *호스트가 아닌* 방에 대해 나가기를 요청할 때, **When** 작업이 성공하면, **Then** 사용자는 멤버에서 제외되고 방의 memberCount가 1 감소한다. +4. **Given** 사용자가 *호스트인* 방에 대해 나가기를 요청하면, **Then** 작업이 거부된다(현재 정책상 호스트는 어떤 경로로도 방을 떠날 수 없다 — 양도·방 폐쇄 기능 미제공). +5. **Given** 사용자가 자신이 참여한 방의 참여자 목록을 요청하면, **Then** 호스트 표시가 포함된 참여자 명단이 반환된다. + +--- + +### User Story 4 - 호스트로서 방 운영하기 (Priority: P2) + +호스트는 자신이 만든 방의 모집을 마감해 *진행 중* 상태로 전환한다. 진행 중인 방에서는 멤버들의 기록·투표·오늘의 한마디가 누적된다. + +**Why this priority**: 운영 권한은 일반 멤버 시나리오 이후 의미가 있다. 호스트의 의도적 액션이 라이프사이클 전환의 핵심 트리거. + +**Independent Test**: 호스트가 자신의 *모집 중* 방에 대해 모집 마감을 요청하면 방의 상태가 *진행 중*으로 바뀌고 시작일이 그 시점의 날짜로 갱신된다. 호스트가 아닌 사용자의 마감 요청은 거부된다. + +**Acceptance Scenarios**: + +1. **Given** *호스트*가 자신의 *모집 중* 방에 대해 모집 마감을 요청할 때, **When** 작업이 성공하면, **Then** 방의 상태가 *진행 중*으로 전환되고 시작일이 마감 시점의 날짜로 갱신된다. +2. **Given** *호스트가 아닌* 사용자가 임의 방의 모집 마감을 요청할 때, **Then** 작업이 거부된다. +3. **Given** *이미 진행 중이거나 만료된* 방에 대한 모집 마감 요청, **Then** 작업이 거부된다(모집 중 상태가 아니므로). + +--- + +### User Story 5 - 방 안에서 활동 컨텍스트 확인하기 (Priority: P2) + +방 안에서 기록 작성을 시작하려는 사용자는 *책 전체 페이지 수*와 *총평 작성이 가능한지 여부*를 컨텍스트로 받는다. 방의 진행 상태(진행 중/완료)에 따라 상세 화면의 구성이 달라진다. + +**Why this priority**: P1 시나리오(작성)의 *진입 직전* 정보를 제공하는 보조 흐름. 마찰을 줄이는 보조 시나리오. + +**Independent Test**: 방 참여자가 (a) 방의 책 페이지 정보와 총평 가능 여부를 단일 호출로 받고, (b) 진행 중 또는 완료된 방 상세보기에서 상태에 맞는 정보가 반환된다. + +**Acceptance Scenarios**: + +1. **Given** 방 참여자가 기록 작성 화면에 진입할 때, **When** 사용자가 책 페이지 정보를 요청하면, **Then** 책의 전체 페이지 수와 *총평 작성이 가능한 상태인지 여부*가 함께 응답된다. +2. **Given** 사용자가 *진행 중* 또는 *완료(만료)* 방의 상세를 요청할 때, **When** 작업이 성공하면, **Then** 그 상태에 맞는 상세 정보(진행률·기록·투표 요약 등 화면에 필요한 정보)가 반환된다. +3. **Given** 사용자가 방 비참여자일 때, **When** 그 방에 대한 책 페이지 정보 또는 참여자 전용 상세를 요청하면, **Then** 작업이 거부되거나 비참여자용 정보만 반환된다(상세 정책은 방 라이프사이클 규칙에 따른다). + +--- + +### User Story 6 - 카테고리별 추천 방 발견 (Priority: P2) + +사용자는 카테고리별로 "마감 임박/인기/최근 생성된" 방 묶음을 한눈에 본다. 능동 검색을 하지 않아도 카테고리 단위로 의미 있는 방을 발견한다. + +**Why this priority**: 검색 외의 발견 진입점. 사용자 활성도와 신규 방 노출의 균형을 맞춤. + +**Independent Test**: 사용자가 카테고리(기본: 문학)를 지정해 추천 목록을 요청하면 *마감 임박 방*, *인기 방*, *최근 생성된 방* 세 묶음이 응답된다. + +**Acceptance Scenarios**: + +1. **Given** 시스템에 충분한 모집 중 방이 존재할 때, **When** 사용자가 카테고리를 지정해 추천 방 목록을 요청하면, **Then** *마감 임박*·*인기*·*최근 생성* 세 묶음이 응답된다. +2. **Given** 카테고리 미지정 시, **Then** 기본 카테고리("문학")가 적용된다. +3. **Given** 어떤 카테고리에 모집 중인 방이 없을 때, **Then** 그 묶음은 빈 결과로 일관되게 응답된다. + +**"인기 방" 산정 기준 (확정)**: + +- **신호**: 모집 중 방의 **현재 참여자 수**(`memberCount`) *내림차순*. *정원 대비 충원율*이 아닌 *절대 참여자 수* 기준이다. +- **개인화**: 없음. 모든 사용자에게 동일한 결과가 노출된다. +- **대상**: *모집 중* 상태의 방으로 한정한다(진행 중·만료는 후보 아님). +- 같은 카운트인 방 사이의 결정적 순서는 본 PRD가 강제하지 않으며, 다른 순서로 노출되어도 사용자 가치에 영향이 없는 수준이어야 한다. + +--- + +### Edge Cases + +- **공개 여부와 비밀번호의 짝**: 공개 방에 비밀번호를 지정하거나 비공개 방을 비밀번호 없이 만들려는 요청은 거부된다. +- **시작일·종료일 유효성**: 시작일은 오늘 이후여야 하고, 종료일은 시작일 이후여야 한다. 같은 날짜는 허용되지 않는다. +- **카테고리 정합성**: 시스템이 인정하는 카테고리(과학·IT/문학/예술/사회과학/인문학) 외 입력은 거부된다. +- **모집 만료 후 참여 시도**: 모집 중 상태가 아닌 방에 참여 또는 비밀번호 검증을 시도하면 거부된다. +- **정원 초과 참여 시도**: memberCount가 recruitCount에 도달한 방에 참여를 요청하면 거부된다. +- **참여자 수 무결성**: memberCount는 어떤 동시성 시나리오에서도 활성 참여자 수와 일치한다. 1 미만으로 감소할 수 없다(호스트는 항상 존재). +- **호스트의 방 이탈 (확정 — 현재 정책)**: 호스트는 어떤 경로로도 방을 떠날 수 없다. *일반 나가기*는 거부되고, *호스트 권한 양도*나 *방 폐쇄(삭제)* 기능도 현재 제공되지 않는다. 따라서 한번 호스트가 된 사용자는 해당 방이 *만료* 상태에 도달할 때까지 호스트로 남는다. +- **공개 방의 비밀번호 검증 요청**: 공개 방에 대한 비밀번호 검증 요청은 "비밀번호 불필요" 오류로 응답된다. +- **방 상태 자동 전환 (확정)**: 종료일이 지난 *진행 중* 방은 시간 기반(스케줄) 트리거로 *만료* 상태로 자동 전환된다. 사용자가 어떤 액션을 하지 않아도 종료일 경계 이후의 첫 가시점부터 방은 *만료* 상태로 노출되어야 한다. 사용자 액션에 의한 명시적 만료 전환 경로는 본 PRD가 정의하지 않는다. + +## Requirements *(mandatory)* + +### Functional Requirements + +#### 생성 + +- **FR-001**: 사용자는 책·제목·설명·시작일·종료일·모집 정원·카테고리·공개 여부(공개 시 비밀번호 없음, 비공개 시 비밀번호 필수)를 지정해 방을 만들 수 있다. +- **FR-002**: 생성된 방은 *모집 중* 상태로 시작하며, 만든 사용자는 *호스트*로 자동 참여하고 memberCount=1, roomPercentage=0으로 초기화된다. +- **FR-003**: 공개 방에 비밀번호 지정, 비공개 방에 비밀번호 미지정은 거부된다(둘은 정확히 짝을 이뤄야 한다). +- **FR-004**: 시작일은 오늘 *이후*, 종료일은 시작일 *이후*여야 한다. +- **FR-005**: 카테고리는 시스템이 인정하는 5종(과학·IT, 문학, 예술, 사회과학, 인문학) 중 하나여야 한다. + +#### 발견·검색 + +- **FR-006**: 사용자는 키워드(책 이름 또는 방 이름)·카테고리·정렬(마감 임박/신청 인원)·커서로 모집 중인 방을 검색할 수 있다. 공개/비공개 방 모두 결과에 포함된다. +- **FR-007**: 사용자가 *전체 검색* 모드를 지정하면 카테고리 제약 없이 모든 카테고리의 모집 중 방이 검색된다. +- **FR-008**: 사용자가 *검색 입력 확정*(`isFinalized=true`)으로 검색에 성공하면 시스템은 키워드를 사용자별 최근 검색어로 기록한다. 입력 중에는 기록하지 않는다(책 검색 PRD와 일관). +- **FR-009**: 사용자는 카테고리별로 *마감 임박*·*인기*·*최근 생성* 세 묶음의 추천 방 목록을 단일 호출로 받을 수 있다. *인기* 묶음은 *모집 중* 방을 대상으로 *현재 참여자 수(memberCount)* 내림차순으로 산정하며, 모든 사용자에게 동일한 결과로 노출된다(충원율 아닌 절대 참여자 수 기준). +- **FR-010**: 사용자는 *모집 중* 방의 상세 정보와 *진행 중/완료* 방의 상세 정보를 각각 별도의 진입점에서 조회할 수 있어야 하며, 상태에 맞는 정보 구성을 받는다. + +#### 참여·이탈 + +- **FR-011**: 사용자는 *공개* 방에 직접 참여할 수 있다. 정원 미달이고 본인이 미참여 상태일 때만 성공한다. +- **FR-012**: 사용자는 *비공개* 방의 비밀번호를 별도 호출로 검증할 수 있다. 공개 방에 대한 비밀번호 검증 요청은 거부된다. +- **FR-013**: 사용자는 *비공개* 방에 대해 비밀번호 검증을 통과한 뒤 참여를 요청할 수 있다. +- **FR-014**: 모집 중 상태가 아닌 방(진행 중·만료)에 대한 참여 또는 비밀번호 검증은 거부된다. +- **FR-015**: 정원에 도달한 방에 대한 참여는 거부된다. +- **FR-016**: 사용자는 *호스트가 아닌* 방에서 나갈 수 있다. 나간 사용자는 멤버 목록에서 제외되며 memberCount가 1 감소한다. +- **FR-017**: 호스트는 어떤 경로로도 방을 떠날 수 없다. 일반 "나가기"는 거부되고, *호스트 권한 양도*나 *방 폐쇄(삭제)* 기능도 현재 제공되지 않는다. 호스트는 방이 *만료* 상태에 도달할 때까지 호스트로 남는다. +- **FR-018**: memberCount는 1 미만으로 감소할 수 없다(호스트는 항상 존재). 어떤 동시성 시나리오에서도 활성 참여 관계 수와 일치해야 한다. + +#### 운영(호스트) + +- **FR-019**: 호스트는 자신의 *모집 중* 방에 대해 모집 마감을 요청할 수 있다. 작업 성공 시 방의 상태는 *진행 중*으로 전환되고 시작일이 마감 시점의 날짜로 갱신된다. +- **FR-020**: 호스트가 아닌 사용자의 모집 마감 요청은 거부된다. + +#### 라이프사이클 상태 + +- **FR-021**: 방은 정확히 다음 셋 중 하나의 상태를 가진다: *모집 중*, *진행 중*, *만료*. 상태 전이는 (a) 생성 시 *모집 중*, (b) 호스트의 모집 마감 시 *진행 중*, (c) 종료일 도달 시 *만료*로 이루어진다. +- **FR-022**: 사용자가 보는 방의 상태는 항상 그 시점의 실제 상태와 일치해야 한다. *진행 중* 방의 *만료* 전환은 종료일을 기준으로 시간 기반(스케줄) 트리거에 의해 자동으로 수행되며, 사용자 액션은 필요하지 않다. + +#### 내 방 관리 + +- **FR-023**: 사용자는 홈 화면에서 자신이 참여한 *활성*(모집 중·진행 중) 방의 커서 페이지 목록을 받을 수 있다. 만료된 방은 홈 목록에서 제외된다. +- **FR-024**: 사용자는 전용 목록에서 상태 필터(`playingAndRecruiting`/`recruiting`/`playing`/`expired`)를 지정해 자신의 방을 조회할 수 있다. 미지정 시 기본은 `playingAndRecruiting`이다. +- **FR-025**: 사용자는 자신이 참여한 방의 참여자 목록을 조회할 수 있다. 호스트 표시가 포함된다. + +#### 방 안 활동 컨텍스트 + +- **FR-026**: 방 참여자는 기록 작성 화면 진입 시점에 책 전체 페이지 수와 총평 작성 가능 여부를 단일 호출로 받을 수 있다. + +#### 방 게시물 좋아요 + +- **FR-027**: 사용자는 방 게시물(기록·투표)의 좋아요 상태를 토글할 수 있다(상세 동작은 RoomPost PRD(#357)와 좋아요 공통 정책 PRD를 따른다). 본 PRD는 방 식별자가 게시물에 일관되게 연결되어 있어야 함만 보장한다. + +### Key Entities + +- **Room (방)**: 같은 책을 함께 읽는 사용자 모임. 제목·설명·공개 여부(+ 해시된 비밀번호)·시작일·종료일·모집 정원·진행률·상태(모집 중/진행 중/만료)·연결된 책·카테고리를 가진다. +- **Room Participant (방 참여자)**: 한 사용자가 한 방에 속하는 연결. 역할(*호스트*/*멤버*)을 가진다. +- **Room Status (상태)**: 방의 라이프사이클. *모집 중* → *진행 중* → *만료* 순으로 전이한다. +- **Room Participant Role (참여자 역할)**: *호스트*(방 생성자, 운영 권한 보유)와 *멤버*(일반 참여자). +- **Category (카테고리)**: 시스템이 인정하는 5종. 과학·IT / 문학 / 예술 / 사회과학 / 인문학. +- **Book (책)**: 방이 연결되는 도서. 본 PRD는 *전체 페이지 수* 같은 외부 속성만 사용하며 상세는 책 도메인 PRD(#356)가 정의. + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-001**: 사용자가 방 생성 진입부터 생성 완료까지 평균 90초 이내에 마칠 수 있다(책 선택 후 기준). +- **SC-002**: 정원 초과 사건(memberCount > recruitCount)이 운영 중 0건 발생한다. +- **SC-003**: 호스트가 일반 "나가기" 경로로 방을 떠나 호스트 없는 방이 생기는 사건이 0건이다. +- **SC-004**: 사용자가 보는 방 상태(모집 중/진행 중/만료)와 실제 상태가 일치하지 않는 사건이 0건이다. +- **SC-005**: 만료된 방이 사용자 홈의 *내 참여 방* 목록에 노출되는 사건이 0건이다. +- **SC-006**: 비공개 방의 비밀번호가 잘못된 검증 요청에 의해 통과되는 사건이 0건이다. +- **SC-007**: 사용자가 추천 묶음(마감 임박/인기/최근 생성)을 카테고리별로 단일 호출로 받는 비율이 100%이다(추가 호출 없이 화면 구성 가능). + +> 본 PRD는 사용자 경험 차원의 성공 기준만 정의하며, 백엔드 응답 시간/RPS 등 기술 임계치는 헌법(constitution)의 성능 가드 원칙과 별도의 부하 시나리오에서 정의한다. + +## Assumptions + +- **이미 운영 중인 기능의 역설계**: 본 PRD는 신규 기능 정의가 아니라 기존 구현을 사용자 관점으로 정형화한 산출물이다. 요구사항은 "구현이 보장해야 한다(혹은 보장하고 있어야 한다)"의 형태로 읽힌다. +- **인증 전제**: 모든 방 시나리오는 인증된 사용자를 전제로 한다. +- **책 도메인은 외부 의존**: 책 메타데이터(제목·전체 페이지 수 등)는 책 도메인 PRD(#356)가 제공한다. +- **방 안 활동(기록·투표·오늘의 한마디)은 별도 PRD**: 본 PRD는 방의 라이프사이클·참여·운영에 집중한다. 기록·투표 흐름은 RoomPost PRD(#357), 오늘의 한마디는 별도 PRD가 다룬다. +- **좋아요·댓글의 사용자 흐름은 공통 도메인**: 방 게시물 좋아요 자체의 토글 흐름은 좋아요 공통 도메인(Post)에서 정의한다. 본 PRD는 방 식별자 연계만 보장한다. +- **최근 검색어는 별도 도메인**: 방 검색 시 *확정* 키워드의 기록 정책은 별도 검색 이력 도메인 PRD가 정의한다(책 검색과 동일). +- **알림 연동**: 본 PRD는 *본 도메인이 직접 발화하는* 알림 트리거를 보증한다. 알림 트리거 수신 이후의 저장·표시·푸시 전달 흐름은 알림 도메인 PRD(#359)를 따른다. + + **본 도메인이 직접 발화하는 트리거**: + - **"내가 호스트인 방에 새 참여자가 들어옴"**: 사용자가 방에 참여하는 데 성공한 시점에, *그 방의 호스트*에게 알림 트리거가 발화된다. 참여 취소(나가기)는 트리거를 발화하지 않는다(참여 성공 1회당 1건). + - **"내가 참여한 방의 모집이 조기 마감됨"**: 호스트가 모집을 마감(`closeRoomRecruit`)해 방의 상태가 `RECRUITING` → `IN_PROGRESS`로 전환된 시점에, 그 방의 *호스트를 제외한* 모든 참여자에게 알림 트리거가 발화된다. 자연 시작일 도래에 의한 자동 전환과 구분된다. + - **"내가 참여한 방의 활동이 시작됨"**: 방의 상태가 `IN_PROGRESS`로 진입한 시점에 그 방의 *참여자 전원*(또는 호스트를 제외한 멤버 — 운영 구현에 따름)에게 알림 트리거가 발화된다. 본 PRD는 *발화한다는 사실*과 *대상 범위가 방 참여자라는 점*만 보장한다. + + > 위 \"조기 마감\"과 \"활동 시작\"은 모집 마감 시점에서 *함께* 발화될 수 있는 별개의 트리거다. 둘의 정확한 메시지·발화 순서·중복 방지 정책은 알림 도메인 PRD(#359)와 알림 템플릿이 다룬다. +- **신고**: 방·게시물 신고 흐름은 별도 신고 도메인 PRD가 정의한다. +- **카테고리 사전**: 시스템이 인정하는 5종 카테고리는 본 PRD 작성 시점의 운영 기준이며, 향후 추가/변경은 본 PRD 개정 트리거다. +- **페이지네이션 일관성**: 방 도메인의 목록(검색·내 방·참여자)은 다른 도메인과 동일하게 커서 기반을 따른다. +- **향후 도입 예정 — 호스트 양도 및 호스트 단독 방 삭제**: 본 PRD 작성 시점에는 미제공 기능이나, 후속 단계에서 (a) 호스트가 다른 멤버에게 호스트 권한을 양도하는 기능과 (b) *방에 호스트만 남았을 때*에 한정해 호스트가 방을 폐쇄(삭제)할 수 있는 기능 도입이 예정되어 있다. 두 기능 모두 도입 시 본 PRD를 개정 트리거로 본다(FR-016, FR-017, Edge Cases 업데이트 필요).