docs(gift): 선물 상세 구현 프롬프트를 추가한다
This commit is contained in:
@@ -245,7 +245,7 @@ type AdminGiftOperationStatusResponse = {
|
||||
|
||||
목표:
|
||||
- 관리자 메뉴 `선물함 관리 > 받을 주소`에서 팬이 선물을 보낼 전역 단일 주소를 조회하고 저장한다.
|
||||
- 이 주소는 팬의 선물 상세 API에서 운송장 등록 전 상태일 때 `mailbox`로 노출된다.
|
||||
- 이 주소는 팬의 보낸 선물 상세 API에서 정상 진행 상태 중 전달 완료 전(`RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED`)일 때 `mailbox`로 노출된다.
|
||||
- 기존 관리자 페이지의 폼, 저장 버튼, 토스트, 에러 표시 패턴을 그대로 따른다.
|
||||
|
||||
라우트:
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
# 선물 상세 및 운송장 등록 페이지 생성 프롬프트
|
||||
|
||||
이 문서는 Android/iOS 앱의 `선물함` 리스트에서 진입하는 선물 상세 페이지와 운송장 등록 페이지를 생성할 때 사용하는 프롬프트다.
|
||||
|
||||
참고 Figma:
|
||||
|
||||
- 선물 보내기 신청 상세 운송장 등록 전 UI: `2481:19011`
|
||||
- 선물 보내기 신청 상세 운송장 등록 후 UI: `2481:19152`
|
||||
- 선물 보내기 신청 상세 전달 완료 UI: `2481:19320`
|
||||
- 전달 불가 UI: `2481:19456`
|
||||
- 취소 버튼 다이얼로그 UI: `2481:19500`
|
||||
- 취소 완료 페이지 UI: `2481:19521`
|
||||
- 운송장 등록 UI: `2481:19066`
|
||||
- 운송장 정보를 채우고 난 후 UI: `2481:19109`
|
||||
|
||||
전제:
|
||||
|
||||
- 선물함 리스트 item 탭 시 `applicationNo`를 전달받아 상세 페이지로 이동한다.
|
||||
- 새 디자인 시스템을 만들지 말고 Android/iOS 각각의 기존 디자인 시스템, 컴포넌트, API client, 상태 관리, toast/dialog, navigation 패턴을 재사용한다.
|
||||
- 모든 API 응답은 공통 envelope를 사용하며 실제 payload는 `response.data`다.
|
||||
- Figma의 진행 상태는 4단계처럼 보이지만 실제 구현은 서버의 `statusTimeline` 5개 상태를 모두 사용한다.
|
||||
- 운송장 등록의 택배사 선택지는 서버 API가 없으므로 앱 로컬 상수로 관리한다.
|
||||
- 취소 확인 팝업은 앱의 `v2 dialog` 컴포넌트를 기본으로 사용한다.
|
||||
|
||||
---
|
||||
|
||||
## 모바일 앱 구현 프롬프트
|
||||
|
||||
```text
|
||||
Android/iOS 앱의 선물 상세 페이지와 운송장 등록 페이지를 Figma 기준으로 생성하고 API 연동을 구현해줘.
|
||||
|
||||
범위:
|
||||
- 선물함 리스트에서 `applicationNo`를 받아 선물 상세 페이지로 이동한다.
|
||||
- 상세 페이지는 `GET /api/v2/gifts/{applicationNo}` 응답으로 렌더링한다.
|
||||
- `trackingRequired=true`인 보낸 선물 상세에서는 운송장 등록 CTA를 표시하고 운송장 등록 페이지로 이동한다.
|
||||
- 운송장 등록 페이지는 같은 상세 API를 조회해 신청 요약과 보낼 주소를 표시한 뒤 `POST /api/v2/gifts/{applicationNo}/tracking`으로 운송장 정보를 등록한다.
|
||||
|
||||
중요 전제:
|
||||
- Figma의 visual structure와 문구를 우선 따르되, 상태 개수와 데이터 노출은 API 계약을 우선한다.
|
||||
- 상세 진행 단계는 Figma처럼 4단계로 고정하지 말고 API `statusTimeline`의 5개 정상 진행 상태를 모두 렌더링한다.
|
||||
- 택배사 목록은 API로 조회하지 않는다. 앱 로컬 상수로 관리하고 선택한 한글 표시명을 `courierCompanyName`으로 전송한다.
|
||||
- 운송장 등록 완료 후 수정 기능은 만들지 않는다.
|
||||
|
||||
상세 페이지 진입:
|
||||
- 선물함 리스트 item에서 받은 `applicationNo`로 진입한다.
|
||||
- 화면 진입 시 `GET /api/v2/gifts/{applicationNo}`를 호출한다.
|
||||
- 조회 결과 `status=CANCELED`이면 일반 신청 상세 화면을 렌더링하지 말고 취소 완료 페이지 UI를 보여준다.
|
||||
- 로딩 중에는 기존 앱 상세 화면 로딩/스켈레톤 패턴을 사용한다.
|
||||
- 실패 시 기존 앱 toast/dialog와 재시도 패턴을 사용한다.
|
||||
|
||||
상세 페이지 공통 레이아웃:
|
||||
1. 상단 고정 내비게이션
|
||||
- 타이틀: `신청 상세`
|
||||
- 뒤로가기 버튼은 기존 앱 패턴을 사용한다.
|
||||
2. 스크롤 콘텐츠
|
||||
- 배송 상태 hero
|
||||
- 진행 상태 progress
|
||||
- 상태별 action button 영역
|
||||
- 선물 정보
|
||||
- 필요한 경우 보내는 사람, 받는 사서함, 사유 섹션
|
||||
3. 하단 safe area
|
||||
- 기존 앱 패턴을 따른다.
|
||||
|
||||
상태별 hero 문구:
|
||||
| status | chip | title | body |
|
||||
|---|---|---|---|
|
||||
| `RECEIVED` | 없음 또는 `접수 완료` | `선물 신청 완료!` | `선물이 정상적으로 접수되었어요!` |
|
||||
| `TRACKING_REGISTERED` | `발송 확인` | `사서함으로 이동 중이에요` | 없음 |
|
||||
| `ARRIVED_AT_MAILBOX` | `사서함 도착` | `선물이 사서함에 도착했어요` | 없음 |
|
||||
| `INSPECTION_COMPLETED` | `검수 완료` | `선물 검수를 완료했어요` | 없음 |
|
||||
| `DELIVERED` | `전달 완료` | `크리에이터에게 선물을 전달했어요` | 없음 |
|
||||
| `UNDELIVERABLE` | `전달 불가` | `전달할 수 없는 품목입니다` | `보내주신 선물은 SODALIVE 선물 정책에 따라 크리에이터에게 전달할 수 없는 품목으로 확인되었습니다.` |
|
||||
| `CANCELED` | `신청 취소` | `선물 신청이 취소되었어요` | `사용한 캔은 환불 처리됩니다.` |
|
||||
|
||||
진행 상태 progress:
|
||||
- `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으로 추가하지 않는다.
|
||||
- 종료 상태에서도 `statusTimeline`에 있는 정상 진행 이력은 그대로 표시한다.
|
||||
|
||||
상세 페이지 섹션 표시 규칙:
|
||||
- `선물 정보`는 항상 표시한다.
|
||||
- `direction=SENT`이면 크리에이터명은 `giftInfo.recipientCreatorNickname`을 사용한다.
|
||||
- `direction=RECEIVED`이면 보낸 팬명은 `giftInfo.senderNickname`을 사용한다.
|
||||
- `senderInfo`가 있으면 `보내는 사람` 섹션을 표시한다.
|
||||
- `mailbox`가 있으면 `받는 사서함` 또는 `보내실 주소` 섹션을 표시한다.
|
||||
- `recipientAddress`가 있으면 `받는 사람` 또는 `배송지` 섹션을 표시한다.
|
||||
- `delivery.undeliverableReason`이 있으면 `사유` 섹션을 표시한다.
|
||||
|
||||
선물 정보 표시 필드:
|
||||
- 크리에이터 또는 보낸 팬 닉네임
|
||||
- 선물 사이즈: `giftInfo.sizeName`
|
||||
- 카테고리: `giftInfo.categoryName`
|
||||
- 신청번호: `applicationNo` 또는 `giftInfo.applicationNo`
|
||||
- 캔: `giftInfo.paidCan`이 null이 아닐 때만 표시
|
||||
- 운송장 번호: `giftInfo.tracking`이 null이 아닐 때 표시
|
||||
- 검수일: `statusTimeline`에서 `INSPECTION_COMPLETED.occurredAt`이 있으면 표시
|
||||
- 전달 완료일: `statusTimeline`에서 `DELIVERED.occurredAt`이 있으면 표시
|
||||
- 취소일: `delivery.canceledAt`이 있으면 표시
|
||||
|
||||
받는 사서함/보내실 주소 표시:
|
||||
- `mailbox.name`을 사서함/받는 분으로 표시한다.
|
||||
- `mailbox.address`를 주소로 표시한다.
|
||||
- `mailbox.phoneNumber`를 연락처로 표시한다.
|
||||
- `mailbox`는 관리자가 등록한 전역 단일 주소다. 상대방 주소가 아니다.
|
||||
- `mailbox`는 `direction=SENT`이고 `status`가 `RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED`일 때 내려온다.
|
||||
- `DELIVERED`, `UNDELIVERABLE`, `CANCELED` 또는 `direction=RECEIVED`에서는 `mailbox=null`이다.
|
||||
- `mailbox=null`이면 해당 섹션을 표시하지 않는다.
|
||||
|
||||
보내는 사람 표시:
|
||||
- `senderInfo.name`
|
||||
- `senderInfo.phoneNumber`
|
||||
- `senderInfo.address`
|
||||
- 이 정보는 보낸 팬 본인이 신청 시 입력한 정보다.
|
||||
|
||||
전달 불가 표시:
|
||||
- `status=UNDELIVERABLE`이면 Figma `2481:19456` 패턴을 따른다.
|
||||
- 사유 섹션의 제목은 `delivery.undeliverableReason`을 사용한다.
|
||||
- `delivery.undeliverableReason`이 null이면 사유 섹션 제목은 `전달 불가`로 표시한다.
|
||||
- 안내 문구는 `해당 선물은 정책에 따라 폐기되며 반송되지 않습니다.`를 사용한다.
|
||||
|
||||
상세 페이지 action:
|
||||
- `trackingRequired=true`이면 하단 주요 CTA `운송장 작성하기`를 표시하고 운송장 등록 페이지로 이동한다.
|
||||
- `direction=SENT && status=RECEIVED`이면 보조 action `신청 취소`를 표시할 수 있다.
|
||||
- 신청 취소 버튼을 누르면 즉시 API를 호출하지 말고 `v2 dialog` 확인 팝업을 먼저 표시한다.
|
||||
- `status=DELIVERED && direction=SENT`이면 `리뷰 남기기` 버튼을 표시하고 기존 리뷰 작성 화면/플로우로 이동한다.
|
||||
- `문의하기`, `선물 정책 확인` 버튼은 기존 앱에 해당 이동 경로가 있으면 연결하고, 없으면 기존 정책에 맞춰 숨기거나 비활성 처리한다.
|
||||
|
||||
신청 취소 확인 다이얼로그:
|
||||
- Figma `2481:19500`을 참고한다.
|
||||
- 앱의 기본 `v2 dialog` 컴포넌트를 사용한다. 새 dialog 컴포넌트를 만들지 않는다.
|
||||
- 제목: `신청 취소`
|
||||
- 본문:
|
||||
- `{크리에이터}에게 보내는 선물을 취소할까요?`
|
||||
- `선물 보내기 규정에 따라 선물 취소 및 사용한 캔이 모두 환불됩니다.`
|
||||
- 왼쪽 action: `나가기`
|
||||
- dialog만 닫고 상세/운송장 등록 화면 상태를 유지한다.
|
||||
- 오른쪽 destructive action: `취소하기`
|
||||
- `POST /api/v2/gifts/{applicationNo}/cancel`을 호출한다.
|
||||
- 호출 중에는 중복 탭을 막고 loading 상태를 표시한다.
|
||||
- 실패 시 dialog를 닫지 말고 기존 앱 에러 toast/dialog 패턴으로 안내한다.
|
||||
- 성공 시 취소 완료 페이지로 replace navigation 한다.
|
||||
|
||||
취소 완료 페이지:
|
||||
- Figma `2481:19521`을 참고한다.
|
||||
- 취소 API 성공 직후뿐 아니라 상세 페이지 접근 시 `GET /api/v2/gifts/{applicationNo}` 응답이 `status=CANCELED`인 경우에도 이 화면을 보여준다.
|
||||
- 상단 타이틀: `취소 완료`
|
||||
- hero chip: `취소 완료`
|
||||
- hero title: `선물 신청이 취소되었어요`
|
||||
- hero body: `{크리에이터}에게 보내는 선물 신청({applicationNo})이 취소되었습니다. 취소한 신청은 되돌릴 수 없으며, 다시 보내려면 새로 신청해 주세요.`
|
||||
- 섹션 제목: `취소 상세`
|
||||
- 취소 상세 카드 표시 필드:
|
||||
- 크리에이터: `giftInfo.recipientCreatorNickname`
|
||||
- 선물 사이즈: `giftInfo.sizeName`
|
||||
- 카테고리: `giftInfo.categoryName`
|
||||
- 신청번호: `applicationNo`
|
||||
- 신청일: `statusTimeline`의 `RECEIVED.occurredAt`
|
||||
- 취소일: 취소 API response의 `canceledAt` 또는 상세 API 조회 시 `delivery.canceledAt`
|
||||
- 환불 캔: 취소 API response의 `priceCan` 또는 상세 API의 `giftInfo.paidCan`
|
||||
- 하단 또는 카드 아래에 `문의하기` 버튼을 표시한다.
|
||||
- 뒤로가기 동작은 취소 전 상세/운송장 등록 화면으로 돌아가지 않게 한다. 기존 앱 navigation 정책에 맞춰 선물함 리스트 또는 이전 안전 화면으로 이동한다.
|
||||
|
||||
운송장 등록 페이지 진입:
|
||||
- 상세 페이지에서 `trackingRequired=true`일 때만 진입시킨다.
|
||||
- 진입 시 `GET /api/v2/gifts/{applicationNo}`를 다시 조회한다.
|
||||
- 조회 결과에서 `trackingRequired=false`이면 이미 등록 불가 상태이므로 기존 오류 처리 후 상세 페이지로 돌아간다.
|
||||
|
||||
운송장 등록 페이지 레이아웃:
|
||||
1. 상단 고정 내비게이션
|
||||
- 타이틀: `운송장 등록`
|
||||
2. 신청 건 요약 카드
|
||||
- `applicationNo`
|
||||
- 크리에이터 닉네임: `giftInfo.recipientCreatorNickname`
|
||||
- `giftInfo.sizeName · giftInfo.categoryName · 신청일`
|
||||
- 신청일은 `statusTimeline`의 `RECEIVED.occurredAt`을 `YYYY.MM.DD`로 표시한다.
|
||||
3. 보내실 주소
|
||||
- `mailbox`를 사용한다.
|
||||
- `mailbox=null`이면 운송장 등록을 막고 `받을 주소가 등록되어 있지 않습니다.` 오류를 표시한다.
|
||||
4. 운송장 정보
|
||||
- 택배사 select
|
||||
- 운송장 번호 input
|
||||
5. 등록 안내
|
||||
- Figma의 안내 문구를 유지한다.
|
||||
6. 하단 action bar
|
||||
- 왼쪽: `신청 취소`
|
||||
- 오른쪽: `등록 완료`
|
||||
|
||||
택배사 로컬 상수:
|
||||
- 서버에서 택배사 옵션을 조회하지 않는다.
|
||||
- 앱에 아래 로컬 옵션을 둔다. 이미 앱 공통 택배사 상수가 있으면 그것을 우선 사용한다.
|
||||
```ts
|
||||
const giftCourierCompanyNames = [
|
||||
"CJ대한통운",
|
||||
"우체국택배",
|
||||
"GS25 편의점택배",
|
||||
"CU 편의점택배",
|
||||
"한진택배",
|
||||
"롯데택배",
|
||||
"로젠택배",
|
||||
"경동택배",
|
||||
"대신택배",
|
||||
"일양로지스",
|
||||
"천일택배",
|
||||
"합동택배",
|
||||
"건영택배",
|
||||
"농협택배"
|
||||
];
|
||||
```
|
||||
- 선택 UI는 기존 앱의 bottom sheet, picker, dialog 중 이미 쓰는 방식을 사용한다.
|
||||
- API에는 선택된 한글 표시명을 그대로 `courierCompanyName`으로 보낸다.
|
||||
|
||||
운송장 등록 CTA 활성 조건:
|
||||
- 택배사를 선택했다.
|
||||
- 운송장 번호를 입력했다.
|
||||
- 운송장 번호는 공백만 입력할 수 없다.
|
||||
- submit 중에는 중복 탭을 막고 loading 상태를 표시한다.
|
||||
- 비활성 상태는 Figma `2481:19066`, 활성 상태는 Figma `2481:19109`를 따른다.
|
||||
|
||||
운송장 등록 성공 처리:
|
||||
- `POST /api/v2/gifts/{applicationNo}/tracking` 성공 후 성공 toast를 표시한다.
|
||||
- 상세 페이지로 돌아가거나 replace navigation으로 상세 페이지를 다시 연다.
|
||||
- 상세 페이지는 `GET /api/v2/gifts/{applicationNo}`를 다시 호출해 `TRACKING_REGISTERED` 상태를 표시한다.
|
||||
|
||||
API 명세:
|
||||
|
||||
선물 상세 조회:
|
||||
- Method: `GET`
|
||||
- URL: `/api/v2/gifts/{applicationNo}`
|
||||
- 인증: 로그인 필요
|
||||
- Response envelope 예시:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"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": [
|
||||
{
|
||||
"status": "RECEIVED",
|
||||
"statusName": "접수 완료",
|
||||
"occurredAt": "2026-09-18T03:00:00Z"
|
||||
},
|
||||
{
|
||||
"status": "TRACKING_REGISTERED",
|
||||
"statusName": "발송 확인",
|
||||
"occurredAt": null
|
||||
},
|
||||
{
|
||||
"status": "ARRIVED_AT_MAILBOX",
|
||||
"statusName": "사서함 도착",
|
||||
"occurredAt": null
|
||||
},
|
||||
{
|
||||
"status": "INSPECTION_COMPLETED",
|
||||
"statusName": "검수완료",
|
||||
"occurredAt": null
|
||||
},
|
||||
{
|
||||
"status": "DELIVERED",
|
||||
"statusName": "전달완료",
|
||||
"occurredAt": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"message": ""
|
||||
}
|
||||
```
|
||||
|
||||
Response `data` 필드:
|
||||
|
||||
| 필드 | 타입 | 설명 |
|
||||
|---|---|---|
|
||||
| `applicationNo` | string | 선물 신청번호 |
|
||||
| `direction` | string | `SENT` 또는 `RECEIVED` |
|
||||
| `status` | string | 선물 상태 코드 |
|
||||
| `statusName` | string | 서버 상태 표시명 |
|
||||
| `giftInfo` | object | 선물 기본 정보 |
|
||||
| `giftInfo.recipientCreatorNickname` | string 또는 null | 보낸 선물 관점에서 받는 크리에이터 닉네임 |
|
||||
| `giftInfo.senderNickname` | string 또는 null | 받은 선물 관점에서 보낸 팬 닉네임 |
|
||||
| `giftInfo.sizeName` | string | 선물 사이즈명 |
|
||||
| `giftInfo.categoryName` | string | 카테고리명 |
|
||||
| `giftInfo.applicationNo` | string 또는 null | 보낸 선물 관점 신청번호 |
|
||||
| `giftInfo.paidCan` | number 또는 null | 보낸 선물 관점 사용 캔 |
|
||||
| `giftInfo.tracking` | string 또는 null | 운송장 표시 문자열. 예: `한진택배_123456789012` |
|
||||
| `giftInfo.shippingRequestedAt` | string 또는 null | 받은 선물 관점 배송신청일. UTC ISO 문자열 |
|
||||
| `senderInfo` | object 또는 null | 보낸 팬 본인이 신청 시 입력한 보내는 사람 정보 |
|
||||
| `senderInfo.name` | string | 이름 |
|
||||
| `senderInfo.phoneNumber` | string | 휴대폰 번호 |
|
||||
| `senderInfo.address` | string | `(우편번호) 주소, 상세주소` 형식 주소 |
|
||||
| `recipientAddress` | object 또는 null | 받는 크리에이터 본인이 입력한 배송지 |
|
||||
| `mailbox` | object 또는 null | 관리자가 등록한 전역 선물 받을 주소. 보낸 선물의 `DELIVERED` 전 정상 진행 상태에서만 값이 있다 |
|
||||
| `mailbox.name` | string | 사서함/받는 분 이름 |
|
||||
| `mailbox.address` | string | `(우편번호) 주소, 상세주소` 형식 주소 |
|
||||
| `mailbox.phoneNumber` | string | 연락처 |
|
||||
| `trackingRequired` | boolean | 보낸 팬이 운송장 등록을 해야 하는 상태인지 여부 |
|
||||
| `recipientAddressRequired` | boolean | 받는 크리에이터가 배송지를 입력해야 하는 상태인지 여부 |
|
||||
| `recipientAddressDeadlineAt` | string 또는 null | 배송지 입력 마감일. UTC ISO 문자열 |
|
||||
| `delivery.canceledAt` | string 또는 null | 취소일. UTC ISO 문자열 |
|
||||
| `delivery.undeliverableAt` | string 또는 null | 전달 불가 처리일. UTC ISO 문자열 |
|
||||
| `delivery.undeliverableReason` | string 또는 null | 전달 불가 사유 |
|
||||
| `statusTimeline` | array | 정상 진행 5단계 타임라인 |
|
||||
| `statusTimeline[].status` | string | 단계 상태 코드 |
|
||||
| `statusTimeline[].statusName` | string | 단계 표시명 |
|
||||
| `statusTimeline[].occurredAt` | string 또는 null | 해당 단계 도달 시각. UTC ISO 문자열 |
|
||||
|
||||
운송장 등록:
|
||||
- Method: `POST`
|
||||
- URL: `/api/v2/gifts/{applicationNo}/tracking`
|
||||
- 인증: 로그인 필요
|
||||
- Request:
|
||||
```json
|
||||
{
|
||||
"courierCompanyName": "한진택배",
|
||||
"trackingNumber": "123456789012"
|
||||
}
|
||||
```
|
||||
- Response envelope 예시:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"applicationNo": "A-1002609300001",
|
||||
"status": "TRACKING_REGISTERED",
|
||||
"statusName": "발송 확인",
|
||||
"courierCompanyName": "한진택배",
|
||||
"trackingNumber": "123456789012",
|
||||
"trackingRegisteredAt": "2026-09-19T03:00:00Z",
|
||||
"recipientAddressDeadlineAt": "2026-09-26T03:00:00Z"
|
||||
},
|
||||
"message": ""
|
||||
}
|
||||
```
|
||||
|
||||
운송장 등록 response `data` 필드:
|
||||
|
||||
| 필드 | 타입 | 설명 |
|
||||
|---|---|---|
|
||||
| `applicationNo` | string | 선물 신청번호 |
|
||||
| `status` | string | 등록 후 상태. `TRACKING_REGISTERED` |
|
||||
| `statusName` | string | 서버 상태 표시명 |
|
||||
| `courierCompanyName` | string | 선택한 택배사 한글명 |
|
||||
| `trackingNumber` | string | 운송장 번호 |
|
||||
| `trackingRegisteredAt` | string | 운송장 등록일. UTC ISO 문자열 |
|
||||
| `recipientAddressDeadlineAt` | string | 크리에이터 배송지 입력 마감일. UTC ISO 문자열 |
|
||||
|
||||
선물 보내기 취소:
|
||||
- Method: `POST`
|
||||
- URL: `/api/v2/gifts/{applicationNo}/cancel`
|
||||
- Request: 없음
|
||||
- 용도: `direction=SENT && status=RECEIVED`에서만 `신청 취소` action에 사용한다.
|
||||
- Response envelope 예시:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"applicationNo": "A-1002609300001",
|
||||
"status": "CANCELED",
|
||||
"statusName": "신청 취소",
|
||||
"priceCan": 100,
|
||||
"canceledAt": "2026-09-18T04:00:00Z"
|
||||
},
|
||||
"message": ""
|
||||
}
|
||||
```
|
||||
|
||||
선물 보내기 취소 response `data` 필드:
|
||||
|
||||
| 필드 | 타입 | 설명 |
|
||||
|---|---|---|
|
||||
| `applicationNo` | string | 선물 신청번호 |
|
||||
| `status` | string | 취소 후 상태. `CANCELED` |
|
||||
| `statusName` | string | 서버 상태 표시명 |
|
||||
| `priceCan` | number | 환불되는 캔 수량 |
|
||||
| `canceledAt` | string | 취소일. UTC ISO 문자열 |
|
||||
|
||||
테스트/검증:
|
||||
- 리스트 item 탭 시 `applicationNo`로 상세 페이지가 열리는지 확인한다.
|
||||
- 상세 진입 시 `GET /api/v2/gifts/{applicationNo}`가 호출되는지 확인한다.
|
||||
- 상세 조회 결과 `status=CANCELED`이면 일반 상세가 아니라 취소 완료 페이지가 표시되는지 확인한다.
|
||||
- `statusTimeline` 5개 단계가 모두 표시되는지 확인한다.
|
||||
- Figma처럼 4단계로 하드코딩하지 않았는지 확인한다.
|
||||
- `trackingRequired=true`이면 `운송장 작성하기`가 표시되는지 확인한다.
|
||||
- 신청 취소 버튼 탭 시 `v2 dialog` 확인 팝업이 뜨고 즉시 API를 호출하지 않는지 확인한다.
|
||||
- 취소 확인 dialog에서 `나가기`를 누르면 dialog만 닫히는지 확인한다.
|
||||
- 취소 확인 dialog에서 `취소하기`를 누르면 `POST /api/v2/gifts/{applicationNo}/cancel`을 호출하는지 확인한다.
|
||||
- 취소 성공 후 취소 완료 페이지가 표시되고 취소 전 상세/등록 화면으로 돌아가지 않는지 확인한다.
|
||||
- `mailbox`가 있을 때만 사서함 주소 섹션이 표시되는지 확인한다.
|
||||
- `direction=SENT`의 `RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED` 상태에서 `mailbox`가 표시되는지 확인한다.
|
||||
- `DELIVERED`, `UNDELIVERABLE`, `CANCELED`, `direction=RECEIVED`에서는 `mailbox`가 표시되지 않는지 확인한다.
|
||||
- `UNDELIVERABLE`이면 전달 불가 hero와 사유 섹션이 표시되는지 확인한다.
|
||||
- 운송장 등록 페이지에서 택배사/운송장 번호 누락 시 API를 호출하지 않는지 확인한다.
|
||||
- 운송장 등록 시 선택한 택배사 한글명과 운송장 번호가 request body로 전송되는지 확인한다.
|
||||
- 운송장 등록 성공 후 상세 페이지가 재조회되어 `TRACKING_REGISTERED` 상태로 보이는지 확인한다.
|
||||
- 모든 날짜는 UTC ISO 문자열을 앱 표시 형식으로 변환해 표시하는지 확인한다.
|
||||
```
|
||||
@@ -0,0 +1,114 @@
|
||||
# 선물 받을 주소 관리자 페이지 구현 프롬프트
|
||||
|
||||
이 문서는 관리자 페이지에서 전역 선물 받을 주소를 등록/수정하는 화면을 구현할 때 사용하는 프롬프트다.
|
||||
|
||||
## 전제
|
||||
|
||||
- 여기서 말하는 `선물 받을 주소`는 선물 상대방 주소가 아니다.
|
||||
- 관리자가 등록하는 전역 단일 주소다.
|
||||
- 팬이 보낸 선물 상세를 정상 진행 상태 중 전달 완료 전(`RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED`)에 조회할 때, 이 주소가 `mailbox`로 노출된다.
|
||||
- 새 디자인 시스템을 만들지 말고 기존 관리자 페이지의 폼, 버튼, 토스트, 에러 표시 패턴을 재사용한다.
|
||||
|
||||
## 구현 프롬프트
|
||||
|
||||
```text
|
||||
관리자 선물 받을 주소 설정 페이지를 구현해줘.
|
||||
|
||||
목표:
|
||||
- 관리자 메뉴 `선물함 관리 > 받을 주소`에서 팬이 선물을 보낼 전역 단일 주소를 조회하고 저장한다.
|
||||
- 이 주소는 상대방 주소가 아니라 관리자가 등록하는 운영 주소다.
|
||||
- 팬의 선물 상세 API에서는 `direction=SENT`이고 `status`가 `RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED`일 때 이 주소가 `mailbox`로 노출된다.
|
||||
- 기존 관리자 페이지의 폼, 저장 버튼, 토스트, 에러 표시 패턴을 그대로 따른다.
|
||||
|
||||
라우트:
|
||||
- `/gift/mailbox`
|
||||
|
||||
메뉴:
|
||||
- parent: `선물함 관리`
|
||||
- label: `받을 주소`
|
||||
- path: `/gift/mailbox`
|
||||
|
||||
필수 화면 구성:
|
||||
- 상단 제목: `받을 주소`
|
||||
- 설명 문구: `팬이 선물을 발송할 때 확인하는 받을 주소입니다.`
|
||||
- 입력 폼:
|
||||
- 받을 사람 이름 `name`
|
||||
- 연락처 `phoneNumber`
|
||||
- 우편번호 `zipCode`
|
||||
- 주소 `address`
|
||||
- 상세주소 `addressDetail`
|
||||
- 저장 버튼: `저장`
|
||||
|
||||
초기 조회 API:
|
||||
- Method: `GET`
|
||||
- URL: `/api/v2/admin/gift-mailbox`
|
||||
- 모든 API 응답은 기존 공통 envelope를 사용한다.
|
||||
- 화면에서는 `response.data`를 실제 payload로 사용한다.
|
||||
- response `data`:
|
||||
```ts
|
||||
type AdminGiftMailboxResponse = {
|
||||
name: string;
|
||||
address: string;
|
||||
phoneNumber: string;
|
||||
} | null;
|
||||
```
|
||||
- `data`가 null이면 아직 등록된 받을 주소가 없는 상태다.
|
||||
- null이면 빈 폼을 표시한다.
|
||||
|
||||
저장 API:
|
||||
- Method: `PUT`
|
||||
- URL: `/api/v2/admin/gift-mailbox`
|
||||
- Request:
|
||||
```ts
|
||||
type AdminGiftMailboxRequest = {
|
||||
name: string;
|
||||
phoneNumber: string;
|
||||
zipCode: string;
|
||||
address: string;
|
||||
addressDetail: string | null;
|
||||
};
|
||||
```
|
||||
- Response `data`:
|
||||
```ts
|
||||
type AdminGiftMailboxResponse = {
|
||||
name: string;
|
||||
address: string;
|
||||
phoneNumber: string;
|
||||
};
|
||||
```
|
||||
|
||||
동작 규칙:
|
||||
- 받을 주소는 전역 단일 설정이다.
|
||||
- 등록과 수정은 같은 `PUT /api/v2/admin/gift-mailbox` API를 사용한다.
|
||||
- 최초 저장이면 생성처럼 동작한다.
|
||||
- 이미 저장된 값이 있으면 기존 주소를 수정한다.
|
||||
- 별도 ID, 목록, 삭제 기능은 만들지 않는다.
|
||||
|
||||
폼 validation:
|
||||
- `name`, `phoneNumber`, `zipCode`, `address`는 필수다.
|
||||
- 필수값은 공백만 입력할 수 없다.
|
||||
- `addressDetail`은 선택이다.
|
||||
- validation 실패 시 API를 호출하지 않고 기존 관리자 페이지의 필드 에러 표시 방식을 따른다.
|
||||
|
||||
저장 성공 UX:
|
||||
- 성공 토스트를 표시한다.
|
||||
- 저장 API response 기준으로 화면 값을 갱신한다.
|
||||
- response의 `address`는 서버가 조립한 `(우편번호) 주소, 상세주소` 형식이다.
|
||||
- 입력 필드는 사용자가 입력한 `zipCode`, `address`, `addressDetail` 값을 유지해도 된다.
|
||||
|
||||
빈 상태 UX:
|
||||
- 조회 결과 `data=null`이면 빈 폼을 보여준다.
|
||||
- 필요하면 안내 문구 `등록된 받을 주소가 없습니다. 주소를 입력하고 저장해주세요.`를 표시한다.
|
||||
|
||||
에러 처리:
|
||||
- API 실패 시 기존 관리자 페이지의 공통 에러 토스트/알림 패턴을 따른다.
|
||||
- 인증/권한 처리는 기존 관리자 API client와 토큰 처리 방식을 따른다.
|
||||
|
||||
테스트/검증:
|
||||
- `/gift/mailbox` route가 선물함 관리 메뉴에서 접근되는지 확인한다.
|
||||
- 초기 조회 시 `GET /api/v2/admin/gift-mailbox`를 호출하는지 확인한다.
|
||||
- 조회 결과가 null이면 빈 폼을 표시하는지 확인한다.
|
||||
- 저장 시 `PUT /api/v2/admin/gift-mailbox`와 request body가 정확한지 확인한다.
|
||||
- 필수값 공백 validation 시 API를 호출하지 않는지 확인한다.
|
||||
- 저장 성공 후 성공 토스트와 화면 갱신이 일어나는지 확인한다.
|
||||
```
|
||||
Reference in New Issue
Block a user