docs(creator): 커뮤니티 잠금 기록을 갱신한다
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 문서 상태 | 공지 상세 구매 일원화·Debug build 완료·`EXT-001`/Simulator 통합 검증 대기 |
|
||||
| 문서 상태 | `P2-R3` 상태별 수동 검증 완료·`P2-R4` 코드/자동 검증 완료·더보기 상태별 수동 확인 대기 |
|
||||
| 작성일 | 2026-08-10 |
|
||||
| 최종 수정일 | 2026-08-10 |
|
||||
| 대상 제품 | SodaLive iOS 크리에이터 채널 홈·커뮤니티 |
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
## 1. Overview
|
||||
|
||||
크리에이터 채널 홈 탭의 커뮤니티 요약 아이템을 커뮤니티 탭 리스트와 동일한 `CreatorChannelCommunityListItem`으로 표시한다. 홈의 커뮤니티·공지와 커뮤니티 탭의 아이템 터치는 게시글 상세로 이동하고, 유료 게시글 구매는 상세 화면에서만 시작한다. 유료 미구매 이미지 영역은 상세 화면의 `Color.gray800`과 동일하게 통일하고, 하트·댓글을 모두 표시하지 않는 상태에서는 reaction bar의 빈 높이도 제거한다.
|
||||
크리에이터 채널 홈 탭의 커뮤니티 요약 아이템을 커뮤니티 탭 리스트와 동일한 `CreatorChannelCommunityListItem`으로 표시한다. 홈의 커뮤니티·공지와 커뮤니티 탭의 아이템 터치는 게시글 상세로 이동하고, 유료 게시글 구매는 상세 화면에서만 시작한다. 유료 미구매 이미지 영역은 상세 화면의 `Color.gray800`과 동일하게 통일하고, 하트·댓글을 모두 표시하지 않는 상태에서는 reaction bar의 빈 높이도 제거한다. 커뮤니티 상세의 유료 미구매 방문자에게는 댓글 영역을 표시하지 않고 댓글 작성·수정·삭제 동작을 차단한다.
|
||||
|
||||
## 2. Problem Statement
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
- 현재 `CreatorChannelCommunityListItem`의 유료 미구매 영역은 목록에서 구매 다이얼로그를 여는 별도 `Button`이므로, 전체 아이템을 상세 진입으로 통일하려는 요구와 충돌한다.
|
||||
- 홈·커뮤니티 리스트와 상세는 하트·댓글을 모두 숨기는 유료 잠금 상태에서도 높이 24pt의 빈 reaction bar를 삽입해 불필요한 세로 공간이 남는다.
|
||||
- 홈 공지 카드는 상세·댓글·구매 callback이 기본 no-op이고, 유료 잠금 영역에 목록 구매용 `Button`이 남아 있어 상세 전용 구매 원칙에서 제외되어 있다.
|
||||
- 커뮤니티 상세는 `isCommentAvailable`만으로 댓글 목록과 입력창을 표시해, 유료 미구매 잠금 상태에서도 댓글을 조회하고 작성·수정·삭제할 수 있다.
|
||||
|
||||
## 3. Goals
|
||||
|
||||
@@ -36,6 +37,7 @@
|
||||
- 홈 공지 게시글도 본체·댓글·유료 잠금 영역을 상세 진입으로 통일한다.
|
||||
- 유료 미구매 이미지 영역을 `Color.gray800`으로 통일한다.
|
||||
- 하트·댓글을 모두 표시하지 않는 상태에서는 reaction bar가 레이아웃 공간을 차지하지 않게 한다.
|
||||
- 유료 미구매 방문자는 커뮤니티 상세에서 댓글 영역을 보거나 댓글 작성·수정·삭제 동작을 실행할 수 없게 한다.
|
||||
|
||||
### 3.2 UX 목표
|
||||
|
||||
@@ -43,6 +45,7 @@
|
||||
- 아이템 본문·이미지·유료 잠금 영역을 터치하면 예상 가능하게 상세 화면으로 이동해야 한다.
|
||||
- 더보기·좋아요 같은 독립 제어의 터치는 아이템 상세 터치와 중복 실행되지 않아야 한다.
|
||||
- 표시할 reaction이 없으면 콘텐츠 아래에 빈 여백이 남지 않아야 한다.
|
||||
- 유료 게시글 구매 전에는 상세의 댓글 구분선·목록·입력창이 보이지 않고, 구매 후에는 `isCommentAvailable` 정책에 따라 다시 표시되어야 한다.
|
||||
|
||||
## 4. Non-Goals
|
||||
|
||||
@@ -50,19 +53,23 @@
|
||||
- 썸네일형 커뮤니티 아이템의 레이아웃은 변경하지 않는다.
|
||||
- `CommunityPostCard`의 다른 사용처인 메인 홈 팔로잉·추천 섹션은 변경하지 않는다.
|
||||
- 신규 endpoint, 앱 dependency, 범위 밖 커뮤니티 리팩터링은 추가하지 않는다.
|
||||
- 상세 API 응답의 댓글 payload 계약과 별도 답글 상세 화면은 변경하지 않는다.
|
||||
|
||||
## 5. Target Users and Permissions
|
||||
|
||||
- 타인 채널 방문자: 더보기에서 신고 동작을 사용할 수 있다.
|
||||
- 본인 채널의 크리에이터: 기존 커뮤니티 게시글 관리 권한 범위 안에서 더보기 동작을 사용한다.
|
||||
- 유료 미구매 방문자: 목록에서 구매하지 않고 상세 화면에서 구매를 시작한다.
|
||||
- 유료 미구매 방문자: 구매 전에는 상세의 reaction·댓글 영역을 볼 수 없고 댓글 mutation을 실행할 수 없다.
|
||||
|
||||
## 6. 핵심 사용자 흐름
|
||||
|
||||
1. 사용자가 크리에이터 채널 홈의 커뮤니티 요약 또는 공지 아이템을 확인한다.
|
||||
2. 아이템 본체를 터치하면 해당 `postId`의 커뮤니티 게시글 상세로 이동한다.
|
||||
3. 유료 미구매 게시글이면 상세 화면에서 기존 구매 다이얼로그와 API를 사용한다.
|
||||
4. 사용자가 우측 상단 더보기를 터치하면 확정된 권한 규칙에 맞는 메뉴가 표시된다.
|
||||
4. 구매 전에는 상세의 reaction·댓글 영역을 표시하지 않고 댓글 작성·수정·삭제 진입을 차단한다.
|
||||
5. 구매 성공 후 상세를 다시 조회하고 `isCommentAvailable`에 따라 댓글 영역을 표시한다.
|
||||
6. 사용자가 우측 상단 더보기를 터치하면 확정된 권한 규칙에 맞는 메뉴가 표시된다.
|
||||
|
||||
## 7. 기능 요구사항
|
||||
|
||||
@@ -78,6 +85,8 @@
|
||||
| `CCCI-008` | 외부 의존 | 백엔드 홈 API의 `communities`·`notices` 아이템에 `isCommentAvailable: Bool`·`isPinned: Bool`을 커뮤니티 탭과 동일하게 제공한다. | iOS가 두 필드를 임의 기본값 없이 디코딩해 고정·댓글 상태를 표시한다. | `P1-T1`, `EXT-001` |
|
||||
| `CCCI-009` | 확정 | 홈·커뮤니티 리스트와 커뮤니티 상세에서 하트·댓글을 모두 표시하지 않을 때 reaction bar를 삽입하지 않는다. | 유료 미구매 잠금 상태에서 하트·댓글과 고정 24pt 높이가 모두 사라지고, 하나라도 표시되면 기존 reaction bar가 유지된다. | `P2-R1` |
|
||||
| `CCCI-010` | 확정 | 홈 공지 게시글도 목록 구매를 제거하고 본체·댓글·유료 잠금 영역을 상세 이동으로 처리한다. | 공지 카드에서 구매 다이얼로그가 직접 열리지 않고 세 영역 모두 선택한 `postId`의 상세로 이동한다. | `P2-R2` |
|
||||
| `CCCI-011` | 확정 | 커뮤니티 상세의 유료 미구매 잠금 상태에서는 댓글 영역을 표시하지 않고 댓글 작성·수정·삭제 동작을 차단한다. | 댓글 구분선·제목·목록·하단 입력창이 View 계층에 없고 ViewModel의 댓글 mutation 진입점이 API 요청을 실행하지 않는다. 구매 후에는 `isCommentAvailable`에 따라 기존 댓글 UI와 동작이 복원된다. | `P2-R3` |
|
||||
| `CCCI-012` | 확정 | 홈·커뮤니티 탭 리스트 아이템의 더보기는 유료 미구매 타인 게시글에서 숨기고, 무료·구매 완료 게시글과 작성자 본인 게시글에서 표시한다. | `CreatorChannelCommunityListItem`의 더보기가 유료 미구매 타인에게 없고 나머지 상태에는 표시된다. | `P2-R4` |
|
||||
|
||||
## 8. UI/UX Expectations
|
||||
|
||||
@@ -87,6 +96,8 @@
|
||||
- 리스트형 유료 미구매 영역은 236pt 높이를, 썸네일형은 기존 정사각형 비율을 유지한다. 두 형태 모두 잠금 icon·가격 capsule을 유지하고 배경만 `Color.gray800`으로 통일한다.
|
||||
- 더보기와 좋아요 제어는 자신의 action만 실행하고 상세 이동을 중복 실행하지 않는다.
|
||||
- 유료 미구매 잠금 상태처럼 하트·댓글을 모두 숨기는 경우 reaction bar 전체를 조건부로 제외해 고정 높이와 `VStack` 간격을 남기지 않는다.
|
||||
- 커뮤니티 상세의 유료 미구매 잠금 상태에서는 본문 아래 댓글 구분선·댓글 제목과 목록·하단 입력창을 모두 삽입하지 않는다.
|
||||
- 홈·커뮤니티 탭 리스트 아이템의 우측 상단 더보기는 유료 미구매 타인 게시글에서 레이아웃과 hit-testing 모두 제거하고, 작성자에게는 구매 여부와 무관하게 항상 표시한다.
|
||||
|
||||
## 9. 기술·운영 제약
|
||||
|
||||
@@ -96,6 +107,8 @@
|
||||
- 백엔드는 홈 API 응답에 커뮤니티 탭과 동일한 `isCommentAvailable: Bool`·`isPinned: Bool`을 제공한다. iOS는 두 필드가 없는 응답을 추정 기본값으로 보정하지 않는다.
|
||||
- 홈 응답 모델과 커뮤니티 탭 응답 모델의 차이를 제거하기 위한 투기적 protocol·factory·신규 dependency를 추가하지 않는다.
|
||||
- 본인 게시글 판정은 기존처럼 `creatorId == UserDefaults.int(forKey: .userId)`를 사용한다.
|
||||
- 유료 미구매 잠금 판정은 기존처럼 `price > 0 && existOrdered == false && isOwnPost == false`를 사용하며, 본인 게시글에는 댓글 차단을 적용하지 않는다.
|
||||
- 더보기 노출도 같은 유료 미구매 잠금 판정을 재사용하고 별도 권한 상태를 추가하지 않는다.
|
||||
|
||||
## 10. 성공 기준
|
||||
|
||||
@@ -108,6 +121,9 @@
|
||||
- [ ] 더보기·좋아요 터치가 상세 이동을 중복 발생시키지 않는다.
|
||||
- [x] 홈·커뮤니티 리스트와 상세의 유료 미구매 상태에서 하트·댓글용 빈 공간이 남지 않는다.
|
||||
- [ ] 홈 공지의 본체·댓글·유료 잠금 영역이 상세로 이동하고 공지 카드에서 구매 다이얼로그가 직접 열리지 않는다.
|
||||
- [x] 커뮤니티 상세의 유료 미구매 상태에서 댓글 구분선·목록·입력창이 보이지 않고 작성·수정·삭제 API 요청이 실행되지 않는다.
|
||||
- [x] 무료·구매 완료·본인 게시글은 기존 `isCommentAvailable` 값에 따라 댓글 영역과 동작을 유지한다.
|
||||
- [ ] 홈·커뮤니티 탭 리스트 아이템의 더보기는 유료 미구매 타인 게시글에서 숨고, 무료·구매 완료·작성자 게시글에서 표시된다.
|
||||
- [x] `SodaLive-dev` 스킴 Debug 빌드가 성공한다.
|
||||
- [ ] 시뮬레이터에서 홈·커뮤니티 탭·상세의 아이템 터치, 더보기, 유료 잠금 색상을 확인한다.
|
||||
|
||||
@@ -119,6 +135,7 @@
|
||||
| `OQ-002` | 확정 | 목록 구매 제거를 홈과 커뮤니티 탭 모두에 적용한다. | A안 확정 | 사용자 | 2026-08-10 | `CCCI-004`, `CCCI-007` |
|
||||
| `OQ-003` | 확정 | 백엔드 홈 API 응답에 `isCommentAvailable`·`isPinned`을 커뮤니티 탭과 동일하게 추가한다. | A안 확정 | 사용자·API 담당 | iOS 통합 검증 전 | `CCCI-001`, `CCCI-008`, `EXT-001` |
|
||||
| `OQ-004` | 확정 | 홈 API의 날짜 키 `dateUtc`를 유지하고 iOS 로컬 변환에서 같은 UTC 문자열을 커뮤니티 탭 모델의 `createdAtUtc`로 연결한다. | A안 확정·날짜 값 파싱·시간대·포맷 변환 없음 | 사용자 | 2026-08-10 | `DEC-008` |
|
||||
| `OQ-005` | 확정 | 유료 미구매 상태의 댓글 차단을 UI에만 적용할지 ViewModel mutation까지 방어할지 결정한다. | A안 확정·댓글 UI와 작성·수정·삭제 동작 모두 차단 | 사용자 | 2026-08-10 | `CCCI-011`, `DEC-015` |
|
||||
|
||||
## 12. 외부 제공 대기 계약
|
||||
|
||||
@@ -135,6 +152,7 @@
|
||||
| `CCCI-008` | 홈 API 필드 확장 | `P1-T1`, `P3-GATE`, `EXT-001` | decoding contract·Debug build | 백엔드 반영 후 홈 응답 확인 |
|
||||
| `CCCI-009` | 기존 목록·상세 모델 재사용 | `P2-R1` | reaction bar 조건부 삽입 contract check·Debug build | 홈·커뮤니티 리스트·상세 유료 잠금 여백 확인 |
|
||||
| `CCCI-010` | 기존 홈 상세 route·상세 구매 API 재사용 | `P2-R2` | 공지 구매 callback 제거·상세 callback 연결 contract check·Debug build | 공지 본체·댓글·유료 잠금 상세 진입 확인 |
|
||||
| `CCCI-011` | 기존 상세 응답·댓글 mutation API 재사용 | `P2-R3` | 댓글 표시 조건·mutation guard contract check·Debug build | 구매 전 댓글 UI 미노출·구매 후 복원 확인 |
|
||||
|
||||
## 14. Decision Log
|
||||
|
||||
@@ -154,6 +172,8 @@
|
||||
| 2026-08-10 | `DEC-012` | 확정 | 목록에 있던 기존 구매 다이얼로그·API 호출 소유권을 상세 화면으로 옮긴다. | 구현 전 기준선에서 상세 잠금 버튼이 구매가 아닌 `showPurchaseError()`만 호출함을 확인·사용자 직접 요구 충족 | `CCCI-004`, `CCCI-007` |
|
||||
| 2026-08-10 | `DEC-013` | 확정 | 하트·댓글이 모두 숨겨지는 유료 잠금 상태에서는 고정 높이를 보정하지 않고 reaction bar 전체를 조건부로 삽입하지 않는다. | 목록·상세의 공통 원인은 빈 `HStack`의 `.frame(height: 24)` 유지이며, 상위 조건부 삽입이 가장 작은 근본 수정이다. | `CCCI-009`, `P2-R1` |
|
||||
| 2026-08-10 | `DEC-014` | 확정 | 홈 공지도 별도 구매 callback을 제거하고 기존 `onTapCommunityDetail`·`showCommunityPostDetail(_:)` 흐름을 재사용한다. | 공지도 커뮤니티 게시글이며 구매는 상세에서만 가능해야 한다는 사용자 요청 | `CCCI-010`, `P2-R2` |
|
||||
| 2026-08-10 | `DEC-015` | 확정 | 상세의 유료 미구매 잠금 상태에서는 기존 reaction bar 차단을 유지하고, 댓글 구분선·목록·입력창과 ViewModel의 작성·수정·삭제 mutation 진입을 모두 차단한다. | 사용자 후속 요청과 인터뷰 A안 선택 | `CCCI-009`, `CCCI-011`, `P2-R1`, `P2-R3` |
|
||||
| 2026-08-10 | `DEC-016` | 확정 | 홈·커뮤니티 탭의 공용 리스트 아이템은 기존 `isPaidLocked`를 재사용해 더보기를 조건부 삽입한다. 작성자는 구매 여부와 무관하게 표시한다. | 사용자 상태별 수동 검증 후속 요청·공용 컴포넌트와 기존 잠금 판정 재사용 | `CCCI-012`, `P2-R4` |
|
||||
|
||||
## 15. 구현 설계
|
||||
|
||||
@@ -173,7 +193,10 @@
|
||||
- 상세 진입 helper는 표시 모델 전체 대신 `postId` 만 받아 홈·리스트형·썸네일형이 같은 route를 사용한다.
|
||||
- 더보기·좋아요 `Button`은 각자의 action만 실행하고 부모 아이템의 상세 진입을 중복 실행하지 않아야 한다.
|
||||
- `CreatorChannelCommunityListItem`과 `CreatorChannelCommunityPostDetailContentView`는 `isPaidLocked`가 `true`이면 `reactionBar` 자체를 View 계층에 삽입하지 않는다. 댓글만 비활성이고 좋아요가 표시되는 상태는 기존 reaction bar를 유지한다.
|
||||
- `CreatorChannelCommunityPostDetailViewModel`은 상세의 `isCommentAvailable`과 기존 유료 잠금 조건을 함께 판정하는 단일 댓글 상호작용 상태를 제공한다. 상세 View는 이 상태가 `true`일 때만 댓글 구분선·목록·하단 입력창을 삽입한다.
|
||||
- 같은 상태를 댓글 전송 가능 여부와 작성·수정·삭제 mutation 진입 guard에 재사용한다. 잠금 상태에서는 댓글 API 요청을 시작하지 않는다.
|
||||
- `CreatorChannelNoticeSection`은 본체·댓글·유료 잠금 영역에 하나의 상세 callback을 사용하고, `CreatorChannelHomeView`의 기존 `onTapCommunityDetail`을 전달한다. 공지 카드의 외형은 변경하지 않는다.
|
||||
- `CreatorChannelCommunityListItem.header`의 방문자 더보기 분기를 `!isPaidLocked`로 제한한다. 기존 작성자 제어 분기·가격 표시·callback은 유지한다.
|
||||
|
||||
### 15.3 더보기와 변경 후 갱신
|
||||
|
||||
@@ -197,6 +220,8 @@
|
||||
- 정적 검증은 `rg`로 목록 `onTapPurchase`·목록 구매 dialog 호출처 제거, 상세 구매 dialog·API 연결, `Color.gray800`, 홈·목록·썸네일 상세 route 연결을 확인하고 `git diff --check`를 실행한다.
|
||||
- reaction bar 회귀 검증은 두 대상 View의 body에 무조건 삽입된 `reactionBar`가 없고 `if !isPaidLocked` 내부에만 삽입되는지 focused `rg`로 확인한다.
|
||||
- 공지 회귀 검증은 `CreatorChannelNoticeSection`의 `onTapPurchase`·`onTapNotice`·`onTapComment`가 제거되고 단일 상세 callback이 `CreatorChannelHomeView.onTapCommunityDetail`에 연결되는지 focused `rg`로 확인한다.
|
||||
- 상세 댓글 차단 검증은 `CreatorChannelCommunityPostDetailView`가 ViewModel의 단일 댓글 상호작용 상태로 댓글 구분선·목록·입력창을 조건부 삽입하고, `CreatorChannelCommunityPostDetailViewModel`의 전송·삭제 mutation 진입점이 같은 상태를 guard하는지 focused `rg`로 확인한다.
|
||||
- 더보기 노출 검증은 공용 리스트 아이템의 기존 `else if !isOwnPost`를 RED로 확인하고 `else if !isPaidLocked`로 변경된 것을 GREEN으로 확인한다.
|
||||
- 자동 검증은 `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`로 컴파일·링크 성공을 확인한다.
|
||||
- `EXT-001` 반영 후 시뮬레이터에서 홈·리스트형·썸네일형의 상세 진입, 더보기 권한 메뉴, 목록 구매 dialog 미노출, 상세 구매, `Color.gray800`을 수동 확인한다.
|
||||
|
||||
@@ -216,3 +241,10 @@
|
||||
- 2026-08-10: 두 body의 무조건 `reactionBar` 삽입을 `if !isPaidLocked` 조건부 삽입으로 변경했다. focused `rg`에서 무조건 삽입 0건·조건부 삽입 2건, `git diff --check` 오류 0건, 동일 iPhone 17 Pro 대상 `SodaLive-dev` Debug build exit code 0을 확인했다.
|
||||
- 2026-08-10: `CreatorChannelNoticeSection`에 기본 no-op인 상세·댓글 callback과 별도 구매 callback·잠금 `Button`이 남고 `CreatorChannelHomeView`가 상세 callback을 전달하지 않는 `REV-P3-004`를 확인해 `CCCI-010`, `DEC-014`, `P2-R2`로 수정 범위를 확정했다.
|
||||
- 2026-08-10: 공지 callback을 단일 `onTapDetail`로 통일하고 잠금 영역의 직접 구매 `Button`을 제거한 뒤 `CreatorChannelHomeView.onTapCommunityDetail`에 연결했다. RED 계약 검사는 직접 구매 callback과 미연결 생성부를 각각 검출했고, GREEN은 기존 callback 0건·상세 연결 match, `git diff --check` 오류 0건, `SodaLive-dev` Debug build exit code 0을 확인했다.
|
||||
- 2026-08-10: 사용자 후속 요청과 인터뷰 A안 선택으로 커뮤니티 상세의 유료 미구매 상태에서 reaction bar 차단을 유지하고 댓글 UI·작성·수정·삭제 mutation을 모두 차단하기로 확정했다. 현재 상세 View와 ViewModel이 `isCommentAvailable`만으로 댓글 표시·전송을 허용하는 기준선을 확인했으며 구현·커밋은 진행하지 않았다.
|
||||
- 2026-08-10: `P2-R3` RED에서 상세 View의 `isCommentAvailable` 직접 조건 2건과 ViewModel의 paid-lock 미반영 상태를 확인했다. `isCommentInteractionAvailable` 하나로 댓글 구분선·목록·입력창과 페이지네이션·전송·편집·삭제·상태 초기화를 통일했다.
|
||||
- 2026-08-10: GREEN focused contract check에서 상세 View 직접 조건 0건, shared 상태 참조 10건, mutation guard match를 확인했고 `git diff --check`가 통과했다. device와 iPhone 17 Pro iOS 26.0 Simulator 대상 `SodaLive-dev` Debug build가 각각 exit code 0으로 성공했다.
|
||||
- 2026-08-10: iPhone 17 Pro Simulator에 앱을 설치·실행해 홈 진입을 확인하고 `/tmp/p2-r3-launch.png`를 캡처했다. 익명 세션에 유효한 유료 커뮤니티 상세 데이터가 없어 `CCCI-011` 상태별 화면·요청 검증은 완료하지 못했으며 Simulator는 종료했다.
|
||||
- 2026-08-10: 사용자가 유료 미구매·무료·구매 완료·본인 게시글의 상세 댓글 상태를 수동 확인해 `CCCI-011`의 두 성공 기준을 완료 처리했다.
|
||||
- 2026-08-10: 후속 요청으로 홈·커뮤니티 탭 리스트 아이템의 더보기를 유료 미구매 타인에게 숨기고 무료·구매 완료·작성자에게 표시하는 `CCCI-012`, `DEC-016`, `P2-R4`를 확정했다.
|
||||
- 2026-08-10: `P2-R4` RED에서 방문자 분기의 `else if !isOwnPost` 1건을 확인하고 `else if !isPaidLocked`로 교체했다. GREEN에서 기존 조건 0건·신규 조건 1건, production diff 한 줄, `git diff --check` 성공과 `SodaLive-dev` Debug build exit code 0을 확인했다.
|
||||
|
||||
Reference in New Issue
Block a user