# V2 ActionModal 전환 조사 및 구현 ## 목적 V2 폴더/Group에서 사용하는 `SodaDialog`, popup, modal, sheet, toast 계열 표시 지점을 조사하고, 1차 전환 후보를 `SodaV2ActionModal`로 구현한다. 이번 문서는 조사, Phase 1 구현, Phase 2 판단, 검증 기록을 함께 다룬다. ## 조사 범위 - 앱 코드: `SodaLive/Sources/V2/**` - V2에서 직접 호출하는 shared dialog/component: - `SodaLive/Sources/Dialog/**` - `SodaLive/Sources/Report/**` - `SodaLive/Sources/Main/**` - `SodaLive/Sources/Content/Detail/**` - `SodaLive/Sources/Settings/Notification/**` - `SodaLive/Sources/Common/**` - 제외: `Pods/**`, `generated/**`, `build/**` ## 조사 명령 - `rg -n --glob '*.swift' 'SodaDialog\(' 'SodaLive/Sources/V2'` - `rg -n --glob '*.swift' '(NotificationSettingsDialog\(|LivePaymentDialog\(|LiveRoomPasswordDialog\(|EventPopupDialogView\(|LiveRoomDonationDialogView\(|CommunityPostPurchaseDialog\(|CheersReportDialogView\(|CreatorCommunityReportView\(|CreatorCommunityMenuView\()' 'SodaLive/Sources/V2'` - `rg -n --glob '*.swift' '(CreatorChannelSortContextPopup\(|CreatorChannelActionPopup\(|CreatorChannelReplyDetailActionPopup\(|\.sodaToast\(|\.sheet\(|fullScreenCover\()' 'SodaLive/Sources/V2'` - `rg -n --glob '*.swift' 'SodaV2ActionModal\(' 'SodaLive/Sources/V2'` - `ast-grep` pattern: `SodaDialog($$$)` on `SodaLive/Sources/V2` ## 기준 컴포넌트 ### `SodaV2ActionModal` - 정의: `SodaLive/Sources/V2/Component/Modal/SodaV2ActionModal.swift` - API: - `title: String` - `message: String` - `button1: SodaV2ActionModalButton?` - `button2: SodaV2ActionModalButton?` - `onDimmedTap: () -> Void` - 버튼 규칙: - `button1`은 primary action이며 `Color.soda400`로 표시된다. - `button2`는 secondary action이며 `Color.white`로 표시된다. - 렌더링 순서는 `button2`가 왼쪽, `button1`이 오른쪽이다. - 버튼이 1개만 유효하면 primary 스타일로 단독 표시된다. - `label` 또는 `action`이 `nil`이면 해당 버튼은 표시되지 않는다. - UI 규칙: - dimmed background: `Color.black.opacity(0.6)` - modal width: `340` - background: `Color.gray900` - corner radius: `14` ## 1차 전환 후보: 높음 아래 4곳은 V2 내부에서 `SodaDialog(...)`를 직접 호출하며, 모두 확인/취소 또는 삭제/취소 형태의 action-only dialog다. `SodaV2ActionModal` 전환 우선순위가 높다. ### `SodaLive/Sources/V2/Main/MainView.swift` - `authConfirmDialog` - 위치: `MainView.swift:285` - 전환 전: `SodaDialog(title: I18n.Main.Auth.dialogTitle, desc: I18n.Main.Auth.liveEntryVerificationDescription, ...)` - 상태: `isShowAuthConfirmView` - 동작: 본인 인증 안내 후 `BootpayUI` 인증 화면으로 이동하거나 취소 시 `pendingAction`을 비운다. - 적용 결과: `title`/`message`/인증 이동 primary/취소 secondary로 `SodaV2ActionModal`을 적용한다. dimmed tap은 V2 modal 동작에 맞춰 취소 흐름과 동일하게 처리한다. - `leaveLiveNavigationDialog` - 위치: `MainView.swift:303` - 전환 전: `SodaDialog(title: I18n.Common.alertTitle, desc: I18n.LiveRoom.leaveLiveForNavigationDesc, ...)` - 상태: `isShowLeaveLiveNavigationDialog` - 동작: 라이브 시청 중 외부 이동 시 `confirmExternalNavigation()` 또는 `cancelExternalNavigation()` 실행. - 적용 결과: 확인 primary, 취소 secondary로 `SodaV2ActionModal`을 적용한다. dimmed tap은 V2 modal 동작에 맞춰 취소 흐름과 동일하게 처리한다. ### `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` - `authConfirmDialog` - 위치: `CreatorChannelView.swift:329` - 전환 전: `SodaDialog(title: I18n.Chat.Auth.dialogTitle, desc: I18n.Chat.Auth.dialogDescription, ...)` - 상태: `isShowAuthConfirmView` - 동작: 인증 안내 후 `BootpayUI` 인증 화면으로 이동하거나 취소 시 `pendingAction`을 비운다. - 적용 결과: `MainView.authConfirmDialog`와 같은 패턴으로 `SodaV2ActionModal`을 적용한다. dimmed tap은 V2 modal 동작에 맞춰 취소 흐름과 동일하게 처리한다. - community delete confirm - 위치: `CreatorChannelView.swift:597` - 전환 전: `SodaDialog(title: I18n.Common.postDeleteTitle, desc: I18n.Common.confirmDeleteQuestion, ...)` - 상태: `communityViewModel.isShowDeleteConfirm` - 동작: 확인 시 `communityViewModel.deleteCommunityPost(creatorId:)` 후 confirm 상태를 닫고, 취소 시 confirm 상태만 닫는다. - 적용 결과: 삭제 primary, 취소 secondary로 `SodaV2ActionModal`을 적용한다. dimmed tap은 V2 modal 동작에 맞춰 취소와 동일하게 `isShowDeleteConfirm = false` 처리한다. ## 이미 `SodaV2ActionModal`을 사용하는 기준 사례 구현 시 아래 사용처의 구조를 기준으로 삼는다. - `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift:347` - `fanTalkDeleteDialog` - 팬Talk 삭제 확인. - 삭제 primary, 취소 secondary, dimmed tap dismiss 패턴. - `SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailView.swift:54` - 댓글 삭제 확인. - `viewModel.confirmDelete`와 `dismissDeleteDialog` 분리 패턴. - `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift:49` - 답글/댓글 삭제 확인. - `viewModel.confirmDelete`와 `dismissDeleteDialog` 분리 패턴. - `SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift:106` - 작성 중 나가기 확인. - primary가 항상 destructive는 아니며, 강조할 CTA를 `button1`에 둔다. ## 별도 검토 후보 아래 항목은 V2에서 dialog/popup처럼 표시되지만, 입력 폼/도메인 UI/선택 UI가 포함되어 action-only `SodaV2ActionModal`로 단순 전환할 수 있는지 별도 판단이 필요하다. - `SodaLive/Sources/V2/Main/MainView.swift:66` - `NotificationSettingsDialog()` - 알림 설정 전용 dialog. 설정 UI 포함 여부 확인 필요. - `SodaLive/Sources/V2/Main/MainView.swift:74` - `LivePaymentDialog(...)` - 유료 라이브 결제/입장 확인. 시작/현재 시간 등 도메인 표시가 있어 단순 전환 전 UX 확인 필요. - `SodaLive/Sources/V2/Main/MainView.swift:88` - `LiveRoomPasswordDialog(...)` - 비밀번호 입력 field가 있어 action-only modal 전환 대상 가능성이 낮다. - `SodaLive/Sources/V2/Main/MainView.swift:98` - `EventPopupDialogView(eventPopup:)` - 이벤트 콘텐츠 팝업. 이미지/콘텐츠 표시 목적이면 `SodaV2ActionModal`과 목적이 다르다. - `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift:164` - `LiveRoomDonationDialogView(...)` - 후원 금액, 메시지, 비밀후원 입력이 포함된 form dialog. 단순 전환 대상 가능성이 낮다. - `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift:591` - `CreatorCommunityReportView(...)` - 신고 사유 선택 UI. 기존 report flow 재사용 범위로 유지할지 확인 필요. - `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift:614` - `CommunityPostPurchaseDialog(...)` - 유료 게시글 구매 확인. 금액 표시와 구매 action 중심이므로 V2 action modal 통일 여부를 UX 기준으로 결정한다. - `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift:114` - `CommunityPostPurchaseDialog(...)` - 위와 동일한 구매 확인 dialog. `CreatorChannelView` 사용처와 함께 판단한다. - `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift:129` - `CheersReportDialogView(...)` - 신고 사유 선택 dialog. 확인형 action modal과 목적이 다르므로 별도 검토. ## 전환 대상에서 제외할 가능성이 높은 항목 아래 항목은 이름에 popup/modal/presented가 포함되거나 overlay로 표시되지만, 확인형 dialog가 아니므로 이번 `SodaV2ActionModal` 전환 대상에서는 제외하는 것이 안전하다. - `CreatorChannelSortContextPopup` - 정의: `SodaLive/Sources/V2/CreatorChannel/Components/CreatorChannelSortContextPopup.swift` - 호출: - `SodaLive/Sources/V2/Main/Content/All/MainContentAllView.swift:31` - `SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift:77` - `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift:87` - `SodaLive/Sources/V2/CreatorChannel/Series/CreatorChannelSeriesTabView.swift:66` - 이유: 정렬 선택용 context menu다. - `CreatorChannelActionPopup` / `CreatorChannelReplyDetailActionPopup` - 정의: - `SodaLive/Sources/V2/CreatorChannel/FanTalk/Components/CreatorChannelFanTalkActionPopup.swift` - `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailActionPopup.swift` - 호출: - `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift:102` - `SodaLive/Sources/V2/CreatorChannel/Community/Detail/Components/CreatorChannelCommunityPostDetailCommentListView.swift:35` - `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/Components/CreatorChannelReplyDetailFeedView.swift:48` - 이유: 수정/삭제 action 선택용 anchored context popup이며, 실제 삭제 확인은 별도 modal이 담당한다. - `.sheet(isPresented:)` - `SodaLive/Sources/V2/CreatorChannel/Community/CreatorChannelCommunityTabView.swift:23` - 이유: 댓글 리스트 화면 표시용 sheet다. - `.fullScreenCover(isPresented:)` - `SodaLive/Sources/V2/Main/MainView.swift:118` - `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift:190` - 이유: `BootpayUI` 인증 화면 presentation이다. - `.sodaToast(...)` - 이유: `isShowPopup` 네이밍을 쓰는 ViewModel이 많지만 실제 UI는 toast다. action modal 전환 대상이 아니다. - 주요 호출 파일: - `SodaLive/Sources/V2/Main/MainView.swift` - `SodaLive/Sources/V2/Main/Chat/MainChatView.swift` - `SodaLive/Sources/V2/Main/Content/Overview/ContentOverviewView.swift` - `SodaLive/Sources/V2/Main/Content/All/MainContentAllView.swift` - `SodaLive/Sources/V2/Main/Content/Ranking/MainContentRankingView.swift` - `SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift` - `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift` - `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` - `SodaLive/Sources/V2/CreatorChannel/Community/CreatorChannelCommunityTabView.swift` - `SodaLive/Sources/V2/CreatorChannel/Community/Detail/CreatorChannelCommunityPostDetailView.swift` - `SodaLive/Sources/V2/CreatorChannel/ReplyDetail/CreatorChannelReplyDetailView.swift` - `SodaLive/Sources/V2/CreatorChannel/Live/CreatorChannelLiveTabView.swift` - `SodaLive/Sources/V2/CreatorChannel/Audio/CreatorChannelAudioTabView.swift` - `SodaLive/Sources/V2/CreatorChannel/Series/CreatorChannelSeriesTabView.swift` - `SodaLive/Sources/V2/CreatorChannel/Donation/CreatorChannelDonationTabView.swift` - `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift` - `SodaLive/Sources/V2/CreatorChannel/FanTalk/Write/CreatorChannelFanTalkWriteView.swift` ## 구현 체크리스트 ### Phase 1: 1차 전환 후보 적용 - [x] **Task 1.1: `MainView.authConfirmDialog`를 `SodaV2ActionModal`로 전환** - 대상 파일: `SodaLive/Sources/V2/Main/MainView.swift` - 검증 기준: `rg "authConfirmDialog|SodaV2ActionModal|SodaDialog" SodaLive/Sources/V2/Main/MainView.swift` 실행 시 `authConfirmDialog`가 `SodaV2ActionModal`을 사용한다. - [x] **Task 1.2: `MainView.leaveLiveNavigationDialog`를 `SodaV2ActionModal`로 전환** - 대상 파일: `SodaLive/Sources/V2/Main/MainView.swift` - 검증 기준: `confirmExternalNavigation()` / `cancelExternalNavigation()` 호출 흐름이 유지된다. - [x] **Task 1.3: `CreatorChannelView.authConfirmDialog`를 `SodaV2ActionModal`로 전환** - 대상 파일: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` - 검증 기준: 인증 이동, 취소 시 `pendingAction = nil` 흐름이 유지된다. - [x] **Task 1.4: `CreatorChannelView` community delete confirm을 `SodaV2ActionModal`로 전환** - 대상 파일: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` - 검증 기준: 삭제 확인, 취소, dimmed tap에서 `communityViewModel.isShowDeleteConfirm` 상태가 올바르게 닫힌다. ### Phase 2: 별도 검토 후보 판단 - [x] **Task 2.1: 구매/결제 dialog 전환 여부 결정** - 대상 파일: - `SodaLive/Sources/Dialog/LivePaymentDialog.swift` - `SodaLive/Sources/Dialog/CommunityPostPurchaseDialog.swift` - `SodaLive/Sources/V2/Main/MainView.swift` - `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` - `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift` - 검증 기준: action-only modal로 대체 가능한지, 금액/시간 표시가 필요한지 결정이 문서에 남는다. - [x] **Task 2.2: 입력/신고/이벤트 dialog 제외 여부 결정** - 대상 파일: - `SodaLive/Sources/Dialog/LiveRoomPasswordDialog.swift` - `SodaLive/Sources/Content/Detail/LiveRoomDonationDialogView.swift` - `SodaLive/Sources/Report/CheersReportDialogView.swift` - `SodaLive/Sources/Main/EventPopupDialogView.swift` - 검증 기준: `SodaV2ActionModal` 전환 제외 또는 별도 V2 컴포넌트화 여부가 문서에 남는다. ## Phase 2 판단 결과 ### 구매/결제 dialog - `SodaLive/Sources/Dialog/CommunityPostPurchaseDialog.swift` - 결정: 후속 구현에서 `SodaV2ActionModal` 전환 후보로 확정한다. - 이유: 제목, 설명, 취소, `I18n.Dialog.CommunityPostPurchase.viewWithCans(can)` 확인 CTA만 있는 action-only 구매 확인 dialog다. - V2 호출부: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift`, `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift` - 후속 전환 방향: 구매 CTA를 `button1`, 취소를 `button2`, dimmed tap을 취소와 동일하게 `isShowing = false` 흐름으로 둔다. - `SodaLive/Sources/Dialog/LivePaymentDialog.swift` - 결정: 현재 shared dialog를 유지한다. - 이유: 기본 확인/취소 구조 외에 유료 라이브 시작 시간, 현재 시간, 추가 설명 영역을 조건부로 표시한다. 현재 `SodaV2ActionModal`의 `title/message/buttons` API로 단순 대체하면 시간 정보가 손실된다. - V2 호출부: `SodaLive/Sources/V2/Main/MainView.swift` - 후속 방향: V2 통일이 필요하면 `SodaV2ActionModal` 확장이 아니라 결제 전용 V2 dialog/component로 별도 설계한다. ### 입력/신고/이벤트 dialog - `SodaLive/Sources/Dialog/LiveRoomPasswordDialog.swift` - 결정: `SodaV2ActionModal` 전환 대상에서 제외한다. - 이유: 비밀번호 입력 field, 키보드 대응, 캔 수 조건부 CTA가 있는 input dialog다. - V2 호출부: `SodaLive/Sources/V2/Main/MainView.swift` - 후속 방향: 필요 시 V2 input modal로 별도 컴포넌트화한다. - `SodaLive/Sources/Content/Detail/LiveRoomDonationDialogView.swift` - 결정: `SodaV2ActionModal` 전환 대상에서 제외한다. - 이유: 후원 금액 입력, 단축 금액 버튼, 비밀 후원 토글, 메시지 입력, 충전 이동, validation toast를 포함한 복합 form/bottom-sheet 성격이다. - V2 호출부: `SodaLive/Sources/V2/CreatorChannel/CreatorChannelView.swift` - 후속 방향: 필요 시 donation 전용 V2 modal/bottom-sheet로 별도 설계한다. - `SodaLive/Sources/Report/CheersReportDialogView.swift` - 결정: `SodaV2ActionModal` 전환 대상에서 제외한다. - 이유: 신고 사유 radio list 선택 상태가 필요한 selector dialog다. - V2 호출부: `SodaLive/Sources/V2/CreatorChannel/FanTalk/CreatorChannelFanTalkTabView.swift` - 후속 방향: 필요 시 V2 report selector 컴포넌트로 별도 설계한다. - `SodaLive/Sources/Main/EventPopupDialogView.swift` - 결정: `SodaV2ActionModal` 전환 대상에서 제외한다. - 이유: 원격 이미지 중심 콘텐츠 팝업이며 이미지 탭 상세 이동, 다시 보지 않기, 닫기 액션을 제공한다. 텍스트 확인형 action modal과 목적이 다르다. - V2 호출부: `SodaLive/Sources/V2/Main/MainView.swift` - 후속 방향: 필요 시 이벤트 팝업 전용 V2 component로 별도 설계한다. ## 검증 기록 - 2026-07-08: `rg`와 `ast-grep`로 `SodaLive/Sources/V2` 내부 `SodaDialog(...)` 직접 호출이 4곳임을 확인했다. 대상은 `MainView.authConfirmDialog`, `MainView.leaveLiveNavigationDialog`, `CreatorChannelView.authConfirmDialog`, `CreatorChannelView` community delete confirm이다. - 2026-07-08: `rg 'SodaV2ActionModal\(' SodaLive/Sources/V2`로 이미 적용된 기준 사례 4곳을 확인했다. 대상은 `CreatorChannelView.fanTalkDeleteDialog`, `CreatorChannelCommunityPostDetailView`, `CreatorChannelReplyDetailView`, `CreatorChannelFanTalkWriteView`다. - 2026-07-08: `rg`로 V2에서 호출하는 shared dialog 컴포넌트를 확인하고, 구매/결제/입력/신고/이벤트 dialog는 action-only modal 전환 여부를 별도 검토 후보로 분리했다. - 2026-07-08: `isShowPopup` 검색 결과의 대부분은 `.sodaToast(...)` 상태임을 확인해 `SodaV2ActionModal` 전환 대상에서 제외했다. - 2026-07-08: Phase 1 구현으로 `MainView.authConfirmDialog`, `MainView.leaveLiveNavigationDialog`, `CreatorChannelView.authConfirmDialog`, `CreatorChannelView` community delete confirm을 `SodaV2ActionModal`로 전환했다. 확인/취소 action은 유지하고, dimmed tap은 V2 modal 동작에 맞춰 각 취소 흐름과 동일하게 연결했다. - 2026-07-08: 전환 후 `rg -n --glob '*.swift' 'SodaDialog\(' 'SodaLive/Sources/V2'` 결과가 없어 V2 내부 직접 `SodaDialog(...)` 호출이 남지 않았음을 확인했다. - 2026-07-08: `xcodebuild -workspace SodaLive.xcworkspace -scheme SodaLive-dev -configuration Debug -sdk iphonesimulator -derivedDataPath /tmp/SodaLiveDerivedData-SodaLiveDev CODE_SIGNING_ALLOWED=NO build` 실행 결과 `BUILD SUCCEEDED`를 확인했다. - 2026-07-08: 기존 `SodaDialog`는 dimmed tap dismiss가 없지만, 이번 전환에서는 `SodaV2ActionModal`의 현재 UX에 맞춰 `onDimmedTap`을 각 취소 흐름과 동일하게 연결하는 것을 의도된 동작으로 정리했다. - 2026-07-08: Phase 2 구매/결제 dialog 검토 결과, `CommunityPostPurchaseDialog`는 action-only 구매 확인 dialog이므로 후속 `SodaV2ActionModal` 전환 후보로 확정하고, `LivePaymentDialog`는 시작/현재 시간 정보 박스와 추가 설명이 있어 shared dialog 유지로 결정했다. - 2026-07-08: Phase 2 입력/신고/이벤트 dialog 검토 결과, `LiveRoomPasswordDialog`, `LiveRoomDonationDialogView`, `CheersReportDialogView`, `EventPopupDialogView`는 입력/선택/이미지 콘텐츠 UI를 포함하므로 `SodaV2ActionModal` 전환 대상에서 제외하고 필요 시 별도 V2 컴포넌트화 대상으로 결정했다.