Files
sodalive-ios/docs/20260707_커뮤니티_게시글_상세/plan-task.md

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.getCommunitypage=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.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailView.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailViewModel.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailContentView.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentListView.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentRow.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailInputBar.swift

수정

  • SodaLive/Sources/V2/CreatorChannel/Community/Repository/CreatorChannelCommunityApi.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/Repository/CreatorChannelCommunityRepository.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/CreatorChannelCommunityTabView.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/Components/CreatorChannelCommunityListItem.swift
  • SodaLive/Sources/V2/CreatorChannel/Community/Components/CreatorChannelCommunityGridItem.swift
  • SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailContext.swift
  • SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Models/CreatorChannelReplyDetailDisplayItem.swift
  • SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailViewModel.swift
  • SodaLive/Sources/App/AppStep.swift
  • SodaLive/Sources/ContentView.swift
  • SodaLive/Sources/I18n/I18n.swift
  • SodaLive.xcodeproj/project.pbxproj

확인/재사용

  • SodaLive/Sources/V2/CreatorChannel/Community/Components/CreatorChannelCommunityListItem.swift
  • SodaLive/Sources/V2/Component/Card/CommunityPostCard.swift
  • SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift
  • SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkListItem.swift
  • SodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityRepository.swift
  • SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/ModifyCommunityPostCommentRequest.swift

TASK 체크리스트

Phase 1: 조회 모델과 API

  • Task 1.1: 커뮤니티 게시글 상세 응답 모델 생성

    • 대상 파일:
      • 생성: SodaLive/Sources/V2/CreatorChannel/Community/Models/CreatorChannelCommunityPostDetailResponse.swift
    • 작업 내용:
      • CreatorChannelCommunityPostDetailResponseDecodable, Identifiable로 정의한다.
      • CreatorChannelCommunityCommentsResponse, CreatorChannelCommunityCommentResponse, CreatorChannelCommunityReplyResponse, CreatorChannelCommunityRepliesResponse를 정의한다.
      • Kotlin Long 필드는 Swift Int로 선언한다.
      • CreatorChannelCommunityCommentResponseCreatorChannelCommunityReplyResponse에는 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
    • 작업 내용:
      • CreatorChannelCommunityApigetCommunityPostDetail(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
    • 작업 내용:
      • AppStepcreatorChannelCommunityPostDetail(postId:onCommunityRefresh:) case를 추가한다.
      • ContentView switch에서 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에 onTapDetail closure를 추가한다.
      • item 전체 tap은 상세 route로 연결한다.
      • 좋아요, 댓글, 더보기, 구매, 오디오 재생 버튼 tap은 상세 진입과 충돌하지 않게 button action으로 유지한다.
      • 상세 route에는 post.postIdviewModel.fetchFirstPage(creatorId:) callback을 전달한다.
    • 검증 기준:
      • 실행 명령: 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이면 댓글 입력/목록 상태를 비활성 상태로 유지한다.
      • isShowSecret computed 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를 호출한다.
      • 중복 호출을 막기 위해 isLoadingNextPageisLoading guard를 둔다.
      • 첫 페이지 재조회 시 기존 댓글 목록을 교체하고, 다음 페이지 성공 시 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 직후 isLikedlikeCount를 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 성공 시 onCommunityRefresh callback을 호출해 목록 갱신을 예약한다.
    • 검증 기준:
      • 실행 명령: 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을 표시한다.
      • 반응 정보는 댓글 icon/count를 먼저, 좋아요 icon/count를 뒤에 표시한다.
      • 유료 미구매 상태는 price > 0 && existOrdered == false && isOwnPost == false로 판정한다.
      • 유료 미구매 상태에서는 gray800 계열 이미지 영역, 잠금 아이콘, 캐시 가격 capsule을 표시한다.
      • 구매 완료 상태는 구매완료 capsule tag를 표시한다.
      • 접근 가능한 이미지/GIF는 기존 WebImage 또는 DownsampledKFImage 패턴을 따른다.
      • audioUrl이 있으면 기존 CreatorCommunityMediaPlayerManagerContentPlayManager 흐름을 재사용한다.
      • 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에서 다음 페이지 조회를 요청한다.
      • 댓글 row 사이에는 Figma 기준 gray800 1pt separator를 표시한다.
      • 댓글이 비어 있으면 header만 유지하거나 Figma 기준 빈 목록 영역을 유지한다.
      • 선택된 댓글의 popup은 기존 CreatorChannelActionPopup 또는 CreatorChannelReplyDetailActionPopup 기반으로 수정/삭제 action을 표시한다.
    • 검증 기준:
      • 실행 명령: 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를 최소 범위로 제공한다.
    • 검증 기준:
      • 실행 명령: 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를 표시한다.
      • 댓글 가능 상세에서는 게시글 영역과 댓글 목록 사이에 Figma 기준 gray800 1pt separator를 표시한다.
      • 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를 추가한다.
      • CreatorChannelCommunityCommentResponsecommentId, 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:) 경로를 유지한다.
      • 신규 CreatorChannelCommunityReplyResponseCreatorChannelReplyDetailDisplayItem으로 변환한다.
      • hasNext, page, size를 사용해 답글 페이지네이션을 처리한다.
    • 검증 기준:
      • 실행 명령: 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 성공 시 호출할 onCommunityRefresh callback을 추가하고, 게시글 상세 진입 시 viewModel.fetchDetail(postId:)를 전달한다.
    • 검증 기준:
      • 실행 명령: 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_white asset이 존재하는지 확인하고 다른 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는 아직 수행하지 않았다.