# 팔로잉 해제 확인 팝업 PRD ## 문서 정보 | 항목 | 내용 | |---|---| | 문서 상태 | 완료 | | 작성일 | `2026-08-18` | | 최종 수정일 | `2026-08-18` | | 대상 제품 | SodaLive iOS 팔로잉 리스트, V2 크리에이터 채널 | | 작성자·결정권자 | 작성자: Sisyphus / 결정권자: 제품 담당자 | | 관련 API Contract | 해당 없음: 기존 팔로우 API 사용 | | 관련 구현 계획 | `docs/20260818_팔로잉_해제_확인_팝업/plan-task.md` | | 관련 review | 없음 | ### 요구사항 상태 | 상태 | 의미 | 구현 처리 | |---|---|---| | 확정 | 제품·기술 결정이 완료되어 구현 기준으로 사용 | `plan-task.md`의 Task와 완료 증거로 추적 | | 미결 | 제품·UX 결정이 더 필요함 | 권고안과 결정 주체를 기록하고 임의 구현 금지 | | 제외 | 현재 릴리스에서 구현하지 않기로 결정 | 제외 이유와 후속 조건을 Decision Log에 기록 | ## 1. Overview 팔로잉 리스트 페이지와 V2 크리에이터 채널에서 팔로잉 해제 동작을 더 안전하게 만든다. 사용자가 팔로잉 중인 크리에이터를 해제하려 할 때 `SodaV2ActionModal`로 한 번 더 확인하고, 확인한 경우에만 기존 팔로우 해제 API 흐름을 실행한다. ## 2. Problem Statement 현재 사용자는 다음 문제를 겪는다. - 팔로잉 리스트에서 팔로잉 중 버튼을 터치하면 알림 설정 Bottom Sheet가 먼저 열려 팔로우 해제까지 추가 선택이 필요하다. - 크리에이터 채널에서는 팔로잉 중 버튼을 터치하면 별도 확인 없이 즉시 팔로우 해제가 진행된다. - 두 진입점의 팔로우 해제 경험이 서로 달라 destructive action의 실수 방지가 일관되지 않다. 문제를 해결했다는 판단은 두 화면 모두 팔로우 해제 전 동일한 `SodaV2ActionModal` 확인 단계를 거치고, 사용자가 취소하면 API 호출과 상태 변경이 발생하지 않는 것으로 한다. ## 3. Goals ### 3.1 제품 목표 - 팔로잉 리스트 페이지에서 팔로잉 중 버튼을 누르면 Bottom Sheet 없이 팔로우 해제 확인 Dialog를 표시한다. - 크리에이터 채널에서 팔로잉 중 버튼을 누르면 즉시 해제하지 않고 팔로우 해제 확인 Dialog를 표시한다. - 확인 버튼을 누른 경우에만 기존 팔로우 해제 프로세스를 실행한다. ### 3.2 UX 목표 - 팔로잉 리스트의 팔로잉 중 버튼은 `ic_new_following_white`, `Color.gray800` 배경, 원형 스타일로 표시한다. - 팔로우 해제 확인 Dialog는 `SodaV2ActionModal`을 사용한다. - Dialog의 취소/확인 문구와 본문은 현재 앱 언어에 맞게 한국어, 일본어, 영어로 표시한다. ## 4. Non-Goals - 팔로우 API, 알림 설정 API, DTO, 서버 계약은 변경하지 않는다. - 팔로우 시작 동작과 팔로우 알림 설정 변경 UX는 이 작업에서 변경하지 않는다. - 팔로잉 리스트 외의 `FollowCreatorItemView` 재사용 화면은 제품 범위가 확인되지 않았으므로 임의로 동작을 바꾸지 않는다. 재사용 영향은 구현 전 확인하고 필요한 경우 별도 결정으로 기록한다. - Bottom Sheet 컴포넌트 `CreatorFollowNotifyDialog` 자체는 삭제하지 않는다. ## 5. Target Users and Permissions | 사용자 | 목표 | 주요 작업 | 사용 환경 | |---|---|---|---| | 로그인 사용자 | 팔로잉 중인 크리에이터를 실수 없이 해제 | 팔로잉 리스트 또는 크리에이터 채널에서 팔로우 해제 | iOS 앱 | 권한과 인증은 기존 팔로우 API 정책을 따른다. 인증 토큰 처리와 권한 오류 처리는 기존 `UserRepository` 및 각 ViewModel의 오류 표시 정책을 변경하지 않는다. ## 6. 핵심 사용자 흐름 1. 사용자가 팔로잉 리스트 페이지에 진입한다. 2. 팔로잉 중인 크리에이터 row의 원형 팔로잉 버튼을 누른다. 3. 앱은 Bottom Sheet를 표시하지 않고 팔로우 해제 확인 Dialog를 표시한다. 4. 사용자가 `취소`를 누르면 Dialog만 닫히고 팔로우 상태는 유지된다. 5. 사용자가 `해제하기`를 누르면 Dialog를 닫고 기존 팔로우 해제 프로세스를 실행한다. 6. 사용자가 V2 크리에이터 채널의 팔로잉 중 버튼을 누른 경우도 3~5와 동일하게 동작한다. ## 7. 정보 구조와 라우팅 라우팅 변경은 없다. - 팔로잉 리스트: 기존 `FollowCreatorView` 진입 경로 유지. - 크리에이터 채널: 기존 `CreatorChannelView(creatorId:)` 진입 경로 유지. - Dialog는 별도 route를 만들지 않고 현재 화면 overlay로 표시한다. ## 8. 기능 요구사항 ### 8.1 팔로잉 리스트 버튼과 액션 | ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 | |---|---|---|---|---| | `UNFOLLOW-LIST-001` | 확정 | 팔로잉 리스트에서 팔로잉 중 상태의 액션 버튼은 `ic_new_following_white` 이미지를 사용한다. | 팔로잉 중 row에 기존 `btn_following_big` 또는 `btn_following_no_alarm_big` 대신 흰색 아이콘 원형 버튼이 보인다. | `P1-T1` | | `UNFOLLOW-LIST-002` | 확정 | 팔로잉 중 버튼 배경은 `Color.gray800`을 사용한다. | 버튼 배경 색상은 `SodaLive/Sources/UI/Theme/Color.swift`의 `gray800` 토큰을 참조한다. | `P1-T1` | | `UNFOLLOW-LIST-003` | 확정 | 팔로잉 중 버튼 형태는 크리에이터 채널 팔로잉 중 버튼과 동일한 원형 스타일을 따른다. | 팔로잉 리스트 버튼의 touch target과 시각적 형태가 `CreatorChannelTitleBar`의 팔로잉 중 버튼과 일치한다. | `P1-T1` | | `UNFOLLOW-LIST-004` | 확정 | 팔로잉 리스트에서 팔로잉 중 버튼을 누르면 Bottom Sheet 액션 메뉴를 표시하지 않는다. | `CreatorFollowNotifyDialog`가 팔로잉 리스트의 팔로우 해제 진입점에서 열리지 않는다. | `P1-T2` | | `UNFOLLOW-LIST-005` | 확정 | 팔로잉 리스트에서 팔로잉 중 버튼을 누르면 팔로우 해제 확인 Dialog를 표시한다. | Dialog 확인 전에는 `creatorFollow(follow: false, notify: false)` 또는 동등한 해제 요청이 호출되지 않는다. | `P1-T2` | ### 8.2 크리에이터 채널 팔로우 해제 확인 | ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 | |---|---|---|---|---| | `UNFOLLOW-CHANNEL-001` | 확정 | 크리에이터 채널에서 팔로잉 중 버튼을 누르면 팔로우 해제 확인 Dialog를 표시한다. | `CreatorChannelTitleBar`의 `onTapUnfollow`가 즉시 해제를 실행하지 않고 Dialog 표시 상태를 켠다. | `P2-T1` | | `UNFOLLOW-CHANNEL-002` | 확정 | Dialog 확인 버튼을 누른 경우에만 기존 크리에이터 채널 팔로우 해제 프로세스를 실행한다. | 확인 시 `CreatorChannelViewModel.creatorFollow(follow: false, notify: false)` 흐름이 실행되고 성공 시 `isFollow=false`, `isNotify=false`로 갱신된다. | `P2-T1` | | `UNFOLLOW-CHANNEL-003` | 확정 | Dialog 취소 버튼을 누르면 API 호출 없이 Dialog만 닫는다. | 취소 후 기존 팔로우 상태와 알림 상태가 유지된다. | `P2-T1` | ### 8.3 Dialog 문구와 다국어 | ID | 상태 | 요구사항 | 수용 기준 | 계약/Goal 연결 | |---|---|---|---|---| | `UNFOLLOW-DIALOG-001` | 확정 | Dialog title은 한국어 `팔로우 해제`, 일본어 `フォロー解除`, 영어 `Unfollow`를 사용한다. | 앱 언어별 title이 정확히 표시된다. | `P1-T2`, `P2-T1` | | `UNFOLLOW-DIALOG-002` | 확정 | Dialog 본문은 크리에이터 이름을 포함하고 줄바꿈을 유지한다. | 한국어, 일본어, 영어 모두 `\n` 줄바꿈과 크리에이터 이름 치환이 적용된다. | `P1-T2`, `P2-T1` | | `UNFOLLOW-DIALOG-003` | 확정 | 취소 버튼은 한국어 `취소`, 일본어 `キャンセル`, 영어 `Cancel`을 사용한다. | 취소 버튼이 언어별 문구로 표시되고 API 호출 없이 닫힌다. | `P1-T2`, `P2-T1` | | `UNFOLLOW-DIALOG-004` | 확정 | 확인 버튼은 한국어 `해제하기`, 일본어 `解除する`, 영어 `Unfollow`를 사용한다. | 확인 버튼이 언어별 문구로 표시되고 팔로우 해제 요청을 실행한다. | `P1-T2`, `P2-T1` | Dialog 본문 문구: | 언어 | 문구 | |---|---| | ko | `팔로우를 해제할까요?\n{{creatorName}}의 소식을 받을 수 없어요.` | | ja | `{{creatorName}}からの最新情報を受け取れなくなります。\nフォローを解除しますか?` | | en | `You won't receive updates from {{creatorName}}.\nUnfollow this creator?` | ## 9. 반응형 기능 범위 | 기능 | iPhone | iPad | 비고 | |---|---:|---:|---| | 팔로잉 리스트 버튼 표시 | 전체 | 전체 | 기존 SwiftUI layout 유지 | | 팔로우 해제 확인 Dialog | 전체 | 전체 | overlay 기반 표시 | | Dynamic Type / 긴 닉네임 | 확인 필요 | 확인 필요 | Dialog 본문 줄바꿈과 clipping 수동 확인 | ## 10. UI/UX Expectations ### 10.1 디자인과 component 원칙 - 팔로잉 리스트의 팔로잉 중 버튼은 크리에이터 채널 팔로잉 중 버튼의 원형 시각 규칙을 따른다. - 버튼 색상은 `Color.gray800`, 이미지는 `ic_new_following_white`를 사용한다. - Dialog는 새 임의 component를 만들지 않고 기존 `SodaV2ActionModal`을 사용한다. ### 10.2 화면 상태 - Dialog 표시 중 배경 화면의 팔로우 상태는 바뀌지 않는다. - 확인 후 API loading과 오류 toast/popup은 기존 ViewModel 정책을 따른다. - API 실패 시 Dialog는 이미 닫힌 상태로 두고 기존 오류 표시를 사용한다. ### 10.3 접근성 - 팔로잉 중 버튼의 accessibility label은 팔로잉 상태와 팔로우 해제 의도를 알 수 있어야 한다. - Dialog title, message, 취소, 확인 버튼이 VoiceOver에서 읽혀야 한다. - 취소와 확인 버튼의 touch target은 기존 Dialog 기준을 유지한다. ## 11. API 계약 신규 API 계약은 없다. 기존 팔로우 상태 변경 API를 그대로 사용한다. | 요구사항 | Method | Path | 계약 상태 | API Contract | 소유 Goal | |---|---|---|---|---|---| | `UNFOLLOW-LIST-005` | 기존 계약 | 기존 `UserRepository.creatorFollow` 경유 | 제공됨 | 해당 없음 | `P1-T2` | | `UNFOLLOW-CHANNEL-002` | 기존 계약 | 기존 `UserRepository.creatorFollow` 경유 | 제공됨 | 해당 없음 | `P2-T1` | ## 12. 보안과 데이터 취급 - 인증 header와 token 저장 로직은 변경하지 않는다. - Dialog 문구와 로그에 token, 인증 정보, 민감정보를 기록하지 않는다. - 크리에이터 이름은 화면에 이미 표시되는 공개 프로필 이름만 사용한다. ## 13. 성능과 품질 요구사항 - Dialog 표시를 위해 새 네트워크 요청을 추가하지 않는다. - 팔로잉 리스트 row 렌더링에서 이미지 에셋과 색상 토큰 외 추가 비동기 작업을 만들지 않는다. - 저장소에 테스트 번들 타깃이 확인되지 않으므로 구현 검증은 focused preview/수동 확인과 `xcodebuild` 빌드 중심으로 수행한다. ## 14. 성공 기준 ### 14.1 기능 수용 기준 - [x] 팔로잉 리스트에서 팔로잉 중 버튼이 `ic_new_following_white` + `Color.gray800` 원형 버튼으로 표시된다. (`UNFOLLOW-LIST-001~003`) - [x] 팔로잉 리스트에서 팔로잉 중 버튼을 눌러도 Bottom Sheet가 뜨지 않고 확인 Dialog가 표시된다. (`UNFOLLOW-LIST-004~005`) - [x] 크리에이터 채널에서 팔로잉 중 버튼을 누르면 확인 Dialog가 표시되고, 확인 전에는 해제 API가 호출되지 않는다. (`UNFOLLOW-CHANNEL-001`) - [x] 두 화면 모두 취소 시 상태 변경 없이 Dialog만 닫힌다. (`UNFOLLOW-CHANNEL-003`) - [x] 두 화면 모두 확인 시 기존 팔로우 해제 프로세스가 성공적으로 실행된다. (`UNFOLLOW-CHANNEL-002`, `UNFOLLOW-LIST-005`) ### 14.2 UI/UX 수용 기준 - [x] Dialog 문구가 한국어, 일본어, 영어에서 지정된 title/body/button으로 표시된다. - [x] 크리에이터 이름이 Dialog 본문에 정확히 치환된다. - [x] 긴 크리에이터 이름에서도 Dialog 본문이 잘리거나 버튼을 밀어내지 않는다. ### 14.3 추적성 완료 기준 - [x] 모든 확정 요구사항이 `plan-task.md`의 Goal로 연결된다. - [x] 모든 미결 항목에 현재 권고와 결정 주체가 있다. - [x] 코드 구현 전에 PRD 변경이 필요한 경우 Decision Log를 먼저 갱신한다. ## 15. Open Questions | ID | 상태 | 결정 필요 사항 | 현재 권고 | 결정 주체 | 결정 기한/시점 | 영향 Goal | |---|---|---|---|---|---|---| | `OQ-001` | 해결 | 한국어 본문 `{{creatorName}}의. 소식을 받을 수 없어요.`의 마침표 위치 | 마침표를 제거해 `{{creatorName}}의 소식을 받을 수 없어요.`를 사용 | 사용자 | `2026-08-18` | `P1-T2`, `P2-T1` | | `OQ-002` | 해결 | “V2 Dialog”의 정확한 공통 component | `SodaV2ActionModal`을 사용 | 사용자 | `2026-08-18` | `P1-T2`, `P2-T1` | ## 16. 요구사항 추적표 | 요구사항 범위 | API Contract | 계획 Phase | Goal | 자동 검증 | 수동 검증 | |---|---|---:|---|---|---| | `UNFOLLOW-LIST-001~003` | 해당 없음 | 1 | `P1-T1` | 빌드 | 팔로잉 리스트 버튼 시각 확인 | | `UNFOLLOW-LIST-004~005` | 기존 팔로우 API | 1 | `P1-T2` | 빌드 | Bottom Sheet 미표시, Dialog 취소/확인 확인 | | `UNFOLLOW-CHANNEL-001~003` | 기존 팔로우 API | 2 | `P2-T1` | 빌드 | 크리에이터 채널 Dialog 취소/확인 확인 | | `UNFOLLOW-DIALOG-001~004` | 해당 없음 | 1, 2 | `P1-T2`, `P2-T1` | 빌드 | ko/en/ja 문구 확인 | ## 17. Decision Log | 날짜 | ID | 상태 | 결정 | 근거 | 영향 요구사항·계약·Goal | |---|---|---|---|---|---| | `2026-08-18` | `DEC-001` | 확정 | 코드 구현 없이 PRD와 구현 계획 문서만 작성한다. | 사용자 직접 지시 | 전체 | | `2026-08-18` | `DEC-002` | 확정 | 팔로잉 리스트의 팔로잉 중 액션은 Bottom Sheet를 건너뛰고 팔로우 해제 확인 Dialog로 진입한다. | 사용자 요구사항 | `UNFOLLOW-LIST-004~005`, `P1-T2` | | `2026-08-18` | `DEC-003` | 확정 | 영어 문구는 `Unfollow`, `You won't receive updates from {{creatorName}}.\nUnfollow this creator?`, `Cancel`, `Unfollow`로 사용한다. | 사용자 위임 | `UNFOLLOW-DIALOG-001~004` | | `2026-08-18` | `DEC-004` | 확정 | 한국어 Dialog 본문은 `팔로우를 해제할까요?\n{{creatorName}}의 소식을 받을 수 없어요.`로 사용한다. | 사용자 결정: `OQ-001` | `UNFOLLOW-DIALOG-002`, `P1-T2`, `P2-T1` | | `2026-08-18` | `DEC-005` | 확정 | 팔로우 해제 확인 Dialog는 `SodaV2ActionModal`을 사용한다. | 사용자 결정: `OQ-002` | `UNFOLLOW-DIALOG-001~004`, `P1-T2`, `P2-T1` | | `2026-08-18` | `DEC-006` | 확정 | Visual QA는 에이전트가 진행하지 않고 사용자가 직접 진행한다. | 사용자 직접 지시 | `UNFOLLOW-LIST-001~005`, `UNFOLLOW-CHANNEL-001~003`, `UNFOLLOW-DIALOG-001~004` | ## 18. 변경 관리 요구사항 변경 시 다음을 확인한다. - [x] Decision Log에 변경 이유와 날짜를 기록했다. - [x] 관련 요구사항 상태·본문·수용 기준을 갱신했다. - [x] `plan-task.md`의 범위·Files·체크박스·완료 증거를 코드 변경 전에 갱신했다. - [x] 기존 Progress·검증 기록을 삭제하거나 덮어쓰지 않았다.