Files
sodalive-ios/docs/20261006_선물_UI_추가/prd.md
T

36 KiB

크리에이터 선물하기 사용자 기능 PRD

문서 정보

항목 내용
문서 상태 구현 기준 확정
작성일 2026-10-06
최종 수정일 2026-10-08
대상 제품 크리에이터 선물 보내기, 선물함, 선물 상세, 운송장 등록, 배송지 입력, 선물 딥링크
작성자·결정권자 사용자 요청 기준
관련 API Contract 이 문서 §11 API 계약
관련 구현 계획 docs/20261006_선물_UI_추가/plan-task.md
관련 review 없음

요구사항 상태

상태 의미 구현 처리
확정 제품·기술 결정이 완료되어 구현 기준으로 사용 plan-task.md의 Phase/Task와 완료 증거로 추적
제외 현재 릴리스에서 구현하지 않기로 결정 Non-Goals와 Decision Log에 기록

1. Overview

크리에이터 선물하기는 팬이 크리에이터에게 오프라인 선물을 신청하고, 소다라이브 사서함과 검수 과정을 거쳐 안전하게 전달되도록 돕는 iOS 사용자 기능이다. 팬은 선물 신청, 운송장 등록, 신청 취소, 보낸 선물 상세 조회를 수행하고, 크리에이터는 받은 선물 목록 확인, 배송지 입력, 전달 상태 확인을 수행한다.

기존 선물 진입점 UI는 Phase 1에서 구현 완료됐다. 이번 문서는 해당 진입점을 실제 선물 보내기/선물함/상세/딥링크 흐름에 연결하는 후속 구현 기준이다.

2. Problem Statement

  • 현재 iOS에는 선물 기능 진입점만 있고 실제 신청, 목록, 상세, 운송장, 배송지 입력 흐름이 연결되어 있지 않다.
  • 선물 기능은 팬과 크리에이터의 개인정보 노출 경계가 다르므로, direction별 UI 분기와 표시 금지 필드를 명확히 고정해야 한다.
  • 신청 성공, 목록 item, 푸시/딥링크가 서로 다른 상세 화면으로 흩어지면 상태 분기와 개인정보 노출 회귀 위험이 커진다.

문제를 해결했다는 판단은 모든 진입점이 같은 상세 조회 흐름을 사용하고, API 계약·Figma 구조·개인정보 표시 금지 조건이 검증되는 것으로 한다.

3. Goals

3.1 제품 목표

  • 팬이 크리에이터 상세에서 선물 신청을 완료하고, 신청 완료 후 같은 신청번호의 상세로 이동한다.
  • 사용자가 선물함에서 보낸 선물과 받은 선물을 type=ALL 목록으로 확인하고 상세로 진입한다.
  • 보낸 팬은 운송장 등록과 신청 취소를 수행할 수 있다.
  • 받은 크리에이터는 필요한 경우 배송지를 입력하고 전달 상태를 확인할 수 있다.
  • 푸시/딥링크, 리스트 item, 신청 완료 이동이 모두 GET /api/v2/gifts/{applicationNo} 기반 상세 화면을 사용한다.

3.2 UX 목표

  • Figma의 iPhone 13 402px 폭, black/dark surface, 14pt 좌우 여백, rounded card/input/banner, section divider 구조를 따른다.
  • 상단 navigation/status 영역은 고정하고 navigation 아래 본문만 스크롤한다.
  • bottom action bar, keyboard avoidance, loading/error/toast/dialog는 기존 iOS 앱 패턴을 재사용한다.
  • Figma에 보이는 신청 내역, 선물 내역 섹션 타이틀은 실제 선물함 리스트에 노출하지 않는다.

4. Non-Goals

  • 관리자 화면.
  • 새 디자인 시스템 생성.
  • 외부 택배 조회 또는 운송장 실시간 검증.
  • 약관 본문 제공 API 신규 구현.
  • 선물 이미지, 첨부파일, 메시지 카드.
  • 임의 URL 연결. 정책/문의/리뷰 등 기존 앱 이동 경로가 없으면 숨기거나 준비 중입니다. toast만 사용한다.
  • API에 없는 취소일 기준 재정렬, 사이즈 설명 서버 필드 추정, 오류 status/key 추정.

5. Target Users and Permissions

사용자 목표 주요 작업 사용 환경
팬 크리에이터에게 선물을 신청하고 진행 상태를 확인 선물 신청, 선물함 조회, 운송장 등록, 신청 취소, 상세 조회 iOS 앱
크리에이터 팬이 보낸 선물을 받을 수 있게 배송지를 입력하고 상태를 확인 선물함 조회, 배송지 입력, 상세 조회, 문의 iOS 앱

권한은 기존 인증 토큰과 서버 리소스 소유권 검증을 따른다. 권한 오류 또는 미존재 오류는 기존 앱의 딥링크/상세 오류 처리 패턴을 사용한다.

6. 핵심 사용자 흐름

  1. 팬이 크리에이터 상세의 선물 보내기 버튼으로 신청 화면에 진입한다.
  2. 앱은 전달받은 크리에이터 닉네임과 memberId를 검증하고 GET /api/v2/gifts/form-options를 호출한다.
  3. 팬은 사이즈, 카테고리, 발신자 정보, 약관 동의를 입력하고 POST /api/v2/gifts를 호출한다.
  4. 신청 성공 시 완료 dialog에 머무르지 않고 응답의 applicationNo로 상세 화면을 replace 또는 stack 정리 방식으로 연다.
  5. 사용자는 마이 탭 선물함에서 GET /api/v2/gifts?type=ALL&page=0&size=20 목록을 확인한다.
  6. 리스트 item 탭, 신청 완료 이동, 푸시 딥링크는 모두 같은 선물 상세 route로 진입한다.
  7. 상세 화면은 direction, trackingRequired, recipientAddressRequired, status에 따라 보낸 상세, 받은 상세, 운송장 CTA, 배송지 입력 UI, 취소 완료 UI를 분기한다.

7. 정보 구조와 라우팅

크리에이터 상세
  선물 보내기 버튼
    -> GiftSendView(recipientMemberId, recipientCreatorNickname, recipientCreatorProfileImageUrl)
      -> success replace GiftDetailView(applicationNo)

마이 탭
  선물함 버튼
    -> GiftBoxListView
      -> GiftDetailView(applicationNo)

GiftDetailView(applicationNo)
  direction=SENT
    -> GiftTrackingRegistrationView(applicationNo)
    -> GiftCancelConfirmDialog
    -> GiftCancelCompleteView 또는 GiftDetailView(status=CANCELED)
  direction=RECEIVED
    -> inline recipient address form when recipientAddressRequired=true

DeepLink GIFT_DETAIL(applicationNo)
DeepLink gift-detail/applicationNo
  -> GiftDetailView(applicationNo)

신규 route는 기존 AppStep/AppState/ContentView 방식에 맞춰 추가한다. 신청 성공과 취소 성공은 뒤로가기로 직전 입력/등록 화면에 돌아가지 않도록 replaceCurrentAppStep 또는 동등한 stack 정리 정책을 사용한다.

8. 기능 요구사항

8.1 기존 진입점

ID 상태 요구사항 수용 기준 계약/Goal 연결
GIFT-001 확정 크리에이터 상세에 선물 보내기 버튼을 표시한다. 내 채널이 아니고 creator.isDmAvailable == true, 한국 접속, LanguageHeaderProvider.current == "ko"일 때만 표시된다. P1-T3
GIFT-002 확정 마이 탭 카테고리 그리드 첫 항목에 선물함을 표시한다. ic_my_present 아이콘과 선물함 텍스트가 보관함 앞에 표시된다. P1-T4
GIFT-003 확정 기존 inert action을 실제 선물 화면 이동으로 교체한다. 선물 보내기는 신청 화면, 선물함은 리스트 화면으로 이동한다. P2-T3
GIFT-004 확정 크리에이터 채널의 선물 보내기 버튼을 흰색 capsule로 표시한다. ic_send_gift가 문구 왼쪽에 표시되고 배경은 흰색, 문구는 검은색이며 기존 노출 조건과 이동 동작은 유지된다. P11-T3

8.2 선물 보내기 신청

ID 상태 요구사항 수용 기준 계약/Goal 연결
SEND-001 확정 진입 시 크리에이터 닉네임과 memberId를 소비한다. 닉네임은 받는 크리에이터 영역에 표시되고 memberId는 recipientMemberId로 전송된다. 둘 중 하나가 없으면 기존 오류/뒤로가기 패턴을 따른다. P3-T1
SEND-002 확정 화면 진입 시 폼 옵션 API를 호출한다. GET /api/v2/gifts/form-options의 첫 번째 sizes를 기본 선택하고 categories를 picker/bottom sheet/dialog로 선택한다. P3-T1
SEND-003 확정 사이즈 설명은 로컬 매핑을 사용한다. SMALL, MEDIUM, LARGE 설명이 요구 문구와 일치한다. P3-T2
SEND-004 확정 카테고리의 파손 면책 요구를 반영한다. requiresDamageWaiver=true일 때만 면책 동의 row를 표시하고 필수로 검증한다. false로 바뀌면 숨기고 damageWaiverAgreed=false로 초기화한다. P3-T2
SEND-005 확정 필수 입력/동의가 충족될 때만 CTA를 활성화한다. 미충족 상태에서는 POST /api/v2/gifts를 호출하지 않는다. submit 중 중복 탭이 방지된다. P3-T3
SEND-006 확정 신청 성공 후 상세 화면으로 이동한다. 응답의 applicationNo로 GiftDetailView를 replace 방식으로 열고 뒤로가기로 입력 폼에 돌아가지 않는다. P3-T4
SEND-007 확정 보내는 사람 이름과 휴대폰 번호를 제한한다. 이름은 한글·영문 1~12자이며 한글 조합 중 임시 문자열을 확정값으로 덮어쓰지 않는다. 휴대폰은 편집 중 ASCII 숫자 최대 11자, 제출 시 010으로 시작하는 정확히 11자리만 허용한다. P10-T1~P10-T4
SEND-008 확정 크리에이터 채널에서 받은 프로필 이미지 URL을 선물 보내기 화면까지 전달한다. 받는 크리에이터 영역에 전달받은 프로필 이미지가 표시되며 기존 닉네임과 recipientMemberId 전달은 유지된다. P11-T2

8.3 선물함 리스트

ID 상태 요구사항 수용 기준 계약/Goal 연결
LIST-001 확정 선물함은 type=ALL 목록만 호출한다. SENT, RECEIVED를 따로 호출하지 않고 items[].direction으로 카드 UI를 결정한다. P4-T1
LIST-002 확정 pagination과 refresh를 지원한다. hasNext=true이면 하단 도달 시 다음 page를 조회하고, pull-to-refresh가 가능하면 page 0부터 다시 조회한다. P4-T2
LIST-003 확정 보낸/받은 선물함 상단 안내 배너와 empty를 분기한다. 보낸 선물 목록은 상단 안내 배너 사서함을 통해 안전하게 전달됩니다를 표시한다. 받은 선물 목록에 item이 있으면 상단 안내 배너 배송지 입력 기간 안내를 표시한다. 보낸 empty는 보낸 선물 상단 안내 배너만 표시하고, 받은 empty는 제목/본문/의견 남기기 버튼을 표시한다. P4-T3
LIST-004 확정 받은 선물의 배송지 미입력 상태는 각 카드에서 표시한다. 별도 파란 상단 배너를 표시하지 않는다. 목록 응답 기준 direction=RECEIVED && status=TRACKING_REGISTERED인 item 카드에 배송지 입력 필요 상태를 표시하고, 해당 카드 tap 시 그 선물 상세로 이동한다. recipientAddressDeadlineAt이 없으면 임의 남은 일수를 만들지 않는다. P4-T3
LIST-005 확정 리스트 item 탭은 상세 화면으로 이동한다. applicationNo를 전달하고 상세 API 결과로 UI를 분기한다. P4-T4

8.4 공통 상세와 보낸 상세

ID 상태 요구사항 수용 기준 계약/Goal 연결
DETAIL-001 확정 모든 상세 진입은 상세 API를 호출한다. 리스트, 신청 성공, 푸시 딥링크가 모두 GET /api/v2/gifts/{applicationNo} 결과의 direction으로 렌더링한다. P5-T1, P7-T1
DETAIL-002 확정 progress는 statusTimeline 5단계를 사용한다. RECEIVED, TRACKING_REGISTERED, ARRIVED_AT_MAILBOX, INSPECTION_COMPLETED, DELIVERED 순서를 사용하고 UNDELIVERABLE, CANCELED를 step으로 추가하지 않는다. P5-T2
DETAIL-003 확정 보낸 상세 개인정보 경계를 지킨다. recipientAddress를 표시하지 않고, senderInfo와 mailbox는 있을 때만 표시한다. P5-T3
DETAIL-004 확정 trackingRequired=true일 때만 운송장 등록 CTA를 표시한다. CTA 탭 시 운송장 등록 화면으로 이동한다. P5-T4
DETAIL-005 확정 direction=SENT && status=RECEIVED이면 신청 취소 action을 제공할 수 있다. 확인 dialog의 destructive action 이후에만 취소 API를 호출한다. P5-T5
DETAIL-006 확정 status=CANCELED는 취소 완료 UI를 표시한다. 취소 전 상세/등록 화면으로 back 되지 않도록 replace 한다. P5-T5
DETAIL-007 확정 선물 상세의 모든 문의하기 버튼은 고객센터의 TALK 문의와 같은 action을 실행한다. 발신·수신 전달 불가와 취소 완료 상세에서 앱 내 고객센터로 이동하지 않고 공용 TALK action으로 http://pf.kakao.com/_lkxgxhG/chat을 연다. 기존 문구·노출 조건·스타일은 유지한다. P12-T1

8.5 운송장 등록

ID 상태 요구사항 수용 기준 계약/Goal 연결
TRACK-001 확정 운송장 등록 화면 진입 시 상세를 재조회한다. 재조회 결과 trackingRequired=false이면 기존 오류 처리 후 상세로 복귀한다. P6-T1
TRACK-002 확정 mailbox가 없으면 등록을 막는다. 받을 주소가 등록되어 있지 않습니다. 오류를 표시하고 submit을 허용하지 않는다. P6-T1
TRACK-003 확정 택배사 목록은 기존 공통 상수가 있으면 재사용하고 없으면 로컬 상수를 사용한다. 선택한 한글 표시명을 그대로 courierCompanyName으로 보낸다. P6-T2
TRACK-004 확정 택배사와 운송장 번호가 있을 때만 등록 CTA를 활성화한다. 공백 운송장 번호는 submit하지 않고, submit 중 중복 탭을 방지한다. P6-T3
TRACK-005 확정 등록 성공 후 상세를 갱신한다. 성공 toast 후 상세로 replace/복귀하고 상세 API 재조회 결과로 TRACKING_REGISTERED를 표시한다. P6-T3
TRACK-006 확정 운송장 등록 화면의 신청 취소는 선물 신청을 취소한다. 확인 dialog의 destructive action 이후에만 기존 취소 API를 호출하고, 성공 시 동일 신청번호 상세로 guarded replace해 취소 완료 UI를 표시한다. P6-T2, P6-T3
TRACK-007 확정 운송장 번호는 숫자 형식과 길이를 입력·제출 경계에서 검증한다. 편집 중 ASCII 숫자 최대 14자만 허용하고, 택배사 선택과 함께 10~14자리일 때만 등록 CTA와 POST를 허용한다. P10-T5

8.6 받은 상세와 배송지 입력

ID 상태 요구사항 수용 기준 계약/Goal 연결
RECV-001 확정 받은 상세 개인정보 경계를 지킨다. senderInfo, mailbox, giftInfo.applicationNo, giftInfo.paidCan, giftInfo.tracking을 표시하지 않는다. P7-T1
RECV-002 확정 recipientAddressRequired=true이면 배송지 입력 UI를 표시한다. 이름, 휴대폰, 우편번호, 주소, 상세 주소와 약관 2개를 입력한다. P7-T2
RECV-003 확정 배송지 CTA는 필수값과 약관 2개가 충족될 때만 활성화한다. validation 실패 시 POST /recipient-address를 호출하지 않는다. P7-T3
RECV-004 확정 배송지 입력 성공 후 상세를 재조회한다. 성공 toast 후 배송지 입력 완료 UI를 표시한다. P7-T3
RECV-005 확정 recipientAddress=null이면 받는 주소 섹션 전체를 숨긴다. 빈 섹션, skeleton, - 값을 표시하지 않는다. P7-T4
RECV-006 확정 status=UNDELIVERABLE이면 배송지 입력과 주소 섹션을 숨긴다. 전달 불가 hero, 문의/정책 action, 사유/폐기 안내만 표시한다. P7-T4
RECV-007 확정 받는 크리에이터 배송지의 이름과 휴대폰 번호에 보내는 사람과 같은 규칙을 적용한다. 이름은 한글·영문 1~12자이며 한글 조합을 보존한다. 휴대폰은 편집 중 ASCII 숫자 최대 11자, 제출 시 010으로 시작하는 정확히 11자리만 허용한다. P10-T1~P10-T4
RECV-008 확정 전달 불가 사유 아래 붉은 안내 문구는 앱 내 고정 문구로 표시한다. 발신·수신 상세 모두 해당 선물은 보관기간(입고일 포함 최대 7일) 내에 반송 등 필요한 조치가 완료되지 않을 경우, 사전에 동의한 약관 및 폐기 등의 내용에 따라 폐기될 수 있습니다. 자세한 내용은 고객센터로 문의해 주세요.를 표시하고 서버 응답 필드는 추가하지 않는다. P7-R1

8.7 약관, 딥링크, 날짜

ID 상태 요구사항 수용 기준 계약/Goal 연결
COMMON-001 확정 sender/recipient 약관 (필수) 링크는 같은 Notion 페이지로 이동한다. URL은 https://app.notion.com/p/3eb27b359673802b937ae741ab7b4ad3를 사용한다. P3-T2, P7-T2
COMMON-002 확정 날짜는 UTC ISO 문자열을 앱 표시 형식으로 변환한다. 상세 필드는 YYYY.MM.DD, progress는 MM.DD로 표시한다. P2-T2
COMMON-003 확정 푸시 딥링크는 deepLinkValue=GIFT_DETAIL, deepLinkId=applicationNo를 사용한다. 딥링크 진입 시 상세 API를 먼저 호출하고 응답으로 UI를 결정한다. P8-T1
COMMON-004 확정 API 실패는 기존 오류 노출 방식을 사용한다. 입력값과 스크롤 상태를 불필요하게 초기화하지 않는다. 각 Phase Gate
COMMON-005 확정 선물 상세의 전달 불가 선물 정책 확인은 약관 (필수) 링크와 같은 페이지로 이동한다. 발신·수신 상세 모두 GiftConstants.termsURL을 시스템 브라우저로 연다. P2-R3, P5-T4, P7-T4
COMMON-006 확정 voiceon://gift-detail/{applicationNo}와 voiceon-test://gift-detail/{applicationNo} 경로형 딥링크를 지원한다. gift-detail 뒤 첫 path component를 신청번호로 사용해 기존 GiftDetailView(applicationNo)로 이동하며 scheme별 별도 분기는 추가하지 않는다. P11-T1
COMMON-007 확정 파손 면책 동의 (필수)는 별도 파손 정책 페이지로 이동한다. requiresDamageWaiver=true인 동의 행의 (필수)만 https://app.notion.com/p/3f227b3596738018ba5dea2e5205293b를 열고 기존 sender/recipient 약관 링크는 유지한다. P11-T2

9. 반응형 기능 범위

기능 iPhone iPad 비고
선물 보내기 전체 전체 iPhone 13 402px Figma 구조 우선, 기존 SwiftUI adaptive 정책 적용
선물함 전체 전체 navigation 고정, 본문 scroll
선물 상세 전체 전체 status/action별 bottom bar safe area 유지
운송장 등록 전체 전체 keyboard avoidance 필요
배송지 입력 전체 전체 keyboard avoidance 필요
딥링크 전체 전체 권한/미존재 오류는 기존 패턴

10. UI/UX Expectations

10.1 Figma 기준

  • 선물 보내기: 2481:18904, 2481:18957
  • 선물함 리스트: 2481:19382, 2520:35587, 2531:36283, 2531:36194, 2531:36237
  • 보낸 상세/운송장: 2481:19011, 2481:19152, 2481:19320, 2481:19456, 2481:19500, 2481:19521, 2481:19066, 2481:19109
  • 받은 상세/배송지: 2531:36290, 2531:36399, 2531:36330, 2531:36422

10.2 디자인과 component 원칙

  • 기존 V2 dark surface, TitleBar/DefaultTitleBar, SodaV2ActionModal, sodaToast, LottieRefreshableScrollView, 기존 input/button/picker/dialog 패턴을 재사용한다.
  • 재사용 근거가 없는 선물 전용 UI는 SodaLive/Sources/V2/Gift/**/Components에 둔다.
  • 여러 선물 화면에서 같은 의미로 반복되는 card/banner/field/action bar만 SodaLive/Sources/V2/Gift/Components/** 또는 기존 V2/Component/**로 올린다.
  • 새 디자인 시스템, 새 dependency, 새 전역 style token은 만들지 않는다.

10.3 화면 상태

  • 모든 network 화면은 loading, error, empty/success 상태를 가진다.
  • mutation은 submit 중 중복 탭을 막고 실패 시 입력값을 유지한다.
  • destructive action인 신청 취소는 확인 dialog 없이 API를 호출하지 않는다.
  • P6-T2는 운송장 등록·신청 취소의 입력 및 확인 UI만 구성하고 mutation은 P6-T3에서 연결한다.

11. API 계약

11.1 공통 규칙

  • base URL과 인증 header는 기존 Moya TargetType/UserDefaultsKey.token 패턴을 따른다.
  • 모든 응답은 공통 envelope를 사용하며 payload는 ApiResponse<T>.data 또는 기존 data unwrap 결과다.
  • 이 문서의 endpoint/request/response 필드는 사용자 제공 프롬프트를 확정 계약으로 삼는다.
  • 폼 옵션의 sizes[], categories[] 표시명 key는 각각 name이며, 상세 statusTimeline[]은 statusName을 포함한다.
  • 상세 giftInfo.applicationNo는 null일 수 있으며, 화면과 후속 요청에는 top-level data.applicationNo를 권위 값으로 사용한다.
  • page는 이 기능 API 계약 기준으로 0-base 요청을 사용한다. 기존 앱의 1-base wrapper를 재사용할 경우 adapter에서 명시적으로 변환하지 않는다.

11.2 Endpoint 추적

요구사항 Method Path 계약 상태 소유 Goal
SEND-002 GET /api/v2/gifts/form-options 확정 P2-T1, P3-T1
SEND-006 POST /api/v2/gifts 확정 P2-T1, P3-T4
LIST-001 GET /api/v2/gifts?type=ALL&page=0&size=20 확정 P2-T1, P4-T1
DETAIL-001 GET /api/v2/gifts/{applicationNo} 확정 P2-T1, P5-T1, P7-T1
DETAIL-005, TRACK-006 POST /api/v2/gifts/{applicationNo}/cancel 확정 P2-T1, P5-T5, P6-T3
TRACK-005 POST /api/v2/gifts/{applicationNo}/tracking 확정 P2-T1, P6-T3
RECV-004 POST /api/v2/gifts/{applicationNo}/recipient-address 확정 P2-T1, P7-T3
fan review POST /api/v2/gifts/{applicationNo}/review 기존 리뷰 플로우 있을 때만 연결 후속

11.3 Enum과 상태 표시

  • direction: SENT, RECEIVED
  • status: RECEIVED, TRACKING_REGISTERED, ARRIVED_AT_MAILBOX, INSPECTION_COMPLETED, DELIVERED, UNDELIVERABLE, CANCELED
  • size code: SMALL, MEDIUM, LARGE

11.4 운송장 등록 택배사 목록

기존 앱 공통 택배사 상수가 있으면 우선 재사용한다. 없으면 아래 한글 표시명을 로컬 상수로 사용하고, 선택한 값을 그대로 courierCompanyName으로 보낸다.

  • CJ대한통운
  • 우체국택배
  • GS25 편의점택배
  • CU 편의점택배
  • 한진택배
  • 롯데택배
  • 로젠택배
  • 경동택배
  • 대신택배
  • 일양로지스
  • 천일택배
  • 합동택배
  • 건영택배
  • 농협택배

12. 보안과 데이터 취급

  • 보낸 팬 상세에는 크리에이터가 입력한 recipientAddress를 표시하지 않는다.
  • 받은 크리에이터 상세에는 팬의 senderInfo, mailbox, giftInfo.applicationNo, giftInfo.paidCan, giftInfo.tracking을 표시하지 않는다.
  • recipientAddress=null이면 주소 섹션을 통째로 숨긴다.
  • UNDELIVERABLE 받은 상세에서는 응답에 주소가 있어도 받는 주소 섹션을 표시하지 않는다.
  • 이름, 휴대폰, 주소, 운송장 번호, token은 debug log, error log, fixture 문서에 기록하지 않는다.

13. 성능과 품질 요구사항

  • 신규 의존성을 추가하지 않는다.
  • 목록은 hasNext 기반 pagination을 사용하고 서버 응답 순서를 유지한다.
  • 화면별 ViewModel은 기존 ObservableObject, Combine, MoyaProvider, ApiResponse<T> 패턴을 따른다.
  • 현재 테스트 번들 타깃이 확인되지 않으므로 구현 계획은 가능한 ViewModel 순수 로직 test를 우선 요구하되, 불가능한 SwiftUI visual 분기는 build와 수동 검증으로 대체한다.
  • 기본 검증 명령은 xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build와 SodaLive-dev Debug build다.

14. 성공 기준

14.1 기능 수용 기준

  • 선물 보내기 진입 시 전달받은 닉네임과 memberId가 화면/API에 연결된다.
  • 선물 보내기 진입 시 전달받은 크리에이터 프로필 이미지 URL이 받는 크리에이터 영역에 표시된다.
  • 필수 입력/동의 누락 시 신청 API를 호출하지 않는다.
  • requiresDamageWaiver=true 카테고리에서만 파손 면책 동의가 표시되고 필수다.
  • 신청 성공 후 applicationNo 상세로 replace 이동하고 뒤로가기로 입력 폼에 돌아가지 않는다.
  • 선물함 리스트는 type=ALL 한 번 조회와 direction 카드 분기를 사용한다.
  • Figma의 신청 내역, 선물 내역 섹션 타이틀이 노출되지 않는다.
  • 리스트 item, 푸시 딥링크, 신청 완료 이동이 같은 상세 조회 플로우를 사용한다.
  • 양 앱 scheme의 gift-detail/{applicationNo} 경로형 딥링크가 같은 상세 조회 플로우를 사용한다.
  • 파손 면책 (필수)는 파손 정책 페이지를 열고 기존 약관 (필수) 링크는 기존 페이지를 유지한다.
  • 상세 progress는 statusTimeline 5단계를 사용한다.
  • 운송장 등록과 배송지 입력 성공 후 상세를 재조회해 최신 상태를 표시한다.
  • 신청 취소는 확인 dialog 이후에만 API를 호출하고 성공 시 취소 완료 UI로 replace 된다.
  • 발신·수신 전달 불가와 취소 완료 상세의 문의하기가 고객센터의 TALK 문의와 같은 action을 실행한다.

14.2 개인정보 수용 기준

  • direction=SENT 상세에서 recipientAddress를 표시하지 않는다.
  • direction=RECEIVED 상세에서 senderInfo, mailbox, giftInfo.paidCan, giftInfo.tracking을 표시하지 않는다.
  • recipientAddress=null이면 받는 주소 섹션을 통째로 숨긴다.
  • status=UNDELIVERABLE이면 받은 상세에서 배송지 입력 UI와 받는 주소 섹션을 숨긴다.

14.3 추적성 완료 기준

  • 모든 확정 요구사항이 plan-task.md의 Phase/Task와 연결된다.
  • 모든 Phase Gate에 자동 또는 대체 수동 검증 기록이 누적된다.
  • API 계약과 문서 요구사항이 충돌하면 Decision Log에 정정 기록을 남긴다.

15. Open Questions

없음. 약관 Notion URL과 API 계약은 사용자 답변으로 확정됐다.

16. 요구사항 추적표

요구사항 범위 API Contract 계획 Phase 자동 검증 수동 검증
GIFT-001~004 해당 없음 1, 2, 11 Debug build/source check 진입점 표시/이동·버튼 UI
SEND-001~008 §11.2 2, 3, 10, 11 ViewModel validation/build/source check 선물 신청 journey·프로필 이미지
LIST-001~005 §11.2 2, 4 pagination/build 선물함 상태별 화면
DETAIL-001~007 §11.2 2, 5, 12 status mapping/inquiry source contract/build 보낸 상세/취소/TALK 문의
TRACK-001~007 §11.2 2, 6, 10 validation/build 운송장 등록·신청 취소
RECV-001~008 §11.2 2, 7, 10 privacy/validation/build 받은 상세/배송지
COMMON-001~007 §11.2 2, 8, 11 deeplink parsing/link source/build 푸시/딥링크·약관 링크

17. Decision Log

날짜 ID 상태 결정 근거 영향 요구사항·계약·Goal
2026-10-06 DEC-001 확정 선물 보내기는 DM 보내기 노출 조건인 isDmAvailable을 재사용한다. 사용자 답변: “해당 조건과 동일하게 설정하면 된다.” GIFT-001, P1-T3
2026-10-06 DEC-002 확정 한국 접속은 기존 countryCode 판정, 한국어 설정은 LanguageHeaderProvider.current == "ko"를 사용한다. 기존 코드 검색 결과 GIFT-001, P1-T3
2026-10-06 DEC-003 확정 크리에이터 상세 선물 보내기는 아이콘 없이 타인 채널에서만 표시한다. 사용자 추가 요청 GIFT-001, P1-T5
2026-10-06 DEC-004 확정 약관 (필수) 링크는 sender/recipient 모두 같은 Notion URL을 사용한다. 사용자 제공 URL COMMON-001, P3-T2, P7-T2
2026-10-06 DEC-005 확정 선물 API 계약은 사용자 프롬프트의 endpoint/request/response 필드를 구현 기준으로 삼는다. 사용자 답변 A SEND-*, LIST-*, DETAIL-*, TRACK-*, RECV-*
2026-10-06 DEC-006 확정 후속 기능 문서는 기존 docs/20261006_선물_UI_추가/ PRD/plan-task를 보강한다. 문서 정책: 연속 작업은 기존 문서에 기록 전체
2026-10-06 DEC-007 확정 선물함에서 받은 선물의 배송지 미입력 상태는 별도 파란 배너가 아니라 각 item 카드에서 표시한다. 사용자 정정 요청 LIST-004, P4-T3
2026-10-06 DEC-008 확정 운송장 등록 화면의 신청 취소는 입력 중단이 아니라 선물 신청 취소이며, 확인 이후 기존 취소 API를 호출한다. 사용자 선택 TRACK-006, P6-T2, P6-T3
2026-10-08 DEC-009 확정 휴대폰 제출은 010으로 시작하는 ASCII 숫자 11자리만 허용하고, 운송장 번호는 ASCII 숫자 10~14자리만 제출한다. 사용자 선택과 현행 국내 이동전화 번호체계 조사 SEND-007, TRACK-007, RECV-007, Phase 10
2026-10-08 DEC-010 정정 DEC-003의 아이콘 없는 버튼 결정을 정정해 ic_send_gift, 흰색 배경, 검은색 문구를 적용한다. 사용자 요청 및 설계 승인 GIFT-004, P11-T3
2026-10-08 DEC-011 확정 경로형 선물 상세 딥링크는 gift-detail/{applicationNo}를 사용하고 voiceon, voiceon-test는 기존 scheme 지원만 재사용한다. 사용자 제공 딥링크 예시 및 설계 승인 COMMON-006, P11-T1
2026-10-08 DEC-012 확정 파손 면책 (필수)에만 별도 파손 정책 URL을 사용하고 기존 약관 링크는 유지한다. 사용자 제공 URL 및 설계 승인 COMMON-007, P11-T2
2026-10-08 DEC-013 확정 Gift 모듈의 브랜드 문구는 이미 모두 소다라이브이므로 추가 문자열 변경 없이 충족 처리한다. 코드 검색 결과와 사용자 선택 A Phase 11
2026-10-08 DEC-014 확정 전달 불가 상세의 붉은 안내 문구는 서버 필드 추가 없이 앱 내 고정 문구를 Figma 문구로 교체한다. 사용자 확인: “동일하게 앱 내에 심으면 된다.” RECV-008, P7-R1
2026-10-08 DEC-015 확정 선물 상세의 발신·수신 전달 불가와 취소 완료 문의하기는 고객센터 화면으로 이동하지 않고 고객센터 TALK 문의와 같은 공용 action을 호출한다. 사용자 요청과 기존 고객센터 TALK action 확인 DETAIL-007, P12-T1

18. 변경 관리

Phase 9 주소 검색 및 제한적 Figma 정렬 (2026-10-07 확정)

  • 기존 수동 우편번호/기본주소 입력 정책을 공용 SodaAddressInputView로 대체한다. 상세주소만 편집하며 기존 validation/request mapping과 상세주소 선택 입력은 유지한다.
  • 내부 WKWebView에 HTML 문자열을 로드하고 HTTPS Kakao postcode.v2.js의 kakao.Postcode.embed()를 사용한다. 선택 결과는 정확한 data.zonecode, data.address만 반환한다. hosted HTTPS 권장과 HTML 문자열 방식의 런타임 호환성 위험을 사용자가 수용했다.
  • 외부 메시지의 handler 이름, 문자열 타입, 공백 여부를 검사한다. 유효한 선택 때만 우편번호/기본주소를 함께 갱신하고 닫는다. 취소/오류에서는 기존 값을 유지하며 loading/error/닫기와 handler 해제를 제공한다.
  • Figma 2481:18957, 2531:36290의 제공 geometry를 기준으로 기존 SodaSpacing, SodaRadius, appFont를 재사용한다. 그룹 간격 20, label 간격 4, 주소 행 간격 8, padding/radius 14, label 14 medium, 값 16 regular, gray900 입력 배경이다. 검색 capsule은 intrinsic 폭과 최소 높이 44를 갖는다.
  • sender helper는 발송 확인 외의 용도로 사용하지 않으며, 전달이 완료되면 파기됩니다, recipient helper는 제출 후 수정할 수 없으니 입력한 정보를 다시 한번 확인해 주세요다. 이름 helper는 sender 택배 발송 시 이름과 동일하게 적어주세요, recipient 입력한 이름으로 선물이 전달돼요, 전화 helper는 공통 문제 발생 시 연락이 가능한 번호를 입력해 주세요다.
  • 카테고리는 16 regular, padding/radius 14로 줄이고 77pt 최소 높이만 제거한다. sizeSection, sizeTitle, 크기 선택/가격/문구/스타일은 현재 작업본 그대로 보존한다. 배너/요약/약관/하단 action 재설계, dependency, route, backend, retry/timeout은 제외한다.
  • 코드·계약 검증과 실제 Kakao 검색/픽셀 fidelity 검증은 분리 기록한다. 실기기 증거 없이는 runtime 또는 픽셀 일치 완료를 주장하지 않는다.

Phase 10 연락처·운송장 입력 제한 (2026-10-08 확정)

  • 이름은 한글·영문 1~12자만 허용하며 UIKit marked text를 조합 중 binding에 재할당하지 않는다.
  • 휴대폰은 편집 중 ASCII 숫자 최대 11자를 허용하고 제출 시 010으로 시작하는 정확히 11자리만 허용한다. 하이픈 자동 입력과 SMS 실재성 검증은 제외한다.
  • 운송장 번호는 편집 중 ASCII 숫자 최대 14자를 허용하고 제출 시 10~14자리만 허용한다. 외부 택배 조회와 실재 운송장 검증은 제외한다.
  • 기존 request, repository, API, 택배사 선택, mutation/navigation lifecycle과 선물 사이즈 선택은 변경하지 않는다.

Phase 11 선물 딥링크·정책 링크·크리에이터 표시 보정 (2026-10-08 확정)

  • 기존 딥링크 parser에 gift-detail/{applicationNo} path route만 추가하고 query형 GIFT_DETAIL과 scheme 판정은 유지한다.
  • 크리에이터 채널의 기존 creator.profileImageUrl을 선물 보내기 route와 화면에 전달하며 별도 크리에이터 조회 API는 추가하지 않는다.
  • 공용 약관 행은 호출부가 링크 URL을 지정할 수 있게 최소 확장하고 파손 면책 행에만 새 정책 URL을 전달한다.
  • 선물 보내기 버튼은 기존 노출 조건과 action을 유지하면서 사용자 제공 ic_send_gift asset, 흰색 배경, 검은색 문구를 적용한다.
  • Gift 모듈의 브랜드 문구는 이미 소다라이브로 확인되어 변경하지 않는다.

Phase 12 선물 상세 TALK 문의 연결 (2026-10-08 확정)

  • 고객센터 TALK 문의의 기존 Kakao URL 열기 action을 작은 공용 함수로 두고 고객센터와 선물 상세가 함께 사용한다.
  • 발신 전달 불가, 수신 전달 불가, 취소 완료 상세의 세 문의하기만 공용 action으로 교체한다.
  • 버튼 문구·노출 조건·스타일·레이아웃과 다른 선물 action은 변경하지 않는다.
  • 새 route, webview, dependency, API 변경은 추가하지 않는다.

Phase 7 후속 전달 불가 안내 문구 보정 (2026-10-08 확정)

  • 전달 불가 사유 아래 붉은 안내 문구는 서버에서 받지 않고 앱 내 고정 문구로 유지한다.

  • 발신·수신 상세 모두 Figma 기준 문구 해당 선물은 보관기간(입고일 포함 최대 7일) 내에 반송 등 필요한 조치가 완료되지 않을 경우, 사전에 동의한 약관 및 폐기 등의 내용에 따라 폐기될 수 있습니다. 자세한 내용은 고객센터로 문의해 주세요.를 사용한다.

  • delivery.undeliverableReason은 기존처럼 상단 흰색 사유에만 사용하고 API schema는 변경하지 않는다.

  • 요구사항 변경 시 Decision Log에 정정 행을 추가한다.

  • 구현 범위가 바뀌면 이 PRD를 먼저 갱신하고 plan-task.md의 Phase/Task를 갱신한 뒤 코드를 수정한다.

  • 기존 완료된 Phase 1 기록은 삭제하지 않는다.