Files
sodalive-backend-spring-boot/docs/20260929_크리에이터_선물하기/ios-gift-feature-implementation-prompt.md
T

31 KiB

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 구현 프롬프트

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:
{
  "applicationNo": "A-1002609300001",
  "status": "RECEIVED",
  "statusName": "접수 완료",
  "priceCan": 80,
  "trackingDeadlineAt": "2026-10-02T03:00:00Z"
}
  • 성공 시 applicationNo로 선물 상세 페이지로 이동한다.
  • 실패 시 서버 메시지를 기존 앱 오류 노출 방식으로 표시하고 입력값은 유지한다.
  1. 선물함 리스트 화면

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 예시:

{
  "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 없음
  1. 공통 선물 상세 화면

API:

  • Method: GET
  • URL: /api/v2/gifts/{applicationNo}
  • 리스트 item 탭, 신청 완료 후 이동, 푸시 딥링크 진입 모두 이 API로 상세를 조회한다.

Response data 핵심 필드:

{
  "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으로 추가하지 않는다.
  1. 보낸 선물 상세와 운송장 등록

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: 왼쪽 신청 취소, 오른쪽 등록 완료

택배사 로컬 상수:

let giftCourierCompanyNames = [
    "CJ대한통운",
    "우체국택배",
    "GS25 편의점택배",
    "CU 편의점택배",
    "한진택배",
    "롯데택배",
    "로젠택배",
    "경동택배",
    "대신택배",
    "일양로지스",
    "천일택배",
    "합동택배",
    "건영택배",
    "농협택배"
]
  • 이미 앱 공통 택배사 상수가 있으면 그것을 우선 사용
  • API에는 선택한 한글 표시명을 그대로 courierCompanyName으로 보낸다.

운송장 등록 CTA 활성 조건:

  • 택배사 선택됨
  • 운송장 번호가 공백이 아님
  • submit 중 중복 탭 방지

운송장 등록 API:

  • Method: POST
  • URL: /api/v2/gifts/{applicationNo}/tracking
  • Request:
{
  "courierCompanyName": "한진택배",
  "trackingNumber": "123456789012"
}
  • 성공 시 성공 toast 표시 후 상세 페이지로 돌아가거나 replace navigation으로 상세를 다시 열고, 상세 API를 재조회해 TRACKING_REGISTERED 상태를 표시한다.
  1. 받은 선물 상세와 배송지 입력

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:
{
  "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는 표시하지 않는다.
  1. 푸시/딥링크
  • 선물 푸시 딥링크는 deepLinkValue=GIFT_DETAIL, deepLinkId=applicationNo를 사용한다.
  • 딥링크 진입 시 바로 특정 보낸/받은 화면을 결정하지 말고, GET /api/v2/gifts/{applicationNo}를 호출한 뒤 응답의 direction, trackingRequired, recipientAddressRequired, status로 상세 UI를 결정한다.
  • 권한 오류 또는 미존재 오류는 기존 앱 딥링크 오류 처리 패턴을 따른다.
  1. 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: 전달 완료 후 팬 리뷰 작성, 기존 리뷰 플로우가 있을 때 연결
  1. 검증 체크리스트
  • 선물 보내기 진입 시 전달받은 크리에이터 닉네임과 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 실패 시 기존 앱 오류 표시 방식을 사용하고 입력값/스크롤 상태를 불필요하게 초기화하지 않는지 확인한다.