Files
sodalive-ios/docs/20260705_크리에이터_채널_커뮤니티_탭/prd.md
2026-07-05 22:25:07 +09:00

15 KiB

PRD: 크리에이터 채널 커뮤니티 탭

1. Overview

크리에이터 채널 공통 shell의 커뮤니티 탭에서 크리에이터의 커뮤니티 게시글을 리스트형 또는 썸네일형으로 제공한다. 상단 title bar, header, sticky tab-bar는 기존 크리에이터 채널 홈/라이브/오디오/시리즈/팬Talk/후원 구현을 재사용하고, tab-bar 아래 콘텐츠만 커뮤니티 탭 전용 API 응답으로 구성한다.

API는 GET /api/v2/creator-channels/{creatorId}/community를 사용한다. creatorId는 path variable이며, query parameter는 page, size를 사용한다. 기본값은 page=0, size=20이다.

Figma 참조:

  • 전체 리스트형: 290:9061, 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-9061&m=dev
  • 전체 썸네일형: 290:9073, 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-9073&m=dev
  • 전체 리스트형 + 본인 채널: 665:19021, 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=665-19021&m=dev
  • 전체 썸네일형 + 본인 채널: Figma 설명 기준, 썸네일 UI는 290:9073과 동일하고 하단 고정 커뮤니티 글 올리기 버튼을 표시한다.
  • 유료이고 구매하지 않은 게시글: 290:9066, 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-9066&m=dev
  • 유료 금액 우측 상단 표시: 665:19024, 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=665-19024&m=dev
  • Empty: 290:8994, 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-8994&m=dev

2. Problem

  • 현재 CreatorChannelTab.community는 임시 화면으로 표시되어 V2 크리에이터 채널 안에서 커뮤니티 게시글 목록을 확인할 수 없다.
  • 기존 CreatorCommunityAllView는 구 프로필 커뮤니티 전체보기 화면이고, V2 채널 탭의 sticky header, tab-bar, sort-bar 구조와 직접 맞지 않는다.
  • 커뮤니티 탭은 오디오 탭의 정렬 선택과 달리 리스트형/썸네일형 보기 전환 버튼이 필요하다.
  • 유료 미구매 게시글, 댓글 비활성 게시글, 본인 채널 게시글의 표시 조건이 기존 전체보기와 일부 달라 V2 탭 기준으로 명확히 분리해야 한다.

3. Goals

  • CreatorChannelTab.community 선택 시 커뮤니티 탭 API를 호출하고 응답 데이터로 화면을 구성한다.
  • 기본 query는 page=0, size=20이다.
  • sort-bar 좌측에는 전체 {communityPostCount}를 표시한다.
  • sort-bar 우측은 정렬이 아니라 리스트형/썸네일형 토글 버튼으로 제공한다.
  • 기본 보기 방식은 리스트형이고, 기본 아이콘은 ic_new_list를 사용한다.
  • 토글 후 썸네일형에서는 ic_new_grid를 사용한다.
  • 리스트형/썸네일형 표시 문구는 I18n에 ko/en/ja 다국어로 추가한다.
  • 리스트형은 기존 커뮤니티 리스트 카드의 정보 구조를 따르되 V2 채널 탭 디자인에 맞춰 작성한다.
  • 썸네일형은 3열 grid로 표시하고, 이미지가 있으면 이미지 또는 GIF를 재생 가능하게 표시한다.
  • 썸네일형에서 imageUrl == nil이면 유/무료 관계없이 이미지를 표시하지 않고 기존 크리에이터 커뮤니티 페이지의 fallback 규칙을 참고해 글 일부를 표시한다.
  • 유료 미구매 게시글은 이미지 영역에 회색 RoundedRectangle을 표시하고, 오디오 재생 버튼은 표시하지 않는다.
  • 본인 또는 구매한 유저는 이미지와 중앙 오디오 재생 버튼을 기존 커뮤니티 페이지와 동일한 방식으로 표시한다.
  • 댓글을 사용할 수 없는 게시글은 댓글 아이콘과 댓글 개수를 숨긴다.
  • 본인 채널에 본인이 쓴 커뮤니티 게시글에서만 상단 더보기와 가격 표시를 표시한다.
  • 본인 채널이면 하단 고정 커뮤니티 글 올리기 버튼을 표시한다.
  • 게시글 우측 더보기 액션은 기존 커뮤니티 전체보기의 수정/삭제/신고/고정 액션 흐름을 재사용한다.

4. Non-Goals

  • 크리에이터 채널 공통 shell, header, sticky tab-bar 동작을 다시 설계하지 않는다.
  • 커뮤니티 게시글 상세 화면은 이번 범위에서 구현하지 않는다.
  • 커뮤니티 글쓰기/수정 화면 자체를 새로 구현하지 않고 기존 creatorCommunityWrite / creatorCommunityModify navigation step을 재사용한다.
  • 커뮤니티 댓글 리스트, 댓글 작성, 신고, 삭제, 구매 mutation API를 새로 만들지 않고 기존 커뮤니티 구현의 흐름을 재사용한다.
  • 오디오 탭의 정렬 popup을 커뮤니티 탭에 표시하지 않는다.
  • Figma localhost asset URL을 앱 코드에 직접 사용하지 않는다.
  • Pods/**, generated/**, build/**는 수정하지 않는다.

5. Core Requirements

5.1 API

  • Method: GET
  • Path: /api/v2/creator-channels/{creatorId}/community
  • Path parameter:
    • creatorId
  • Query parameters:
    • page: 기본값 0
    • size: 기본값 20
  • 응답 래퍼는 기존 패턴대로 ApiResponse<CreatorChannelCommunityTabResponse>로 디코딩한다.
data class CreatorChannelCommunityTabResponse(
    val communityPostCount: Int,
    val communityPosts: List<CreatorChannelCommunityPostResponse>,
    val page: Int,
    val size: Int,
    @JsonProperty("hasNext")
    val hasNext: Boolean
)

data class CreatorChannelCommunityPostResponse(
    val postId: Long,
    val creatorId: Long,
    val creatorNickname: String,
    val creatorProfileUrl: String,
    val createdAtUtc: String,
    val content: String,
    val imageUrl: String?,
    val audioUrl: String?,
    val price: Int,
    @JsonProperty("isCommentAvailable")
    val isCommentAvailable: Boolean,
    val likeCount: Int,
    val commentCount: Int,
    @JsonProperty("isPinned")
    val isPinned: Boolean,
    val existOrdered: Boolean
)
  • Kotlin Long은 기존 V2 모델 관례대로 Swift Int로 선언한다.
  • creatorProfileUrl, existOrdered는 서버 응답에 추가되는 필드로 포함한다.
  • createdAtUtc는 기존 DateParser.relativeTimeText(fromUTC:fallback:now:) 계열 helper를 사용해 상대 시간으로 표시한다.

5.2 View mode toggle

  • 기본 보기 방식은 리스트형이다.
  • sort-bar 우측 버튼을 터치할 때마다 리스트형과 썸네일형을 토글한다.
  • 리스트형 상태에서는 버튼에 ic_new_list와 리스트형 문구를 표시한다.
  • 썸네일형 상태에서는 버튼에 ic_new_grid와 썸네일형 문구를 표시한다.
  • 리스트형/썸네일형 문구는 I18n.CreatorChannelCommunity 또는 같은 범위의 신규 enum에 ko/en/ja로 추가한다.
  • 보기 방식 전환은 현재까지 로드된 communityPosts를 그대로 사용하고 API를 다시 호출하지 않는다.

5.3 List layout

  • 리스트형은 Figma 290:9061665:19021을 기준으로 표시한다.
  • 작성자 프로필 이미지, 닉네임, 상대 시간, 본문, 이미지/오디오 영역, 좋아요 수, 댓글 수를 표시한다.
  • isPinned == true이면 게시글에 고정 표시를 제공한다.
  • price > 0 && existOrdered == false && isOwnPost == false이면 유료 미구매 상태로 표시한다.
  • 유료 미구매 상태에서는 이미지 URL도 nil로 내려올 수 있으므로 이미지 영역에 회색 RoundedRectangle을 표시한다.
  • 유료 미구매 상태에서는 오디오 재생 버튼을 표시하지 않는다.
  • 본인 또는 구매한 유저는 이미지가 있으면 이미지 또는 GIF를 표시하고, audioUrl != nil이면 이미지 중앙에 재생 버튼을 표시한다.
  • GIF URL은 정지 이미지로 변환하지 않고 재생 가능한 이미지 컴포넌트를 사용한다.
  • imageUrl == nil이면 유/무료 관계없이 이미지 영역을 표시하지 않는다.
  • isCommentAvailable == false이면 댓글 아이콘과 댓글 개수를 숨긴다.

5.4 Thumbnail layout

  • 썸네일형은 Figma 290:9073 기준 3열 grid로 표시한다.
  • 이미지 또는 GIF가 있으면 정방형 썸네일에 표시한다.
  • 이미지가 없으면 유/무료 관계없이 기존 CreatorCommunityAllGridItemView의 fallback 규칙을 참고해 글 일부를 표시한다.
  • 유료 미구매 게시글은 회색 배경과 중앙형 가격 UI를 표시한다.
  • isPinned == true이면 썸네일 우측 상단에 고정 표시를 제공한다.
  • grid item tap은 후속 상세 화면 구현 전까지 상세 이동을 연결하지 않는다.

5.5 Paid post display

  • 유료 미구매 판정은 price > 0 && existOrdered == false && isOwnPost == false이다.
  • 리스트형 중앙 가격 표시는 Figma 290:9066을 따른다.
  • 우측 상단 가격 표시는 Figma 665:19024를 따른다.
  • 썸네일형 유료 미구매 가격 UI는 중앙형을 사용한다.
  • 상단 더보기와 가격 표시는 본인 채널에 본인이 쓴 커뮤니티 게시글에서만 표시한다.
  • 유료 미구매 게시글의 구매 dialog는 기존 CommunityPostPurchaseDialog를 재사용한다.
  • 구매 성공 후 현재 페이지 범위를 첫 페이지부터 다시 조회하거나 해당 item을 갱신한다.

5.6 Actions

  • 좋아요는 기존 CreatorCommunityRepository.communityPostLike(postId:) 흐름을 재사용한다.
  • 댓글 목록은 기존 CreatorCommunityCommentListView를 재사용하되, isCommentAvailable == false이면 진입 UI를 표시하지 않는다.
  • 더보기 메뉴는 기존 CreatorCommunityMenuView 또는 V2 anchored popup 패턴 중 구현 시점에 더 작은 변경으로 맞는 방식을 사용한다.
  • 본인 채널의 내 게시글에서 더보기 메뉴는 기존과 동일하게 고정/수정/삭제 액션을 제공한다.
  • 내 게시글이 아니면 신고 액션을 기존 CreatorCommunityReportViewReportType.COMMUNITY_POST 흐름으로 처리한다.
  • 수정 성공, 삭제 성공, 고정 상태 변경 성공 후 커뮤니티 탭 첫 페이지를 다시 조회한다.

5.7 Own channel CTA

  • 본인 채널이고 selectedTab == .community이면 하단 고정 커뮤니티 글 올리기 버튼을 표시한다.
  • 버튼 문구는 기존 I18n.CreatorChannelHome.uploadCommunityPost 또는 동일 의미의 기존 다국어 문구를 우선 재사용한다.
  • 버튼 tap은 기존 showCommunityWrite() 흐름을 재사용한다.
  • 글 작성 성공 후 홈 정보와 커뮤니티 탭 목록을 갱신한다.

5.8 Pagination and state

  • 첫 진입 시 page=0, size=20으로 조회한다.
  • 다음 페이지 로딩 중 중복 호출을 막는다.
  • 마지막 item이 화면에 나타났고 hasNext == true이면 page + 1을 조회한다.
  • 다음 페이지 성공 시 기존 communityPosts 뒤에 append한다.
  • 첫 페이지 재조회 시 기존 목록을 교체한다.
  • Empty 상태는 communityPostCount == 0 기준으로 판정하고 Figma 290:8994를 따른다.

6. Success Criteria

  • 커뮤니티 탭 진입 시 GET /api/v2/creator-channels/{creatorId}/community?page=0&size=20가 호출된다.
  • count bar에는 전체 {communityPostCount}가 표시된다.
  • 우측 버튼은 정렬이 아니라 리스트형/썸네일형 토글로 동작한다.
  • 기본값은 리스트형이며 ic_new_list 상태로 표시된다.
  • 토글 시 썸네일형으로 바뀌고 ic_new_grid 상태로 표시된다.
  • 리스트형/썸네일형 문구는 ko/en/ja 다국어로 제공된다.
  • 유료 미구매 게시글은 회색 RoundedRectangle 이미지 영역과 가격 UI를 표시하고 오디오 재생 버튼은 표시하지 않는다.
  • 본인 또는 구매한 유저의 게시글은 이미지/GIF와 중앙 오디오 재생 버튼을 표시한다.
  • isCommentAvailable == false이면 댓글 아이콘과 댓글 개수가 표시되지 않는다.
  • 본인 채널의 내 게시글에서만 상단 더보기와 가격 표시가 노출된다.
  • 본인 채널이면 리스트형/썸네일형 모두에서 하단 고정 커뮤니티 글 올리기 버튼이 표시된다.
  • 게시글 더보기 액션은 기존 커뮤니티 페이지와 동일한 수정/삭제/신고/고정 흐름을 사용한다.
  • communityPostCount == 0이면 커뮤니티 empty 상태가 표시된다.
  • 목록 하단 도달 시 hasNext == true이면 다음 페이지가 append된다.

7. Technical Constraints

  • 기능 변경은 SodaLive/Sources/V2/CreatorChannel/** 하위에서 해결한다.
  • 커뮤니티 탭 전용 View, ViewModel, Repository, API, 모델은 SodaLive/Sources/V2/CreatorChannel/Community/** 아래에 둔다.
  • 기존 구 커뮤니티 전체보기 파일은 필요한 재사용 지점만 참고하고, V2 탭 구현을 위해 직접 대규모 수정하지 않는다.
  • 기존 커뮤니티 mutation, 댓글, 구매, 신고, 오디오 플레이어 흐름은 가능한 한 재사용한다.
  • 신규 문구는 SodaLive/Sources/I18n/I18n.swift에 ko/en/ja를 추가한다.
  • 프로젝트 설정 변경은 신규 Swift 파일 target 등록이 필요한 경우에만 수행한다.

8. Assumptions

  • creatorProfileUrlexistOrdered는 커뮤니티 탭 API 응답에 포함된다.
  • 현재 로그인 사용자의 id는 기존 관례대로 UserDefaults.int(forKey: .userId)로 확인한다.
  • isOwnPostpost.creatorId == UserDefaults.int(forKey: .userId)로 판정한다.
  • 커뮤니티 게시글 상세 화면은 후속 범위이므로 grid/list item tap 상세 이동은 이번 작업에서 연결하지 않는다.
  • 본인 채널 여부는 CreatorChannelViewisOwnCreatorChannel 값을 커뮤니티 탭에 주입한다.

9. Open Questions

  • 해당 없음

10. Verification Notes

  • 2026-07-05: 사용자 제공 API, 응답 모델, Figma URL, 본인 채널/유료/댓글/토글 요구사항을 기준으로 PRD를 작성했다.
  • 2026-07-05: docs/agent-guides/documentation-policy.md, 기존 크리에이터 채널 오디오/팬Talk 문서, CreatorChannelView, 기존 CreatorCommunityAllView, CreatorCommunityAllItemView, CreatorCommunityAllViewModel을 확인해 문서 범위를 정했다.
  • 2026-07-05: 사용자 추가 확인으로 Empty Figma 290:8994, 썸네일형 유료 미구매 가격 UI 중앙형, imageUrl == nil일 때 유/무료 관계없이 이미지 미표시 규칙을 반영했다.