321 lines
25 KiB
Markdown
321 lines
25 KiB
Markdown
# 글 상세 - 답글 쓰기 구현 계획
|
|
|
|
> **For agentic workers:** PRD를 기준으로 task 단위로 진행한다. 구현 중 요구사항 변경이 생기면 먼저 `docs/20260707_글_상세_답글_쓰기/prd.md`를 보강하고 이 문서의 체크리스트를 갱신한다.
|
|
|
|
**Goal:** 커뮤니티 댓글 또는 본인 채널 팬Talk 댓글을 터치해 답글 상세 화면에 진입하고, 기존 API로 답글 작성/수정/삭제를 수행한다.
|
|
|
|
**Architecture:** V2 전용 답글 상세 화면과 ViewModel을 새로 만들고, 네트워크 mutation은 기존 `CreatorCommunityRepository`와 `ExplorerRepository`를 재사용한다. 커뮤니티와 팬Talk의 화면 표시/입력/수정 모드는 공통 ViewModel 상태로 처리하되, API 호출과 갱신 전략은 source enum으로 분기한다.
|
|
|
|
**Tech Stack:** SwiftUI, Combine, Moya, Kingfisher, 기존 `AppCoordinator`, 기존 V2 design token(`Color`, `SodaSpacing`, `appFont`)
|
|
|
|
---
|
|
|
|
## 기준 문서
|
|
|
|
- PRD: `docs/20260707_글_상세_답글_쓰기/prd.md`
|
|
- 문서 정책: `docs/agent-guides/documentation-policy.md`
|
|
- 코드 스타일: `docs/agent-guides/code-style.md`
|
|
- 빌드/검증: `docs/agent-guides/build-test-verification.md`
|
|
- Figma 답글 없음: `669:40462`, `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=669-40462&m=dev`
|
|
- Figma 답글 있음: `669:40468`, `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=669-40468&m=dev`
|
|
|
|
## 구현 원칙
|
|
|
|
- 신규 화면과 상태 관리는 `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/**` 아래에 둔다.
|
|
- 기존 답글 작성 API를 새로 만들지 않는다.
|
|
- 커뮤니티 답글 API는 `CreatorCommunityRepository.createCommunityPostComment`, `getCommentReplyList`, `modifyComment`를 사용한다.
|
|
- 팬Talk 답글 API는 `ExplorerRepository.writeCheers`, `modifyCheers`를 사용한다.
|
|
- 팬Talk 답글 작성/수정/삭제 성공 후에는 팬Talk 목록 첫 페이지를 재조회하는 callback을 호출한다.
|
|
- 커뮤니티 원댓글이 비밀댓글이면 답글 작성 시 `isSecret`에 원댓글의 `isSecret` 값을 그대로 전달한다.
|
|
- send 버튼 활성 기준은 `trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false`로 통일한다.
|
|
- 답글 수정은 별도 화면을 열지 않고 하단 입력창에 수정 대상 내용을 채운 뒤 send 버튼으로 수정 API를 호출한다.
|
|
- 수정/삭제는 Figma 더보기 메뉴 노출 범위 안에서 구현하되, 기존 권한 규칙을 유지한다.
|
|
- `ic_arrow_up_gray`, `ic_arrow_up_white` asset은 이미 추가된 것으로 간주하고, 구현 시 asset catalog 등록 상태를 검증한다.
|
|
|
|
## 주요 대상 파일
|
|
|
|
### 생성
|
|
- `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailContext.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailDisplayItem.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailTitleBar.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailFeedView.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailInputBar.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailActionPopup.swift`
|
|
|
|
### 수정
|
|
- `SodaLive/Sources/App/AppStep.swift`
|
|
- `SodaLive/Sources/ContentView.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift`
|
|
- `SodaLive/Sources/V2/CreatorChannel/Community/CreatorChannelCommunityTabView.swift`
|
|
- `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentListView.swift`
|
|
- `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentItemView.swift`
|
|
- `SodaLive/Sources/I18n/I18n.swift`
|
|
- `SodaLive.xcodeproj/project.pbxproj`
|
|
|
|
## TASK 체크리스트
|
|
|
|
### Phase 1: 모델과 라우팅 기반
|
|
|
|
- [x] **Task 1.1: 답글 상세 context/display 모델 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailContext.swift`
|
|
- 생성: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailDisplayItem.swift`
|
|
- 작업 내용:
|
|
- `CreatorChannelReplyDetailContext`에 `.community`와 `.fanTalk` source를 둔다.
|
|
- 커뮤니티 context는 `creatorId`, `postId`, `parentComment: GetCommunityPostCommentListItem`을 보유한다.
|
|
- 팬Talk context는 `creatorId`, `parentFanTalk: CreatorChannelFanTalkItemResponse`를 보유한다.
|
|
- display item은 id, writerId, nickname, profileImageUrl, content, relativeTimeText, isSecret, isMine, isCreatorReply 여부를 담는다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "enum CreatorChannelReplyDetailContext|case community|case fanTalk|struct CreatorChannelReplyDetailDisplayItem|isSecret|isCreatorReply" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models`
|
|
- 기대 결과: 커뮤니티와 팬Talk를 모두 표현하는 context/display 모델이 확인된다.
|
|
|
|
- [x] **Task 1.2: AppStep에 답글 상세 route 추가**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/App/AppStep.swift`
|
|
- 수정: `SodaLive/Sources/ContentView.swift`
|
|
- 작업 내용:
|
|
- `AppStep`에 `creatorChannelReplyDetail(context:onFanTalkRefresh:)` case를 추가한다.
|
|
- `ContentView` switch에서 `CreatorChannelReplyDetailView(context:onFanTalkRefresh:)`를 반환한다.
|
|
- `onFanTalkRefresh`는 팬Talk 상세에서 작성/수정/삭제 성공 후 팬Talk 목록 첫 페이지를 재조회하기 위한 optional callback으로 둔다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "creatorChannelReplyDetail|CreatorChannelReplyDetailView|onFanTalkRefresh" SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift`
|
|
- 기대 결과: 새 navigation step과 화면 연결이 확인된다.
|
|
|
|
### Phase 2: ViewModel과 기존 API 재사용
|
|
|
|
- [x] **Task 2.1: 답글 상세 ViewModel 생성**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- 작업 내용:
|
|
- `@Published` 상태로 `replyText`, `replies`, `isLoading`, `errorMessage`, `isShowPopup`, `selectedActionItem`, `editingItem`을 둔다.
|
|
- `isSendEnabled`는 `replyText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false`로 계산한다.
|
|
- `configure(context:)`에서 원댓글 display item과 초기 답글 display items를 구성한다.
|
|
- 커뮤니티 context는 `fetchCommunityReplies()`로 기존 답글 목록을 조회한다.
|
|
- 팬Talk context는 `parentFanTalk.creatorReplies`를 초기 답글 목록으로 표시한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "final class CreatorChannelReplyDetailViewModel|replyText|replies|isSendEnabled|editingItem|configure\\(|fetchCommunityReplies|creatorReplies" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- 기대 결과: 입력 상태, 수정 상태, 커뮤니티 조회, 팬Talk 초기 답글 표시 상태가 확인된다.
|
|
|
|
- [x] **Task 2.2: 커뮤니티 답글 작성/수정/삭제 처리**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- 작업 내용:
|
|
- 작성 모드 send 시 `CreatorCommunityRepository.createCommunityPostComment(comment:postId:parentId:isSecret:)`를 호출한다.
|
|
- `parentId`는 원댓글 id, `postId`는 context post id, `isSecret`은 원댓글 `isSecret` 값을 전달한다.
|
|
- 수정 모드 send 시 `ModifyCommunityPostCommentRequest(commentId:)`에 수정 content를 담아 `modifyComment(request:)`를 호출한다.
|
|
- 삭제 confirm 시 `ModifyCommunityPostCommentRequest(commentId:)`에 `isActive = false`를 담아 `modifyComment(request:)`를 호출한다.
|
|
- 성공 후 `replyText`와 `editingItem`을 초기화하고 `fetchCommunityReplies()`로 목록을 갱신한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "createCommunityPostComment|parentId|isSecret|ModifyCommunityPostCommentRequest|modifyComment|isActive = false|fetchCommunityReplies" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- 기대 결과: 커뮤니티 작성/수정/삭제가 기존 repository로 연결된다.
|
|
|
|
- [x] **Task 2.3: 팬Talk 답글 작성/수정/삭제 처리**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- 작업 내용:
|
|
- 작성 모드 send 시 `ExplorerRepository.writeCheers(parentCheersId:creatorId:content:)`를 호출한다.
|
|
- `parentCheersId`는 원 팬Talk의 `fanTalkId`, `creatorId`는 context creator id를 전달한다.
|
|
- 수정 모드 send 시 `ExplorerRepository.modifyCheers(cheersId:content:isActive:)`를 호출하고 `isActive`는 `nil`로 전달한다.
|
|
- 삭제 confirm 시 `ExplorerRepository.modifyCheers(cheersId:content:isActive:)`를 호출하고 `content`는 `nil`, `isActive`는 `false`로 전달한다.
|
|
- 성공 후 `replyText`와 `editingItem`을 초기화하고 `onFanTalkRefresh` callback을 호출한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "writeCheers|parentCheersId|modifyCheers|isActive: nil|isActive: false|onFanTalkRefresh|editingItem = nil" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- 기대 결과: 팬Talk 작성/수정/삭제가 기존 ExplorerRepository로 연결되고 성공 후 첫 페이지 재조회 callback이 호출된다.
|
|
|
|
### Phase 3: Figma 기반 UI 컴포넌트
|
|
|
|
- [x] **Task 3.1: back-only title bar 구현**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailTitleBar.swift`
|
|
- 작업 내용:
|
|
- Figma 기준으로 status bar 아래 60pt 높이 영역에 back chevron만 표시한다.
|
|
- title text는 표시하지 않는다.
|
|
- tap action은 부모에서 주입한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct CreatorChannelReplyDetailTitleBar|ic_back|chevron|frame\\(height: 60\\)|onTapBack|Text\\(" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailTitleBar.swift`
|
|
- 기대 결과: back 전용 title bar가 있고 title text가 없어야 한다.
|
|
|
|
- [x] **Task 3.2: 원댓글/답글 feed 컴포넌트 구현**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailFeedView.swift`
|
|
- 작업 내용:
|
|
- 원댓글은 42pt 프로필, 닉네임, 상대 시간, 본문, 우측 더보기 버튼을 표시한다.
|
|
- 답글이 없으면 답글 카드와 연결선을 표시하지 않는다.
|
|
- 답글이 있으면 좌측 연결선과 `Color.gray900`, radius 14pt의 답글 카드를 표시한다.
|
|
- 답글 카드는 20pt 프로필, 닉네임, 상대 시간, 본문, 우측 더보기 버튼을 표시한다.
|
|
- 더보기 버튼은 `CreatorChannelReplyDetailActionPopup` 표시를 위한 item 선택 action을 호출한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct CreatorChannelReplyDetailFeedView|Color\\.gray900|Color\\.gray800|RoundedRectangle|42|20|selectedActionItem|CreatorChannelReplyDetailDisplayItem|Shape|Path" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailFeedView.swift`
|
|
- 기대 결과: 원댓글/답글 카드, 답글 연결선, action item 선택 구조가 확인된다.
|
|
|
|
- [x] **Task 3.3: 하단 답글 input bar 구현**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailInputBar.swift`
|
|
- 작업 내용:
|
|
- 하단 고정 영역 높이는 Figma 기준 60pt로 시작한다.
|
|
- placeholder는 `I18n.CreatorChannelReplyDetail.replyPlaceholder`를 사용한다.
|
|
- 비활성 send 버튼은 `Color.gray900` 배경과 `ic_arrow_up_gray` 아이콘을 표시한다.
|
|
- 활성 send 버튼은 `Color.soda400` 배경과 `ic_arrow_up_white` 아이콘을 표시한다.
|
|
- `isSendEnabled == false`이면 tap 시 action을 호출하지 않는다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct CreatorChannelReplyDetailInputBar|replyPlaceholder|ic_arrow_up_gray|ic_arrow_up_white|Color\\.gray900|Color\\.soda400|isSendEnabled|onTapSend" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailInputBar.swift`
|
|
- 기대 결과: 활성/비활성 send 버튼 상태가 PRD와 일치한다.
|
|
|
|
- [x] **Task 3.4: 수정/삭제 action popup 구현**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailActionPopup.swift`
|
|
- 작업 내용:
|
|
- Figma `669:40468` 기준으로 `수정하기`, `삭제하기` 항목을 표시한다.
|
|
- item 권한에 따라 수정/삭제 노출 여부를 주입받는다.
|
|
- `수정하기` tap은 ViewModel의 `beginEditing(item:)`으로 연결한다.
|
|
- `삭제하기` tap은 삭제 confirm dialog 표시로 연결한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct CreatorChannelReplyDetailActionPopup|수정하기|삭제하기|canEdit|canDelete|onTapEdit|onTapDelete|Color\\.gray900|Color\\.gray700" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailActionPopup.swift`
|
|
- 기대 결과: 수정/삭제 팝업과 권한별 노출 조건이 확인된다.
|
|
|
|
- [x] **Task 3.5: 답글 상세 화면 조립**
|
|
- 대상 파일:
|
|
- 생성: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift`
|
|
- 작업 내용:
|
|
- `CreatorChannelReplyDetailTitleBar`, `CreatorChannelReplyDetailFeedView`, `CreatorChannelReplyDetailInputBar`를 배치한다.
|
|
- 배경은 `Color.black`을 사용한다.
|
|
- 하단 input bar는 safe area 하단에 고정한다.
|
|
- 수정 모드 진입 시 input bar에 기존 답글 내용을 표시한다.
|
|
- 삭제 confirm dialog와 toast/loading 표시를 연결한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "struct CreatorChannelReplyDetailView|CreatorChannelReplyDetailTitleBar|CreatorChannelReplyDetailFeedView|CreatorChannelReplyDetailInputBar|SodaDialog|sodaToast|Color\\.black|safeAreaInset|editingItem" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift`
|
|
- 기대 결과: 상세 화면 조립과 수정/삭제/오류 UI 연결이 확인된다.
|
|
|
|
### Phase 4: 커뮤니티 진입 연결
|
|
|
|
- [x] **Task 4.1: 커뮤니티 댓글 목록에서 답글 상세로 진입**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentListView.swift`
|
|
- 수정: `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentItemView.swift`
|
|
- 작업 내용:
|
|
- 기존 V1 댓글 목록 전체를 교체하지 않고, 원댓글 item tap 또는 답글 텍스트 tap을 새 V2 답글 상세 route로 연결한다.
|
|
- context는 `.community(creatorId:postId:parentComment:)`로 구성한다.
|
|
- 커뮤니티는 본인 여부와 관계없이 진입 가능하게 한다.
|
|
- 기존 댓글 작성 sheet 동작은 유지한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "creatorChannelReplyDetail|CreatorChannelReplyDetailContext|case \\.community|parentComment|postId|creatorId" SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentListView.swift SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentItemView.swift`
|
|
- 기대 결과: 커뮤니티 댓글에서 V2 답글 상세 route를 생성하는 코드가 확인된다.
|
|
|
|
- [x] **Task 4.2: V2 커뮤니티 탭의 댓글 흐름 회귀 확인**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive/Sources/V2/CreatorChannel/Community/CreatorChannelCommunityTabView.swift`
|
|
- 작업 내용:
|
|
- 현재 댓글 버튼이 여는 `CreatorCommunityCommentListView` sheet가 유지되는지 확인한다.
|
|
- sheet 내부 댓글 item에서 새 답글 상세 route로 진입 가능한지 확인한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "CreatorCommunityCommentListView|isShowCommentListView|NavigationStack|creatorChannelReplyDetail" SodaLive/Sources/V2/CreatorChannel/Community/CreatorChannelCommunityTabView.swift SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment`
|
|
- 기대 결과: V2 커뮤니티 탭에서 댓글 목록 진입과 답글 상세 진입 경로가 함께 확인된다.
|
|
|
|
### Phase 5: 팬Talk 진입 연결
|
|
|
|
- [x] **Task 5.1: 팬Talk item tap action 추가**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift`
|
|
- 수정: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
|
|
- 작업 내용:
|
|
- `CreatorChannelFanTalkListItem`에 `onTapDetail` closure를 추가한다.
|
|
- `CreatorChannelFanTalkTabView`에서 `isOwnCreatorChannel == true`일 때만 detail tap action을 route로 연결한다.
|
|
- 본인 채널이 아니면 item tap으로 상세 화면에 진입하지 않는다.
|
|
- 더보기/신고 버튼 tap은 상세 진입과 충돌하지 않게 button action으로 유지한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "onTapDetail|isOwnCreatorChannel|creatorChannelReplyDetail|case \\.fanTalk|contentShape|Button" SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
|
|
- 기대 결과: 팬Talk 본인 채널에서만 상세 진입 route가 생성된다.
|
|
|
|
- [x] **Task 5.2: 팬Talk 성공 후 첫 페이지 재조회 callback 연결**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift`
|
|
- 수정: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- 작업 내용:
|
|
- 팬Talk detail context 생성 시 `onFanTalkRefresh`에 `viewModel.fetchFirstPage(creatorId:)`를 전달한다.
|
|
- 팬Talk 답글 작성/수정/삭제 성공 후 callback을 호출한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "onFanTalkRefresh|fetchFirstPage\\(creatorId: creatorId\\)|writeCheers|modifyCheers" SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- 기대 결과: 팬Talk 답글 mutation 성공 후 첫 페이지 재조회 연결이 확인된다.
|
|
|
|
- [x] **Task 5.3: 팬Talk 답글 작성 응답 직접 binding 전환**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift`
|
|
- 수정: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailDisplayItem.swift`
|
|
- 작업 내용:
|
|
- 팬Talk 답글 작성/수정 성공 응답을 `ApiResponse<CreatorChannelFanTalkReplyResponse>`로 decode한다.
|
|
- 성공 응답의 `data`를 현재 답글 상세 화면의 `replies`에 바로 binding한다.
|
|
- 팬Talk 전체 목록을 다시 읽어서 parent `fanTalkId`에 해당하는 답글을 찾는 재조회 경로를 mutation 성공 binding에 사용하지 않는다.
|
|
- 팬Talk 목록 갱신 callback은 상위 목록 최신화를 위해 유지한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "ApiResponse<CreatorChannelFanTalkReplyResponse>|applyFanTalkReply|fetchFanTalkReplies|getFanTalks" SodaLive/Sources/V2/CreatorChannel/ReplyDetail`
|
|
- 기대 결과: 팬Talk mutation 성공 응답 data를 직접 display item으로 변환하고, mutation 성공 경로에서 전체 팬Talk 재조회가 호출되지 않는다.
|
|
|
|
### Phase 6: 문구, asset, 프로젝트 등록
|
|
|
|
- [x] **Task 6.1: I18n 문구 추가**
|
|
- 대상 파일:
|
|
- 수정: `SodaLive/Sources/I18n/I18n.swift`
|
|
- 작업 내용:
|
|
- `I18n.CreatorChannelReplyDetail.replyPlaceholder`에 `답글을 입력하세요` ko/en/ja 문구를 추가한다.
|
|
- 수정/삭제 문구는 기존 공통 문구를 우선 재사용하고, 새 scope 문구가 필요하면 같은 enum에 추가한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "CreatorChannelReplyDetail|replyPlaceholder|답글을 입력하세요" SodaLive/Sources/I18n/I18n.swift`
|
|
- 기대 결과: 답글 placeholder 다국어 문구가 확인된다.
|
|
|
|
- [x] **Task 6.2: asset catalog와 프로젝트 파일 등록**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive/Resources/Assets.xcassets/v2/ic_arrow_up_gray.imageset/Contents.json`
|
|
- 확인: `SodaLive/Resources/Assets.xcassets/v2/ic_arrow_up_white.imageset/Contents.json`
|
|
- 수정: `SodaLive.xcodeproj/project.pbxproj`
|
|
- 작업 내용:
|
|
- `ic_arrow_up_gray`, `ic_arrow_up_white` asset이 Xcode asset catalog에 존재하는지 확인한다.
|
|
- 신규 Swift 파일이 앱 target에 포함되도록 프로젝트 파일을 등록한다.
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "ic_arrow_up_gray|ic_arrow_up_white" SodaLive/Resources/Assets.xcassets/v2 SodaLive.xcodeproj/project.pbxproj`
|
|
- 실행 명령: `rg "CreatorChannelReplyDetailView|CreatorChannelReplyDetailViewModel|CreatorChannelReplyDetailInputBar" SodaLive.xcodeproj/project.pbxproj`
|
|
- 기대 결과: arrow asset과 신규 Swift 파일 target 등록이 확인된다.
|
|
|
|
### Phase 7: 빌드와 수동 검증
|
|
|
|
- [x] **Task 7.1: 정적 검색 검증**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/**`
|
|
- 확인: `SodaLive/Sources/V2/CreatorChannel/FanTalk/**`
|
|
- 확인: `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/**`
|
|
- 검증 기준:
|
|
- 실행 명령: `rg "CreatorChannelReplyDetail|createCommunityPostComment|writeCheers|modifyCheers|ic_arrow_up_gray|ic_arrow_up_white|isSecret|onFanTalkRefresh" SodaLive/Sources`
|
|
- 기대 결과: 답글 상세 화면, 기존 API 재사용, send icon, 비밀 답글, 팬Talk refresh callback이 모두 확인된다.
|
|
|
|
- [x] **Task 7.2: Debug 빌드 검증**
|
|
- 대상 파일:
|
|
- 확인: `SodaLive.xcworkspace`
|
|
- 검증 기준:
|
|
- 실행 명령: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build`
|
|
- 기대 결과: `** BUILD SUCCEEDED **`
|
|
|
|
- [ ] **Task 7.3: 수동 UI 검증**
|
|
- 대상 파일:
|
|
- 확인: 앱 런타임
|
|
- 검증 기준:
|
|
- 커뮤니티 댓글 item 터치 시 본인 여부와 관계없이 답글 상세 화면에 진입한다.
|
|
- 팬Talk item 터치 시 본인 채널에서만 답글 상세 화면에 진입한다.
|
|
- 답글 없음 상태가 Figma `669:40462`와 같은 정보 구조로 표시된다.
|
|
- 답글 있음 상태가 Figma `669:40468`와 같은 답글 카드/연결선 구조로 표시된다.
|
|
- 입력값이 없으면 send 버튼이 `gray900` + `ic_arrow_up_gray` 상태이고 API를 호출하지 않는다.
|
|
- 입력값이 있으면 send 버튼이 `soda400` + `ic_arrow_up_white` 상태이고 기존 답글 작성 API를 호출한다.
|
|
- 커뮤니티 비밀 원댓글에 답글 작성 시 `isSecret == true`로 전송된다.
|
|
- 답글 `수정하기` 터치 시 하단 입력창에 기존 답글 내용이 채워지고 send 시 수정 API가 호출된다.
|
|
- 답글 `삭제하기` confirm 시 삭제 API가 호출되고 목록이 갱신된다.
|
|
- 팬Talk 답글 작성/수정/삭제 성공 후 팬Talk 목록 첫 페이지가 재조회된다.
|
|
|
|
## 검증 기록
|
|
|
|
- 2026-07-07: PRD `docs/20260707_글_상세_답글_쓰기/prd.md`, 문서 정책, 빌드/검증 가이드, 기존 팬Talk plan-task 형식을 확인하고 계획 문서를 작성했다.
|
|
- 2026-07-07: 답글 상세 구현, 정적 검색 검증, `SodaLive-dev` Debug 빌드 검증을 완료했다. 런타임 수동 UI 검증(Task 7.3)은 미수행 상태로 남겼다.
|
|
- 2026-07-07: 리뷰 지적 사항을 반영해 팬Talk 임시 답글 row 생성을 제거하고 FanTalk 수정/삭제에 `id > 0` guard를 추가했다. 답글 action popup은 row frame 기반 위치로 변경했고, popup 문구는 `I18n.Explorer.edit`, `I18n.Common.delete`를 사용하도록 수정했다. 정적 검색, ast-grep 검색, `SodaLive-dev` Debug 빌드 검증을 완료했다.
|
|
- 2026-07-07: 추가 리뷰 지적 사항을 반영해 FanTalk 답글 갱신이 parent `fanTalkId`를 찾을 때까지 `hasNext` 기반으로 페이지를 조회하도록 수정했다. 커뮤니티 답글 상세는 `totalCount` 기반 pagination과 마지막 답글 `onAppear` 추가 조회를 연결했고, 커뮤니티 댓글 item의 전체 row tap은 제거해 본문/답글 텍스트만 답글 상세로 진입하도록 제한했다. 정적 검색, ast-grep 검색, `SodaLive-dev` Debug 빌드 검증을 완료했다.
|
|
- 2026-07-09: 팬Talk 답글 작성/수정 성공 응답이 `ApiResponse<CreatorChannelFanTalkReplyResponse>`로 내려오는 변경을 반영해, 답글 상세 화면에서 전체 팬Talk 목록을 재조회하지 않고 응답 `data`를 현재 화면 답글로 즉시 binding하도록 계획과 구현을 갱신했다.
|