Files
sodalive-ios/docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md

641 lines
60 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 크리에이터 채널 팬Talk 탭 구현 계획
## 기준 문서
- PRD: `docs/20260704_크리에이터_채널_팬Talk_탭/prd.md`
- 기존 크리에이터 채널 홈 PRD: `docs/20260701_크리에이터_채널_홈/prd.md`
- 기존 크리에이터 채널 후원 탭 구현 계획: `docs/20260704_크리에이터_채널_후원_탭/plan-task.md`
- 코드 스타일: `docs/agent-guides/code-style.md`
- 빌드/검증: `docs/agent-guides/build-test-verification.md`
- Figma 전체 화면: `290:9139`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9139&m=dev`
- Figma 아이템 뷰(답글 없음): `290:9145`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9145&m=dev`
- Figma 아이템 뷰(답글 있음): `290:9147`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9147&m=dev`
- Figma 더보기 anchored context popup: `666:36114`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=666-36114&m=dev`
- Figma Empty: `290:9001`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9001&m=dev`
- Figma 글 입력(입력 값 없음, 키보드 있음): `290:9235`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9235&m=dev`
- Figma 글 입력(입력 값 있음, 키보드 있음): `290:9282`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9282&m=dev`
- Figma 글 입력(입력 값 있음, 키보드 없음): `290:9290`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=290-9290&m=dev`
- Figma 입력 종료 모달: `308:19046`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=308-19046&m=dev`
- Figma 공용 모달(버튼 1개): `567:17662`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=567-17662&m=dev`
- Figma 공용 모달(버튼 2개): `567:17666`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=567-17666&m=dev`
- Figma 공용 모달 dimmed 포함: `567:17712`, `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=567-17712&m=dev`
## 구현 원칙
- 기존 `CreatorChannelView`의 공통 shell, header, sticky tab-bar, title bar 동작은 유지한다.
- `CreatorChannelTab.fanTalk` 콘텐츠만 신규 `CreatorChannelFanTalkTabView`로 교체한다.
- 팬Talk API 모델은 홈 탭 모델과 타입명 충돌이 나지 않도록 분리한다.
- `fanTalkCount > 0`일 때만 sort-bar에 정렬 없이 `전체 {fanTalkCount}`를 표시하고 empty 상태에서는 count bar를 숨긴다.
- 신고는 기존 `CheersReportDialogView``ReportRepository.report``ReportType.CHEERS` 흐름을 재사용한다.
- item의 `신고` 문구는 신규 I18n을 만들지 않고 기존 `I18n.Explorer.report`를 재사용한다.
- 삭제는 기존 `ExplorerRepository.modifyCheers(cheersId:content:isActive:)``isActive=false`를 전달하는 흐름을 재사용한다.
- 더보기 버튼은 `ic_new_more`를 사용한다.
- 내가 쓴 글이면 `수정하기`, `삭제하기`를 표시하고, 내가 쓴 글이 아니지만 내 채널이면 `삭제하기`만 표시한다.
- 글쓰기와 수정하기 action은 이번에 작성하는 팬Talk 쓰기 페이지로 연결한다.
- 작성 API는 기존 팬Talk API인 `ExplorerRepository.writeCheers(parentCheersId:creatorId:content:)`를 사용한다.
- 수정 API는 기존 팬Talk API인 `ExplorerRepository.modifyCheers(cheersId:content:isActive:)`를 사용하고 `isActive``nil`로 전달한다.
- 내 채널인 경우 우측하단 글쓰기 floating button과 empty view 내부 `응원남기기` 버튼을 모두 표시하지 않는다.
- 우측하단 plus button은 `CreatorChannelFloatingActionMenu`의 private `floatingButton`과 동일한 UI이므로, 팬Talk 전용으로 별도 구현하지 않고 `CreatorChannel/Components` 하위 공용 icon floating button으로 추출해 재사용한다.
- 팬Talk 쓰기 화면은 크리에이터 채널 팬Talk 탭의 우측하단 plus button, empty state의 `응원남기기` 버튼, 내가 쓴 글의 `수정하기`에서 진입한다.
- 팬Talk 쓰기 화면 진입 시 키보드가 올라온 상태를 기본으로 하며, 입력 값 유무에 따라 보내기 버튼 disabled/enabled 상태를 분리한다.
- 입력 값이 있는 상태에서 취소 버튼을 터치하면 입력 종료 안내 모달을 표시한다. 입력 값이 없으면 별도 모달 없이 이전 화면으로 닫는다.
- 취소 문구의 고정 `width: 64`, `height: 56`만 제거하고 내비게이션 높이 `56`과 보내기 capsule 높이 `44`는 유지한다.
- 입력 영역은 `I18n.CreatorChannelFanTalk`의 ko/en/ja placeholder를 사용한다.
- 입력은 Swift `String.count` 기준 500자까지 허용하고, 501번째 이후 사용자 입력은 `prefix(500)`으로 반영하지 않는다.
- 예외적으로 content가 500자를 초과하면 메시지 본문과 현재 글자 수를 `Color.red400`으로 표시하고 보내기 버튼을 비활성화한다.
- 공용 모달은 요청 명칭상 `V2/Components` 대상이지만, 현재 저장소 경로 관례에 맞춰 `SodaLive/Sources/V2/Component/Modal/**` 아래에 작성한다.
## 주요 대상 파일
### 생성
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Models/CreatorChannelFanTalkTabResponse.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Repository/CreatorChannelFanTalkApi.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Repository/CreatorChannelFanTalkRepository.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkViewModel.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkCountBar.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkActionPopup.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift`
- `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelFloatingIconButton.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift`
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteViewModel.swift`
- `SodaLive/Sources/V2/Component/Modal/SodaV2ActionModal.swift`
### 수정
- `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelFloatingActionMenu.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift`
- `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelFanTalkSection.swift`
- `SodaLive/Sources/App/AppStep.swift`
- `SodaLive/Sources/ContentView.swift`
- `SodaLive/Sources/Explorer/ExplorerRepository.swift`
- `SodaLive/Sources/I18n/I18n.swift`
- 필요 시 `SodaLive.xcodeproj/project.pbxproj`
## TASK 체크리스트
### Phase 1: 문서와 API 모델
- [x] **Task 1.1: PRD와 계획 문서 생성**
- 대상 파일:
- 생성: `docs/20260704_크리에이터_채널_팬Talk_탭/prd.md`
- 생성: `docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md`
- 검증 기준:
- 실행 명령: `rg "290:9139|290:9145|290:9147|666:36114|290:9001|CreatorChannelFanTalkTabResponse|/api/v2/creator-channels/.*/fan-talks|ic_new_more" docs/20260704_크리에이터_채널_팬Talk_탭`
- 기대 결과: Figma URL, 응답 모델, API path, 액션 요구사항이 문서에 기록된다.
- [x] **Task 1.2: 팬Talk 탭 Response model 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Models/CreatorChannelFanTalkTabResponse.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/Models/CreatorChannelHomeResponse.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Home/Components/CreatorChannelFanTalkSection.swift`
- 작업 내용:
- `CreatorChannelFanTalkTabResponse`, `CreatorChannelFanTalkItemResponse`, `CreatorChannelFanTalkReplyResponse``Decodable`로 생성한다.
- 홈 탭 기존 `CreatorChannelFanTalkResponse`와 충돌하지 않도록 홈 탭 타입명을 변경하거나 신규 타입명을 다르게 둔다.
- `Identifiable``fanTalkId``id`로 사용한다.
- 검증 기준:
- 실행 명령: `rg "struct CreatorChannelFanTalkTabResponse|fanTalkCount|fanTalks|hasNext|struct CreatorChannelFanTalkItemResponse|writerId|creatorReplies|struct CreatorChannelFanTalkReplyResponse" SodaLive/Sources/V2/CreatorChannel/FanTalk/Models/CreatorChannelFanTalkTabResponse.swift`
### Phase 2: API, Repository, ViewModel
- [x] **Task 2.1: 팬Talk 탭 API endpoint 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Repository/CreatorChannelFanTalkApi.swift`
- 작업 내용:
- `CreatorChannelFanTalkApi.getFanTalks(creatorId:page:size:)`를 만든다.
- path는 `/api/v2/creator-channels/{creatorId}/fan-talks`로 구성한다.
- query parameter는 `page`, `size`만 보낸다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkApi|getFanTalks|/api/v2/creator-channels/|/fan-talks|page|size|URLEncoding.queryString" SodaLive/Sources/V2/CreatorChannel/FanTalk/Repository/CreatorChannelFanTalkApi.swift`
- [x] **Task 2.2: 팬Talk 탭 Repository 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Repository/CreatorChannelFanTalkRepository.swift`
- 작업 내용:
- `MoyaProvider<CreatorChannelFanTalkApi>`를 사용하는 `getFanTalks(...)` publisher를 제공한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkRepository|MoyaProvider<CreatorChannelFanTalkApi>|AnyPublisher<Response, MoyaError>|getFanTalks" SodaLive/Sources/V2/CreatorChannel/FanTalk/Repository/CreatorChannelFanTalkRepository.swift`
- [x] **Task 2.3: 팬Talk 탭 ViewModel 생성**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkViewModel.swift`
- 작업 내용:
- 상태는 loading, loadingNextPage, response, fanTalks, page, size, hasNext, hasLoaded, error toast, 신고/삭제 dialog 상태를 둔다.
- `fetchFirstPage(creatorId:)``page=0`, `size=20`으로 조회한다.
- `fetchNextPageIfNeeded(creatorId:currentItem:)`는 마지막 item 기준으로 다음 페이지를 조회한다.
- 첫 페이지는 목록 교체, 다음 페이지는 append한다.
- `report(reason:)``ReportRequest(type: .CHEERS, cheersId: selectedFanTalkId, reason: reason)`를 호출한다.
- `deleteSelectedFanTalk(creatorId:)``ExplorerRepository.modifyCheers(..., isActive: false)`를 호출하고 성공 후 첫 페이지를 다시 조회한다.
- 최신 요청만 반영하도록 `latestRequestId` 패턴을 사용한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkViewModel|fetchFirstPage|fetchNextPageIfNeeded|isLoadingNextPage|latestRequestId|report\\(|deleteSelectedFanTalk|ReportType\\.CHEERS|modifyCheers|hasNext" SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkViewModel.swift`
### Phase 3: Figma 섹션별 UI
- [x] **Task 3.1: 팬Talk count bar 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkCountBar.swift`
- 작업 내용:
- Figma sort-bar 기준으로 높이 52pt, 좌측 `전체 {fanTalkCount}`만 표시한다.
- 정렬 버튼/아이콘/action은 포함하지 않는다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkCountBar|fanTalkCount|전체|frame\\(height: 52\\)|ic_new_sort|selectedSort" SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkCountBar.swift`
- 기대 결과: count bar 구현이 확인되고 정렬 관련 문자열은 없어야 한다.
- [x] **Task 3.2: 팬Talk item 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift`
- 작업 내용:
- 작성자 프로필 42pt, 닉네임/상대 시간, 본문 50pt inset, 하단 separator를 구현한다.
- 답글이 있으면 좌측 라인과 답글 카드를 표시한다.
- `isMine || isOwnCreatorChannel`이면 `ic_new_more` 버튼을 표시한다.
- 그 외에는 `신고` 텍스트 버튼을 표시한다.
- 프로필 이미지와 답글 프로필 이미지는 Kingfisher 기반 기존 컴포넌트를 사용한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkListItem|writerProfileImageUrl|writerNickname|createdAtUtc|creatorReplies|ic_new_more|신고|Color\\.gray800|Color\\.gray900|42|20" SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift`
- [x] **Task 3.3: 더보기 anchored context popup 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkActionPopup.swift`
- 작업 내용:
- Figma `666:36114` 기준으로 border `Color.gray700`, background `Color.gray900`, radius 14pt를 사용한다.
- 내가 쓴 글이면 `수정하기`, `삭제하기`를 표시한다.
- 내가 쓴 글이 아니지만 내 채널이면 `삭제하기`만 표시한다.
- tap action은 부모에서 주입한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkActionPopup|수정하기|삭제하기|Color\\.gray700|Color\\.gray900|cornerRadius\\(14\\)|onTapEdit|onTapDelete" SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkActionPopup.swift`
- [x] **Task 3.4: Empty view 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift`
- 작업 내용:
- Figma `290:9001` 기준으로 `아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요!` 문구를 표시한다.
- plus 아이콘과 `응원 남기기` 텍스트가 있는 capsule button을 표시한다.
- 내 채널이면 button을 숨긴다.
- tap action은 부모에서 주입한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkEmptyView|아직 응원이 없습니다|처음으로 크리에이터를 응원해 보세요|응원 남기기|showsWriteButton|onTapWrite" SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift`
- [x] **Task 3.5: 공용 icon floating button 추출**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelFloatingIconButton.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelFloatingActionMenu.swift`
- 작업 내용:
- `CreatorChannelFloatingActionMenu` 내부 private `floatingButton`과 같은 66pt 원형 버튼, 38pt 아이콘, background color, plain button style을 공용 컴포넌트로 분리한다.
- 공용 컴포넌트는 `imageName`, `backgroundColor`, `accessibilityLabel`, `action`을 받는다.
- 기존 `CreatorChannelFloatingActionMenu`는 private `floatingButton` 구현 대신 공용 컴포넌트를 사용하도록 변경한다.
- 팬Talk 우측하단 글쓰기 plus button은 이 공용 컴포넌트에 `imageName: "ic_plus_no_bg"`, `backgroundColor: Color.soda400`를 전달해 사용한다.
- 글쓰기 plus button은 텍스트를 표시하지 않는다.
- 검증 기준:
- 실행 명령: `rg "struct CreatorChannelFloatingIconButton|imageName|backgroundColor|accessibilityLabel|frame\\(width: 38, height: 38\\)|frame\\(width: 66, height: 66\\)|clipShape\\(Circle\\(\\)\\)" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelFloatingIconButton.swift`
- 실행 명령: `rg "CreatorChannelFloatingIconButton|ic_plus_no_bg|ic_new_upload_community_post|ic_new_upload_audio|ic_new_create_live|ic_new_x_black" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelFloatingActionMenu.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 실행 명령: `rg "Text\\(" SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelFloatingIconButton.swift`
- 기대 결과: 공용 icon-only floating button과 기존 floating action menu 재사용이 확인되고, 공용 컴포넌트에는 텍스트가 없어야 한다.
- [x] **Task 3.6: 팬Talk 탭 본문 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
- 작업 내용:
- count bar, empty view, fanTalk list를 배치한다.
- 첫 진입 시 `fetchFirstPage(creatorId:)`를 호출한다.
- 마지막 fanTalk item 노출 시 `fetchNextPageIfNeeded`를 호출한다.
- `fanTalkCount == 0`이면 `CreatorChannelFanTalkEmptyView`를 표시한다.
- 내 채널인 경우 empty view 내부 글쓰기 버튼을 숨긴다.
- `CheersReportDialogView`와 삭제 confirm dialog를 표시한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkTabView|CreatorChannelFanTalkCountBar|CreatorChannelFanTalkEmptyView|CreatorChannelFanTalkListItem|fetchFirstPage|fetchNextPageIfNeeded|CheersReportDialogView|SodaDialog|onTapWrite" SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
### Phase 4: CreatorChannelView 연결
- [x] **Task 4.1: 팬Talk 탭 콘텐츠 연결**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 작업 내용:
- `@StateObject`로 팬Talk 탭 ViewModel을 보유한다.
- `selectedTabContent`에서 `.fanTalk`일 때 `CreatorChannelFanTalkTabView`를 표시한다.
- 우측하단 글쓰기 floating button은 `selectedTab == .fanTalk`, 내 채널이 아니고 `fanTalkCount > 0`일 때만 `CreatorChannelFloatingIconButton`으로 표시한다.
- 글쓰기 action은 Phase 8에서 팬Talk 쓰기 페이지 작성 모드로 연결한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkViewModel|CreatorChannelFanTalkTabView|viewModel\\.selectedTab == \\.fanTalk|CreatorChannelFloatingIconButton|showFanTalkWrite|fanTalkCount|ic_plus_no_bg" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
### Phase 5: 프로젝트 등록과 검증
- [x] **Task 5.1: 프로젝트 파일 등록과 정적 검증**
- 대상 파일:
- 확인/수정: `SodaLive.xcodeproj/project.pbxproj`
- 검증 기준:
- 실행 명령: `git diff --check -- docs/20260704_크리에이터_채널_팬Talk_탭 SodaLive/Sources/V2/CreatorChannel SodaLive/Sources/I18n/I18n.swift SodaLive.xcodeproj/project.pbxproj`
- 기대 결과: 공백 오류 없이 성공한다.
- 실행 명령: `plutil -lint SodaLive.xcodeproj/project.pbxproj`
- 기대 결과: `OK` 출력.
- [x] **Task 5.2: 빌드 검증**
- 대상 파일:
- 확인: `SodaLive.xcworkspace`
- 확인: `SodaLive.xcodeproj`
- 검증 기준:
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- 기대 결과: `BUILD SUCCEEDED`.
- workspace 인식 실패 등 환경 문제가 있으면 `xcodebuild -project "SodaLive.xcodeproj" -scheme "SodaLive-dev" -configuration Debug build`를 대체 검증으로 실행하고 실패 원인을 검증 기록에 남긴다.
### Phase 6: 팬Talk 글 입력 문서와 공용 모달
- [x] **Task 6.1: 팬Talk 쓰기 Figma URL과 후속 구현 범위 기록**
- 대상 파일:
- 수정: `docs/20260704_크리에이터_채널_팬Talk_탭/prd.md`
- 수정: `docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md`
- 작업 내용:
- 팬Talk 쓰기 화면 Figma URL `290:9235`, `290:9282`, `290:9290`을 기록한다.
- 입력 종료 모달 Figma URL `308:19046`을 기록한다.
- 공용 모달 Figma URL `567:17662`, `567:17666`, `567:17712`을 기록한다.
- PRD에 팬Talk 작성/수정 진입, 입력 상태별 보내기 버튼, 입력 종료 안내 모달, 공용 모달 요구사항을 기록한다.
- 이번 작업은 문서만 작성하고 Swift 구현은 후속 task로 남긴다.
- 검증 기준:
- 실행 명령: `rg "290:9235|290:9282|290:9290|308:19046|567:17662|567:17666|567:17712|SodaV2ActionModal|ExplorerRepository.writeCheers|ExplorerRepository.modifyCheers|수정 모드" docs/20260704_크리에이터_채널_팬Talk_탭/prd.md docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md`
- 기대 결과: 팬Talk 쓰기와 공용 모달 Figma node, 공용 모달, 기존 작성/수정 API 사용 방침이 PRD와 plan-task에 기록된다.
- [x] **Task 6.2: V2 공용 액션 모달 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/Component/Modal/SodaV2ActionModal.swift`
- 작업 내용:
- Figma `567:17662`, `567:17666`, `567:17712` 기준의 공용 모달을 만든다.
- `title`, `message`, `button1`, `button2`, `onDimmedTap`을 외부에서 주입받는다.
- `button1`은 오른쪽, `button2`는 왼쪽에 배치한다.
- 각 버튼은 label과 action이 모두 있을 때만 표시한다.
- 버튼 1개만 표시될 때는 Figma `567:17662` 기준으로 단일 버튼 레이아웃을 사용한다.
- 버튼 2개가 표시될 때는 Figma `567:17666` 기준으로 왼쪽 `button2`, 오른쪽 `button1` 순서로 배치한다.
- dimmed는 `Color.black.opacity(0.6)`로 화면 전체를 덮고, dimmed tap 시 별도 액션 없이 모달만 닫히도록 `onDimmedTap`을 호출한다.
- 기존 `SodaDialog`는 구 UI와 opacity 50% 규칙을 사용하므로 이번 V2 Figma 모달 구현에는 직접 수정하지 않는다.
- 검증 기준:
- 실행 명령: `rg "struct SodaV2ActionModal|button1|button2|onDimmedTap|Color\\.black\\.opacity\\(0\\.6\\)" SodaLive/Sources/V2/Component/Modal/SodaV2ActionModal.swift`
- 기대 결과: 공용 모달의 주입값, 버튼 표시 조건, dimmed 규칙이 코드에서 확인된다.
### Phase 7: 팬Talk 쓰기 화면
- [x] **Task 7.1: 팬Talk 쓰기 ViewModel 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteViewModel.swift`
- 확인/필요 시 수정: `SodaLive/Sources/Explorer/ExplorerRepository.swift`
- 작업 내용:
- 작성/수정 mode를 구분하는 enum을 둔다.
- `@Published var content`를 둔다.
- 수정 모드는 route에서 받은 `fanTalkId`와 기존 content를 필수 입력값으로 사용한다.
- 수정 모드의 `content` 초기값은 route에서 받은 기존 content와 같아야 한다.
- 수정 모드의 `fanTalkId`는 0보다 커야 한다.
- `canSend``content.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false`일 때만 `true`가 되게 한다.
- `shouldShowExitModal`은 입력 값이 있을 때 취소 버튼을 누르면 `true`로 전환한다.
- 작성 mode의 보내기 action은 기존 `ExplorerRepository.writeCheers(parentCheersId:nil, creatorId:content:)`를 호출한다.
- 수정 mode의 보내기 action은 기존 `ExplorerRepository.modifyCheers(cheersId:fanTalkId, content:content, isActive:nil)`를 호출한다.
- 작성/수정 성공 시 `onSuccess`를 호출하고 화면을 닫을 수 있도록 성공 callback을 분리한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkWriteViewModel|enum .*Mode|fanTalkId|initialContent|@Published var content|canSend|trimmingCharacters|shouldShowExitModal|writeCheers|modifyCheers|isActive: nil|onSuccess" SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteViewModel.swift SodaLive/Sources/Explorer/ExplorerRepository.swift`
- 기대 결과: 입력 값 기반 send 활성화 조건, 수정 모드의 `fanTalkId`/기존 content 필수 전달, 기존 작성/수정 API 사용 지점이 확인된다.
- [x] **Task 7.2: 팬Talk 쓰기 View 구현**
- 대상 파일:
- 생성: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift`
- 수정: `SodaLive/Sources/I18n/I18n.swift`
- 작업 내용:
- Figma `290:9235` 기준으로 입력 값이 없을 때 보내기 버튼을 비활성화한다.
- Figma `290:9282` 기준으로 입력 값이 있을 때 보내기 버튼을 활성화한다.
- Figma `290:9290` 기준으로 입력 값이 있고 키보드가 내려간 상태의 레이아웃을 지원한다.
- 화면 진입 시 `FocusState`로 입력 영역에 focus를 주어 키보드가 올라오게 한다.
- 수정 모드 진입 시 route에서 받은 기존 content가 입력 영역에 표시되도록 ViewModel 초기값을 사용한다.
- 배경 tap 또는 기존 keyboard helper로 키보드를 내릴 수 있게 하되, 입력 값은 유지한다.
- 취소 버튼 tap 시 입력 값이 있으면 `SodaV2ActionModal`로 입력 종료 안내 모달을 표시하고, 입력 값이 없으면 이전 화면으로 닫는다.
- 입력 종료 모달은 Figma `308:19046` 기준으로 표시한다.
- 입력 종료 모달의 dimmed tap은 모달만 닫고 입력 화면은 유지한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelFanTalkWriteView|FocusState|focused\\(|canSend|SodaV2ActionModal|shouldShowExitModal|initialContent|mode" SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift`
- 기대 결과: 입력 상태별 UI, keyboard focus, 수정 모드 초기값, 취소 시 모달 표시가 코드에서 확인된다.
### Phase 8: 팬Talk 쓰기 진입 연결
- [x] **Task 8.1: AppStep route 추가**
- 대상 파일:
- 수정: `SodaLive/Sources/App/AppStep.swift`
- 수정: `SodaLive/Sources/ContentView.swift`
- 작업 내용:
- `AppStep`에 팬Talk 쓰기 route를 추가한다.
- route parameter는 최소 `creatorId`, 작성/수정 mode, 수정 대상 `fanTalkId`, 초기 content, `onSuccess`를 포함한다.
- 작성 모드는 `fanTalkId`와 초기 content를 전달하지 않거나 비어 있는 값으로 전달한다.
- 수정 모드는 수정 대상 `fanTalkId`와 기존 글 content를 반드시 함께 전달한다.
- `ContentView``AppStepLayerView`에서 route를 `CreatorChannelFanTalkWriteView`로 연결한다.
- 작성/수정 성공 시 `onSuccess`를 호출해 팬Talk 첫 페이지를 재조회할 수 있게 한다.
- 검증 기준:
- 실행 명령: `rg "creatorChannelFanTalkWrite|CreatorChannelFanTalkWriteView|onSuccess|fanTalkId|initialContent|mode" SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift`
- 기대 결과: route와 화면 연결이 확인된다.
- [x] **Task 8.2: CreatorChannel 팬Talk 탭 진입 action 연결**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift`
- 작업 내용:
- 크리에이터 채널 팬Talk 탭 우측하단 plus button tap 시 팬Talk 쓰기 route를 작성 모드로 이동한다.
- empty state의 `응원 남기기` 버튼 tap 시 같은 팬Talk 쓰기 route를 작성 모드로 이동한다.
- 내가 쓴 글의 `수정하기` tap 시 같은 팬Talk 쓰기 route를 수정 모드로 이동하고 수정 대상 `fanTalkId`, 기존 글 content를 반드시 함께 전달한다.
- 작성/수정 전송 성공 후 `fanTalkViewModel.fetchFirstPage(creatorId:)`를 호출하도록 `onSuccess`를 전달한다.
- 내 채널에서는 기존 규칙대로 plus button과 empty state `응원 남기기` 버튼을 표시하지 않는다.
- 검증 기준:
- 실행 명령: `rg "showFanTalkWriteView|showFanTalkModifyView|creatorChannelFanTalkWrite|fetchFirstPage\\(creatorId: creatorId\\)|onTapWrite|onTapEdit|응원 남기기|fanTalkId|content|initialContent" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift`
- 기대 결과: plus button, empty state button, 수정하기가 같은 팬Talk 쓰기 route로 연결되고 수정 모드는 `fanTalkId`와 기존 글 content를 함께 전달한다.
### Phase 9: 팬Talk 쓰기 검증
- [x] **Task 9.1: 정적 검증**
- 대상 파일:
- 확인: `docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md`
- 확인: `SodaLive/Sources/V2/Component/Modal`
- 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Write`
- 확인: `SodaLive/Sources/App/AppStep.swift`
- 확인: `SodaLive/Sources/ContentView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 검증 기준:
- 실행 명령: `git diff --check -- docs/20260704_크리에이터_채널_팬Talk_탭 SodaLive/Sources/V2/Component/Modal SodaLive/Sources/V2/CreatorChannel/FanTalk/Write SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/I18n/I18n.swift SodaLive.xcodeproj/project.pbxproj`
- 기대 결과: 공백 오류 없이 성공한다.
- 실행 명령: `plutil -lint SodaLive.xcodeproj/project.pbxproj`
- 기대 결과: `OK` 출력.
- [x] **Task 9.2: 빌드 검증**
- 대상 파일:
- 확인: `SodaLive.xcworkspace`
- 확인: `SodaLive.xcodeproj`
- 검증 기준:
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- 기대 결과: `BUILD SUCCEEDED`.
- workspace 인식 실패 등 환경 문제가 있으면 `xcodebuild -project "SodaLive.xcodeproj" -scheme "SodaLive-dev" -configuration Debug build`를 대체 검증으로 실행하고 실패 원인을 검증 기록에 남긴다.
### Phase 10: 팬Talk 팝업 위치와 문구 통일
- [x] **Task 10.1: 삭제 확인 모달 표시 위치 상위 이동**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
- 작업 내용:
- 팬Talk row 더보기의 `삭제` 선택 시 기존 선택 상태와 삭제 API 흐름은 유지한다.
- 삭제 확인 모달 렌더링은 팬Talk 탭 내부 overlay에서 제거하고 `CreatorChannelView`의 상위 `ZStack`에서 표시한다.
- 삭제 확인 모달은 기존 `SodaDialog` 대신 `SodaV2ActionModal`을 사용한다.
- 신고 사유 선택 UI인 `CheersReportDialogView`는 기존 신고 플로우 재사용 범위로 유지하고 이번 확인형 V2 popup dialog 전환 대상에서 제외한다.
- 검증 기준:
- 실행 명령: `rg "fanTalkDeleteDialog|SodaV2ActionModal|isShowDeleteDialog|deleteSelectedFanTalk|overlay\(deleteDialog\)|private var deleteDialog" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
- 기대 결과: `CreatorChannelView``fanTalkDeleteDialog``SodaV2ActionModal`이 있고, `CreatorChannelFanTalkTabView`에는 삭제 dialog overlay가 남지 않는다.
- [x] **Task 10.2: 팬Talk 리스트/상세 anchored context popup 공용화**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkActionPopup.swift`
- 수정: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailActionPopup.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailFeedView.swift`
- 작업 내용:
- 공용 `CreatorChannelActionPopup`을 사용해 리스트와 상세의 anchored context popup UI와 문구를 통일한다.
- 팬Talk 목록 row의 사용자 노출 문구는 `I18n.Explorer.editAction`, `I18n.Explorer.deleteAction`을 사용해 `수정하기`, `삭제하기`로 표시한다.
- 검증 기준:
- 실행 명령: `rg "CreatorChannelActionPopup|I18n\.Explorer\.editAction|I18n\.Explorer\.deleteAction|수정하기|삭제하기" SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkActionPopup.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift SodaLive/Sources/I18n/I18n.swift`
- 기대 결과: 팬Talk 목록 row가 공용 popup에 action 문구를 주입하고, `수정하기`/`삭제하기` 문구는 I18n 키에서 확인된다.
- [x] **Task 10.3: 팬Talk 상세 삭제 dialog V2 모달 적용**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift`
- 작업 내용:
- 팬Talk 상세/답글 화면의 삭제 확인 dialog도 `SodaV2ActionModal`로 교체한다.
- 확인, 취소, dimmed tap에서 기존 삭제/닫기 상태 흐름을 유지한다.
- 검증 기준:
- 실행 명령: `rg "SodaV2ActionModal|dismissDeleteDialog|confirmDelete|SodaDialog" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift`
- 기대 결과: 상세 삭제 확인 dialog가 `SodaV2ActionModal`을 사용하고 `SodaDialog`는 남지 않는다.
- [x] **Task 10.4: 정적/빌드 검증**
- 대상 파일:
- 확인: `docs/20260704_크리에이터_채널_팬Talk_탭/prd.md`
- 확인: `docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md`
- 확인: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkActionPopup.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailActionPopup.swift`
- 검증 기준:
- 실행 명령: `git diff --check -- docs/20260704_크리에이터_채널_팬Talk_탭 SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkActionPopup.swift SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailActionPopup.swift`
- 기대 결과: 공백 오류 없이 성공한다.
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- 기대 결과: `BUILD SUCCEEDED`.
## 검증 기록
- 2026-07-04: Figma `get_design_context``get_screenshot`으로 `290:9139`, `290:9145`, `290:9147`, `666:36114`, `290:9001`을 확인했다.
- 2026-07-04: `rg``sed`로 기존 `CreatorChannelView`, 후원 탭 구현, `UserProfileFanTalkViewModel`, `UserProfileFanTalkCheersItemView`, `UserProfileView` 신고/삭제 흐름을 확인했다.
- 2026-07-04: 사용자 피드백에 따라 우측하단 plus button은 `CreatorChannelFloatingActionMenu`의 private `floatingButton`과 동일 UI로 판단했다. 중복 컴포넌트를 만들지 않고 `CreatorChannelFloatingIconButton` 공용 컴포넌트로 추출해 `CreatorChannelFloatingActionMenu`와 팬Talk 글쓰기 floating button에서 재사용하는 방향으로 계획을 보완했다.
- 2026-07-04: `xcodebuild -workspace "SodaLive.xcworkspace" -list``SodaLive`, `SodaLive-dev` 앱 스킴은 확인했지만 테스트 action 구성은 확인되지 않았다.
- 2026-07-04: `rg` 검증으로 팬Talk 탭 response model, API endpoint, Repository, ViewModel, count bar, list item, action popup, empty view, tab view, `CreatorChannelView` 연결 문자열을 확인했다.
- 2026-07-04: `plutil -lint SodaLive.xcodeproj/project.pbxproj` 결과 `OK`를 확인했다.
- 2026-07-04: `git diff --check -- docs/20260704_크리에이터_채널_팬Talk_탭 SodaLive/Sources/V2/CreatorChannel SodaLive/Sources/I18n/I18n.swift SodaLive.xcodeproj/project.pbxproj`를 실행해 공백 오류가 없음을 확인했다.
- 2026-07-04: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 결과 `BUILD SUCCEEDED`를 확인했다.
- 2026-07-04: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test``Scheme SodaLive-dev is not currently configured for the test action.`으로 실패했다. 현재 범위에서는 새 테스트 타깃을 추가하지 않고 빌드/정적 검증으로 대체했다.
- 2026-07-06: 사용자 요청에 따라 Swift 구현 없이 `prd.md``plan-task.md`에 팬Talk 글 입력 화면 Figma URL(`290:9235`, `290:9282`, `290:9290`), 입력 종료 모달(`308:19046`), 공용 모달(`567:17662`, `567:17666`, `567:17712`)과 후속 구현 task를 추가했다.
- 2026-07-06: `rg "290:9235|290:9282|290:9290|308:19046|567:17662|567:17666|567:17712|SodaV2ActionModal|ExplorerRepository.writeCheers" docs/20260704_크리에이터_채널_팬Talk_탭/prd.md docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md`로 Figma node, 공용 모달, 기존 작성 API 재사용 방침이 문서에 포함된 것을 확인했다.
- 2026-07-06: `git diff --check -- docs/20260704_크리에이터_채널_팬Talk_탭/prd.md docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md` 실행 결과 출력 없이 종료되어 공백 오류가 없음을 확인했다.
- 2026-07-06: 사용자 요청에 따라 팬Talk 작성/수정 모두 같은 쓰기 페이지에서 수행하고, 작성/수정 API는 기존 팬Talk API(`ExplorerRepository.writeCheers`, `ExplorerRepository.modifyCheers`)를 사용하도록 계획을 보완했다.
- 2026-07-06: `rg "작성/수정|쓰기 페이지|수정 모드|작성 모드|ExplorerRepository.writeCheers|ExplorerRepository.modifyCheers|modifyCheers\\(cheersId:fanTalkId, content:content, isActive:nil\\)|creatorChannelFanTalkWrite|showFanTalkModifyView" docs/20260704_크리에이터_채널_팬Talk_탭/prd.md docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md`로 작성/수정 공용 쓰기 페이지와 기존 팬Talk API 사용 방침을 확인했다.
- 2026-07-06: `rg -n "no-op|수정 페이지는|수정 mutation|작성용 새 API|재사용 가능 여부|불가능한 것이 확인|미결정 사항|수정하기 tap은 수정 페이지" docs/20260704_크리에이터_채널_팬Talk_탭/prd.md docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md` 실행 결과 매칭이 없어 이전 충돌 문구가 남지 않았음을 확인했다.
- 2026-07-06: 사용자 확인에 따라 수정 모드 진입 시 수정 대상 `fanTalkId`와 기존 글 content를 반드시 함께 전달하도록 계획을 보완했다.
- 2026-07-06: `rg '수정 모드.*fanTalkId|fanTalkId.*기존 글 content|fanTalkId.*초기 content|fanTalkId.*initialContent|기존 content.*content 초기값|수정 대상.*fanTalkId.*기존 글 content|fanTalkId.*0보다 커야' docs/20260704_크리에이터_채널_팬Talk_탭/prd.md docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md`로 수정 모드 route에 `fanTalkId`와 기존 글 content를 함께 전달한다는 조건을 확인했다.
- 2026-07-06: Phase 6.2~8.2 구현을 완료했다. `SodaV2ActionModal`, `CreatorChannelFanTalkWriteViewModel`, `CreatorChannelFanTalkWriteView`를 추가하고 `AppStep.creatorChannelFanTalkWrite`, `ContentView` route, `CreatorChannelView`의 plus/empty/edit 진입 action을 연결했다.
- 2026-07-06: `rg "struct SodaV2ActionModal|button1|button2|onDimmedTap|Color\.black\.opacity\(0\.6\)" SodaLive/Sources/V2/Component/Modal/SodaV2ActionModal.swift`로 V2 공용 액션 모달의 주입값, 버튼 표시 조건, dimmed 규칙을 확인했다. Figma node id는 코드 주석으로 남기지 않고 문서 기준으로만 유지했다.
- 2026-07-06: `rg "CreatorChannelFanTalkWriteViewModel|enum .*Mode|fanTalkId|initialContent|@Published var content|canSend|trimmingCharacters|shouldShowExitModal|writeCheers|modifyCheers|isActive: nil|onSuccess" SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteViewModel.swift SodaLive/Sources/Explorer/ExplorerRepository.swift`로 작성/수정 mode, 입력값 검증, 기존 작성/수정 API 재사용을 확인했다.
- 2026-07-06: `rg "CreatorChannelFanTalkWriteView|FocusState|focused\(|canSend|SodaV2ActionModal|shouldShowExitModal|initialContent|mode" SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift`로 쓰기 화면의 focus, send 활성화, 수정 모드 초기값, 취소 모달 연결을 확인했다.
- 2026-07-06: `rg "creatorChannelFanTalkWrite|CreatorChannelFanTalkWriteView|onSuccess|fanTalkId|initialContent|mode" SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift``rg "showFanTalkWriteView|showFanTalkModifyView|creatorChannelFanTalkWrite|fetchFirstPage\(creatorId: creatorId\)|onTapWrite|onTapEdit|응원 남기기|fanTalkId|content|initialContent" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift`로 작성/수정 route와 진입 action 연결을 확인했다.
- 2026-07-06: `plutil -lint SodaLive.xcodeproj/project.pbxproj` 결과 `OK`를 확인했다.
- 2026-07-06: `git diff --check -- docs/20260704_크리에이터_채널_팬Talk_탭 SodaLive/Sources/V2/Component/Modal SodaLive/Sources/V2/CreatorChannel/FanTalk/Write SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift SodaLive/Sources/I18n/I18n.swift SodaLive.xcodeproj/project.pbxproj` 실행 결과 출력 없이 종료되어 공백 오류가 없음을 확인했다.
- 2026-07-06: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build` 결과 `BUILD SUCCEEDED`를 확인했다. 빌드 중 `Alamofire` dependency scan warning이 출력되었으나 빌드는 성공했다.
- 2026-07-07: `rg "fanTalkDeleteDialog|SodaV2ActionModal|isShowDeleteDialog|deleteSelectedFanTalk|overlay\(deleteDialog\)|private var deleteDialog" SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`로 팬Talk 삭제 모달이 상위 `CreatorChannelView`에서 `SodaV2ActionModal`로 표시되고, 팬Talk 탭 내부 삭제 dialog overlay가 제거된 것을 확인했다.
- 2026-07-07: `rg "CreatorChannelActionPopup|I18n\.Explorer\.edit|I18n\.Common\.delete|수정하기|삭제하기" SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkActionPopup.swift SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailActionPopup.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailFeedView.swift`로 리스트와 상세 anchored context popup이 공용 컴포넌트와 동일 `I18n` 문구를 사용하는 것을 확인했다.
- 2026-07-07: `rg "SodaV2ActionModal|dismissDeleteDialog|confirmDelete|SodaDialog" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift`로 상세 삭제 확인 dialog가 `SodaV2ActionModal`을 사용하는 것을 확인했다.
- 2026-07-07: Reviewer gate 지적에 따라 PRD 본문 요구사항/성공 기준/제약 조건에 삭제 확인 모달의 상위 `CreatorChannelView` 표시, `SodaV2ActionModal` 사용, list/detail anchored context popup 공용화, `CheersReportDialogView` 제외 범위를 명시했다.
- 2026-07-07: 사용자 후속 요청에 따라 팬Talk 목록 row anchored context popup 문구를 `수정`/`삭제`에서 `수정하기`/`삭제하기`로 변경했다. 상세 popup 기본 문구는 기존 공용 기본값을 유지하고, 팬Talk row 호출부에서 `I18n.Explorer.editAction`, `I18n.Explorer.deleteAction`을 주입한다.
### Phase 11: 팬레터 empty state 다국어 보강
- [x] **Task 11.1: 팬레터 empty state 문구 정의**
- 대상 파일:
- 수정: `SodaLive/Sources/I18n/I18n.swift`
- 작업 내용:
- Empty 문구를 한국어 `아직 응원이 없습니다.\n처음으로 크리에이터를 응원해 보세요!`, 영어 `No fan letters yet.\nBe the first to send one!`, 일본어 `まだファンレターがありません。\n最初のファンレターを送ってみましょう`로 정의한다.
- 작성 버튼을 한국어 `응원남기기`, 영어 `Send a fan letter`, 일본어 `ファンレターを送る`로 정의한다.
- 지정된 `\n` 줄바꿈을 모든 언어에서 유지한다.
- 검증 기준:
- 실행 명령: `rg -n "아직 응원이 없습니다|No fan letters yet|まだファンレターがありません|응원남기기|Send a fan letter|ファンレターを送る" SodaLive/Sources/I18n/I18n.swift`
- 기대 결과: empty 문구와 작성 버튼의 ko/en/ja 값이 `pick(ko:en:ja:)`에서 확인된다.
- [x] **Task 11.2: 팬레터 empty View에 I18n 적용**
- 대상 파일:
- 수정: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 작업 내용:
- 하드코딩된 empty 문구와 `응원 남기기` 버튼을 Task 11.1의 I18n 값으로 교체한다.
- 기존 팬레터 작성 route 연결과 내 채널의 empty 버튼·floating button 숨김 규칙을 유지한다.
- empty 작성 버튼은 `Color.gray800` 배경과 white 아이콘·텍스트를 사용한다.
- 신규 route나 API를 추가하지 않는다.
- 검증 기준:
- 실행 명령: `rg -n "CreatorChannelFanTalkEmptyView|I18n\.|showsWriteButton|onTapWrite|creatorChannelFanTalkWrite|Color\.gray800|foregroundColor\(\.white\)|응원 남기기" SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`
- 기대 결과: View는 I18n 값을 사용하고 기존 작성 route와 내 채널 숨김 조건 및 gray800/white 버튼 색상을 유지하며 하드코딩된 `응원 남기기`가 없다.
- [x] **Task 11.3: 정적 검사와 Debug 빌드 검증**
- 대상 파일:
- 확인: `SodaLive/Sources/I18n/I18n.swift`
- 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkEmptyView.swift`
- 검증 기준:
- 실행 명령: `git diff --check`
- 기대 결과: 출력 없이 exit code 0이다.
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
- 기대 결과: `** BUILD SUCCEEDED **`.
- 2026-08-14: 사용자 인터뷰로 팬레터 탭 empty 문구와 작성 버튼의 ko/en/ja 값을 확정하고 Phase 11에 미완료 후속 Task로 추가했다. 기존 작성 route와 내 채널 버튼 숨김 규칙은 유지하며 이번 작업은 문서만 보강했다.
- 2026-08-14: Phase 11 구현을 완료했다. 팬레터 탭 empty 문구와 작성 버튼을 ko/en/ja I18n으로 교체하고 기존 작성 route 및 내 채널 버튼 숨김 규칙을 유지했다. 사용자 후속 요청에 따라 작성 버튼은 `Color.gray800` 배경과 white 아이콘·텍스트로 변경했다. 23개 정적 계약, `git diff --check`, iPhone 16 Pro 시뮬레이터 Debug 빌드가 통과했고, 세 언어 재캡처와 두 독립 색상·CJK 리뷰에서 차단 항목 없이 PASS를 확인했다.
### Phase 12: 팬Talk 쓰기 입력 UX 보강
**Phase 결과:** 팬Talk 쓰기 화면에서 취소 문구가 잘리지 않고, 다국어 placeholder와 500자 입력 제한 및 초과 예외 상태가 일관되게 동작한다.
**선행조건:** PRD `§5.10`, `DEC-FANTALK-WRITE-001~003` 확정.
**Phase 완료 조건:** `P12-T1`, `P12-T2`, `P12-GATE` 완료와 검증 기록 누적.
#### Task 12.1 다국어 placeholder와 입력 상태 계약
**Goal 실행 `P12-T1`:** placeholder 문구와 500자 제한에 필요한 ViewModel 상태를 기존 타입에 최소 추가한다.
- **시작 조건:** Phase 12 선행조건 충족.
- **완료 증거:** ko/en/ja 문구, 500자 허용 경계, 초과 상태 및 전송 비활성화 조건이 코드와 정적 검사에서 확인된다.
- **범위 밖:** 신규 입력 컴포넌트, dependency, route, API 변경.
- **TDD 예외 사유:** 현재 프로젝트에 unit test bundle target이 없으며 이번 범위에서 신규 test target을 추가하지 않는다.
- **대체 검증 방법:** `rg` 정적 계약 검사, Swift 표준 문자열 경계 확인, Phase Gate의 앱 빌드와 수동 입력 검증을 사용한다.
**Files:**
- Modify: `SodaLive/Sources/I18n/I18n.swift`
- Modify: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteViewModel.swift`
**Interfaces:**
- Consumes: 기존 `I18n.pick(ko:en:ja:)`, `content`, `maxContentCount`, `canSend`.
- Produces: `I18n.CreatorChannelFanTalk.writePlaceholder`, `content.count > maxContentCount` 초과 상태, 초과 상태를 포함한 `canSend`.
- [x] `I18n.CreatorChannelFanTalk.writePlaceholder`에 한국어 `응원 메시지를 입력해 주세요.`, 영어 `Enter a message of support.`, 일본어 `応援メッセージを入力してください。`를 추가한다.
- [x] `maxContentCount``500`을 유지하고 500자는 유효, 500자 초과만 예외인 상태를 제공한다.
- [x] `canSend`는 기존 공백·줄바꿈 검증에 더해 content가 500자를 초과하면 `false`가 되게 한다.
- [x] 수정 모드의 초기 content는 임의로 자르지 않아 500자 초과 예외 상태를 화면에서 판정할 수 있게 한다.
- [x] `rg -n "writePlaceholder|maxContentCount|content.count|canSend|whitespacesAndNewlines" SodaLive/Sources/I18n/I18n.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteViewModel.swift`로 상태 계약을 확인한다.
#### Task 12.2 내비게이션·placeholder·500자 UI 적용
**Goal 실행 `P12-T2`:** 기존 쓰기 View 안에서 취소 문구 잘림을 제거하고 입력 제한과 초과 표시를 연결한다.
- **시작 조건:** `P12-T1` 완료.
- **완료 증거:** View 정적 검사와 ko/en/ja 및 499·500·501자 수동 검증 통과.
- **범위 밖:** 내비게이션 전체 높이, 보내기 capsule 높이, `/500` 색상, 작성·수정 API 흐름 변경.
- **TDD 예외 사유:** 현재 프로젝트에 UI test bundle target이 없으며 이번 범위에서 신규 test target을 추가하지 않는다.
- **대체 검증 방법:** View 정적 계약 검사와 Phase Gate의 시뮬레이터 수동 검증을 사용한다.
**Files:**
- Modify: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift`
**Interfaces:**
- Consumes: `P12-T1``writePlaceholder`, `maxContentCount`, 초과 상태, `canSend`.
- Produces: 500자로 제한된 `TextEditor` binding과 정상·초과 입력 UI 상태.
- [x] 취소 `Text``.frame(width: 64, height: 56, alignment: .leading)`만 제거한다.
- [x] 내비게이션 `.frame(height: 56)`과 보내기 capsule `.frame(height: 44)`는 유지한다.
- [x] content가 비어 있을 때 `writePlaceholder`를 표시하고 입력이 시작되면 숨긴다.
- [x] `TextEditor`가 전달한 새 content는 `String(newValue.prefix(viewModel.maxContentCount))`까지만 반영해 타이핑과 붙여넣기 모두 500자를 넘지 않게 한다.
- [x] ViewModel에 이미 500자를 초과한 content가 있으면 메시지 본문과 현재 글자 수를 `Color.red400`으로 표시한다.
- [x] 500자 초과 상태에서는 `canSend == false`에 따라 보내기 버튼을 비활성화하고 `/500`은 기존 `Color.gray500`을 유지한다.
- [x] 기존 focus, 키보드, 취소 모달, 작성·수정 submit 흐름은 변경하지 않는다.
- [x] `rg -n "writePlaceholder|prefix\(viewModel.maxContentCount\)|Color.red400|frame\(width: 64, height: 56|frame\(height: 56\)|frame\(height: 44\)" SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift`로 추가·유지·제거 계약을 확인한다.
#### Phase 12 Gate
**Goal 실행 `P12-GATE`:** Phase 12 요구사항과 기존 작성·수정 흐름의 회귀 여부를 최종 판정한다.
- **시작 조건:** `P12-T1`, `P12-T2` 완료.
- **완료 증거:** 아래 자동·수동 검증 통과와 실제 결과를 검증 기록에 누적.
- **범위 밖:** Gate 통과를 위한 기존 검증 완화나 관련 없는 소스 수정.
```bash
rg -n "응원 메시지를 입력해 주세요|Enter a message of support|応援メッセージを入力してください|writePlaceholder" SodaLive/Sources/I18n/I18n.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift
rg -n "maxContentCount|content.count|prefix\(viewModel.maxContentCount\)|Color.red400|canSend" SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteViewModel.swift
git diff --check -- SodaLive/Sources/I18n/I18n.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteViewModel.swift docs/20260704_크리에이터_채널_팬Talk_탭/prd.md docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
```
**Expected:** 다국어 문구와 500자 경계 및 초과 상태 계약이 확인되고, 공백 오류 없이 `** BUILD SUCCEEDED **`가 출력된다.
수동 검증:
- [x] ko/en/ja에서 취소 문구가 잘리지 않고 placeholder가 정확히 표시된다.
- [x] 499자와 500자는 입력되며 유효 content일 때 보내기 버튼이 활성화된다.
- [x] 501번째 타이핑 문자와 500자를 초과하는 붙여넣기 문자는 반영되지 않고 글자 수가 500을 넘지 않는다.
- [x] 수정 초기값 등으로 500자를 초과한 예외 상태를 주입하면 메시지 본문과 현재 글자 수가 `Color.red400`으로 표시되고 보내기 버튼이 비활성화된다.
- [x] 공백·줄바꿈만 입력한 상태, 취소 모달, 작성·수정 submit 흐름이 기존과 동일하다.
- 2026-08-25: 사용자 인터뷰에서 500자 허용·501번째 이후 입력 차단, 500자 초과 예외 상태의 본문·현재 글자 수 붉은 표시와 보내기 비활성화, 취소 문구의 고정 크기만 제거하는 범위를 확정했다. PRD와 Phase 12 계획만 추가했으며 Swift 구현과 빌드 검증은 수행하지 않았다.
- 2026-08-25: Phase 12 구현을 완료했다. `writePlaceholder`, 500자 제한 binding, 500자 초과 상태와 전송 비활성화, 취소 문구의 고정 frame 제거를 적용했으며 기존 내비게이션·보내기 높이와 작성·수정 흐름은 유지했다.
- 2026-08-25: Phase 12의 두 `rg` 정적 계약 검사, Swift `String.count`/`prefix(_:)` 499·500·501자 경계 검사, `git diff --check`, `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`를 실행했고 `** BUILD SUCCEEDED **`를 확인했다.
- 2026-08-25: 사용자 요청에 따라 visual QA와 시뮬레이터 수동 검증은 수행하지 않았다. ko/en/ja 표시와 실제 입력 동작에 관한 수동 검증 체크박스 5개는 사용자 확인 전까지 미완료로 유지한다.
### Phase 13: 팬레터 item·empty 표시 회귀 수정
**Phase 결과:** 팬레터 item의 신고 문구가 앱 언어를 따르고, empty 상태에서는 `전체 0` count bar가 표시되지 않는다.
**선행조건:** PRD `DEC-FANTALK-BUG-001`, `DEC-FANTALK-BUG-002` 확정.
**Phase 완료 조건:** `P13-T1`, `P13-GATE` 완료와 검증 기록 누적.
#### Task 13.1 신고 다국어와 empty count bar 수정
**Goal 실행 `P13-T1`:** 기존 View 두 곳에서 신고 문구와 count bar 표시 조건만 최소 수정한다.
- **시작 조건:** Phase 13 선행조건 충족.
- **완료 증거:** 정적 계약 검사, ko/en/ja 및 empty/non-empty 수동 확인, Debug 빌드 결과가 기록된다.
- **범위 밖:** 신규 I18n 키, 신고 API·dialog 변경, count bar 디자인 변경, 관련 없는 리팩터링.
- **TDD 예외 사유:** 현재 프로젝트에 test bundle target이 없고 변경 대상은 기존 I18n 참조와 단순 표시 조건이다.
- **대체 검증 방법:** `rg` 정적 계약 검사, Debug 빌드와 Simulator 수동 확인을 사용한다.
**Files:**
- Modify: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift`
- Modify: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
- Verify: `SodaLive/Sources/I18n/I18n.swift`
- [x] 타인 item의 하드코딩 `Text("신고")``Text(I18n.Explorer.report)`로 교체한다.
- [x] `fanTalkCount > 0`일 때만 `CreatorChannelFanTalkCountBar`를 표시한다.
- [x] `fanTalkCount == 0`인 기존 empty view와 작성 버튼 표시 규칙은 유지한다.
- [x] 신고 API·dialog와 item action 조건은 변경하지 않는다.
- [x] 아래 Gate 결과를 검증 기록에 누적한다.
#### Phase 13 Gate
```bash
rg -n 'I18n\.Explorer\.report|Text\("신고"\)' SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift
rg -n 'fanTalkCount > 0|CreatorChannelFanTalkCountBar|isEmptyState' SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift
git diff --check -- SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift docs/20260704_크리에이터_채널_팬Talk_탭/prd.md docs/20260704_크리에이터_채널_팬Talk_탭/plan-task.md
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
```
**Expected:** item은 기존 `I18n.Explorer.report`를 사용하고 한국어 literal이 없으며, count bar는 양수 count에서만 표시되고 Debug 빌드가 성공한다.
수동 검증:
- [ ] ko/en/ja에서 타인 item의 신고 문구가 각각 `신고`, `Report`, `通報`로 표시된다.
- [ ] empty 상태에서 팬레터 empty view만 표시되고 `전체 0`은 표시되지 않는다.
- [ ] non-empty 상태에서 `전체 {fanTalkCount}`와 기존 item action이 표시된다.
- 2026-08-26: 사용자 요청과 PRD `DEC-FANTALK-BUG-001~002`를 기준으로 Phase 13을 추가했다. 이번 작업은 문서만 작성했으며 Swift 구현과 Gate 실행은 수행하지 않았다.
- 2026-08-26: Phase 13을 구현해 타인 item의 신고 문구를 기존 `I18n.Explorer.report`로 교체하고 `fanTalkCount > 0`일 때만 count bar를 표시하도록 수정했다. 기존 empty view·작성 버튼·신고 action/dialog/API 조건은 유지했다.
- 2026-08-26: Phase 13의 `rg` 정적 계약, `git diff --check`, `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`를 실행했고 `** BUILD SUCCEEDED **`를 확인했다. 사양·코드 품질 리뷰는 모두 승인됐으며 `reviews/phase13-fan-talk-item-empty-regression.md`에 기록했다.
- 2026-08-26: 사용자 요청에 따라 visual QA와 Simulator 수동 검증은 수행하지 않았다. ko/en/ja 신고 문구와 empty/non-empty 실제 화면 체크박스 3개는 사용자 확인 전까지 미완료로 유지한다.