docs(gift): 모바일 선물함 구현 프롬프트를 기록한다
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
# 선물함 리스트 페이지 생성 프롬프트
|
||||
|
||||
이 문서는 Android/iOS 앱의 `선물함` 리스트 페이지를 생성할 때 사용하는 프롬프트다.
|
||||
|
||||
참고 Figma:
|
||||
|
||||
- 팬 모드, 신청한 내역이 있을 때: `2481:19382`
|
||||
- 팬 모드, 신청한 내역이 없을 때: `2520:35587`
|
||||
- 크리에이터 모드, 받은 선물 내역이 없을 때: `2531:36283`
|
||||
- 크리에이터 모드, 받은 선물 내역이 있을 때: `2531:36194`
|
||||
- 크리에이터 모드, 받은 선물 상태별 카드 표시: `2531:36237`
|
||||
|
||||
전제:
|
||||
|
||||
- Android/iOS 각각의 기존 디자인 시스템, 컴포넌트, API client, 상태 관리, toast/dialog, navigation 패턴을 재사용한다.
|
||||
- Figma의 visual structure를 기준으로 구현하되, 아래 사용자 요구사항을 Figma보다 우선한다.
|
||||
- 상단 뒤로가기 내비게이션 영역은 고정한다.
|
||||
- 상단 뒤로가기 내비게이션 영역을 제외한 전체 콘텐츠 영역은 스크롤되어야 한다.
|
||||
- `신청 내역`, `선물 내역` 섹션 타이틀은 표시하지 않는다.
|
||||
- 리스트는 신청일/취소일 최신순으로 보여야 한다.
|
||||
- request/response 설명은 모바일 공통 JSON 기준이다.
|
||||
|
||||
---
|
||||
|
||||
## 모바일 앱 구현 프롬프트
|
||||
|
||||
```text
|
||||
Android/iOS 앱의 선물함 리스트 페이지를 Figma 기준으로 생성하고 API 연동을 구현해줘.
|
||||
|
||||
Figma는 두 관점의 상태를 보여준다.
|
||||
- 보낸 선물 관점: 사용자가 본인이 신청한 선물 내역을 확인하는 상태다. Figma `2481:19382`, `2520:35587`를 기준으로 한다.
|
||||
- 받은 선물 관점: 사용자가 본인이 받을 선물 내역을 확인하는 상태다. Figma `2531:36283`, `2531:36194`, `2531:36237`를 기준으로 한다.
|
||||
- 실제 선물함 API는 두 관점을 나누어 호출하지 않고 `type=ALL`로 한 번 호출한다.
|
||||
- 응답 item의 `direction` 값이 `SENT`이면 보낸 선물 카드로, `RECEIVED`이면 받은 선물 카드로 렌더링한다.
|
||||
- 각 카드에는 상대방 프로필 이미지와 닉네임을 표시한다.
|
||||
- `direction=SENT`: 상대방은 선물을 받는 크리에이터다.
|
||||
- `direction=RECEIVED`: 상대방은 선물을 보낸 팬이다.
|
||||
|
||||
중요 요구사항:
|
||||
- 상단 뒤로가기 내비게이션은 고정한다.
|
||||
- 상단 내비게이션 아래의 전체 콘텐츠만 스크롤되게 한다.
|
||||
- Figma에 보이는 `신청 내역`, `선물 내역` 섹션 타이틀은 실제 구현에서 표시하지 않는다.
|
||||
- 선물함 리스트는 신청일/취소일 최신순으로 정렬한다.
|
||||
- 현재 API 응답만으로 취소일 기준 정렬/표시는 완전하지 않으므로, API가 취소일을 내려주지 않으면 서버 응답 순서를 우선 사용하고 취소 건 날짜 라벨은 `createdAt` 기준으로 표시한다. 취소일 표시가 필수이면 API 보강이 필요하다.
|
||||
|
||||
API 호출:
|
||||
- 선물함 리스트는 항상 `GET /api/v2/gifts?type=ALL&page=0&size=20`로 조회한다.
|
||||
- `SENT`, `RECEIVED`로 따로 호출하지 않는다.
|
||||
- `items[].direction`으로 보낸 선물/받은 선물 UI를 결정한다.
|
||||
- 페이지 진입 시 첫 페이지를 조회한다.
|
||||
- `hasNext=true`이면 스크롤 하단에서 다음 page를 추가 조회한다.
|
||||
- pull-to-refresh가 기존 앱에 있으면 page를 0으로 초기화하고 다시 조회한다.
|
||||
|
||||
공통 레이아웃:
|
||||
1. 상단 고정 내비게이션
|
||||
- 타이틀: `선물함`
|
||||
- 뒤로가기 버튼은 기존 앱 패턴을 사용한다.
|
||||
- 이 영역은 스크롤되지 않는다.
|
||||
|
||||
2. 스크롤 콘텐츠
|
||||
- nav 아래부터 시작한다.
|
||||
- 안내 배너, 빈 상태, 리스트, 하단 여백 또는 하단 문의 버튼까지 스크롤 영역에 포함한다.
|
||||
- iOS safe area와 Android navigation bar 여백은 기존 앱 패턴을 따른다.
|
||||
|
||||
보낸 선물 UI:
|
||||
- `direction=SENT`인 item에 적용한다.
|
||||
- 사용자가 신청한 선물 내역을 보여준다.
|
||||
- 보낸 선물만 있는 상태는 Figma `2481:19382`를 따른다.
|
||||
- 상단 안내 배너를 표시한다.
|
||||
- 제목: `사서함을 통해 안전하게 전달됩니다`
|
||||
- 본문: `크리에이터 및 팬의 주소는 공개되지 않습니다. 선물은 소다라이브 사서함에 도착한 뒤 크리에이터에게 전달됩니다.`
|
||||
- `신청 내역` 섹션 타이틀은 표시하지 않는다.
|
||||
- 카드 구성:
|
||||
- 상단 왼쪽: `statusName`
|
||||
- 상단 오른쪽: 날짜 라벨
|
||||
- 기본: `{createdAt} 신청`
|
||||
- 상태가 `CANCELED`이고 취소일 필드가 있으면 `{canceledAt} 취소`
|
||||
- 현재 리스트 API에는 `canceledAt`이 없으므로 기존 API만 사용할 때는 `{createdAt} 취소` 또는 `{createdAt} 신청` 중 제품 정책에 맞춰 하나로 통일한다.
|
||||
- 프로필 row 내부: `counterpartProfileImageUrl`, `counterpartNickname`을 사용해 받는 크리에이터 프로필을 표시한다.
|
||||
- 프로필 row 아래 보조 문구: `sizeName · categoryName`
|
||||
- 프로필 이미지가 없으면 기존 앱의 기본 프로필 이미지를 표시한다.
|
||||
- 우측 chevron
|
||||
- 탭 시 선물 상세 페이지로 이동하며 `applicationNo`를 전달한다.
|
||||
- 리스트가 비어 있고 보낸 선물 관점으로 진입한 화면이면 Figma `2520:35587`를 따른다.
|
||||
- 상단 안내 배너만 표시한다.
|
||||
- 별도 empty title/body를 추가하지 않는다.
|
||||
|
||||
받은 선물 UI:
|
||||
- `direction=RECEIVED`인 item에 적용한다.
|
||||
- 사용자가 받을 선물 내역을 보여준다.
|
||||
- 리스트가 비어 있고 받은 선물 관점으로 진입한 화면이면 Figma `2531:36283`를 따른다.
|
||||
- 가운데 empty title: `전달 예정인 선물이 없어요`
|
||||
- empty body: `베타 기간에는 팬이 선물을 보내는 기능만 제공돼요. 더 다양해진 선물 기능으로 곧 다시 만나요!`
|
||||
- 버튼: `의견 남기기`
|
||||
- 의견 남기기 동작은 기존 앱의 문의/피드백 이동 패턴을 사용한다.
|
||||
- 받은 선물이 있는 상태는 Figma `2531:36194`, `2531:36237`를 따른다.
|
||||
- 상단 안내 배너를 표시한다.
|
||||
- 제목: `배송지 입력 기간 안내`
|
||||
- 본문: `알림을 받은 날부터 7일 이내에 배송지를 입력해 주세요. 기한 내 입력하지 않으면 선물이 반송됩니다.`
|
||||
- `선물 내역` 섹션 타이틀은 표시하지 않는다.
|
||||
- 배송지 입력이 필요한 선물이 있으면 파란 안내 배너를 리스트 상단에 표시한다.
|
||||
- 제목: `팬이 보낸 선물이 있어요!`
|
||||
- 본문: `선물이 늦지 않게 전달될 수 있도록 배송지를 입력해 주세요.`
|
||||
- 보조 문구: `*n일 이내로 입력하지 않으면 선물이 사라져요.`
|
||||
- 현재 리스트 API에는 배송지 입력 마감일이 없으므로 정확한 n일 계산이 필요하면 API 보강이 필요하다.
|
||||
- 배너 탭 시 배송지 입력이 필요한 첫 번째 선물 상세로 이동한다.
|
||||
- 카드 구성:
|
||||
- 상단 왼쪽 프로필 row 내부: `counterpartProfileImageUrl`, `counterpartNickname`을 사용해 보낸 팬 프로필을 표시한다.
|
||||
- 상단 오른쪽: `{createdAt} 신청`
|
||||
- 본문 메인: 상태 표시 문구
|
||||
- 본문 서브: 상태 보조 문구가 필요한 경우만 표시
|
||||
- 우측 chevron
|
||||
- 탭 시 선물 상세 페이지로 이동하며 `applicationNo`를 전달한다.
|
||||
- 리스트 하단에는 Figma처럼 `문의하기` 하단 버튼을 둘 수 있다. 기존 앱 정책상 고정 버튼이면 하단 safe area를 포함하고, 스크롤 콘텐츠 내부 버튼이면 콘텐츠 하단에 둔다.
|
||||
|
||||
상태 표시 매핑:
|
||||
|
||||
보낸 선물 카드:
|
||||
| API status | 표시 문구 |
|
||||
|---|---|
|
||||
| `RECEIVED` | `운송장 등록 필요` |
|
||||
| `TRACKING_REGISTERED` | `발송 확인` |
|
||||
| `ARRIVED_AT_MAILBOX` | `사서함 도착` |
|
||||
| `INSPECTION_COMPLETED` | `선물 검수 완료` |
|
||||
| `DELIVERED` | `전달 완료` |
|
||||
| `UNDELIVERABLE` | `전달 불가` |
|
||||
| `CANCELED` | `신청 취소` |
|
||||
|
||||
받은 선물 카드:
|
||||
| API status | 메인 문구 | 서브 문구 |
|
||||
|---|---|---|
|
||||
| `TRACKING_REGISTERED` | `배송지 입력 필요` | `전달 예정` |
|
||||
| `ARRIVED_AT_MAILBOX` | `선물 검수` | `선물 확인 중` |
|
||||
| `INSPECTION_COMPLETED` | `선물 검수 완료` | `배송 중` |
|
||||
| `DELIVERED` | `전달 완료` | 없음 |
|
||||
| `UNDELIVERABLE` | `전달 불가` | 없음 |
|
||||
|
||||
API 명세:
|
||||
|
||||
선물함 리스트 조회 API:
|
||||
- Method: `GET`
|
||||
- URL: `/api/v2/gifts`
|
||||
- 인증: 로그인 필요
|
||||
- Query:
|
||||
- `type`: `ALL`, `SENT`, `RECEIVED`
|
||||
- `page`: 0부터 시작
|
||||
- `size`: 페이지 크기. 기본 20
|
||||
- 이 페이지에서는 `type=ALL`만 사용한다.
|
||||
- Response envelope 예시:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"totalCount": 1,
|
||||
"items": [],
|
||||
"page": 0,
|
||||
"size": 20,
|
||||
"hasNext": false
|
||||
},
|
||||
"message": ""
|
||||
}
|
||||
```
|
||||
|
||||
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",
|
||||
"createdAt": "2026-09-18T03:00:00Z"
|
||||
}
|
||||
],
|
||||
"page": 0,
|
||||
"size": 20,
|
||||
"hasNext": false
|
||||
}
|
||||
```
|
||||
|
||||
Response `data` 필드:
|
||||
|
||||
| 필드 | 타입 | 설명 |
|
||||
|---|---|---|
|
||||
| `totalCount` | number | 전체 개수 |
|
||||
| `items` | array | 선물함 리스트 |
|
||||
| `items[].applicationNo` | string | 선물 신청번호. 상세 이동 ID |
|
||||
| `items[].direction` | string | `SENT` 또는 `RECEIVED` |
|
||||
| `items[].status` | string | 선물 상태 코드 |
|
||||
| `items[].statusName` | string | 서버 상태 표시명 |
|
||||
| `items[].priceCan` | number | 사용된 캔 |
|
||||
| `items[].categoryName` | string | 카테고리명 |
|
||||
| `items[].sizeName` | string | 사이즈명 |
|
||||
| `items[].counterpartMemberId` | number | 상대방 회원 ID. `SENT`에서는 받는 크리에이터, `RECEIVED`에서는 보낸 팬 |
|
||||
| `items[].counterpartNickname` | string | 상대방 닉네임 |
|
||||
| `items[].counterpartProfileImageUrl` | string 또는 null | 상대방 프로필 이미지 URL. 없으면 기본 프로필 이미지 표시 |
|
||||
| `items[].createdAt` | string 또는 null | 신청일. UTC ISO 문자열 |
|
||||
| `page` | number | 현재 페이지 |
|
||||
| `size` | number | 페이지 크기 |
|
||||
| `hasNext` | boolean | 다음 페이지 존재 여부 |
|
||||
|
||||
정렬:
|
||||
- 현재 서버 API는 `createdAt desc, id desc`로 정렬한다.
|
||||
- 요구사항은 신청일/취소일 최신순이다.
|
||||
- 취소일 최신순까지 정확히 맞추려면 리스트 API가 `canceledAt` 또는 `displayedAt` 같은 정렬 기준 시각을 내려주고 그 기준으로 정렬해야 한다.
|
||||
- API 보강 전에는 서버 응답 순서를 그대로 사용하고, 클라이언트에서 임의 재정렬하지 않는다.
|
||||
|
||||
상대방 프로필 표시 규칙:
|
||||
- `counterpartNickname`은 카드의 프로필 row 이름으로 표시한다.
|
||||
- `counterpartProfileImageUrl`이 있으면 해당 이미지를 원형 프로필 이미지로 표시한다.
|
||||
- `counterpartProfileImageUrl`이 `null`이거나 빈 값이면 기존 앱의 기본 프로필 이미지를 표시한다.
|
||||
- `direction=SENT`에서는 받는 크리에이터 정보로 해석한다.
|
||||
- `direction=RECEIVED`에서는 보낸 팬 정보로 해석한다.
|
||||
|
||||
현재 API만으로 부족한 Figma 표시값:
|
||||
- 취소일 `canceledAt`
|
||||
- 배송지 입력 마감일 또는 남은 일수
|
||||
|
||||
추가 API 보강이 가능하면 리스트 item에 아래 필드를 추가해달라고 요청한다.
|
||||
```json
|
||||
{
|
||||
"canceledAt": "2026-09-18T03:00:00Z",
|
||||
"recipientAddressDeadlineAt": "2026-09-25T03:00:00Z",
|
||||
"sortAt": "2026-09-18T03:00:00Z"
|
||||
}
|
||||
```
|
||||
- `canceledAt`: 취소 상태의 취소일 표시용
|
||||
- `recipientAddressDeadlineAt`: 크리에이터 배송지 입력 안내 배너의 남은 일수 계산용
|
||||
- `sortAt`: 신청/취소 최신순 정렬 기준. 취소 건은 취소일, 그 외에는 신청일
|
||||
|
||||
로딩/에러/빈 상태:
|
||||
- 최초 로딩 중에는 기존 앱 리스트 스켈레톤 또는 loading 패턴을 사용한다.
|
||||
- 실패 시 기존 앱 toast/dialog와 재시도 버튼을 사용한다.
|
||||
- 빈 상태는 진입 관점에 따라 다르게 표시한다.
|
||||
- 보낸 선물 관점으로 진입한 화면이면 안내 배너만 남긴다.
|
||||
- 받은 선물 관점으로 진입한 화면이면 중앙 empty UI와 `의견 남기기` 버튼을 표시한다.
|
||||
|
||||
테스트/검증:
|
||||
- 선물함 진입 시 `type=ALL`로 API를 한 번 호출하는지 확인한다.
|
||||
- `direction=SENT` item은 보낸 선물 카드 UI로 표시되는지 확인한다.
|
||||
- `direction=RECEIVED` item은 받은 선물 카드 UI로 표시되는지 확인한다.
|
||||
- 각 item의 `counterpartNickname`, `counterpartProfileImageUrl`이 프로필 row에 표시되는지 확인한다.
|
||||
- `counterpartProfileImageUrl=null`이면 기본 프로필 이미지가 표시되는지 확인한다.
|
||||
- 상단 내비게이션은 고정되고 아래 콘텐츠만 스크롤되는지 확인한다.
|
||||
- `신청 내역`, `선물 내역` 텍스트가 실제 화면에 노출되지 않는지 확인한다.
|
||||
- 리스트 item 탭 시 `applicationNo`로 상세 화면 이동이 되는지 확인한다.
|
||||
- 빈 상태가 진입 관점에 맞는 Figma 기준으로 표시되는지 확인한다.
|
||||
- `hasNext=true`일 때 다음 page를 추가 조회하는지 확인한다.
|
||||
- API 실패 시 입력/스크롤 상태를 깨지 않고 오류와 재시도를 제공하는지 확인한다.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 요약
|
||||
|
||||
| 목적 | Method | URL | Query | Response data |
|
||||
|---|---|---|---|---|
|
||||
| 선물함 리스트 조회 | GET | `/api/v2/gifts` | `type=ALL`, `page`, `size` | 선물 리스트, 페이지 정보 |
|
||||
|
||||
## Figma 반영 메모
|
||||
|
||||
- 보낸 선물 리스트 있음: 안내 배너 + 카드 리스트. 단, 실제 구현에서는 `신청 내역` 타이틀을 제거한다.
|
||||
- 보낸 선물 관점 empty: 안내 배너만 표시한다.
|
||||
- 받은 선물 관점 empty: 중앙 empty UI와 `의견 남기기` 버튼을 표시한다.
|
||||
- 받은 선물 리스트 있음: 안내 배너 + 배송지 입력 CTA 배너 + 카드 리스트. 단, 실제 구현에서는 `선물 내역` 타이틀을 제거한다.
|
||||
- 받은 선물 상태별 카드는 `배송지 입력 완료/전달 예정`, `전달 완료`, `전달 불가`, `선물 검수/선물 확인 중`, `선물 검수 완료/배송 중` 패턴을 사용한다.
|
||||
@@ -0,0 +1,263 @@
|
||||
# 선물 보내기 페이지 생성 프롬프트
|
||||
|
||||
이 문서는 Android/iOS 앱의 `선물 보내기` 페이지를 생성하거나 기존 생성 UI에 API 연동을 붙일 때 사용하는 프롬프트다.
|
||||
|
||||
참고 Figma:
|
||||
|
||||
- 빈 입력 상태: `2481:18904`
|
||||
- 입력 완료 및 CTA 활성화 상태: `2481:18957`
|
||||
|
||||
전제:
|
||||
|
||||
- 크리에이터 닉네임과 `memberId`를 받아 `선물 보내기` 화면으로 이동하는 흐름은 이미 생성되어 있다.
|
||||
- 새 진입 흐름을 만들지 않는다.
|
||||
- Figma에 있는 UI 구조와 문구를 유지한다.
|
||||
- Android/iOS 각각의 기존 디자인 시스템, 컴포넌트, API client, 상태 관리, toast/dialog, navigation 패턴을 재사용한다.
|
||||
- 모든 API 응답은 공통 envelope를 사용하며 실제 payload는 `data`에 있다.
|
||||
- request/response 설명은 모바일 공통 JSON 기준이다.
|
||||
|
||||
---
|
||||
|
||||
## 모바일 앱 구현 프롬프트
|
||||
|
||||
```text
|
||||
Android/iOS 앱의 선물 보내기 페이지를 Figma 기준으로 생성하고 API 연동을 구현해줘.
|
||||
|
||||
중요 전제:
|
||||
- 크리에이터 닉네임과 memberId를 받아 이 화면으로 이동하는 기능은 이미 구현되어 있다.
|
||||
- 이 작업에서는 이동 경로를 새로 만들지 말고, 전달받은 값만 화면과 API request에 연결한다.
|
||||
- 전달받은 크리에이터 닉네임은 `받는 크리에이터` 영역에 표시한다.
|
||||
- 전달받은 memberId는 선물 신청 API의 `recipientMemberId`로 보낸다.
|
||||
|
||||
Figma 참고 상태:
|
||||
- 빈 입력 상태: node `2481:18904`
|
||||
- 입력 완료 및 최하단 CTA 활성화 상태: node `2481:18957`
|
||||
|
||||
화면 구조:
|
||||
|
||||
1. 상단
|
||||
- 타이틀: `선물 보내기`
|
||||
- 뒤로가기 버튼은 기존 앱 패턴을 사용한다.
|
||||
|
||||
2. 안내 배너
|
||||
- 제목: `선물 보내기 베타 서비스 안내`
|
||||
- 본문: `베타 기간 동안은 일부 기능만 제공됩니다. 크리에이터에게 마음을 잘 전할 수 있도록 더 넓어진 선물 보내기로 곧 다시 만나요!`
|
||||
|
||||
3. 받는 크리에이터
|
||||
- 이전 화면에서 전달받은 크리에이터 닉네임을 표시한다.
|
||||
- Figma의 프로필 row 형태를 유지한다.
|
||||
- memberId는 화면 표시용이 아니라 API request용으로만 사용한다.
|
||||
|
||||
4. 선물 사이즈
|
||||
- 화면 진입 시 `GET /api/v2/gifts/form-options`를 호출해 `sizes`를 가져온다.
|
||||
- 조회된 `sizes`를 사이즈 카드 목록에 바인딩한다.
|
||||
- 카드에는 `name`, 사이즈 설명, 캔 가격을 표시한다.
|
||||
- 서버 응답에는 사이즈 설명 필드가 없으므로 설명 문구는 앱에서 코드별로 매핑한다.
|
||||
- `SMALL`: `세 변의 합 100cm 이하 · 5kg 이하`
|
||||
- `MEDIUM`, `LARGE`: 정책 문구가 앱에 이미 있으면 기존 문구를 사용하고, 없으면 이름과 가격만 표시한다.
|
||||
- 가격은 `salePriceCan`을 표시하고 submit 금액도 `salePriceCan`을 사용한다.
|
||||
- `basePriceCan`과 `salePriceCan`이 다르면 기존 앱 할인/정가 표시 패턴이 있을 때만 정가를 함께 보여준다.
|
||||
- 기본 선택은 서버가 내려준 첫 번째 사이즈로 둔다.
|
||||
|
||||
5. 카테고리
|
||||
- 화면 진입 시 같은 `GET /api/v2/gifts/form-options` 응답의 `categories`를 사용한다.
|
||||
- Figma의 select field를 유지한다.
|
||||
- 빈 상태 문구: `카테고리 선택`
|
||||
- 선택 후에는 선택한 카테고리 `name`을 표시한다.
|
||||
- 카테고리 선택 UI는 기존 Android/iOS 앱 패턴에 맞는 bottom sheet, picker, dialog 중 이미 쓰는 방식을 사용한다.
|
||||
- `requiresDamageWaiver=true`인 카테고리를 선택한 경우에만 카테고리 아래에 파손 면책 동의 row를 표시한다.
|
||||
- 파손 면책 동의 문구: `파손 및 분실 면책 사항에 동의합니다. (필수)`
|
||||
- `requiresDamageWaiver=false` 카테고리를 선택하면 파손 면책 동의 row를 숨기고 `damageWaiverAgreed=false`로 초기화한다.
|
||||
|
||||
6. 보내는 사람
|
||||
- 이름 input → `senderName`
|
||||
- 휴대폰 번호 input → `senderPhoneNumber`
|
||||
- 우편번호 input → `senderZipCode`
|
||||
- 주소 input → `senderAddress`
|
||||
- 상세 주소 input → `senderAddressDetail`
|
||||
- `우편번호 검색` 버튼은 기존 주소 검색 기능과 연결한다.
|
||||
- 주소 검색 결과로 우편번호와 기본 주소를 채운다.
|
||||
- 기본 주소는 사용자가 직접 수정하지 못하게 하는 기존 패턴이 있으면 그 패턴을 따른다.
|
||||
|
||||
7. 이용 약관 동의
|
||||
- Figma의 발송 규정 요약 박스와 체크박스 2개를 유지한다.
|
||||
- `소다라이브 크리에이터 상품 전달 이용약관에 동의합니다. (필수)` → `senderTermsAgreed`
|
||||
- `상품 전달을 위한 개인정보 수집·이용에 동의합니다. (필수)` → `senderPrivacyAgreed`
|
||||
|
||||
8. 하단 CTA
|
||||
- 빈 상태 또는 필수값 누락 상태는 Figma 빈 상태처럼 비활성 버튼을 표시한다.
|
||||
- 활성 조건을 모두 만족하면 Figma 활성 상태처럼 soda 색상 CTA로 바꾼다.
|
||||
- 활성 CTA 문구는 선택된 사이즈의 `salePriceCan`을 사용해 `{salePriceCan}캔으로 선물 보내기`로 표시한다.
|
||||
- submit 중에는 중복 탭을 막고 loading 상태를 표시한다.
|
||||
|
||||
화면 진입 시 처리:
|
||||
- 이미 전달받은 `creatorNickname`과 `memberId`를 읽는다.
|
||||
- 둘 중 하나라도 없으면 기존 앱의 오류 처리 또는 뒤로가기 패턴을 따른다.
|
||||
- 즉시 `GET /api/v2/gifts/form-options`를 호출한다.
|
||||
- 로딩 중에는 기존 화면 스켈레톤/로딩 패턴을 사용한다.
|
||||
- 조회 실패 시 toast/dialog와 재시도 동작을 기존 패턴으로 제공한다.
|
||||
- `sizes` 또는 `categories`가 비어 있으면 선물 신청을 막고 CTA를 비활성화한다.
|
||||
|
||||
CTA 활성 조건:
|
||||
- `memberId`가 있다.
|
||||
- 사이즈가 선택되어 있다.
|
||||
- 카테고리가 선택되어 있다.
|
||||
- 이름이 입력되어 있다.
|
||||
- 휴대폰 번호가 입력되어 있다.
|
||||
- 우편번호가 입력되어 있다.
|
||||
- 주소가 입력되어 있다.
|
||||
- 이용약관 동의가 true다.
|
||||
- 개인정보 수집·이용 동의가 true다.
|
||||
- 선택한 카테고리의 `requiresDamageWaiver=true`이면 파손 면책 동의가 true다.
|
||||
|
||||
검증 실패 처리:
|
||||
- CTA는 기본적으로 위 조건을 만족할 때만 활성화한다.
|
||||
- 그래도 submit 시점에 한 번 더 validation 한다.
|
||||
- 누락된 필드는 기존 앱의 input error, toast, dialog 중 현재 화면 패턴에 맞춰 안내한다.
|
||||
- validation 실패 시 `POST /api/v2/gifts`를 호출하지 않는다.
|
||||
|
||||
폼 옵션 조회 API:
|
||||
- Method: `GET`
|
||||
- URL: `/api/v2/gifts/form-options`
|
||||
- Request: 없음
|
||||
- Response envelope 예시:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"sizes": [],
|
||||
"categories": []
|
||||
},
|
||||
"message": ""
|
||||
}
|
||||
```
|
||||
- Response `data` 예시:
|
||||
```json
|
||||
{
|
||||
"sizes": [
|
||||
{
|
||||
"sizeCode": "SMALL",
|
||||
"name": "소형",
|
||||
"basePriceCan": 100,
|
||||
"salePriceCan": 80
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
{
|
||||
"categoryId": 1,
|
||||
"name": "아크릴",
|
||||
"requiresDamageWaiver": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
폼 옵션 필드:
|
||||
|
||||
| 필드 | 타입 | 설명 |
|
||||
|---|---|---|
|
||||
| `sizes` | array | 선택 가능한 선물 사이즈 목록 |
|
||||
| `sizes[].sizeCode` | string | 사이즈 코드. `SMALL`, `MEDIUM`, `LARGE` |
|
||||
| `sizes[].name` | string | 사이즈 표시명 |
|
||||
| `sizes[].basePriceCan` | number | 기본 가격, 단위는 캔 |
|
||||
| `sizes[].salePriceCan` | number | 실제 결제 가격, 단위는 캔 |
|
||||
| `categories` | array | 선택 가능한 활성 카테고리 목록 |
|
||||
| `categories[].categoryId` | number | 카테고리 ID |
|
||||
| `categories[].name` | string | 카테고리 표시명 |
|
||||
| `categories[].requiresDamageWaiver` | boolean | 파손 면책 동의 필요 여부 |
|
||||
|
||||
선물 신청 접수 API:
|
||||
- Method: `POST`
|
||||
- URL: `/api/v2/gifts`
|
||||
- 인증: 로그인 회원 필요. 기존 앱 인증 토큰/세션 처리 방식을 사용한다.
|
||||
- Request body 예시:
|
||||
```json
|
||||
{
|
||||
"recipientMemberId": 100,
|
||||
"senderName": "김소다",
|
||||
"senderPhoneNumber": "01000000000",
|
||||
"senderZipCode": "12345",
|
||||
"senderAddress": "서울시 강남구 ...",
|
||||
"senderAddressDetail": "123동 456호",
|
||||
"sizeCode": "SMALL",
|
||||
"categoryId": 1,
|
||||
"senderTermsAgreed": true,
|
||||
"senderPrivacyAgreed": true,
|
||||
"damageWaiverAgreed": true
|
||||
}
|
||||
```
|
||||
|
||||
Request body 필드:
|
||||
|
||||
| 필드 | 타입 | 필수 | 설명 |
|
||||
|---|---|---|---|
|
||||
| `recipientMemberId` | number | Y | 화면 진입 시 전달받은 memberId |
|
||||
| `senderName` | string | Y | 보내는 사람 이름 |
|
||||
| `senderPhoneNumber` | string | Y | 보내는 사람 휴대폰 번호 |
|
||||
| `senderZipCode` | string | Y | 보내는 사람 우편번호 |
|
||||
| `senderAddress` | string | Y | 보내는 사람 기본 주소 |
|
||||
| `senderAddressDetail` | string 또는 null | N | 보내는 사람 상세 주소 |
|
||||
| `sizeCode` | string | Y | 선택한 사이즈 코드 |
|
||||
| `categoryId` | number | Y | 선택한 카테고리 ID |
|
||||
| `senderTermsAgreed` | boolean | Y | 이용약관 동의 여부. 반드시 `true` |
|
||||
| `senderPrivacyAgreed` | boolean | Y | 개인정보 수집·이용 동의 여부. 반드시 `true` |
|
||||
| `damageWaiverAgreed` | boolean | Y | 파손 면책 동의 여부. 필요한 카테고리에서는 `true`, 필요 없는 카테고리에서는 `false` |
|
||||
|
||||
- Response `data` 예시:
|
||||
```json
|
||||
{
|
||||
"applicationNo": "A-1002609300001",
|
||||
"status": "RECEIVED",
|
||||
"statusName": "접수 완료",
|
||||
"priceCan": 80,
|
||||
"trackingDeadlineAt": "2026-10-02T03:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
Response `data` 필드:
|
||||
|
||||
| 필드 | 타입 | 설명 |
|
||||
|---|---|---|
|
||||
| `applicationNo` | string | 선물 신청번호 |
|
||||
| `status` | string | 신청 상태 코드 |
|
||||
| `statusName` | string | 신청 상태 표시명 |
|
||||
| `priceCan` | number | 실제 차감된 캔 금액 |
|
||||
| `trackingDeadlineAt` | string | 운송장 등록 기한. UTC ISO 문자열 |
|
||||
|
||||
성공 처리:
|
||||
- 신청 성공 시 기존 앱의 완료 dialog 또는 완료 화면 패턴을 사용한다.
|
||||
- 최소 표시값은 `applicationNo`, `statusName`, `priceCan`, `trackingDeadlineAt`이다.
|
||||
- 성공 이후 이동 CTA가 필요하면 기존 선물함 또는 선물 상세 이동 패턴을 사용한다.
|
||||
|
||||
실패 처리:
|
||||
- 서버 오류 메시지가 있으면 기존 앱 에러 노출 방식으로 표시한다.
|
||||
- 잔액 부족, 유효하지 않은 카테고리, 파손 면책 미동의, 약관 미동의 등의 오류는 서버 메시지를 우선 사용한다.
|
||||
- 실패 시 입력값은 유지한다.
|
||||
|
||||
테스트/검증:
|
||||
- 화면 진입 시 `GET /api/v2/gifts/form-options`가 호출되는지 확인한다.
|
||||
- 전달받은 크리에이터 닉네임이 받는 크리에이터 영역에 표시되는지 확인한다.
|
||||
- 전달받은 memberId가 `POST /api/v2/gifts`의 `recipientMemberId`로 들어가는지 확인한다.
|
||||
- 빈 상태에서는 CTA가 비활성화되는지 확인한다.
|
||||
- 필수 입력과 필수 동의를 모두 완료하면 CTA가 활성화되고 `{salePriceCan}캔으로 선물 보내기`가 표시되는지 확인한다.
|
||||
- `requiresDamageWaiver=true` 카테고리 선택 시 파손 면책 동의 row가 표시되고 동의 전에는 CTA가 비활성인지 확인한다.
|
||||
- `requiresDamageWaiver=false` 카테고리 선택 시 파손 면책 동의 row가 숨겨지고 request의 `damageWaiverAgreed`가 false인지 확인한다.
|
||||
- submit 성공 시 요청 body와 성공 처리 값을 확인한다.
|
||||
- submit 실패 시 입력값이 유지되고 기존 앱 방식으로 오류가 표시되는지 확인한다.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 요약
|
||||
|
||||
| 목적 | Method | URL | Request | Response data |
|
||||
|---|---|---|---|---|
|
||||
| 폼 옵션 조회 | GET | `/api/v2/gifts/form-options` | 없음 | 사이즈 목록, 카테고리 목록 |
|
||||
| 선물 신청 접수 | POST | `/api/v2/gifts` | 선물 신청 JSON body | 신청번호, 상태, 차감 금액, 운송장 등록 기한 |
|
||||
|
||||
## Figma 반영 메모
|
||||
|
||||
- 빈 상태 Figma는 카테고리 미선택, 보내는 사람 정보 미입력, 약관 미동의, 하단 CTA 비활성 상태다.
|
||||
- 활성 상태 Figma는 크리에이터 닉네임 표시, 카테고리 선택, 파손 면책 동의 노출, 보내는 사람 정보 입력, 약관 동의, 하단 CTA 활성 상태다.
|
||||
- Figma의 하단 버튼 텍스트는 캔 금액이 포함된 형태이므로 API의 `salePriceCan`으로 동적으로 표시한다.
|
||||
- Figma의 select, checkbox, radio, bottom action bar, safe area는 각 플랫폼의 기존 컴포넌트로 대응한다.
|
||||
Reference in New Issue
Block a user