# 선물 보내기 페이지 생성 프롬프트 이 문서는 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는 각 플랫폼의 기존 컴포넌트로 대응한다.