From ec79e3ce8b4444e53ccbb7e62ab1c55c174ffc51 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Sun, 12 Jul 2026 05:54:02 +0900 Subject: [PATCH] =?UTF-8?q?fix(main):=20=ED=8C=94=EB=A1=9C=EC=9E=89=20?= =?UTF-8?q?=EC=BB=A4=EB=AE=A4=EB=8B=88=ED=8B=B0=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MainHomeFollowingNewsSection.swift | 13 ++++- .../Following/MainHomeFollowingView.swift | 8 ++- .../Sources/V2/Main/Home/MainHomeView.swift | 3 +- docs/20260630_메인_홈_팔로잉_탭/plan-task.md | 53 +++++++++++++++---- docs/20260630_메인_홈_팔로잉_탭/prd.md | 11 ++-- 5 files changed, 67 insertions(+), 21 deletions(-) diff --git a/SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift b/SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift index 463f1ee7..d5cea4b7 100644 --- a/SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift +++ b/SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift @@ -4,15 +4,18 @@ struct MainHomeFollowingNewsSection: View { let recentNews: [FollowingNewsResponse] let onTapCreator: (Int) -> Void let onTapContent: (Int) -> Void + let onTapCommunityPost: (Int) -> Void init( recentNews: [FollowingNewsResponse], onTapCreator: @escaping (Int) -> Void = { _ in }, - onTapContent: @escaping (Int) -> Void = { _ in } + onTapContent: @escaping (Int) -> Void = { _ in }, + onTapCommunityPost: @escaping (Int) -> Void = { _ in } ) { self.recentNews = recentNews self.onTapCreator = onTapCreator self.onTapContent = onTapContent + self.onTapCommunityPost = onTapCommunityPost } var body: some View { @@ -82,7 +85,13 @@ struct MainHomeFollowingNewsSection: View { createdAt: communityPost.relativeCreatedAtText(), isLike: false, likeCount: communityPost.likeCount, - commentCount: communityPost.commentCount + commentCount: communityPost.commentCount, + onTapPost: { + onTapCommunityPost(communityPost.postId) + }, + onTapComment: { + onTapCommunityPost(communityPost.postId) + } ) } diff --git a/SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift b/SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift index 797f2fcd..f659a1d4 100644 --- a/SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift +++ b/SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift @@ -9,6 +9,7 @@ struct MainHomeFollowingView: View { let onTapChatTab: () -> Void let onTapChatRoom: (Int) -> Void let onTapSchedule: (CreatorActivityType, Int) -> Void + let onTapCommunityPost: (Int) -> Void @StateObject private var viewModel = MainHomeFollowingViewModel() @@ -20,7 +21,8 @@ struct MainHomeFollowingView: View { onTapFollowingAll: @escaping () -> Void = {}, onTapChatTab: @escaping () -> Void = {}, onTapChatRoom: @escaping (Int) -> Void = { _ in }, - onTapSchedule: @escaping (CreatorActivityType, Int) -> Void = { _, _ in } + onTapSchedule: @escaping (CreatorActivityType, Int) -> Void = { _, _ in }, + onTapCommunityPost: @escaping (Int) -> Void = { _ in } ) { self.onTapLive = onTapLive self.onTapCreator = onTapCreator @@ -30,6 +32,7 @@ struct MainHomeFollowingView: View { self.onTapChatTab = onTapChatTab self.onTapChatRoom = onTapChatRoom self.onTapSchedule = onTapSchedule + self.onTapCommunityPost = onTapCommunityPost } var body: some View { @@ -93,7 +96,8 @@ struct MainHomeFollowingView: View { MainHomeFollowingNewsSection( recentNews: response.recentNews, onTapCreator: onTapCreator, - onTapContent: onTapContent + onTapContent: onTapContent, + onTapCommunityPost: onTapCommunityPost ) .padding(.top, 48) } diff --git a/SodaLive/Sources/V2/Main/Home/MainHomeView.swift b/SodaLive/Sources/V2/Main/Home/MainHomeView.swift index 060f36e5..f9043bc4 100644 --- a/SodaLive/Sources/V2/Main/Home/MainHomeView.swift +++ b/SodaLive/Sources/V2/Main/Home/MainHomeView.swift @@ -84,7 +84,8 @@ struct MainHomeView: View { onTapFollowingAll: onTapFollowingAll, onTapChatTab: onTapChatTab, onTapChatRoom: onTapChatRoom, - onTapSchedule: onTapFollowingSchedule + onTapSchedule: onTapFollowingSchedule, + onTapCommunityPost: onTapCommunityPost ) } } diff --git a/docs/20260630_메인_홈_팔로잉_탭/plan-task.md b/docs/20260630_메인_홈_팔로잉_탭/plan-task.md index d5848b7e..ee64738b 100644 --- a/docs/20260630_메인_홈_팔로잉_탭/plan-task.md +++ b/docs/20260630_메인_홈_팔로잉_탭/plan-task.md @@ -28,8 +28,8 @@ ## 구현 제약 - `FollowingCommunityPostNewsResponse`에는 `creatorId`가 없다. -- 현재 기존 커뮤니티 게시글 직접 진입 구조는 `AppState.setPendingCommunityCommentDeepLink(creatorId:postId:)`와 `.creatorCommunityAll(creatorId:)` 조합이므로 `postId`만으로는 기존 화면에 정확히 진입할 수 없다. -- `COMMUNITY_POST` tap 액션은 구현 전 서버가 `creatorId`를 추가하거나, `postId` 단독 상세 진입 route/API가 있는지 확인해야 한다. 이 확인 없이 임의로 수정 화면(`creatorCommunityModify`)이나 잘못된 creator 화면으로 이동하지 않는다. +- 현재 코드에는 `AppStep.creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)`와 `CreatorChannelCommunityPostDetailView(postId:onCommunityRefresh:)`가 있어 `postId` 단독 상세 진입이 가능하다. +- `COMMUNITY_POST` tap 액션은 `communityPost.postId`를 이 route로 전달한다. 임의로 수정 화면(`creatorCommunityModify`)이나 잘못된 creator 화면으로 이동하지 않는다. ## 주요 대상 파일 @@ -346,18 +346,18 @@ - [x] **Task 9.2: 커뮤니티 게시글 tap 라우팅 제약 처리** - 대상 파일: - - 확인: `SodaLive/Sources/App/AppState.swift` - 확인: `SodaLive/Sources/App/AppStep.swift` - 확인: `SodaLive/Sources/ContentView.swift` - - 확인: `SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift` + - 확인: `SodaLive/Sources/V2/Main/MainView.swift` + - 확인: `SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailView.swift` - 작업 내용: - `postId` 단독으로 게시글 상세/댓글 화면에 진입 가능한 기존 route가 있는지 확인한다. - - 현재 확인된 기존 route가 `creatorId`를 요구하는 경우, 잘못된 화면 이동을 구현하지 않는다. - - 서버 응답에 `creatorId`가 추가되거나 `postId` 단독 route가 생기기 전까지 `COMMUNITY_POST` tap은 표시 카드 내부의 좋아요/댓글 표시만 제공하고 상세 진입은 연결하지 않는다. - - 이 제약을 구현 결과 보고와 검증 기록에 남긴다. + - `AppStep.creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)`를 확인한다. + - `COMMUNITY_POST` tap은 `postId` 기준 커뮤니티 게시글 상세 진입으로 연결한다. + - 잘못된 creator 화면이나 수정 화면으로 이동하지 않는다. - 검증 기준: - - 실행 명령: `rg "setPendingCommunityCommentDeepLink|consumePendingCommunityCommentPostId|creatorCommunityAll|case chatRoom|case creatorCommunity" SodaLive/Sources/App/AppState.swift SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swift` - - 기대 결과: 기존 커뮤니티 직접 진입 경로가 `creatorId`를 요구하는지 확인된다. + - 실행 명령: `rg "creatorChannelCommunityPostDetail|CreatorChannelCommunityPostDetailView|handleRecommendationCommunityPostTap" SodaLive/Sources/App/AppStep.swift SodaLive/Sources/ContentView.swift SodaLive/Sources/V2/Main/MainView.swift` + - 기대 결과: `postId` 단독 커뮤니티 게시글 상세 route와 기존 홈 추천 탭 커뮤니티 게시글 handler가 확인된다. ### Phase 10: 최근 소식 오디오/화보 콘텐츠 카드 @@ -430,7 +430,7 @@ - content는 기존 `handleRecommendationContentTap`을 재사용한다. - chat room은 `.chatRoom(id:)`로 이동한다. - following all은 `.followingList`로 이동한다. - - community post는 Task 9.2 제약에 따라 postId 단독 route가 없으면 상세 진입을 연결하지 않는다. + - community post는 Task 9.2 확인 결과에 따라 `postId` 단독 route인 `.creatorChannelCommunityPostDetail(postId:)`로 이동한다. - 검증 기준: - 실행 명령: `rg "onTapFollowingAll|onTapChatRoom|onTapFollowingSchedule|handleRecommendationLiveTap|handleRecommendationCreatorTap|handleRecommendationContentTap|followingList|chatRoom\\(id:" SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift` - 기대 결과: 팔로잉 탭의 상세 진입 callback이 기존 MainView guard/route에 연결된다. @@ -486,6 +486,33 @@ - 실행 명령: 수동 확인 - 기대 결과: PRD 성공 기준과 Figma 섹션 순서가 충족된다. +### Phase 13: 최근 소식 커뮤니티 게시글 상세 진입 보강 + +- [x] **Task 13.1: COMMUNITY_POST 상세 진입 문서 갱신** + - 대상 파일: + - 수정: `docs/20260630_메인_홈_팔로잉_탭/prd.md` + - 수정: `docs/20260630_메인_홈_팔로잉_탭/plan-task.md` + - 작업 내용: + - 현재 코드에 `postId` 단독 커뮤니티 게시글 상세 route가 있음을 기록한다. + - 기존 `creatorId` 요구 제약 기록을 최신 route 기준으로 보정한다. + - 검증 기준: + - 실행 명령: `rg "creatorChannelCommunityPostDetail|COMMUNITY_POST|postId 단독" docs/20260630_메인_홈_팔로잉_탭/prd.md docs/20260630_메인_홈_팔로잉_탭/plan-task.md` + - 기대 결과: 문서가 `COMMUNITY_POST` 상세 진입 범위와 route를 명시한다. + +- [x] **Task 13.2: COMMUNITY_POST tap callback 연결** + - 대상 파일: + - 수정: `SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift` + - 수정: `SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift` + - 수정: `SodaLive/Sources/V2/Main/Home/MainHomeView.swift` + - 확인: `SodaLive/Sources/V2/Main/MainView.swift` + - 작업 내용: + - `MainHomeFollowingNewsSection`에 `onTapCommunityPost` callback을 추가한다. + - `CommunityPostCard`의 post/comment tap에서 `communityPost.postId`를 callback으로 전달한다. + - `MainHomeFollowingView`와 `MainHomeView`를 통해 기존 `MainView.handleRecommendationCommunityPostTap(postId:)`로 연결한다. + - 검증 기준: + - 실행 명령: `rg "onTapCommunityPost|handleRecommendationCommunityPostTap|creatorChannelCommunityPostDetail|CommunityPostCard" SodaLive/Sources/V2/Main/Home/Following SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift` + - 기대 결과: 최근 소식 `COMMUNITY_POST` card tap이 기존 커뮤니티 게시글 상세 route로 연결된다. + ## 검증 기록 - 2026-06-30: `docs/agent-guides/documentation-policy.md`, `build-test-verification.md`, `code-style.md`를 확인해 계획 문서 경로, Phase/checklist 형식, 검증 명령 기준을 반영했다. @@ -575,3 +602,9 @@ - 2026-07-01: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataPhase12" build`를 실행해 Phase 12 빌드 성공을 확인했다. 기존 duplicate build file/run script/AppIntents/dependency scan warning은 남아 있으나 컴파일 오류는 없었다. - 2026-07-01: Phase 12.4 수동 UI 검증 항목 중 실제 비로그인/로그인 탭 이동, `/api/v2/home/following` 실응답 섹션 표시, 타입별 UI 표시 확인은 실행 가능한 시뮬레이터 세션과 로그인 계정/API 데이터가 필요해 이번 정적/빌드 검증 범위에서는 완료하지 않았다. 대신 코드 흐름상 비로그인 guard, API endpoint, 섹션 조립, `전체` 버튼 `Color.soda400`, 타입별 recent news 분기, root/section 고정 width/height 미사용 원칙을 정적 검색과 빌드로 확인했다. - 2026-07-01: 독립 explore 점검에서도 `project.pbxproj` 등록, placeholder 제거, Figma localhost asset 미사용, 팔로잉 API/I18n/라우팅 반영을 확인했다. 잔여 리스크로 `MainHomeTab.title`의 기존 탭 타이틀 하드코딩과 계획된 제약사항인 `COMMUNITY_POST` 상세/댓글 진입 미연결을 기록했다. +- 2026-07-12: `AppStep.creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)`, `ContentView`의 `CreatorChannelCommunityPostDetailView(postId:onCommunityRefresh:)`, `MainView.handleRecommendationCommunityPostTap(postId:)`를 확인해 `postId` 단독 커뮤니티 게시글 상세 진입 route가 있음을 확인했다. +- 2026-07-12: Phase 13을 추가해 `MainHomeFollowingNewsSection -> MainHomeFollowingView -> MainHomeView -> MainView.handleRecommendationCommunityPostTap(postId:)` 경로로 최근 소식 `COMMUNITY_POST` card/comment tap을 `.creatorChannelCommunityPostDetail(postId:)` 라우팅에 연결했다. +- 2026-07-12: `rg "onTapCommunityPost|handleRecommendationCommunityPostTap|creatorChannelCommunityPostDetail|onTapPost:|onTapComment:" SodaLive/Sources/V2/Main/Home/Following SodaLive/Sources/V2/Main/Home/MainHomeView.swift SodaLive/Sources/V2/Main/MainView.swift`로 팔로잉 최근 소식 `COMMUNITY_POST` tap callback과 기존 상세 route 연결을 확인했다. +- 2026-07-12: `git diff --check -- SodaLive/Sources/V2/Main/Home/Following/Components/MainHomeFollowingNewsSection.swift SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift SodaLive/Sources/V2/Main/Home/MainHomeView.swift docs/20260630_메인_홈_팔로잉_탭/prd.md docs/20260630_메인_홈_팔로잉_탭/plan-task.md` 실행 결과 출력 없이 종료되어 변경 파일 공백 오류가 없음을 확인했다. +- 2026-07-12: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataFollowingCommunityPost" build`는 로컬에 team `3A8QGPMRXM`용 iOS Development signing certificate이 없어 코드 서명 단계에서 실패했다. +- 2026-07-12: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -destination "generic/platform=iOS Simulator" -derivedDataPath "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/SodaLiveDerivedDataFollowingCommunityPostSimulator" CODE_SIGNING_ALLOWED=NO build`는 의존성 컴파일 중 600초 제한으로 `BUILD INTERRUPTED`되어 완료 여부를 확인하지 못했다. diff --git a/docs/20260630_메인_홈_팔로잉_탭/prd.md b/docs/20260630_메인_홈_팔로잉_탭/prd.md index 2e5a4a3a..00ae57e1 100644 --- a/docs/20260630_메인_홈_팔로잉_탭/prd.md +++ b/docs/20260630_메인_홈_팔로잉_탭/prd.md @@ -421,9 +421,8 @@ Figma 참조: - `postId`, `creatorProfileImage`, `creatorNickname`, `imageUrl`, `content`, `createdAt`, `likeCount`, `commentCount`를 표시한다. - 기존 `CommunityPostCard`를 재사용할 경우 `audioUrl = nil`, `price = 0`, `existOrdered = true`로 매핑해 표시 전용으로 사용한다. - 타입과 대응되는 payload가 `nil`이면 해당 item은 숨긴다. - - 현재 기존 커뮤니티 게시글 직접 진입 구조는 `creatorId`를 요구하지만 `FollowingCommunityPostNewsResponse`에는 `creatorId`가 없다. - - `postId` 단독 상세 route/API가 없으면 잘못된 creator 화면이나 수정 화면으로 이동하지 않는다. - - 서버 응답에 `creatorId`가 추가되거나 `postId` 단독 route/API가 확인되면 `communityPost.postId` 기준 커뮤니티 게시글 진입 흐름을 연결한다. + - 현재 코드에는 `postId` 단독으로 진입 가능한 `AppStep.creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)` 라우트가 있으므로 `communityPost.postId` 기준으로 커뮤니티 게시글 상세 화면에 이동한다. + - 잘못된 creator 화면이나 수정 화면으로 이동하지 않는다. - `AUDIO_CONTENT` - Figma `node-id=1229-27212` 기준 오디오 콘텐츠 소식 카드로 표시한다. - `audioContent` payload를 사용한다. @@ -449,7 +448,7 @@ Figma 참조: - 토큰이 비어 있으면 `AppState.shared.setAppStep(step: .login)`으로 이동한다. - 라이브 진입은 기존 `LiveViewModel.enterLiveRoom(roomId:)` 흐름을 재사용한다. - 콘텐츠, 크리에이터, 커뮤니티, 채팅 이동은 기존 `AppStep` 라우팅을 재사용한다. -- 단, `COMMUNITY_POST`는 `postId` 단독 진입 가능 route가 확인되기 전까지 표시만 제공하고 상세 진입을 연결하지 않는다. +- `COMMUNITY_POST`는 `AppStep.creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)`를 사용해 `postId` 단독으로 커뮤니티 게시글 상세 화면에 진입한다. - 서버 응답에 없는 성인/민감 콘텐츠 여부를 클라이언트에서 임의 추정하지 않는다. ## 8. UX / UI Expectations @@ -546,7 +545,7 @@ Figma 참조: - 최근 소식의 `CREATOR_RANKING`은 `creatorRanking` payload로 랭킹 소식 카드가 표시된다. - 최근 소식의 `CONTENT_RANKING`은 `contentRanking` payload로 랭킹 소식 카드가 표시된다. - 최근 소식의 `COMMUNITY_POST`는 `communityPost` payload로 기존 커뮤니티 Feed View 또는 표시 전용 variant가 표시된다. -- `COMMUNITY_POST`는 `postId` 단독 진입 가능 route가 확인되기 전까지 잘못된 화면 이동을 하지 않는다. +- `COMMUNITY_POST`는 `communityPost.postId`로 커뮤니티 게시글 상세 화면에 이동한다. - 최근 소식의 `AUDIO_CONTENT`는 `audioContent` payload로 Figma 오디오 콘텐츠 카드 형태가 표시된다. - 최근 소식의 `PHOTO_CONTENT`는 `photoContent` payload로 Figma 화보 콘텐츠 카드 형태가 표시된다. - 최근 소식 시간 표시는 `visibleFromAtUtc` 기준으로 표시된다. @@ -569,4 +568,4 @@ Figma 참조: - `SodaLive/Sources/UI/Theme/Color.swift`에서 `Color.soda400`이 `#00BDF7`임을 확인했다. - 사용자 답변을 반영해 `ChatRoomListItemResponse`, `CreatorActivityType`, nested 최근 소식 response 구조, `visibleFromAtUtc` 시간 표시 기준, 기존 전체 팔로잉 목록 화면 재사용, 팔로잉 탭 선택 시 로그인 guard 정책을 확정 요구사항으로 이동했다. - empty state 문구를 `팔로잉 소식이 아직 없어요.\n관심 있는 크리에이터를 팔로우해 보세요.`, API 실패 문구를 `팔로잉 정보를 불러오지 못했습니다.\n잠시 후 다시 시도해 주세요.`로 확정했다. -- 기존 커뮤니티 게시글 직접 진입 구조가 `creatorId`를 요구하는 반면 `FollowingCommunityPostNewsResponse`에는 `creatorId`가 없으므로, `COMMUNITY_POST` tap은 `postId` 단독 route/API 확인 전까지 잘못된 화면 이동을 구현하지 않는 제약을 반영했다. +- 2026-07-12: 현재 코드의 `AppStep.creatorChannelCommunityPostDetail(postId:onCommunityRefresh:)`, `CreatorChannelCommunityPostDetailView(postId:onCommunityRefresh:)`, `/api/v2/creator-channels/community-posts/{postId}` 흐름을 확인해 `COMMUNITY_POST` tap을 `postId` 단독 상세 진입으로 연결하도록 요구사항을 갱신했다.