diff --git a/docs/20260929_크리에이터_선물하기/gift-list-page-prompt.md b/docs/20260929_크리에이터_선물하기/gift-list-page-prompt.md new file mode 100644 index 00000000..f0c70456 --- /dev/null +++ b/docs/20260929_크리에이터_선물하기/gift-list-page-prompt.md @@ -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 배너 + 카드 리스트. 단, 실제 구현에서는 `선물 내역` 타이틀을 제거한다. +- 받은 선물 상태별 카드는 `배송지 입력 완료/전달 예정`, `전달 완료`, `전달 불가`, `선물 검수/선물 확인 중`, `선물 검수 완료/배송 중` 패턴을 사용한다. diff --git a/docs/20260929_크리에이터_선물하기/gift-send-page-prompt.md b/docs/20260929_크리에이터_선물하기/gift-send-page-prompt.md new file mode 100644 index 00000000..65391ab6 --- /dev/null +++ b/docs/20260929_크리에이터_선물하기/gift-send-page-prompt.md @@ -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는 각 플랫폼의 기존 컴포넌트로 대응한다.