33 KiB
커뮤니티 게시글 상세 구현 계획
For agentic workers: PRD를 기준으로 task 단위로 진행한다. 구현 중 요구사항 변경이 생기면 먼저
docs/20260707_커뮤니티_게시글_상세/prd.md를 보강하고 이 문서의 체크리스트를 갱신한다.
Goal: V2 커뮤니티 게시글 상세 화면을 추가하고, 상세/댓글/답글 조회 API와 기존 커뮤니티 mutation API를 연결한다.
Architecture: 상세/댓글/답글 조회는 SodaLive/Sources/V2/CreatorChannel/Community/**의 V2 API/Repository에 추가한다. 상세 화면 상태는 신규 Community/Detail ViewModel이 관리하고, 댓글 작성/수정/삭제/좋아요 mutation은 기존 CreatorCommunityRepository를 재사용한다. 댓글의 답글 상세 화면은 기존 CreatorChannelReplyDetailView 계열을 복사하지 않고 context/display adapter를 확장해 재사용한다.
Tech Stack: SwiftUI, Combine, Moya, Kingfisher/SDWebImageSwiftUI, 기존 AppStep/AppState, 기존 V2 design token(Color, SodaSpacing, appFont)
기준 문서
- PRD:
docs/20260707_커뮤니티_게시글_상세/prd.md - 문서 정책:
docs/agent-guides/documentation-policy.md - 빌드/검증:
docs/agent-guides/build-test-verification.md - 커뮤니티 탭 PRD:
docs/20260705_크리에이터_채널_커뮤니티_탭/prd.md - 글 상세 - 답글 쓰기 PRD:
docs/20260707_글_상세_답글_쓰기/prd.md - Figma 댓글 가능 상세:
290:9243,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-9243&m=dev - Figma 댓글 불가능 상세:
290:9266,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-9266&m=dev
구현 원칙
- 신규 상세 화면 파일은
SodaLive/Sources/V2/CreatorChannel/Community/Detail/**아래에 둔다. - 신규 조회 모델은
SodaLive/Sources/V2/CreatorChannel/Community/Models/**아래에 둔다. - 기존
CreatorChannelCommunityApi.getCommunity의page=0정책을 유지하고, 신규 V2 조회 API에도page - 1보정을 넣지 않는다. - 댓글 작성/답글 작성/댓글 수정/댓글 삭제/좋아요 API는 기존
CreatorCommunityRepository를 사용한다. - 댓글 불가능 게시글은 좋아요 UI는 유지하고, 댓글 icon/count, 댓글 목록, 하단 댓글 입력 바를 숨긴다.
- 댓글 입력창은 3줄까지 자동 확장하고 4줄 이상은 내부 스크롤로 처리한다.
- 전송 버튼 icon은
ic_arrow_up_gray,ic_arrow_up_white를 사용한다. - 댓글 쓰기 UI에는 기존 비밀댓글 toggle을 추가하고, 기존
isShowSecret노출 정책을 따른다. - 답글 상세 UI는 기존
SodaLive/Sources/V2/CreatorChannel/ReplyDetail/**구현을 재사용한다.
주요 대상 파일
생성
SodaLive/Sources/V2/CreatorChannel/Community/Models/CreatorChannelCommunityPostDetailResponse.swiftSodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailView.swiftSodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swiftSodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailContentView.swiftSodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentListView.swiftSodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentRow.swiftSodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailInputBar.swift
수정
SodaLive/Sources/V2/CreatorChannel/Community/Repository/CreatorChannelCommunityApi.swiftSodaLive/Sources/V2/CreatorChannel/Community/Repository/CreatorChannelCommunityRepository.swiftSodaLive/Sources/V2/CreatorChannel/Community/CreatorChannelCommunityTabView.swiftSodaLive/Sources/V2/CreatorChannel/Community/Components/CreatorChannelCommunityListItem.swiftSodaLive/Sources/V2/CreatorChannel/Community/Components/CreatorChannelCommunityGridItem.swiftSodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailContext.swiftSodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailDisplayItem.swiftSodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swiftSodaLive/Sources/App/AppStep.swiftSodaLive/Sources/ContentView.swiftSodaLive/Sources/I18n/I18n.swiftSodaLive.xcodeproj/project.pbxproj
확인/재사용
SodaLive/Sources/V2/CreatorChannel/Community/Components/CreatorChannelCommunityListItem.swiftSodaLive/Sources/V2/Component/Card/CommunityPostCard.swiftSodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swiftSodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityRepository.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/ModifyCommunityPostCommentRequest.swift
TASK 체크리스트
Phase 1: 조회 모델과 API
-
Task 1.1: 커뮤니티 게시글 상세 응답 모델 생성
- 대상 파일:
- 생성:
SodaLive/Sources/V2/CreatorChannel/Community/Models/CreatorChannelCommunityPostDetailResponse.swift
- 생성:
- 작업 내용:
CreatorChannelCommunityPostDetailResponse를Decodable,Identifiable로 정의한다.CreatorChannelCommunityCommentsResponse,CreatorChannelCommunityCommentResponse,CreatorChannelCommunityReplyResponse,CreatorChannelCommunityRepliesResponse를 정의한다.- Kotlin
Long필드는 SwiftInt로 선언한다. CreatorChannelCommunityCommentResponse와CreatorChannelCommunityReplyResponse에는writerId: Int를 포함한다.- 각 시간 필드는
DateParser.relativeTimeText(fromUTC:fallback:now:)를 사용하는 helper를 제공한다.
- 검증 기준:
- 실행 명령:
rg "struct CreatorChannelCommunityPostDetailResponse|struct CreatorChannelCommunityCommentsResponse|struct CreatorChannelCommunityCommentResponse|writerId|struct CreatorChannelCommunityRepliesResponse|relativeTimeText" SodaLive/Sources/V2/CreatorChannel/Community/Models/CreatorChannelCommunityPostDetailResponse.swift - 기대 결과: 상세/댓글/답글 모델과
writerId, 상대 시간 helper가 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 1.2: 커뮤니티 게시글 상세/댓글/답글 조회 API 추가
- 대상 파일:
- 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Repository/CreatorChannelCommunityApi.swift - 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Repository/CreatorChannelCommunityRepository.swift
- 수정:
- 작업 내용:
CreatorChannelCommunityApi에getCommunityPostDetail(postId:),getCommunityPostComments(postId:page:size:),getCommunityCommentReplies(commentId:page:size:)case를 추가한다.- path는 각각
/api/v2/creator-channels/community-posts/{postId},/api/v2/creator-channels/community-posts/{postId}/comments,/api/v2/creator-channels/community-comments/{commentId}/replies로 구성한다. - 댓글/답글 query는
page,size를 그대로 전달하고page - 1보정을 하지 않는다. - Repository에 동일 이름의 publisher 함수를 추가한다.
- 검증 기준:
- 실행 명령:
rg "getCommunityPostDetail|getCommunityPostComments|getCommunityCommentReplies|community-posts|community-comments|URLEncoding.queryString" SodaLive/Sources/V2/CreatorChannel/Community/Repository - 기대 결과: 신규 endpoint 3개와 query 전달이 확인된다.
- 실행 명령:
- 대상 파일:
Phase 2: 라우팅과 목록 진입
-
Task 2.1: 게시글 상세 route 추가
- 대상 파일:
- 수정:
SodaLive/Sources/App/AppStep.swift - 수정:
SodaLive/Sources/ContentView.swift
- 수정:
- 작업 내용:
AppStep에creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)case를 추가한다.ContentViewswitch에서CreatorChannelCommunityPostDetailView(postId:onCommunityRefresh:)를 반환한다.onCommunityRefresh는 상세 mutation 성공 후 커뮤니티 탭 첫 페이지를 갱신하기 위한 optional callback으로 둔다.
- 검증 기준:
- 실행 명령:
rg "creatorChannelCommunityPostDetail|CreatorChannelCommunityPostDetailView|onCommunityRefresh" SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift - 기대 결과: 새 route와 화면 연결이 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 2.2: 커뮤니티 리스트/그리드 item tap에서 상세 진입 연결
- 대상 파일:
- 수정:
SodaLive/Sources/V2/CreatorChannel/Community/CreatorChannelCommunityTabView.swift - 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Components/CreatorChannelCommunityListItem.swift - 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Components/CreatorChannelCommunityGridItem.swift
- 수정:
- 작업 내용:
- 리스트 item과 그리드 item에
onTapDetailclosure를 추가한다. - item 전체 tap은 상세 route로 연결한다.
- 좋아요, 댓글, 더보기, 구매, 오디오 재생 버튼 tap은 상세 진입과 충돌하지 않게 button action으로 유지한다.
- 상세 route에는
post.postId와viewModel.fetchFirstPage(creatorId:)callback을 전달한다.
- 리스트 item과 그리드 item에
- 검증 기준:
- 실행 명령:
rg "onTapDetail|creatorChannelCommunityPostDetail|fetchFirstPage\\(creatorId: creatorId\\)|contentShape|Button" SodaLive/Sources/V2/CreatorChannel/Community - 기대 결과: 리스트/그리드 item tap 상세 진입과 내부 버튼 action 분리가 확인된다.
- 실행 명령:
- 대상 파일:
Phase 3: 상세 ViewModel과 mutation 재사용
-
Task 3.1: 상세 ViewModel 기본 상태와 상세 조회 구현
- 대상 파일:
- 생성:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift
- 생성:
- 작업 내용:
- 상태로
detail,comments,commentText,isSecret,isLoading,isLoadingNextPage,hasLoaded,errorMessage,isShowPopup,selectedComment,editingComment,deletingComment를 둔다. fetchDetail(postId:)에서 상세 API를 호출하고 게시글과 초기 댓글 목록을 구성한다.isCommentAvailable == false이면 댓글 입력/목록 상태를 비활성 상태로 유지한다.isShowSecretcomputed property는 기존 정책인price > 0 && existOrdered && creatorId != UserDefaults.int(forKey: .userId)를 따른다.isCommentSendEnabled는 trim된commentText가 비어 있지 않고 로딩 중이 아닐 때 true로 계산한다.
- 상태로
- 검증 기준:
- 실행 명령:
rg "final class CreatorChannelCommunityPostDetailViewModel|fetchDetail|detail|comments|commentText|isSecret|isShowSecret|isCommentSendEnabled|getCommunityPostDetail" SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift - 기대 결과: 상세 조회와 댓글 입력 상태가 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 3.2: 댓글 페이지네이션 구현
- 대상 파일:
- 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift
- 수정:
- 작업 내용:
fetchNextCommentsIfNeeded(currentComment:)를 추가한다.- 마지막 댓글 노출 시
hasNext == true이면page + 1로 댓글 API를 호출한다. - 중복 호출을 막기 위해
isLoadingNextPage와isLoadingguard를 둔다. - 첫 페이지 재조회 시 기존 댓글 목록을 교체하고, 다음 페이지 성공 시 append한다.
- 검증 기준:
- 실행 명령:
rg "fetchNextCommentsIfNeeded|getCommunityPostComments|isLoadingNextPage|hasNext|page \\+ 1|append\\(contentsOf:" SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift - 기대 결과: 댓글 추가 조회와 append 로직이 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 3.3: 좋아요 mutation과 optimistic state 구현
- 대상 파일:
- 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift
- 수정:
- 작업 내용:
toggleLike()에서 기존CreatorCommunityRepository.communityPostLike(postId:)를 호출한다.- tap 직후
isLiked와likeCount를 local state에 반영한다. - 실패 시 toast를 표시하고 tap 직전의
isLiked,likeCount값으로 local state를 복구한다.
- 검증 기준:
- 실행 명령:
rg "toggleLike|communityPostLike|isLiked|likeCount|errorMessage|isShowPopup" SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift - 기대 결과: 기존 좋아요 API와 local state 반영이 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 3.4: 댓글 작성/수정/삭제 mutation 구현
- 대상 파일:
- 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift
- 수정:
- 작업 내용:
- 신규 댓글 작성은
CreatorCommunityRepository.createCommunityPostComment(comment:postId:parentId:isSecret:)를 사용하고parentId == nil,isSecret == 비밀댓글 toggle 값으로 호출한다. - 댓글 수정은
ModifyCommunityPostCommentRequest(commentId:)에 수정 content를 담아modifyComment(request:)를 호출한다. - 댓글 삭제는
ModifyCommunityPostCommentRequest(commentId:)에isActive = false를 담아modifyComment(request:)를 호출한다. - 성공 후 입력값과 수정/삭제 선택 상태를 초기화하고 상세 또는 댓글 첫 페이지를 재조회한다.
- mutation 성공 시
onCommunityRefreshcallback을 호출해 목록 갱신을 예약한다.
- 신규 댓글 작성은
- 검증 기준:
- 실행 명령:
rg "createCommunityPostComment|parentId: nil|isSecret|ModifyCommunityPostCommentRequest|modifyComment|isActive = false|onCommunityRefresh|commentText = \"\"" SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift - 기대 결과: 기존 커뮤니티 댓글 mutation API 재사용이 확인된다.
- 실행 명령:
- 대상 파일:
Phase 4: 상세 UI 컴포넌트
-
Task 4.1: 상세 게시글 콘텐츠 컴포넌트 구현
- 대상 파일:
- 생성:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailContentView.swift
- 생성:
- 작업 내용:
- Figma 기준으로 작성자 프로필, 닉네임, 상대 시간, 본문, 이미지/잠금/오디오 영역, 좋아요/댓글 reaction을 표시한다.
- 유료 미구매 상태는
price > 0 && existOrdered == false && isOwnPost == false로 판정한다. - 유료 미구매 상태에서는
gray800계열 이미지 영역, 잠금 아이콘, 캐시 가격 capsule을 표시한다. - 구매 완료 상태는
구매완료capsule tag를 표시한다. - 접근 가능한 이미지/GIF는 기존
WebImage또는DownsampledKFImage패턴을 따른다. audioUrl이 있으면 기존CreatorCommunityMediaPlayerManager와ContentPlayManager흐름을 재사용한다.isCommentAvailable == false이면 댓글 icon/count만 숨기고 좋아요는 유지한다.
- 검증 기준:
- 실행 명령:
rg "CreatorChannelCommunityPostDetailContentView|ic_feed_community_heart_fill|ic_feed_community_heart|ic_feed_community_reply|ic_new_community_lock|ic_bar_cash|구매완료|CreatorCommunityMediaPlayerManager|isCommentAvailable" SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailContentView.swift - 기대 결과: 게시글 상세 표시, 좋아요, 댓글 숨김, 잠금/구매완료 UI가 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 4.2: 댓글 row와 최신 답글 카드 구현
- 대상 파일:
- 생성:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentRow.swift
- 생성:
- 작업 내용:
- 댓글 row는 42pt 프로필, 닉네임, 상대 시간, 본문, 우측 더보기 버튼을 표시한다.
writerId == UserDefaults.int(forKey: .userId)로 내 댓글 여부를 판정한다.post.creatorId == UserDefaults.int(forKey: .userId)로 현재 사용자가 해당 게시글을 쓴 크리에이터인지 판정한다.latestReply != nil이면 20pt 프로필, 닉네임, 상대 시간, 본문을 가진Color.gray900, radius 14pt 답글 카드를 표시한다.- 최신 답글이 있으면 기존 팬Talk/ReplyDetail connector와 같은 좌측 연결선을 표시한다.
- row tap은 답글 상세 진입 action을 호출한다.
- 더보기 버튼 tap은 popup/action 선택으로 연결하고 row tap과 충돌하지 않게 한다.
- 검증 기준:
- 실행 명령:
rg "CreatorChannelCommunityPostDetailCommentRow|latestReply|writerId|creatorId|UserDefaults.int|Color\\.gray900|RoundedRectangle|Shape|Path|onTapReplyDetail|onTapMore" SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentRow.swift - 기대 결과: 댓글 row, 최신 답글 카드, 연결선, action 분리가 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 4.3: 댓글 목록 컴포넌트 구현
- 대상 파일:
- 생성:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentListView.swift
- 생성:
- 작업 내용:
- section header는
댓글 {commentCount}를 표시한다. - 댓글 목록은
LazyVStack으로 표시하고 마지막 댓글onAppear에서 다음 페이지 조회를 요청한다. - 댓글이 비어 있으면 header만 유지하거나 Figma 기준 빈 목록 영역을 유지한다.
- 선택된 댓글의 popup은 기존
CreatorChannelActionPopup또는CreatorChannelReplyDetailActionPopup기반으로 수정/삭제 action을 표시한다.
- section header는
- 검증 기준:
- 실행 명령:
rg "CreatorChannelCommunityPostDetailCommentListView|댓글|commentCount|LazyVStack|onAppear|fetchNext|CreatorChannelActionPopup|onTapEdit|onTapDelete" SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentListView.swift - 기대 결과: 댓글 목록, 페이지네이션 trigger, popup action 연결이 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 4.4: 하단 댓글 입력 바 구현
- 대상 파일:
- 생성:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailInputBar.swift - 수정:
SodaLive/Sources/I18n/I18n.swift
- 생성:
- 작업 내용:
- placeholder는
댓글을 입력하세요를 사용한다. - 기존 비밀댓글 toggle UI를 추가하고
isShowSecret일 때만 표시한다. - 입력창은 1줄에서 3줄까지 자동 확장한다.
- 4줄 이상은 3줄 높이로 유지하고 내부 vertical scroll을 허용한다.
- 전송 버튼은 비활성 상태에서
ic_arrow_up_gray/gray900, 활성 상태에서ic_arrow_up_white/soda400를 사용한다. - 수정 모드에서는 취소 버튼 또는 기존 입력 복귀 UX를 최소 범위로 제공한다.
- placeholder는
- 검증 기준:
- 실행 명령:
rg "CreatorChannelCommunityPostDetailInputBar|댓글을 입력하세요|isShowSecret|isSecret|ic_arrow_up_gray|ic_arrow_up_white|Color\\.gray900|Color\\.soda400|axis: \\.vertical|lineLimit" SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailInputBar.swift SodaLive/Sources/I18n/I18n.swift - 기대 결과: placeholder, 비밀댓글 toggle, 3줄 확장, 전송 버튼 상태가 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 4.5: 상세 화면 조립
- 대상 파일:
- 생성:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailView.swift
- 생성:
- 작업 내용:
CreatorChannelReplyDetailTitleBar를 재사용해 back-only title bar를 표시한다.ScrollView안에 게시글 콘텐츠와 댓글 목록을 배치한다.isCommentAvailable == true일 때만 하단 입력 바를safeAreaInset(edge: .bottom)으로 고정한다.- loading, toast, 삭제 confirm modal을 연결한다.
onAppear에서viewModel.fetchDetail(postId:)를 호출한다.
- 검증 기준:
- 실행 명령:
rg "CreatorChannelCommunityPostDetailView|CreatorChannelReplyDetailTitleBar|CreatorChannelCommunityPostDetailContentView|CreatorChannelCommunityPostDetailCommentListView|CreatorChannelCommunityPostDetailInputBar|safeAreaInset|fetchDetail|SodaV2ActionModal|sodaToast" SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailView.swift - 기대 결과: 상세 화면 조립과 댓글 가능 여부별 하단 입력 바 조건이 확인된다.
- 실행 명령:
- 대상 파일:
Phase 5: 답글 상세 재사용 확장
-
Task 5.1: 신규 댓글 응답을 답글 상세 context로 전달할 수 있게 확장
- 대상 파일:
- 수정:
SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailContext.swift - 수정:
SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailDisplayItem.swift - 수정:
SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift
- 수정:
- 작업 내용:
- 기존
.community(creatorId:postId:parentComment: GetCommunityPostCommentListItem)경로는 유지한다. - 신규 상세 댓글 응답을 전달할 수 있는 context case 또는 adapter를 추가한다.
CreatorChannelCommunityCommentResponse는commentId,writerId,writerNickname,writerProfileImageUrl,content,createdAtUtc로 display item을 만든다.- 신규 응답에는
isSecret이 없으므로 원댓글 표시용isSecret은 false로 처리하고, 신규 댓글 입력의 비밀 여부는 상세 댓글 작성 UI에서만 다룬다.
- 기존
- 검증 기준:
- 실행 명령:
rg "CreatorChannelCommunityCommentResponse|communityPostDetail|commentId|writerId|writerProfileImageUrl|DateParser.relativeTimeText|isSecret: false" SodaLive/Sources/V2/CreatorChannel/ReplyDetail - 기대 결과: 신규 댓글 응답에서 답글 상세 display item을 만들 수 있다.
- 실행 명령:
- 대상 파일:
-
Task 5.2: 답글 상세에서 신규 답글 조회 API 사용
- 대상 파일:
- 수정:
SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift
- 수정:
- 작업 내용:
- 신규 상세 댓글 context에서는
CreatorChannelCommunityRepository.getCommunityCommentReplies(commentId:page:size:)를 사용한다. - 기존 V1 댓글 context는 기존
CreatorCommunityRepository.getCommentReplyList(commentId:page:size:)경로를 유지한다. - 신규
CreatorChannelCommunityReplyResponse를CreatorChannelReplyDetailDisplayItem으로 변환한다. hasNext,page,size를 사용해 답글 페이지네이션을 처리한다.
- 신규 상세 댓글 context에서는
- 검증 기준:
- 실행 명령:
rg "getCommunityCommentReplies|CreatorChannelCommunityRepliesResponse|CreatorChannelCommunityReplyResponse|hasNext|communityReplyPage|fetchNextCommunityRepliesIfNeeded" SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift - 기대 결과: 신규 답글 조회 API와 기존 V1 답글 조회 경로가 분리되어 확인된다.
- 실행 명령:
- 대상 파일:
-
Task 5.3: 댓글 row tap에서 답글 상세 route 연결
- 대상 파일:
- 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailView.swift - 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentRow.swift - 수정:
SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift - 수정:
SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift - 수정:
SodaLive/Sources/App/AppStep.swift - 수정:
SodaLive/Sources/ContentView.swift
- 수정:
- 작업 내용:
- 댓글 row tap 시
AppState.shared.setAppStep(step: .creatorChannelReplyDetail(...))을 호출한다. - context에는
creatorId,postId, 선택 댓글 응답을 전달한다. CreatorChannelReplyDetailView에서 커뮤니티 답글 mutation 성공 시 호출할onCommunityRefreshcallback을 추가하고, 게시글 상세 진입 시viewModel.fetchDetail(postId:)를 전달한다.
- 댓글 row tap 시
- 검증 기준:
- 실행 명령:
rg "creatorChannelReplyDetail|CreatorChannelReplyDetailContext|CreatorChannelCommunityCommentResponse|onTapReplyDetail|onCommunityRefresh|postId|creatorId" SodaLive/Sources/V2/CreatorChannel/Community/Detail SodaLive/Sources/V2/CreatorChannel/ReplyDetail SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift - 기대 결과: 상세 댓글에서 기존 답글 상세 화면으로 진입하는 route가 확인된다.
- 실행 명령:
- 대상 파일:
Phase 6: 권한, 문구, 프로젝트 등록
-
Task 6.1: 댓글 popup 권한과 수정/삭제 모드 연결, 신고 차단 기록
- 대상 파일:
- 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift - 수정:
SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentListView.swift
- 수정:
- 작업 내용:
- 내 댓글/답글이면
수정하기,삭제하기를 표시한다. - 내가 해당 커뮤니티 게시글을 쓴 크리에이터이면 내 댓글/답글이 아니어도
삭제하기를 표시한다. 수정하기tap 시 하단 입력창에 기존 댓글 내용을 채우고 send 시 수정 API를 호출한다.삭제하기tap 시SodaV2ActionModal로 삭제 확인을 표시한다.- [blocked] 신고는 기존
ReportRequest/ReportType에 커뮤니티 댓글 신고 식별자가 없고, 기존CreatorCommunityCommentItemView도 신고 action을 노출하지 않아 연결하지 않는다. 백엔드/API 계약에 community comment report identifier가 추가된 뒤 별도 구현한다.
- 내 댓글/답글이면
- 검증 기준:
- 실행 명령:
rg "writerId ==|creatorId ==|currentUserId|canEdit|canDelete|beginEditing|editingComment|deletingComment|SodaV2ActionModal|confirmDelete|CreatorChannelActionPopup" SodaLive/Sources/V2/CreatorChannel/Community/Detail - 기대 결과: 작성자 수정/삭제 권한, 게시글 크리에이터 삭제 권한, 수정 모드가 확인된다. 신고 action은 community comment report identifier API 계약 부재로 노출하지 않는다.
- 실행 명령:
- 대상 파일:
-
Task 6.2: I18n 문구와 asset 참조 확인
- 대상 파일:
- 수정:
SodaLive/Sources/I18n/I18n.swift - 확인:
SodaLive/Resources/Assets.xcassets/v2/ic_arrow_up_gray.imageset/Contents.json - 확인:
SodaLive/Resources/Assets.xcassets/v2/ic_arrow_up_white.imageset/Contents.json
- 수정:
- 작업 내용:
I18n.CreatorChannelCommunity.commentPlaceholder또는 같은 범위의 문구로댓글을 입력하세요ko/en/ja를 추가한다.- 비밀댓글 toggle 문구는 기존
I18n.Explorer.secretComment를 재사용한다. ic_arrow_up_gray,ic_arrow_up_whiteasset이 존재하는지 확인하고 다른 arrow asset 이름을 참조하지 않는다.
- 검증 기준:
- 실행 명령:
rg "commentPlaceholder|댓글을 입력하세요|secretComment|ic_arrow_up_gray|ic_arrow_up_white" SodaLive/Sources/I18n/I18n.swift SodaLive/Sources/V2/CreatorChannel/Community/Detail SodaLive/Resources/Assets.xcassets/v2 - 기대 결과: 문구와 icon 참조가 확정된 asset 이름과 일치한다.
- 실행 명령:
- 대상 파일:
-
Task 6.3: 프로젝트 파일 등록 확인
- 대상 파일:
- 확인/수정:
SodaLive.xcodeproj/project.pbxproj
- 확인/수정:
- 작업 내용:
- 신규 Swift 파일이 Xcode target에 포함되는지 확인한다.
- 수동 등록이 필요한 경우 기존 V2 파일 등록 패턴을 따라 최소 변경으로 추가한다.
- 검증 기준:
- 실행 명령:
rg "CreatorChannelCommunityPostDetailView|CreatorChannelCommunityPostDetailViewModel|CreatorChannelCommunityPostDetailResponse|CreatorChannelCommunityPostDetailInputBar" SodaLive.xcodeproj/project.pbxproj - 기대 결과: 신규 Swift 파일의 project 등록이 확인된다.
- 실행 명령:
- 대상 파일:
Phase 7: 최종 검증
-
Task 7.1: 정적 문서/파일 검증
- 대상 파일:
- 확인:
docs/20260707_커뮤니티_게시글_상세/prd.md - 확인:
docs/20260707_커뮤니티_게시글_상세/plan-task.md - 확인: 구현 변경 파일 전체
- 확인:
- 검증 기준:
- 실행 명령:
git diff --check -- docs/20260707_커뮤니티_게시글_상세 SodaLive/Sources/V2/CreatorChannel/Community SodaLive/Sources/V2/CreatorChannel/ReplyDetail SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift SodaLive/Sources/I18n/I18n.swift SodaLive.xcodeproj/project.pbxproj - 기대 결과: 공백 오류 없이 성공한다.
- 실행 명령:
plutil -lint SodaLive.xcodeproj/project.pbxproj - 기대 결과:
OK가 출력된다.
- 실행 명령:
- 대상 파일:
-
Task 7.2: 빌드 검증
- 대상 파일:
- 확인: 전체 앱 target
- 검증 기준:
- 실행 명령:
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build - 기대 결과: Debug 빌드 성공.
- 참고: 테스트 번들 타깃이 확인되지 않는 저장소 상태이므로, 단위 테스트가 추가되지 않은 경우 빌드 검증을 우선 성공 기준으로 둔다.
- 실행 명령:
- 대상 파일:
-
Task 7.3: 수동 QA 시나리오 기록
- 대상 파일:
- 수정:
docs/20260707_커뮤니티_게시글_상세/plan-task.md
- 수정:
- 검증 기준:
- 댓글 가능 게시글 상세 진입 시 상세 API가 호출되고 게시글/댓글/입력 바가 표시된다.
- 댓글 불가능 게시글 상세 진입 시 좋아요는 표시되고 댓글 icon/count, 댓글 목록, 입력 바는 숨겨진다.
- 댓글 입력값이 없으면
ic_arrow_up_gray/gray900, 입력값이 있으면ic_arrow_up_white/soda400가 표시된다. - 4줄 이상 입력 시 입력창 높이가 3줄로 유지되고 내부 스크롤된다.
- 비밀댓글 toggle 노출 조건에서 toggle 값이 댓글 작성 API의
isSecret으로 전달된다. - 댓글 row tap 시 기존 답글 상세 화면으로 진입하고 신규 답글 조회 API가 호출된다.
- 게시글을 쓴 크리에이터 계정에서는 내가 작성하지 않은 댓글/답글에도
삭제하기가 표시된다. - 작성자도 게시글 크리에이터도 아닌 계정에서는 신고 action이 표시되지 않는다. [blocked] 기존
ReportRequest/ReportType에 community comment report identifier가 없어 백엔드/API 계약 확정 전까지 수동 QA 대상에서 제외한다. - 하트 tap 시 기존 좋아요 API가 호출되고 icon/count가 갱신된다.
- 실행 후 이 문서의
검증 기록섹션에 실행 일자, 명령, 결과를 누적한다.
- 대상 파일:
검증 기록
- 2026-07-07:
docs/20260707_커뮤니티_게시글_상세/prd.md,docs/agent-guides/build-test-verification.md, 기존docs/20260707_글_상세_답글_쓰기/plan-task.md,docs/20260705_크리에이터_채널_커뮤니티_탭/plan-task.md를 확인해 구현 범위와 문서 형식을 정했다. - 2026-07-07: 현재 코드 기준으로
AppStep.creatorChannelReplyDetail,CreatorChannelReplyDetailView, V2 커뮤니티 탭 파일, V2 커뮤니티 API/Repository, 답글 상세 display 모델이 이미 존재함을 확인하고, 게시글 상세는 신규Community/Detail하위 구현으로 계획했다. - 2026-07-07: 기존
CreatorCommunityCommentItemView에서 게시글 크리에이터 또는 댓글 작성자이면 더보기 버튼을 표시하고, 수정은 작성자에게만, 삭제는 작성자 또는 게시글 크리에이터에게 허용하는 것을 확인했다. 계획 문서에 동일 권한 규칙을 반영했다. - 2026-07-07: 사용자 확인 사항에 따라 신고 규칙도 기존 커뮤니티 댓글과 동일하게 검토 대상으로 두었으나, 구현 확인 결과 기존
ReportRequest/ReportType에 community comment report identifier가 없고 기존CreatorCommunityCommentItemView도 신고 action을 노출하지 않아 계획 문서에 [blocked]로 정정했다. - 2026-07-07:
plutil -lint SodaLive.xcodeproj/project.pbxproj실행 결과OK를 확인했다. - 2026-07-07:
git diff --check -- docs/20260707_커뮤니티_게시글_상세 SodaLive/Sources/V2/CreatorChannel/Community SodaLive/Sources/V2/CreatorChannel/ReplyDetail SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift SodaLive/Sources/I18n/I18n.swift SodaLive.xcodeproj/project.pbxproj실행 결과 공백 오류 없이 통과했다. - 2026-07-07:
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build실행 결과BUILD SUCCEEDED를 확인했다. - 2026-07-07:
rg "canReport|onTapReport|showReportError|CreatorChannelActionPopupWithReport|private struct CreatorChannelActionPopup|신고" SodaLive/Sources/V2/CreatorChannel/Community/Detail실행 결과 fake report 제거 후 출력이 없음을 확인했다. - 2026-07-07: 커뮤니티 댓글 신고 흐름은 기존
ReportRequest/ReportType에 커뮤니티 댓글 신고 식별자가 없어 연결하지 못했다. 구현은 기존CreatorCommunityCommentItemView의 수정/삭제 popup 동작과 동일하게 맞췄고, fake report action은 노출하지 않는다. - 2026-07-07: 정적 검증과 Debug 빌드 검증은 완료했다. Task 7.3의 device/simulator/API-backed 환경 수동 runtime QA는 아직 수행하지 않았다.