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

18 KiB

받은 선물 상세 및 배송지 입력 페이지 생성 프롬프트

이 문서는 Android/iOS 앱의 선물함 리스트에서 받은 선물 상세 페이지와 배송지 입력 UI를 생성할 때 사용하는 프롬프트다.

참고 Figma:

  • 배송지 입력 전 상세 UI: 2531:36290
  • 배송지 입력 완료 후 상세 UI: 2531:36399
  • 선물 전달 완료시 상세 UI: 2531:36330
  • 선물 전달 불가시 상세 UI: 2531:36422

전제:

  • 선물함 리스트 item 탭 시 applicationNo를 전달받아 상세 페이지로 이동한다.
  • 새 디자인 시스템을 만들지 말고 Android/iOS 각각의 기존 디자인 시스템, 컴포넌트, API client, 상태 관리, toast/dialog, navigation 패턴을 재사용한다.
  • 모든 API 응답은 공통 envelope를 사용하며 실제 payload는 response.data다.
  • 이 프롬프트는 받는 크리에이터 관점(direction=RECEIVED)만 다룬다.
  • API 예시는 TypeScript 타입이 아니라 JSON으로 작성한다.

모바일 앱 구현 프롬프트

Android/iOS 앱의 받은 선물 상세 페이지와 배송지 입력 UI를 Figma 기준으로 생성하고 API 연동을 구현해줘.

범위:
- 선물함 리스트에서 `applicationNo`를 받아 받은 선물 상세 페이지로 이동한다.
- 상세 페이지는 `GET /api/v2/gifts/{applicationNo}` 응답으로 렌더링한다.
- `direction=RECEIVED && recipientAddressRequired=true`이면 배송지 입력 UI를 표시한다.
- 배송지 입력은 `POST /api/v2/gifts/{applicationNo}/recipient-address`로 연결한다.
- 배송지 입력 완료 후에는 상세를 다시 조회해 배송지 입력 완료 상태 UI를 표시한다.

중요 전제:
- Figma의 visual structure와 문구를 우선 따르되, 데이터 노출과 CTA 조건은 API 계약을 우선한다.
- `direction=RECEIVED` 화면에서 `senderInfo`, `mailbox`, `giftInfo.applicationNo`, `giftInfo.paidCan`, `giftInfo.tracking`은 표시하지 않는다.
- `recipientAddress`가 null이면 받는 주소 섹션 자체를 표시하지 않는다. 전달 완료 후 배송지 정보가 삭제될 예정이므로 빈 섹션, skeleton, `-` 값도 표시하지 않는다.
- 전달 불가 화면의 `문의하기`는 기존 앱의 카카오 채널 문의 연결과 동일하게 연결한다.
- 전달 불가 화면의 `선물 정책 확인`은 버튼/action만 만들어두고 실제 외부 페이지 연결은 하지 않는다. 임의 URL을 연결하지 않는다.

상세 페이지 진입:
- 화면 진입 시 `GET /api/v2/gifts/{applicationNo}`를 호출한다.
- 조회 결과 `direction`이 `RECEIVED`가 아니면 이 화면을 렌더링하지 말고 기존 앱의 잘못된 접근/상세 재라우팅 정책을 따른다.
- 로딩 중에는 기존 앱 상세 화면 로딩/스켈레톤 패턴을 사용한다.
- 실패 시 기존 앱 toast/dialog와 재시도 패턴을 사용한다.

배송지 입력 전 UI:
- 적용 조건: `direction=RECEIVED && recipientAddressRequired=true`.
- Figma `2531:36290`을 따른다.
- 상단 타이틀: `선물 받기`.
- 개인정보 이용 안내 배너를 표시한다.
  - 제목: `개인정보 이용 안내`
  - 본문: `크리에이터 및 팬의 주소는 공개되지 않습니다. 입력한 배송지는 선물 전달 및 필요한 사고 처리에서 사용되며, 전달 완료 후 파기됩니다.`
- `전달 예정 선물` 섹션을 표시한다.
  - 발송인: `giftInfo.senderNickname`
  - 사이즈: `giftInfo.sizeName`
  - 카테고리: `giftInfo.categoryName`
- `배송지 입력` 섹션을 표시한다.
  - 이름 input: `recipientName`
  - 휴대폰 번호 input: `recipientPhoneNumber`
  - 우편번호 input: `recipientZipCode`
  - 우편번호 검색 버튼: 기존 페이지와 동일하게 Kakao 우편번호 서비스를 사용한다.
  - 주소 input: `recipientAddress`
  - 상세 주소 input: `recipientAddressDetail`
- `이용 약관 동의` 섹션을 표시한다.
  - `소다라이브 크리에이터 상품 전달 이용약관 및 전달 가능 제한 품목을 확인하고 동의합니다.(필수)`
  - `상품 전달을 위해 수령인 성명·연락처·주소 등 필요한 개인정보를 일시적으로 수집·이용하는 것에 동의합니다.(필수)`
  - 두 항목 모두 체크해야 제출 가능하다.
  - 약관 상세 링크는 앱에 기존 약관/webview 경로가 있으면 연결하고, 없으면 새 페이지를 만들지 않는다.
- 하단 주요 CTA: `선물 받기`.
- CTA 활성 조건:
  - 이름, 휴대폰 번호, 우편번호, 주소가 공백이 아니다.
  - 상세 주소는 선택 입력이다.
  - 약관 2개가 모두 체크됐다.
  - submit 중에는 중복 탭을 막고 loading 상태를 표시한다.

배송지 입력 API 연결:
- CTA 탭 시 `POST /api/v2/gifts/{applicationNo}/recipient-address`를 호출한다.
- request body에는 화면 입력값과 약관 동의 boolean을 그대로 전송한다.
- 성공 시 성공 toast를 표시하고 상세 페이지를 다시 조회하거나 replace navigation으로 상세를 다시 연다.
- 실패 시 기존 앱 form error/toast/dialog 패턴을 따른다. 실패 상태에서 입력값은 유지한다.

주소 검색 구현:
- 기존 앱/페이지에 Kakao 우편번호 서비스 래퍼가 있으면 반드시 재사용한다.
- 신규 연결이 필요하면 Kakao 우편번호 서비스 공식 가이드 `https://postcode.map.kakao.com/guide`를 따른다.
- SDK script는 공식 가이드의 `//t1.kakaocdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js`를 사용한다. 임의 수정본이나 별도 주소 검색 API를 만들지 않는다.
- Android/iOS WebView 또는 모바일웹 환경에서는 `window.open` 팝업이 막힐 수 있으므로, 기존 페이지와 같은 방식이 없다면 Kakao 가이드의 `embed()` 기반 레이어/페이지 삽입 방식을 우선 검토한다.
- 검색 결과 선택 시:
  - `data.zonecode`를 `recipientZipCode`에 넣는다.
  - `data.userSelectedType === "R"`이면 `data.roadAddress`를, 그 외에는 `data.jibunAddress`를 `recipientAddress`에 넣는다.
  - 주소 선택 후 `recipientAddressDetail` 입력으로 포커스를 이동한다.
- 상세 주소는 Kakao 결과에서 채우지 않고 사용자가 직접 입력하게 한다.
- 주소 검색 실패/취소 시 입력 중이던 이름, 휴대폰 번호, 약관 체크 상태를 초기화하지 않는다.

배송지 입력 완료 후 상세 UI:
- 적용 조건: `direction=RECEIVED && recipientAddressRequired=false && status`가 `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED` 중 하나이며 `recipientAddress`가 있다.
- Figma `2531:36399`를 따른다.
- 상단 타이틀: `선물 상세`.
- hero title: `배송지 입력 완료!`
- hero body: `빠른 시일 내로 선물을 전달 드릴게요.`
- `문의하기` 버튼을 표시하고 기존 카카오 채널 문의로 연결한다.
- `선물 정보` 섹션을 표시한다.
  - 발송인: `giftInfo.senderNickname`
  - 사이즈: `giftInfo.sizeName`
  - 카테고리: `giftInfo.categoryName`
  - 배송 신청일: `giftInfo.shippingRequestedAt`
- `받는 주소` 섹션은 `recipientAddress`가 있을 때만 표시한다.
  - 이름: `recipientAddress.name`
  - 휴대폰 번호: `recipientAddress.phoneNumber`
  - 주소: `recipientAddress.address`

선물 전달 완료 상세 UI:
- 적용 조건: `direction=RECEIVED && status=DELIVERED`.
- Figma `2531:36330`을 따른다.
- 상단 타이틀: `선물 상세`.
- hero title: `선물 전달 완료!`
- hero body: `팬이 보낸 선물이 도착했어요. 선물이 도착하지 않은 경우, 문의해 주세요.`
- `문의하기` 버튼을 표시하고 기존 카카오 채널 문의로 연결한다.
- `선물 정보` 섹션을 표시한다.
  - 발송인: `giftInfo.senderNickname`
  - 사이즈: `giftInfo.sizeName`
  - 카테고리: `giftInfo.categoryName`
  - 배송 신청일: `giftInfo.shippingRequestedAt`
  - 배송 완료일: `statusTimeline`에서 `DELIVERED.occurredAt`
- `받는 주소` 섹션은 `recipientAddress`가 있을 때만 표시한다.
  - 섹션 보조 문구: `배송 완료 3일 후 해당 정보는 사라져요`
  - 이름: `recipientAddress.name`
  - 휴대폰 번호: `recipientAddress.phoneNumber`
  - 주소: `recipientAddress.address`
- `recipientAddress=null`이면 `받는 주소` 섹션 전체를 숨긴다.

선물 전달 불가 상세 UI:
- 적용 조건: `direction=RECEIVED && status=UNDELIVERABLE`.
- Figma `2531:36422`를 따른다.
- 상단 타이틀: `선물 상세`.
- chip: `전달 불가`
- hero title: `전달할 수 없는 품목입니다`
- hero body: `해당 선물은 SODALIVE 선물 정책에 따라 크리에이터에게 전달할 수 없는 품목으로 확인되었습니다.`
- action button:
  - `문의하기`: 기존 카카오 채널 문의로 연결한다.
  - `선물 정책 확인`: action handler만 연결하고 실제 외부 페이지/URL은 연결하지 않는다. 기존 앱 정책상 no-op이 금지되어 있으면 `준비 중입니다.` toast만 표시한다.
- `사유` 섹션을 표시한다.
  - 사유 제목: `delivery.undeliverableReason`이 있으면 그 값을 사용한다.
  - `delivery.undeliverableReason`이 null이면 `전달 불가`로 표시한다.
  - 안내 문구: `해당 선물은 정책에 따라 폐기되며 반송되지 않습니다.`
- 전달 불가 상태에서는 배송지 입력 UI를 표시하지 않는다.
- `recipientAddress`가 응답에 있더라도 전달 불가 Figma에는 받는 주소 섹션이 없으므로 표시하지 않는다.

날짜 표시:
- API의 모든 날짜는 UTC ISO 문자열이다.
- 앱 공통 날짜 formatter가 있으면 재사용한다.
- Figma처럼 상세 필드 날짜는 `YYYY.MM.DD`로 표시한다.
- 값이 null이면 해당 row를 숨긴다.

API 명세:

선물 상세 조회:
- Method: `GET`
- URL: `/api/v2/gifts/{applicationNo}`
- 인증: 로그인 필요
- Response envelope 예시 - 배송지 입력 전:
```json
{
  "success": true,
  "data": {
    "applicationNo": "A-1002609300001",
    "direction": "RECEIVED",
    "status": "TRACKING_REGISTERED",
    "statusName": "발송 확인",
    "giftInfo": {
      "recipientCreatorNickname": null,
      "senderNickname": "팬닉네임이들어가는부분",
      "sizeName": "소형",
      "categoryName": "신규 전자굿즈",
      "applicationNo": null,
      "paidCan": null,
      "tracking": null,
      "shippingRequestedAt": "2026-10-12T03:00:00Z"
    },
    "senderInfo": null,
    "recipientAddress": null,
    "mailbox": null,
    "trackingRequired": false,
    "recipientAddressRequired": true,
    "recipientAddressDeadlineAt": "2026-10-19T03:00:00Z",
    "delivery": {
      "canceledAt": null,
      "undeliverableAt": null,
      "undeliverableReason": null
    },
    "statusTimeline": [
      {
        "status": "RECEIVED",
        "statusName": "접수 완료",
        "occurredAt": "2026-10-11T03:00:00Z"
      },
      {
        "status": "TRACKING_REGISTERED",
        "statusName": "발송 확인",
        "occurredAt": "2026-10-12T03:00:00Z"
      },
      {
        "status": "ARRIVED_AT_MAILBOX",
        "statusName": "사서함 도착",
        "occurredAt": null
      },
      {
        "status": "INSPECTION_COMPLETED",
        "statusName": "검수완료",
        "occurredAt": null
      },
      {
        "status": "DELIVERED",
        "statusName": "전달완료",
        "occurredAt": null
      }
    ]
  },
  "message": ""
}

Response envelope 예시 - 배송지 입력 완료/전달 완료:

{
  "success": true,
  "data": {
    "applicationNo": "A-1002609300001",
    "direction": "RECEIVED",
    "status": "DELIVERED",
    "statusName": "전달완료",
    "giftInfo": {
      "recipientCreatorNickname": null,
      "senderNickname": "팬닉네임이들어가는부분",
      "sizeName": "소형",
      "categoryName": "신규 전자굿즈",
      "applicationNo": null,
      "paidCan": null,
      "tracking": null,
      "shippingRequestedAt": "2026-10-12T03:00:00Z"
    },
    "senderInfo": null,
    "recipientAddress": {
      "name": "크리에이터이름",
      "phoneNumber": "01012345678",
      "address": "(04030) 서울특별시 마포구 양화로 000, 3층"
    },
    "mailbox": null,
    "trackingRequired": false,
    "recipientAddressRequired": false,
    "recipientAddressDeadlineAt": "2026-10-19T03:00:00Z",
    "delivery": {
      "canceledAt": null,
      "undeliverableAt": null,
      "undeliverableReason": null
    },
    "statusTimeline": [
      {
        "status": "RECEIVED",
        "statusName": "접수 완료",
        "occurredAt": "2026-10-11T03:00:00Z"
      },
      {
        "status": "TRACKING_REGISTERED",
        "statusName": "발송 확인",
        "occurredAt": "2026-10-12T03:00:00Z"
      },
      {
        "status": "ARRIVED_AT_MAILBOX",
        "statusName": "사서함 도착",
        "occurredAt": "2026-10-14T03:00:00Z"
      },
      {
        "status": "INSPECTION_COMPLETED",
        "statusName": "검수완료",
        "occurredAt": "2026-10-16T03:00:00Z"
      },
      {
        "status": "DELIVERED",
        "statusName": "전달완료",
        "occurredAt": "2026-10-22T03:00:00Z"
      }
    ]
  },
  "message": ""
}

Response envelope 예시 - 전달 불가:

{
  "success": true,
  "data": {
    "applicationNo": "A-1002609300001",
    "direction": "RECEIVED",
    "status": "UNDELIVERABLE",
    "statusName": "전달불가",
    "giftInfo": {
      "recipientCreatorNickname": null,
      "senderNickname": "팬닉네임이들어가는부분",
      "sizeName": "소형",
      "categoryName": "신규 전자굿즈",
      "applicationNo": null,
      "paidCan": null,
      "tracking": null,
      "shippingRequestedAt": "2026-10-12T03:00:00Z"
    },
    "senderInfo": null,
    "recipientAddress": null,
    "mailbox": null,
    "trackingRequired": false,
    "recipientAddressRequired": false,
    "recipientAddressDeadlineAt": "2026-10-19T03:00:00Z",
    "delivery": {
      "canceledAt": null,
      "undeliverableAt": "2026-10-16T03:00:00Z",
      "undeliverableReason": "음식물 또는 보관이 어려운 품목"
    },
    "statusTimeline": [
      {
        "status": "RECEIVED",
        "statusName": "접수 완료",
        "occurredAt": "2026-10-11T03:00:00Z"
      },
      {
        "status": "TRACKING_REGISTERED",
        "statusName": "발송 확인",
        "occurredAt": "2026-10-12T03:00:00Z"
      },
      {
        "status": "ARRIVED_AT_MAILBOX",
        "statusName": "사서함 도착",
        "occurredAt": "2026-10-14T03:00:00Z"
      },
      {
        "status": "INSPECTION_COMPLETED",
        "statusName": "검수완료",
        "occurredAt": null
      },
      {
        "status": "DELIVERED",
        "statusName": "전달완료",
        "occurredAt": null
      }
    ]
  },
  "message": ""
}

선물 상세 response data 필드:

필드 설명
applicationNo 선물 신청번호. 화면 진입 key로 사용한다.
direction 이 프롬프트에서는 RECEIVED만 렌더링한다.
status TRACKING_REGISTERED, ARRIVED_AT_MAILBOX, INSPECTION_COMPLETED, DELIVERED, UNDELIVERABLE 등을 상태별 UI 분기에 사용한다.
giftInfo.senderNickname 받은 선물 관점의 발송인 팬 닉네임.
giftInfo.sizeName 선물 사이즈명.
giftInfo.categoryName 카테고리명.
giftInfo.shippingRequestedAt 배송 신청일. UTC ISO 문자열.
recipientAddress 크리에이터 본인이 입력한 배송지. null이면 받는 주소 섹션을 숨긴다.
recipientAddressRequired 배송지 입력 UI 표시 여부.
recipientAddressDeadlineAt 배송지 입력 마감일. 필요하면 안내 문구에 사용한다.
delivery.undeliverableReason 전달 불가 사유.
statusTimeline 배송 완료일 등 상태별 날짜를 찾는 데 사용한다.

배송지 입력:

  • Method: POST
  • URL: /api/v2/gifts/{applicationNo}/recipient-address
  • 인증: 로그인 필요
  • Request:
{
  "recipientName": "크리에이터이름",
  "recipientPhoneNumber": "01012345678",
  "recipientZipCode": "04030",
  "recipientAddress": "서울특별시 마포구 양화로 000",
  "recipientAddressDetail": "3층",
  "recipientTermsAgreed": true,
  "recipientPrivacyAgreed": true
}
  • Response envelope 예시:
{
  "success": true,
  "data": {
    "applicationNo": "A-1002609300001",
    "status": "TRACKING_REGISTERED",
    "statusName": "발송 확인",
    "recipientAddressRegisteredAt": "2026-10-13T03:00:00Z"
  },
  "message": ""
}

배송지 입력 response data 필드:

필드 설명
applicationNo 선물 신청번호
status 등록 후 상태. 일반적으로 기존 진행 상태 유지
statusName 서버 상태 표시명
recipientAddressRegisteredAt 배송지 입력 완료 시각. UTC ISO 문자열

테스트/검증:

  • 리스트 item 탭 시 applicationNo로 받은 선물 상세 페이지가 열리는지 확인한다.
  • 상세 진입 시 GET /api/v2/gifts/{applicationNo}가 호출되는지 확인한다.
  • direction=RECEIVED && recipientAddressRequired=true이면 배송지 입력 UI가 표시되는지 확인한다.
  • 배송지 입력 필수값 또는 약관 동의가 누락되면 POST /recipient-address를 호출하지 않는지 확인한다.
  • 배송지 입력 submit 시 request body가 API 명세와 일치하는지 확인한다.
  • 배송지 입력 성공 후 상세가 재조회되고 배송지 입력 완료 UI가 표시되는지 확인한다.
  • 배송지 입력 완료/전달 완료 상태에서 recipientAddress가 있으면 받는 주소 섹션이 표시되는지 확인한다.
  • recipientAddress=null이면 받는 주소 섹션 자체가 표시되지 않는지 확인한다.
  • status=DELIVERED이면 전달 완료 UI와 배송 완료일이 표시되는지 확인한다.
  • status=UNDELIVERABLE이면 전달 불가 UI, 사유, 폐기/반송 불가 안내가 표시되는지 확인한다.
  • 전달 불가 화면의 문의하기는 기존 카카오 채널 문의로 연결되는지 확인한다.
  • 전달 불가 화면의 선물 정책 확인은 버튼 action만 있고 실제 외부 URL을 열지 않는지 확인한다.
  • 모든 날짜는 UTC ISO 문자열을 앱 표시 형식으로 변환해 표시하는지 확인한다.