# iOS 선물하기 전체 기능 단일 구현 프롬프트 이 문서는 iOS 앱에서 크리에이터 선물하기 사용자 기능을 한 번에 구현할 때 사용하는 단일 프롬프트다. 기존 분할 문서의 사용자 화면 범위를 합치고, Figma UI 확인 결과와 신청 완료 후 상세 이동 정책을 반영했다. 관리자 화면은 iOS 앱 범위가 아니므로 제외한다. ## 참고 문서 - `docs/20260929_크리에이터_선물하기/prd.md` - `docs/20260929_크리에이터_선물하기/client-api-summary.md` - `docs/20260929_크리에이터_선물하기/gift-send-page-prompt.md` - `docs/20260929_크리에이터_선물하기/gift-list-page-prompt.md` - `docs/20260929_크리에이터_선물하기/gift-detail-tracking-page-prompt.md` - `docs/20260929_크리에이터_선물하기/gift-received-detail-page-prompt.md` ## Figma 확인 노드 - 선물 보내기: 빈 입력 `2481:18904`, 입력 완료/CTA 활성 `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` Figma 재확인 메모: - 모든 화면은 iPhone 13 기준 402px 폭, black/dark surface 기반이다. - 상단 status bar와 nav/top 영역은 고정하고, 본문은 `scroll area`로 분리한다. - 주요 섹션은 14pt 좌우 여백, 카드/입력/배너는 rounded dark surface, 섹션 사이 divider를 사용한다. - 선물 보내기 화면은 안내 배너, 받는 크리에이터, 선물 사이즈, 카테고리, 보내는 사람, 약관 동의, 하단 single CTA 구조다. - 선물함 리스트는 안내 배너와 104pt 높이 카드 리스트 구조다. 실제 구현에서는 Figma에 있는 `신청 내역`, `선물 내역` 섹션 타이틀을 표시하지 않는다. - 보낸 상세는 상태 hero, 사서함/등록 안내, 선물 정보, 하단 action bar를 중심으로 한다. - 받은 상세는 상태 hero와 문의 버튼, 선물 정보, 받는 주소 카드 구조를 상태별로 바꾼다. --- ## iOS 구현 프롬프트 ```text iOS 앱의 크리에이터 선물하기 사용자 기능 전체를 Figma와 API 계약 기준으로 구현해줘. 범위: - 선물 보내기 신청 화면 - 선물함 리스트 화면 - 선물 상세 화면 - 보낸 선물의 운송장 등록 화면 - 보낸 선물의 신청 취소 확인/취소 완료 화면 - 받은 선물의 배송지 입력 UI - 받은 선물의 배송지 입력 완료/전달 완료/전달 불가 상세 UI - 푸시/딥링크 진입 시 선물 상세 이동 범위 밖: - 관리자 화면 - 새 디자인 시스템 생성 - 외부 택배 조회/운송장 실시간 검증 - 약관 본문 제공 API 신규 구현 - 선물 이미지, 첨부파일, 메시지 카드 - 임의 URL 연결 공통 전제: - iOS 기존 디자인 시스템, 컴포넌트, API client, 인증, 상태 관리, toast/dialog, navigation, loading/error 패턴을 재사용한다. - Figma visual structure와 문구를 우선 따르되, 데이터 노출과 CTA 조건은 API 계약을 우선한다. - 모든 API 응답은 공통 envelope를 사용하며 실제 payload는 `response.data` 또는 기존 iOS API client의 data unwrap 결과다. - 모든 날짜는 UTC ISO 문자열이다. 앱 공통 formatter가 있으면 재사용하고, 상세 필드 날짜는 Figma처럼 `YYYY.MM.DD`로 표시한다. - 개인정보 노출 경계를 반드시 지킨다. 로그인 회원이 알아야 하는 본인 정보만 표시하고, 상대방의 이름/휴대폰/주소는 표시하지 않는다. - Figma 기준으로 상단 navigation은 고정하고, navigation 아래 본문만 스크롤한다. - iOS safe area, keyboard avoidance, bottom action bar는 기존 앱 패턴을 따른다. - 새 route/deep link가 필요하면 기존 navigation/deep link 등록 방식에 맞춘다. 가장 중요한 이동 정책: - 선물 보내기 신청 `POST /api/v2/gifts` 성공 시 완료 dialog에서 멈추지 말고, 성공 응답의 `applicationNo`로 선물 상세 페이지로 이동한다. - 신청 완료 직후 이동 대상은 `GET /api/v2/gifts/{applicationNo}` 기반 상세 페이지다. - 신청 성공 후 뒤로가기로 입력 폼에 다시 돌아와 중복 신청하지 않도록 기존 iOS navigation 정책에 맞춰 replace 또는 stack 정리를 적용한다. - 선물함 리스트 item 탭, 푸시 딥링크, 신청 완료 후 이동은 모두 같은 상세 화면으로 들어가고, 상세 API의 `direction`, `trackingRequired`, `recipientAddressRequired`, `status`로 UI를 분기한다. 보낸 사람/받는 사람 상세 차이: - 같은 `GET /api/v2/gifts/{applicationNo}` 상세 API를 사용하지만 `direction`에 따라 상세 페이지를 다르게 표시한다. - `direction=SENT`는 보낸 팬 관점이다. - 받는 크리에이터 닉네임은 `giftInfo.recipientCreatorNickname`으로 표시한다. - 팬 본인이 신청 시 입력한 발신자 정보 `senderInfo`를 표시할 수 있다. - 관리자 전역 사서함 `mailbox`가 있으면 `보내실 주소` 또는 `받는 사서함` 섹션에 표시한다. - 크리에이터가 입력한 배송지 `recipientAddress`는 표시하지 않는다. - `trackingRequired=true`이면 운송장 등록 CTA를 표시한다. - `status=RECEIVED`이면 신청 취소 action을 제공할 수 있다. - `direction=RECEIVED`는 받는 크리에이터 관점이다. - 보낸 팬 닉네임은 `giftInfo.senderNickname`으로 표시한다. - 팬이 신청 시 입력한 이름/휴대폰/주소 `senderInfo`는 표시하지 않는다. - `mailbox`, `giftInfo.applicationNo`, `giftInfo.paidCan`, `giftInfo.tracking`은 표시하지 않는다. - 크리에이터 본인이 입력한 배송지 `recipientAddress`가 있을 때만 받는 주소 섹션을 표시한다. - `recipientAddressRequired=true`이면 배송지 입력 UI를 표시한다. 1. 선물 보내기 화면 Figma: - 빈 입력 상태: `2481:18904` - 입력 완료/CTA 활성 상태: `2481:18957` 진입 전제: - 크리에이터 닉네임과 `memberId`를 받아 이 화면으로 이동하는 기존 흐름은 이미 있다. - 이 작업에서 새 진입 흐름을 만들지 않는다. - 전달받은 크리에이터 닉네임은 `받는 크리에이터` 영역에 표시한다. - 전달받은 `memberId`는 `POST /api/v2/gifts`의 `recipientMemberId`로 보낸다. - 둘 중 하나라도 없으면 기존 앱의 오류 처리 또는 안전한 뒤로가기 패턴을 따른다. 화면 구조: - 상단 타이틀: `선물 보내기` - 안내 배너 - 제목: `선물 보내기 베타 서비스 안내` - 본문: `베타 기간 동안은 일부 기능만 제공됩니다. 크리에이터에게 마음을 잘 전할 수 있도록 더 넓어진 선물 보내기로 곧 다시 만나요!` - 받는 크리에이터: Figma 프로필 row 형태로 전달받은 닉네임 표시 - 선물 사이즈: `GET /api/v2/gifts/form-options`의 `sizes` 바인딩 - 카테고리: 같은 응답의 `categories` 바인딩 - 보내는 사람 입력: 이름, 휴대폰 번호, 우편번호, 주소, 상세 주소 - 이용 약관 동의: 발송 규정 요약 박스와 체크박스 2개 - 하단 CTA: `{salePriceCan}캔으로 선물 보내기` 폼 옵션 조회: - 화면 진입 시 `GET /api/v2/gifts/form-options` 호출 - `sizes` 기본 선택은 서버가 내려준 첫 번째 항목 - 가격 표시는 `salePriceCan`을 사용하고 신청 금액도 `salePriceCan`을 사용 - `basePriceCan != salePriceCan`이면 기존 앱 할인/정가 표시 패턴이 있을 때만 정가를 함께 표시 - 사이즈 설명은 API에 없으므로 앱 로컬 매핑 사용 - `SMALL`: `세 변의 합 100cm 이하 · 5kg 이하` - `MEDIUM`: `세 변의 합 120cm 이하 · 15kg 이하` - `LARGE`: `세 변의 합 160cm 이하 · 20kg 이하` - 카테고리 select는 기존 iOS picker/bottom sheet/dialog 패턴 중 앱에서 이미 쓰는 방식을 사용 - `requiresDamageWaiver=true` 카테고리 선택 시에만 파손 면책 동의 row 표시 - `requiresDamageWaiver=false` 선택 시 파손 면책 동의 row를 숨기고 `damageWaiverAgreed=false`로 초기화 입력 필드 매핑: - 이름 input -> `senderName` - 휴대폰 번호 input -> `senderPhoneNumber` - 우편번호 input -> `senderZipCode` - 주소 input -> `senderAddress` - 상세 주소 input -> `senderAddressDetail` - 우편번호 검색은 기존 주소 검색 기능을 재사용한다. 동의 필드: - `소다라이브 크리에이터 상품 전달 이용약관에 동의합니다. (필수)` -> `senderTermsAgreed` - `상품 전달을 위한 개인정보 수집·이용에 동의합니다. (필수)` -> `senderPrivacyAgreed` - `파손 및 분실 면책 사항에 동의합니다. (필수)` -> `damageWaiverAgreed`, 필요한 카테고리에서만 표시/필수 - 각 약관 문구의 `(필수)` 부분은 터치 가능한 링크로 처리한다. - `(필수)` 터치 시 약관 Notion 웹페이지로 이동한다. - sender 약관 2개와 recipient 약관 2개는 모두 같은 Notion 페이지로 이동하면 된다. - Notion 페이지 URL은 실제 구현 시 입력받도록 처리하고, 이 프롬프트에서 임의 URL을 하드코딩하지 않는다. CTA 활성 조건: - `memberId` 있음 - 사이즈 선택됨 - 카테고리 선택됨 - 이름, 휴대폰 번호, 우편번호, 주소 입력됨 - 이용약관 동의 true - 개인정보 동의 true - 선택 카테고리의 `requiresDamageWaiver=true`이면 파손 면책 동의 true - submit 중에는 중복 탭 방지와 loading 표시 - validation 실패 시 `POST /api/v2/gifts`를 호출하지 않는다. 선물 신청 API: - Method: `POST` - URL: `/api/v2/gifts` - Request: ```json { "recipientMemberId": 100, "senderName": "김소다", "senderPhoneNumber": "01000000000", "senderZipCode": "12345", "senderAddress": "서울시 강남구 ...", "senderAddressDetail": "123동 456호", "sizeCode": "SMALL", "categoryId": 1, "senderTermsAgreed": true, "senderPrivacyAgreed": true, "damageWaiverAgreed": true } ``` - Response data: ```json { "applicationNo": "A-1002609300001", "status": "RECEIVED", "statusName": "접수 완료", "priceCan": 80, "trackingDeadlineAt": "2026-10-02T03:00:00Z" } ``` - 성공 시 `applicationNo`로 선물 상세 페이지로 이동한다. - 실패 시 서버 메시지를 기존 앱 오류 노출 방식으로 표시하고 입력값은 유지한다. 2. 선물함 리스트 화면 Figma: - 보낸 내역 있음: `2481:19382` - 보낸 내역 없음: `2520:35587` - 받은 내역 없음: `2531:36283` - 받은 내역 있음: `2531:36194` - 받은 상태별 카드: `2531:36237` 공통 요구사항: - 상단 타이틀: `선물함` - 상단 뒤로가기 navigation은 고정 - navigation 아래 콘텐츠만 스크롤 - Figma에 보이는 `신청 내역`, `선물 내역` 섹션 타이틀은 표시하지 않는다. - 리스트는 서버 응답 순서를 우선 사용한다. 현재 API만으로 취소일 기준 재정렬을 임의 구현하지 않는다. - item 탭 시 `applicationNo`를 전달해 선물 상세 페이지로 이동한다. API: - Method: `GET` - URL: `/api/v2/gifts?type=ALL&page=0&size=20` - 이 화면에서는 `SENT`, `RECEIVED`를 따로 호출하지 않고 `type=ALL`을 사용한다. - `items[].direction`으로 보낸/받은 카드 UI를 결정한다. - `hasNext=true`이면 스크롤 하단에서 다음 page를 추가 조회한다. - 기존 앱에 pull-to-refresh가 있으면 page 0부터 다시 조회한다. Response data 예시: ```json { "totalCount": 2, "items": [ { "applicationNo": "A-1002609300001", "direction": "SENT", "status": "RECEIVED", "statusName": "접수 완료", "priceCan": 80, "categoryName": "아크릴/스탠드", "sizeName": "소형", "counterpartMemberId": 200, "counterpartNickname": "달빛수집가", "counterpartProfileImageUrl": "https://example.com/profile.png", "recipientAddressRegistered": false, "recipientAddressDeadlineAt": null, "canceledAt": null, "createdAt": "2026-09-18T03:00:00Z" } ], "page": 0, "size": 20, "hasNext": false } ``` 보낸 선물 카드 (`direction=SENT`): - 상대방은 선물을 받는 크리에이터다. - 상단 안내 배너 표시 - 제목: `사서함을 통해 안전하게 전달됩니다` - 본문: `크리에이터 및 팬의 주소는 공개되지 않습니다. 선물은 소다라이브 사서함에 도착한 뒤 크리에이터에게 전달됩니다.` - 카드에는 `statusName`, 날짜 라벨, 크리에이터 프로필/닉네임, `sizeName · categoryName`, chevron 표시 - 날짜 라벨은 기본 `{createdAt} 신청`; 취소일 필드가 없으면 임의 취소일 계산 금지 - `status=CANCELED`이고 `canceledAt`이 있으면 날짜 라벨은 `{canceledAt} 취소`로 표시한다. - 보낸 선물 empty는 안내 배너만 표시하고 별도 empty title/body를 추가하지 않는다. 받은 선물 카드 (`direction=RECEIVED`): - 상대방은 선물을 보낸 팬이다. - 받은 선물 empty - 제목: `전달 예정인 선물이 없어요` - 본문: `베타 기간에는 팬이 선물을 보내는 기능만 제공돼요. 더 다양해진 선물 기능으로 곧 다시 만나요!` - 버튼: `의견 남기기`, 기존 문의/피드백 이동 패턴 사용 - 받은 선물 있음 상단 안내 배너 - 제목: `배송지 입력 기간 안내` - 본문: `알림을 받은 날부터 7일 이내에 배송지를 입력해 주세요. 기한 내 입력하지 않으면 선물이 반송됩니다.` - 배송지 입력이 필요한 선물이 있으면 리스트 상단에 파란 안내 배너 표시 - 제목: `팬이 보낸 선물이 있어요!` - 본문: `선물이 늦지 않게 전달될 수 있도록 배송지를 입력해 주세요.` - 보조 문구는 `recipientAddressRegistered=false`이고 `recipientAddressDeadlineAt`이 있으면 남은 일수로 표시한다. - 배송지가 이미 입력되어 `recipientAddressRegistered=true`이면 `recipientAddressDeadlineAt`은 null로 온다. - 배너 탭 시 배송지 입력이 필요한 첫 번째 선물 상세로 이동 - 카드에는 팬 프로필/닉네임, `{createdAt} 신청`, 상태 메인/서브 문구, chevron 표시 - 하단 `문의하기` 버튼은 기존 앱 정책에 맞춰 고정 또는 스크롤 내부 버튼으로 둔다. 상태 표시 매핑: - 보낸 선물 카드 - `RECEIVED`: `운송장 등록 필요` - `TRACKING_REGISTERED`: `발송 확인` - `ARRIVED_AT_MAILBOX`: `사서함 도착` - `INSPECTION_COMPLETED`: `선물 검수 완료` - `DELIVERED`: `전달 완료` - `UNDELIVERABLE`: `전달 불가` - `CANCELED`: `신청 취소` - 받은 선물 카드 - `TRACKING_REGISTERED`: main `배송지 입력 필요`, sub `전달 예정` - `ARRIVED_AT_MAILBOX`: main `선물 검수`, sub `선물 확인 중` - `INSPECTION_COMPLETED`: main `선물 검수 완료`, sub `배송 중` - `DELIVERED`: main `전달 완료`, sub 없음 - `UNDELIVERABLE`: main `전달 불가`, sub 없음 3. 공통 선물 상세 화면 API: - Method: `GET` - URL: `/api/v2/gifts/{applicationNo}` - 리스트 item 탭, 신청 완료 후 이동, 푸시 딥링크 진입 모두 이 API로 상세를 조회한다. Response data 핵심 필드: ```json { "applicationNo": "A-1002609300001", "direction": "SENT", "status": "RECEIVED", "statusName": "접수 완료", "giftInfo": { "recipientCreatorNickname": "달빛수집가", "senderNickname": null, "sizeName": "소형", "categoryName": "잡화", "applicationNo": "A-1002609300001", "paidCan": 100, "tracking": null, "shippingRequestedAt": null }, "senderInfo": { "name": "김소다", "phoneNumber": "01012345678", "address": "(04030) 서울특별시 마포구 양화로 000, 3층" }, "recipientAddress": null, "mailbox": { "name": "소다라이브 사서함", "address": "(06174) 서울특별시 강남구 테헤란로108길 8, 유민빌딩 4층", "phoneNumber": "02-2055-1477" }, "trackingRequired": true, "recipientAddressRequired": false, "recipientAddressDeadlineAt": null, "delivery": { "canceledAt": null, "undeliverableAt": null, "undeliverableReason": null }, "statusTimeline": [] } ``` 진입 처리: - 화면 진입 시 상세 API 호출 - 로딩 중에는 기존 상세 로딩/스켈레톤 패턴 사용 - 실패 시 기존 toast/dialog와 재시도 패턴 사용 - `direction`이 현재 화면 분기와 맞지 않아도 별도 화면을 만들지 말고 같은 상세 화면 안에서 `direction` 기준으로 렌더링한다. 진행 상태 progress: - Figma의 진행 단계가 4단계처럼 보여도 4단계로 하드코딩하지 않는다. - API `statusTimeline`의 정상 진행 5개 상태를 이 순서로 표시한다. 1. `RECEIVED`: `신청 접수` 2. `TRACKING_REGISTERED`: `발송 확인` 3. `ARRIVED_AT_MAILBOX`: `사서함 도착` 4. `INSPECTION_COMPLETED`: `검수 완료` 5. `DELIVERED`: `전달 완료` - `occurredAt`이 있으면 완료 색상과 `MM.DD` 표시 - `occurredAt=null`이면 비활성 색상, 날짜 미표시 - `UNDELIVERABLE`, `CANCELED`는 progress step으로 추가하지 않는다. 4. 보낸 선물 상세와 운송장 등록 Figma: - 상세 운송장 등록 전 `2481:19011` - 상세 운송장 등록 후 `2481:19152` - 상세 전달 완료 `2481:19320` - 전달 불가 `2481:19456` - 취소 다이얼로그 `2481:19500` - 취소 완료 `2481:19521` - 운송장 등록 입력 전 `2481:19066` - 운송장 등록 입력 완료 `2481:19109` 적용 조건: - `direction=SENT` 상단 타이틀: - 일반 상세: `신청 상세` - 취소 완료 페이지: `취소 완료` - 운송장 등록: `운송장 등록` 보낸 상세 hero 문구: - `RECEIVED`: title `선물 신청 완료!`, body `선물이 정상적으로 접수되었어요!` - `TRACKING_REGISTERED`: chip `발송 확인`, title `사서함으로 이동 중이에요` - `ARRIVED_AT_MAILBOX`: chip `사서함 도착`, title `선물이 사서함에 도착했어요` - `INSPECTION_COMPLETED`: chip `검수 완료`, title `선물 검수를 완료했어요` - `DELIVERED`: chip `전달 완료`, title `크리에이터에게 선물을 전달했어요` - `UNDELIVERABLE`: chip `전달 불가`, title `전달할 수 없는 품목입니다`, body `보내주신 선물은 SODALIVE 선물 정책에 따라 크리에이터에게 전달할 수 없는 품목으로 확인되었습니다.` - `CANCELED`: 일반 상세 대신 취소 완료 UI 표시 보낸 상세 섹션: - 선물 정보는 항상 표시 - 크리에이터: `giftInfo.recipientCreatorNickname` - 사이즈: `giftInfo.sizeName` - 카테고리: `giftInfo.categoryName` - 신청번호: `applicationNo` 또는 `giftInfo.applicationNo` - 캔: `giftInfo.paidCan`이 null이 아닐 때 표시 - 운송장: `giftInfo.tracking`이 null이 아닐 때 표시 - 검수일/전달 완료일은 `statusTimeline`에서 찾는다. - `senderInfo`가 있으면 보내는 사람 섹션 표시 - `mailbox`가 있으면 보내실 주소/받는 사서함 섹션 표시 - `delivery.undeliverableReason`이 있으면 사유 섹션 표시 - `recipientAddress`는 보낸 팬 상세에 표시하지 않는다. 보낸 상세 action: - `trackingRequired=true`이면 주요 CTA `운송장 작성하기` 표시, 운송장 등록 화면으로 이동 - `direction=SENT && status=RECEIVED`이면 보조 action `신청 취소` 표시 가능 - `status=DELIVERED && direction=SENT`이면 기존 리뷰 작성 화면/플로우가 있으면 `리뷰 남기기` 연결 - `문의하기`, `선물 정책 확인`은 기존 이동 경로가 있으면 연결하고, 없으면 숨기거나 기존 앱 정책에 맞춰 처리 신청 취소: - `신청 취소` 탭 시 즉시 API 호출 금지, 기존 `v2 dialog` 확인 팝업 표시 - 제목: `신청 취소` - 본문: - `{크리에이터}에게 보내는 선물을 취소할까요?` - `선물 보내기 규정에 따라 선물 취소 및 사용한 캔이 모두 환불됩니다.` - 왼쪽 action: `나가기`, dialog만 닫음 - 오른쪽 destructive action: `취소하기`, `POST /api/v2/gifts/{applicationNo}/cancel` 호출 - 실패 시 dialog를 닫지 말고 기존 에러 패턴 표시 - 성공 시 취소 완료 페이지로 replace navigation 취소 완료 페이지: - 상세 API 응답이 `status=CANCELED`인 경우에도 표시 - hero title: `선물 신청이 취소되었어요` - hero body: `{크리에이터}에게 보내는 선물 신청({applicationNo})이 취소되었습니다. 취소한 신청은 되돌릴 수 없으며, 다시 보내려면 새로 신청해 주세요.` - 취소 상세: 크리에이터, 사이즈, 카테고리, 신청번호, 신청일, 취소일, 환불 캔 - 뒤로가기는 취소 전 상세/등록 화면으로 돌아가지 않게 한다. 운송장 등록 화면: - 상세에서 `trackingRequired=true`일 때만 진입 - 진입 시 상세 API를 다시 조회 - 조회 결과 `trackingRequired=false`이면 기존 오류 처리 후 상세로 복귀 - 신청 건 요약 카드: `applicationNo`, 크리에이터 닉네임, `sizeName · categoryName · 신청일` - 보내실 주소: `mailbox` 사용. `mailbox=null`이면 등록을 막고 `받을 주소가 등록되어 있지 않습니다.` 오류 표시 - 운송장 정보: 택배사 select, 운송장 번호 input - 등록 안내: Figma 안내 문구 유지 - 하단 action bar: 왼쪽 `신청 취소`, 오른쪽 `등록 완료` 택배사 로컬 상수: ```swift let giftCourierCompanyNames = [ "CJ대한통운", "우체국택배", "GS25 편의점택배", "CU 편의점택배", "한진택배", "롯데택배", "로젠택배", "경동택배", "대신택배", "일양로지스", "천일택배", "합동택배", "건영택배", "농협택배" ] ``` - 이미 앱 공통 택배사 상수가 있으면 그것을 우선 사용 - API에는 선택한 한글 표시명을 그대로 `courierCompanyName`으로 보낸다. 운송장 등록 CTA 활성 조건: - 택배사 선택됨 - 운송장 번호가 공백이 아님 - submit 중 중복 탭 방지 운송장 등록 API: - Method: `POST` - URL: `/api/v2/gifts/{applicationNo}/tracking` - Request: ```json { "courierCompanyName": "한진택배", "trackingNumber": "123456789012" } ``` - 성공 시 성공 toast 표시 후 상세 페이지로 돌아가거나 replace navigation으로 상세를 다시 열고, 상세 API를 재조회해 `TRACKING_REGISTERED` 상태를 표시한다. 5. 받은 선물 상세와 배송지 입력 Figma: - 배송지 입력 전 `2531:36290` - 배송지 입력 완료 `2531:36399` - 전달 완료 `2531:36330` - 전달 불가 `2531:36422` 적용 조건: - `direction=RECEIVED` 받은 상세 개인정보 규칙: - `senderInfo`, `mailbox`, `giftInfo.applicationNo`, `giftInfo.paidCan`, `giftInfo.tracking`은 표시하지 않는다. - `recipientAddress=null`이면 받는 주소 섹션 전체를 숨긴다. 빈 섹션, skeleton, `-` 값도 표시하지 않는다. - 전달 불가 화면에서는 `recipientAddress`가 응답에 있어도 받는 주소 섹션을 표시하지 않는다. 배송지 입력 전 UI: - 적용 조건: `direction=RECEIVED && recipientAddressRequired=true` - 상단 타이틀: `선물 받기` - 개인정보 이용 안내 배너 - 제목: `개인정보 이용 안내` - 본문: `크리에이터 및 팬의 주소는 공개되지 않습니다. 입력한 배송지는 선물 전달 및 필요한 사고 처리에서 사용되며, 전달 완료 후 파기됩니다.` - 전달 예정 선물 섹션 - 발송인: `giftInfo.senderNickname` - 사이즈: `giftInfo.sizeName` - 카테고리: `giftInfo.categoryName` - 배송지 입력 섹션 - 이름 `recipientName` - 휴대폰 번호 `recipientPhoneNumber` - 우편번호 `recipientZipCode` - 주소 `recipientAddress` - 상세 주소 `recipientAddressDetail` - 우편번호 검색은 기존 앱 주소 검색/Kakao 우편번호 래퍼를 재사용 - 이용 약관 동의 - `소다라이브 크리에이터 상품 전달 이용약관 및 전달 가능 제한 품목을 확인하고 동의합니다.(필수)` -> `recipientTermsAgreed` - `상품 전달을 위해 수령인 성명·연락처·주소 등 필요한 개인정보를 일시적으로 수집·이용하는 것에 동의합니다.(필수)` -> `recipientPrivacyAgreed` - 각 약관 문구의 `(필수)` 부분은 터치 가능한 링크로 처리한다. - `(필수)` 터치 시 약관 Notion 웹페이지로 이동한다. - sender 약관 2개와 recipient 약관 2개는 모두 같은 Notion 페이지로 이동하면 된다. - Notion 페이지 URL은 실제 구현 시 입력받도록 처리하고, 이 프롬프트에서 임의 URL을 하드코딩하지 않는다. - 하단 CTA: `선물 받기` - CTA 활성 조건: 이름, 휴대폰, 우편번호, 주소 입력됨 + 약관 2개 동의됨 배송지 입력 API: - Method: `POST` - URL: `/api/v2/gifts/{applicationNo}/recipient-address` - Request: ```json { "recipientName": "크리에이터이름", "recipientPhoneNumber": "01012345678", "recipientZipCode": "04030", "recipientAddress": "서울특별시 마포구 양화로 000", "recipientAddressDetail": "3층", "recipientTermsAgreed": true, "recipientPrivacyAgreed": true } ``` - 성공 시 성공 toast 표시 후 상세 API를 다시 조회하거나 replace navigation으로 상세를 다시 열어 배송지 입력 완료 UI를 표시한다. - 실패 시 기존 form error/toast/dialog 패턴을 따르고 입력값은 유지한다. 배송지 입력 완료 상세: - 적용 조건: `direction=RECEIVED && recipientAddressRequired=false && status`가 `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED` 중 하나이며 `recipientAddress`가 있음 - 상단 타이틀: `선물 상세` - hero title: `배송지 입력 완료!` - hero body: `빠른 시일 내로 선물을 전달 드릴게요.` - `문의하기` 버튼은 기존 카카오 채널 문의로 연결 - 선물 정보: 발송인, 사이즈, 카테고리, 배송 신청일 - 받는 주소: `recipientAddress.name`, `recipientAddress.phoneNumber`, `recipientAddress.address` 전달 완료 상세: - 적용 조건: `direction=RECEIVED && status=DELIVERED` - hero title: `선물 전달 완료!` - hero body: `팬이 보낸 선물이 도착했어요. 선물이 도착하지 않은 경우, 문의해 주세요.` - 선물 정보: 발송인, 사이즈, 카테고리, 배송 신청일, 배송 완료일 - 받는 주소가 있으면 표시하고 보조 문구 `배송 완료 3일 후 해당 정보는 사라져요` 표시 전달 불가 상세: - 적용 조건: `direction=RECEIVED && status=UNDELIVERABLE` - chip: `전달 불가` - hero title: `전달할 수 없는 품목입니다` - hero body: `해당 선물은 SODALIVE 선물 정책에 따라 크리에이터에게 전달할 수 없는 품목으로 확인되었습니다.` - `문의하기`: 기존 카카오 채널 문의로 연결 - `선물 정책 확인`: 실제 외부 URL을 임의 연결하지 않는다. 기존 앱 정책상 no-op 금지이면 `준비 중입니다.` toast만 표시 - 사유 섹션 - 제목: `delivery.undeliverableReason`이 있으면 그 값, 없으면 `전달 불가` - 안내 문구: `해당 선물은 정책에 따라 폐기되며 반송되지 않습니다.` - 배송지 입력 UI는 표시하지 않는다. 6. 푸시/딥링크 - 선물 푸시 딥링크는 `deepLinkValue=GIFT_DETAIL`, `deepLinkId=applicationNo`를 사용한다. - 딥링크 진입 시 바로 특정 보낸/받은 화면을 결정하지 말고, `GET /api/v2/gifts/{applicationNo}`를 호출한 뒤 응답의 `direction`, `trackingRequired`, `recipientAddressRequired`, `status`로 상세 UI를 결정한다. - 권한 오류 또는 미존재 오류는 기존 앱 딥링크 오류 처리 패턴을 따른다. 7. API 요약 - `GET /api/v2/gifts/form-options`: 선물 신청 폼 옵션 조회 - `POST /api/v2/gifts`: 선물 신청 접수 - `GET /api/v2/gifts?type=ALL&page=0&size=20`: 선물함 리스트 조회 - `GET /api/v2/gifts/{applicationNo}`: 선물 상세 조회 - `POST /api/v2/gifts/{applicationNo}/cancel`: 보낸 선물 신청 취소 - `POST /api/v2/gifts/{applicationNo}/tracking`: 보낸 선물 운송장 등록 - `POST /api/v2/gifts/{applicationNo}/recipient-address`: 받은 선물 배송지 입력 - `POST /api/v2/gifts/{applicationNo}/review`: 전달 완료 후 팬 리뷰 작성, 기존 리뷰 플로우가 있을 때 연결 8. 검증 체크리스트 - 선물 보내기 진입 시 전달받은 크리에이터 닉네임과 `memberId`가 각각 화면/API에 연결되는지 확인한다. - 화면 진입 시 `GET /api/v2/gifts/form-options`가 호출되는지 확인한다. - 필수 입력/동의가 누락되면 신청 API를 호출하지 않는지 확인한다. - `requiresDamageWaiver=true` 카테고리에서만 파손 면책 동의가 표시되고 필수인지 확인한다. - 선물 신청 성공 후 `applicationNo`로 상세 페이지로 이동하는지 확인한다. - 신청 완료 후 뒤로가기로 입력 폼에 돌아와 중복 신청하지 않는지 확인한다. - 선물함 리스트는 `type=ALL`로 한 번 조회하고 `direction`으로 카드 UI를 나누는지 확인한다. - Figma의 `신청 내역`, `선물 내역` 섹션 타이틀이 실제 화면에 노출되지 않는지 확인한다. - 리스트 item 탭, 푸시 딥링크, 신청 완료 이동이 모두 같은 상세 조회 플로우를 사용하는지 확인한다. - `direction=SENT` 상세에서 팬 본인 발신자 정보와 사서함만 표시하고, 크리에이터 배송지는 표시하지 않는지 확인한다. - `direction=RECEIVED` 상세에서 팬 닉네임만 표시하고 팬의 이름/휴대폰/주소는 표시하지 않는지 확인한다. - `direction=RECEIVED` 상세에서 `mailbox`, `giftInfo.paidCan`, `giftInfo.tracking`이 표시되지 않는지 확인한다. - 상세 progress가 `statusTimeline` 5단계를 사용하고 Figma처럼 4단계로 하드코딩되지 않았는지 확인한다. - `trackingRequired=true`일 때만 운송장 등록 CTA가 표시되는지 확인한다. - 운송장 등록 성공 후 상세를 재조회해 `TRACKING_REGISTERED` 상태를 표시하는지 확인한다. - 신청 취소는 확인 dialog 이후에만 API를 호출하고, 성공 시 취소 완료 페이지로 replace 되는지 확인한다. - `recipientAddressRequired=true`일 때 받은 선물 배송지 입력 UI가 표시되는지 확인한다. - 배송지 입력 성공 후 상세를 재조회해 배송지 입력 완료 UI가 표시되는지 확인한다. - `recipientAddress=null`이면 받는 주소 섹션을 통째로 숨기는지 확인한다. - `status=UNDELIVERABLE`이면 전달 불가 UI와 사유/폐기 안내를 표시하고 배송지 입력 UI를 숨기는지 확인한다. - 모든 날짜는 UTC ISO 문자열을 앱 표시 형식으로 변환하는지 확인한다. - API 실패 시 기존 앱 오류 표시 방식을 사용하고 입력값/스크롤 상태를 불필요하게 초기화하지 않는지 확인한다. ```