docs(gift): iOS 선물함 구현 프롬프트를 보강한다
This commit is contained in:
@@ -177,6 +177,9 @@ Response `data` 예시:
|
|||||||
"counterpartMemberId": 200,
|
"counterpartMemberId": 200,
|
||||||
"counterpartNickname": "달빛수집가",
|
"counterpartNickname": "달빛수집가",
|
||||||
"counterpartProfileImageUrl": "https://example.com/profile.png",
|
"counterpartProfileImageUrl": "https://example.com/profile.png",
|
||||||
|
"recipientAddressRegistered": false,
|
||||||
|
"recipientAddressDeadlineAt": null,
|
||||||
|
"canceledAt": null,
|
||||||
"createdAt": "2026-09-18T03:00:00Z"
|
"createdAt": "2026-09-18T03:00:00Z"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -202,6 +205,9 @@ Response `data` 필드:
|
|||||||
| `items[].counterpartMemberId` | number | 상대방 회원 ID. `SENT`에서는 받는 크리에이터, `RECEIVED`에서는 보낸 팬 |
|
| `items[].counterpartMemberId` | number | 상대방 회원 ID. `SENT`에서는 받는 크리에이터, `RECEIVED`에서는 보낸 팬 |
|
||||||
| `items[].counterpartNickname` | string | 상대방 닉네임 |
|
| `items[].counterpartNickname` | string | 상대방 닉네임 |
|
||||||
| `items[].counterpartProfileImageUrl` | string 또는 null | 상대방 프로필 이미지 URL. 없으면 기본 프로필 이미지 표시 |
|
| `items[].counterpartProfileImageUrl` | string 또는 null | 상대방 프로필 이미지 URL. 없으면 기본 프로필 이미지 표시 |
|
||||||
|
| `items[].recipientAddressRegistered` | boolean | 크리에이터 배송지 필수 정보가 모두 입력되었는지 여부 |
|
||||||
|
| `items[].recipientAddressDeadlineAt` | string 또는 null | `direction=RECEIVED`이고 배송지가 아직 입력되지 않은 경우의 배송지 입력 기한. 배송지 입력 완료 후에는 null |
|
||||||
|
| `items[].canceledAt` | string 또는 null | 취소 상태의 취소일. 취소되지 않은 선물은 null |
|
||||||
| `items[].createdAt` | string 또는 null | 신청일. UTC ISO 문자열 |
|
| `items[].createdAt` | string 또는 null | 신청일. UTC ISO 문자열 |
|
||||||
| `page` | number | 현재 페이지 |
|
| `page` | number | 현재 페이지 |
|
||||||
| `size` | number | 페이지 크기 |
|
| `size` | number | 페이지 크기 |
|
||||||
@@ -220,21 +226,11 @@ Response `data` 필드:
|
|||||||
- `direction=SENT`에서는 받는 크리에이터 정보로 해석한다.
|
- `direction=SENT`에서는 받는 크리에이터 정보로 해석한다.
|
||||||
- `direction=RECEIVED`에서는 보낸 팬 정보로 해석한다.
|
- `direction=RECEIVED`에서는 보낸 팬 정보로 해석한다.
|
||||||
|
|
||||||
현재 API만으로 부족한 Figma 표시값:
|
배송지/취소 표시 규칙:
|
||||||
- 취소일 `canceledAt`
|
- 배송지 입력 완료 여부는 `recipientAddressRegistered`로 판단한다.
|
||||||
- 배송지 입력 마감일 또는 남은 일수
|
- 배송지 입력 남은 기간은 `recipientAddressDeadlineAt` 기준으로 앱에서 계산한다.
|
||||||
|
- 배송지가 이미 입력되었으면 `recipientAddressDeadlineAt`은 null이다.
|
||||||
추가 API 보강이 가능하면 리스트 item에 아래 필드를 추가해달라고 요청한다.
|
- 취소 상태의 날짜 라벨은 `canceledAt`이 있으면 `{canceledAt} 취소`로 표시하고, 그 외에는 `{createdAt} 신청`으로 표시한다.
|
||||||
```json
|
|
||||||
{
|
|
||||||
"canceledAt": "2026-09-18T03:00:00Z",
|
|
||||||
"recipientAddressDeadlineAt": "2026-09-25T03:00:00Z",
|
|
||||||
"sortAt": "2026-09-18T03:00:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- `canceledAt`: 취소 상태의 취소일 표시용
|
|
||||||
- `recipientAddressDeadlineAt`: 크리에이터 배송지 입력 안내 배너의 남은 일수 계산용
|
|
||||||
- `sortAt`: 신청/취소 최신순 정렬 기준. 취소 건은 취소일, 그 외에는 신청일
|
|
||||||
|
|
||||||
로딩/에러/빈 상태:
|
로딩/에러/빈 상태:
|
||||||
- 최초 로딩 중에는 기존 앱 리스트 스켈레톤 또는 loading 패턴을 사용한다.
|
- 최초 로딩 중에는 기존 앱 리스트 스켈레톤 또는 loading 패턴을 사용한다.
|
||||||
|
|||||||
@@ -0,0 +1,596 @@
|
|||||||
|
# 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 실패 시 기존 앱 오류 표시 방식을 사용하고 입력값/스크롤 상태를 불필요하게 초기화하지 않는지 확인한다.
|
||||||
|
```
|
||||||
@@ -1406,6 +1406,30 @@ P7은 FCM 확장 후 전체 상태 전이를 연결하고 최종 회귀로 종
|
|||||||
- [x] 공식 `onShutdownForceStop(true)` 옵션으로 native Redis 프로세스 종료가 무기한 `waitFor()`에 걸리지 않도록 변경했다.
|
- [x] 공식 `onShutdownForceStop(true)` 옵션으로 native Redis 프로세스 종료가 무기한 `waitFor()`에 걸리지 않도록 변경했다.
|
||||||
- [x] 후속 요청에 따라 전체 `./gradlew --no-daemon test`를 실행해 정상 종료를 확인했다.
|
- [x] 후속 요청에 따라 전체 `./gradlew --no-daemon test`를 실행해 정상 종료를 확인했다.
|
||||||
|
|
||||||
|
## Phase 13: 선물함 리스트 표시 보강
|
||||||
|
|
||||||
|
**Phase 결과:** 선물함 리스트 item이 배송지 입력 여부, 배송지 입력 기한, 취소일을 내려줘 iOS가 리스트에서 배송지 CTA와 취소일을 표시할 수 있다.
|
||||||
|
|
||||||
|
**선행조건:** Phase 12 완료, 사용자 확정 요구사항 반영.
|
||||||
|
|
||||||
|
### Task 13.1 문서 계약 갱신
|
||||||
|
|
||||||
|
**Goal 실행 `P13-T1`:** PRD, 클라이언트 요약, 모바일 프롬프트에 리스트 item 추가 필드 3개를 반영한다.
|
||||||
|
|
||||||
|
- **Files:** Modify `prd.md`, `client-api-summary.md`, `gift-list-page-prompt.md`, `ios-gift-feature-implementation-prompt.md`, `plan-task.md`.
|
||||||
|
- [x] **RED:** 기존 리스트 응답 계약에 `recipientAddressRegistered`, `canceledAt`이 없고 배송지 deadline 규칙이 불명확함을 확인한다.
|
||||||
|
- [x] **GREEN:** `recipientAddressRegistered`, `recipientAddressDeadlineAt`, `canceledAt`을 문서화한다. 배송지 입력 완료 후 `recipientAddressDeadlineAt=null`, 취소일은 `createdAt` 대입이 아니라 `canceledAt` 별도 필드로 내려주는 정책을 명시한다.
|
||||||
|
|
||||||
|
### Task 13.2 리스트 응답 필드 구현
|
||||||
|
|
||||||
|
**Goal 실행 `P13-T2`:** 사용자 선물함 리스트 응답에 추가 필드 3개를 내려준다.
|
||||||
|
|
||||||
|
- **Files:** Modify `GiftQueryService.kt`, `GiftResponse.kt`.
|
||||||
|
- **Tests:** Modify `GiftQueryServiceTest.kt`, `GiftControllerTest.kt`.
|
||||||
|
- [x] **RED:** 배송지 미입력/입력 완료/취소 상태의 리스트 응답 필드 실패 테스트를 작성하고 실패를 확인했다.
|
||||||
|
- [x] **GREEN:** 기존 `GiftDelivery` 정보를 재사용해 `recipientAddressRegistered`, `recipientAddressDeadlineAt`, `canceledAt`을 매핑했다.
|
||||||
|
- [x] **검증:** focused 테스트와 `ktlintCheck`, `git diff --check`를 실행했다.
|
||||||
|
|
||||||
## Decision Log
|
## Decision Log
|
||||||
|
|
||||||
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 |
|
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 |
|
||||||
|
|||||||
Reference in New Issue
Block a user