# 포인트 결제 모바일 앱 — 설계 기획서 (PRD)

> **문서 목적**: 이 문서는 Design 모드에서 실제 HTML 프로토타입을 생성하기 전 단계의 **단일 진실 원천(source of truth)** 입니다.
> 사람이 검토·편집한 뒤, "이 문서로 화면 만들어줘"라고 하면 여기 정의된 대로 프로토타입을 생성합니다.
> `TODO` / `❓확인` 마커가 있는 항목은 확정 후 채워 주세요.

---

## 1. 개요 / 인텐트

포인트(P)를 QR로 결제하고, 잔액·내역·지갑·전환을 관리하는 **모바일 앱 프로토타입**.

- **플랫폼**: AOS/iOS 앱은 **웹뷰로 감싸는 형태** → **iPhone(iOS) 디바이스 프레임 하나로 통일** (`/frames/iphone-15-pro.html`)
- **디자인 시스템**: Shadcn (미니멀·모노크롬, 블랙 CTA, 흰 배경, 8px radius)
- **충실도(1차)**: **와이어프레임** — 구조·플로우 확정용. 확인 후 괜찮으면 **고충실도**로 재작업 예정
- **언어**: 한국어 (UI 카피 전부 한글)
- **성격**: 실제 백엔드 없는 **목(mock) 데이터 기반 인터랙티브 프로토타입** (탭·다이얼로그·필터·유효성·상태 전환 동작)

**포인트/토큰 모델**: 사용자가 **블록체인 토큰**을 자신의 **입금지갑(전용 입금주소)** 으로 이체하면 → 정해진 **비율에 따라 포인트(단위: `P`)로 전환** → 내가 만든 다른 서비스들에서 사용. 소수점 자릿수·네트워크 UI 표기는 불필요.

한 문장 요약: **"토큰을 입금해 P로 전환하고, QR로 결제하며, 잔액·내역·지갑을 한 앱에서 관리하는 shadcn 스타일 모바일 지갑."**

---

## 2. 사용자 & 핵심 작업 (Users & Jobs)

| 사용자 | 핵심 작업(Job) |
|---|---|
| 일반 회원 | 가입/로그인, P 잔액 확인, QR로 가맹점 결제, 결제·적립 내역 조회 |
| 지갑 보유 회원 | 입금지갑 주소 확인·복사, **포인트 전환**(메타마스크/직접 입금) |
| 지갑 미보유 회원 | 입금지갑 생성 |
| 모든 회원 | 공지·이벤트 확인, 비밀번호 변경, 마이페이지 관리 |

핵심 성공 지표(프로토타입 관점): **로그인 → 포인트 확인 → QR 결제 완료** 및 **입금지갑 → 포인트 전환** 플로우가 막힘 없이 이어지는가.

> **용어 정의 — 입금지갑 vs 개인지갑**
> - **입금지갑**: 서비스가 **사용자별로 발급한 전용 입금(수신) 주소**. 여기로 토큰을 보내면 P로 전환됨.
> - **개인지갑**: 사용자가 소유한 지갑(예: 메타마스크). 전환 시 여기서 입금지갑으로 토큰을 **보내는** 출발점. → 둘은 **별개**.

---

## 3. 디자인 시스템 & 비주얼 방향 (Shadcn · 와이어프레임 1차)

`tokens.css`의 `:root` 블록을 각 화면 첫 `<style>`에 그대로 붙이고 `var(--*)`로만 참조. 원시 hex 금지.

- **와이어프레임 원칙(1차)**: 구조·계층·플로우 우선. 컬러·이미지는 최소화(회색조 표면, 블랙 텍스트/보더), 액센트 블랙은 주요 CTA에만. 실사진 대신 `.ph-img` 플레이스홀더. 고충실도 전환 시 이 골격 위에 컬러·깊이·타이포 강화.
- **색**: 배경 `#ffffff` / 표면 `#ffffff` / 본문 `#111827` / 보조 `#64748b` / 보더 `#e5e7eb` / **액센트=블랙 `#000000`**(Primary CTA, 화면당 1~2회)
- **상태색**: 성공 `#16a34a` / 경고 `#d97706` / 위험 `#dc2626` — 뱃지·유효성에만, 5% 이하
- **타이포**: Geist(display/body), Fira Code(숫자·mono). 스케일 12/14/16/20/24/32
- **형태**: radius sm 6px(버튼·인풋)/md 8px(카드)/lg 12px/pill(뱃지). 1px 보더. 4px 그리드
- **포커스**: `--focus-ring` 모든 인풋/버튼 focus-visible
- **컴포넌트 인벤토리**: `.btn/.btn-primary/.btn-secondary/.btn-ghost`, `.field`, `.card`, `.badge`(default/secondary/outline/success/dot), `kbd`, 텍스트 유틸, 레이아웃 `.container/.stack-*/.row-between`
- **모바일 규칙**: 터치 타깃 ≥ 44px, iPhone 15 Pro 프레임(390×844) 세이프에어리어 존중, 하단 고정 CTA는 홈 인디케이터 여백 확보

---

## 4. 화면 인벤토리 (15개, 0~14) + 화면별 상세

> 각 화면은 `screens/NN-이름.html` 개별 파일. `index.html`은 전체를 iPhone 프레임에 플로우 순서로 나열하는 **런처/갤러리**.

### [화면0] 스플래시 — `screens/00-splash.html`
- **목적**: 앱 진입·인증 판별 → 로그인/메인 전환
- **레이아웃**: 화면 정중앙 **로고 심볼만(글자 없음)**, 흰 배경, 극단적 여백. 로고 없으므로 워드마크/심볼 플레이스홀더 생성
- **인터랙션**: 자동 페이드→다음(프로토타입은 로고 탭 시 전환)

### [화면1] 로그인 — `screens/01-login.html`
- **레이아웃**: 상단 로고, `.field`×2(아이디/비밀번호), Primary "로그인", 텍스트 링크 "회원가입"
- **상태**: 형식 오류 인라인 에러, 로딩 버튼, 비밀번호 표시 토글
- **데이터**: `{ id, password }`

### [화면2] 회원가입 — `screens/02-signup.html`
- **레이아웃(위→아래)**: 아이디 · 비밀번호 · 비밀번호 확인 · 이름 · **성별**(세그먼트: 남/여/선택안함) · **개인정보처리방침 동의**(필수, "전문 보기") · **마케팅 활용 동의**(선택) · Primary "가입하기"
- **약관 본문**: `lorem ipsum`으로 적절히 길게 채운 시트/모달("전문 보기"로 열람)
- **유효성**: 비밀번호 일치, 필수 동의 미체크 시 비활성, 아이디 중복확인(목)
- **데이터**: `{ id, password, passwordConfirm, name, gender, agreePrivacy, agreeMarketing }`

### [화면3] 메인/홈 — `screens/03-home.html`
- **상단바(우측 정렬)**: 🔔 **알림 종 아이콘**(미읽음 dot) → 공지사항 / 👤 **아바타 아이콘** → 마이페이지
- **본문**: 큰 **P 잔액 카드**, **"QR 결제하기"** Primary CTA
- **하단**: **최근 포인트 내역**(사용/지급/차감 구분 뱃지 + 설명 + 금액 + 일시), "전체 보기" → 포인트 내역
- **인터랙션**: 내역 항목 탭 → 상세
- **데이터**: `balance(P)`, `recentTransactions[]`, `unreadNoticeCount`

### [화면4] QR 스캔 — `screens/04-qr-scan.html`
- **레이아웃**: 풀블리드 **카메라 프리뷰**(어두운 플레이스홀더), 중앙 스캔 프레임, 안내 문구, 닫기(✕), 손전등/갤러리 옵션
- **인터랙션**: "스캔 시뮬레이션" 트리거 → 결과 화면

### [화면5] QR 결제 결과(확인) — `screens/05-qr-result.html`
- **레이아웃**: **가맹점명 · 상품명 · 결제 금액(P)** 강조, 결제 후 예상 잔액, Primary "결제하기" + "취소"
- **상태**: 잔액 부족 시 경고/버튼 비활성. (결제 전 PIN/생체인증은 **이번엔 없음** — 추후 추가 가능성 높음)
- **데이터**: `{ merchantName, productName, amount, balanceAfter }`

### [화면6] 결제 완료 — `screens/06-payment-complete.html`
- **레이아웃**: 성공 체크 아이콘, **결제 내용 요약**(가맹점/상품/금액/일시/거래ID), "홈으로"(닫기) + "뒤로가기"
- **데이터**: `{ merchant, product, amount, datetime, txId, newBalance }`

### [화면7] 마이페이지 — `screens/07-mypage.html`  ⭐개선
- **목적**: 프로필·입금지갑·보안 관리 (**두 상태 분기**)
- **레이아웃**: 프로필(아바타/이름/아이디) +
  - **입금지갑 있음**: 입금지갑 주소(축약 `0xAB…12CD` + **복사** + QR 보기) · **[포인트 전환]** 버튼(⭐신규, → 화면13) · [비밀번호 변경](→화면8) · 로그아웃
  - **입금지갑 없음**: 안내 문구 + **[지갑 생성하기]** Primary 버튼 · [비밀번호 변경] · 로그아웃
  - → index 갤러리에서 **두 상태 나란히**(07a 있음 / 07b 없음) 표시
- **데이터**: `{ profile, depositWallet: { exists, address } }`

### [화면8] 비밀번호 변경 — `screens/08-change-password.html`
- **레이아웃**: `.field`×3(현재/새/새 확인), Primary "변경하기"
- **유효성**: 새 비밀번호 강도, 확인 일치, 현재 비밀번호 오류, 완료 토스트
- **데이터**: `{ current, next, nextConfirm }`

### [화면9] 공지사항(목록) — `screens/09-notices.html`
- **레이아웃**: 상단 **필터 Chip**(전체/공지/이벤트), 리스트 = 카테고리 **Chip**(공지/이벤트 구분) + 제목 + 날짜 + 미읽음 표시
- **인터랙션**: Chip 필터, 항목 탭 → 상세
- **데이터**: `notices[] { id, type:'공지'|'이벤트', title, date, isRead }`

### [화면10] 포인트 내역(목록) — `screens/10-point-history.html`
- **레이아웃**: 상단 필터 Chip/세그먼트(전체/**사용**/**지급**/**차감**), 리스트 = 구분 뱃지 + 설명 + 금액(지급 `+`성공색 / 사용·차감 `−`) + 일시
- **인터랙션**: 필터, 항목 탭 → 상세
- **데이터**: `transactions[] { id, type:'use'|'grant'|'deduct', title, amount, balanceAfter, datetime }`

### [화면11] 포인트 내역 상세 — `screens/11-point-detail.html`
- **레이아웃**: 큰 금액(P) + 구분 뱃지 + 상태, 상세 필드(일시/거래ID/거래 후 잔액). **사용 내역**은 추가: **가맹점/지점명, 상품명, 사용처(지점)**
- **데이터**: 거래 상세 전체(사용 시 merchant/branch/product 포함)

### [화면12] 공지사항 상세 — `screens/12-notice-detail.html`
- **레이아웃**: 카테고리 Chip, 제목, 날짜, 본문(`lorem ipsum`). **이벤트**는 진행 기간 표시
- **데이터**: `{ type, title, date, body, period? }`

### [화면13] 포인트 전환 — 방법 선택 — `screens/13-convert-method.html`  ⭐신규
- **목적**: 입금지갑으로 토큰을 보내 P로 전환하는 **방법 선택**
- **레이아웃**: 상단 안내(**전환 비율 1:1 — 1 토큰 = 1 P**, "비율은 추후 관리자 페이지에서 설정" 주석), 두 개의 선택 카드/버튼:
  1. **메타마스크로 입금** (메타마스크 앱으로 전송) → **화면14**
  2. **직접 입금** (주소·QR로 직접 이체) → **직접 입금 상태**로 전환(같은 화면 상태 전환 또는 시트):
     - **입금지갑 전체 주소**(축약 아님, 전체) + **복사** + **QR코드** 표시
     - 안내: "이 주소로 토큰을 보내면 자동으로 P가 적립됩니다"
- **index 갤러리**: 13(방법 선택)과 13-직접입금(주소+QR) 두 상태 모두 표시
- **데이터**: `{ depositAddress, ratio }`

### [화면14] 포인트 전환 — 메타마스크 입금(수량) — `screens/14-convert-metamask.html`  ⭐신규
- **목적**: 전환 수량 선택/입력 후 **메타마스크 앱 실행**
- **레이아웃**:
  - **프리셋 Chip**(6개): `1,000` / `5,000` / `10,000` / `30,000` / `50,000` / `100,000`
  - **직접 입력** 수량 필드(프리셋과 상호 배타 — 하나 선택 시 다른 쪽 해제)
  - 안내(비율/유의사항), Primary **"전환하기"** 버튼
- **"전환하기" 동작 — 메타마스크 딥링크 실행**:
  ```
  https://link.metamask.io/send/0x6b340661bb35553E1279D334f3454cc59A5d1492@137/transfer?address={입금지갑주소}&uint256={amountWei}
  ```
  - ⚠️ **`uint256 = 수량 × 10^18`** — 값이 매우 커서(예: 100000×1e18 = 1e23) **Number 곱셈 시 정밀도 손실**.
    반드시 **BigInt/문자열 처리**: `const wei = (BigInt(qty) * 10n ** 18n).toString();` (또는 `qty` 문자열 뒤에 `"0".repeat(18)` 붙이기)
  - `address` 파라미터 = **입금지갑 주소**. 지금은 **임의의 목업 주소** 사용, 실제로는 사용자가 [지갑 생성하기]로 입금지갑을 만들면 그때 생성된 주소가 여기에 대입됨
  - 전환 비율 = **1:1**(1 토큰 = 1 P). 안내 문구에만 표기(추후 관리자 페이지에서 설정 예정), 딥링크 `uint256`은 입력 수량 그대로 ×10^18
  - 실행 = `window.location.href = link` (또는 `<a href>` 클릭). 프로토타입에선 링크를 그대로 열어 메타마스크로 전환
- **데이터**: `{ presets:[1000,5000,10000,30000,50000,100000], depositAddress, ratio, tokenContract:'0x6b34…1492', chainId:137 }`

---

## 5. 내비게이션 플로우

```
[0] 스플래시 ─┬─→ [1] 로그인 ──→ [3] 메인
             └─→ [2] 회원가입 ──→ (로그인/메인)

[3] 메인 ──→ [4] QR스캔 ──→ [5] QR결과 ──→ [6] 결제완료 ──→ (홈 복귀)
[3] 메인 ──(아바타)──→ [7] 마이페이지
[3] 메인 ──(종 아이콘)──→ [9] 공지사항 ──→ [12] 공지 상세
[3] 메인 ──(내역 카드)──→ [10] 포인트 내역 ──→ [11] 내역 상세

[7] 마이페이지 ──→ 입금지갑 있음 상태(주소 표시 + [포인트 전환] 버튼)
[7] 마이페이지 ──→ 입금지갑 없음 상태([지갑 생성하기])
[7] 마이페이지 ──(비밀번호 변경)──→ [8] 비밀번호 변경

[7] 마이페이지(지갑 있음) ──(포인트 전환)──→ [13] 전환 방법 선택
[13] ──(메타마스크로 입금)──→ [14] 수량 선택 ──(전환하기)──→ 🦊 메타마스크 앱(딥링크)
[13] ──(직접 입금)──→ 입금지갑 전체주소 + QR 표시
```

`index.html` 런처는 이 순서대로 화면을 배치하고, 화면 간 이동은 각 화면의 실제 버튼/아이콘 링크로 클릭 가능하게 연결.

---

## 6. 공통 컴포넌트 / 상태 규칙

- **상단 앱바**: 뒤로가기(좌) + 타이틀 + 액션(우). 홈만 로고/알림/아바타 구성
- **버튼**: Primary=블랙(주요 1개), Secondary=중립 보더, Ghost=텍스트 링크. 44px+
- **입력**: `.field` = label + input + help/error, focus-visible 링, 에러 시 위험색
- **Chip/필터**: 선택=강조, 미선택=중립. 공지/이벤트·내역 구분·전환 프리셋에 사용
- **뱃지**: 지급=성공, 사용/차감=중립·위험, 공지/이벤트 카테고리 구분
- **리스트 항목**: 좌 아이콘/뱃지 · 중앙 제목+메타 · 우 금액/화살표, 탭 → 상세
- **주소 표시**: 축약(`0xAB…12CD`)은 마이페이지, 전체 주소+QR은 직접 입금 화면. 복사 시 토스트
- **상태 전반**: hover(데스크톱 미리보기)/active/disabled/loading/empty/error 명시. **가짜 통계 금지**(라벨드 플레이스홀더)
- **외부 앱 연동**: 메타마스크 딥링크는 사용자 "전환하기" 탭에서만 실행

---

## 7. 데이터 / 콘텐츠 모델 (목 데이터 스키마)

```
User          { id, name, gender, avatar, agreePrivacy, agreeMarketing }
DepositWallet { exists: boolean, address }        // 사용자별 전용 입금(수신) 주소. 미생성 시 목업 주소로 대체
Balance       { points: number, unit: 'P' }
CONVERSION_RATE = 1                                // 토큰:P = 1:1 (추후 관리자 페이지에서 설정)
Transaction   { id, type:'use'|'grant'|'deduct', title, amount, balanceAfter,
                datetime, txId, status,
                merchant?, branch?, product?, location? }   // 사용 시 추가
Notice        { id, type:'공지'|'이벤트', title, date, body, isRead, period? }
Merchant      { name, product, amount }           // QR 스캔 결과
Conversion    { method:'metamask'|'direct', amount, unit:'P' }

// 메타마스크 전환 상수 (화면14)
METAMASK = {
  base: 'https://link.metamask.io/send/0x6b340661bb35553E1279D334f3454cc59A5d1492@137/transfer',
  tokenContract: '0x6b340661bb35553E1279D334f3454cc59A5d1492',
  chainId: 137,
  presets: [1000, 5000, 10000, 30000, 50000, 100000],
  depositAddress: '0x…(임의 목업 주소, 실제로는 입금지갑 생성 시 대입)',
  buildLink: (depositAddress, qty) =>
    `${base}?address=${depositAddress}&uint256=${(BigInt(qty) * 10n ** 18n).toString()}`
}
```

목 데이터는 화면별 `<script>` 상단 `const MOCK = {...}` 로 주입(가맹점명/상품/금액/일시/공지 예시는 현실적 카피, 통계 수치는 라벨드 플레이스홀더, 약관 본문은 `lorem ipsum`).

---

## 8. 파일 아키텍처 (핸드오프 산출물)

```
project/
├── index.html                      ← 런처: 15개 화면을 iPhone 프레임에 플로우 순서로 나열 + 상태 분기 표기
├── screens/
│   ├── 00-splash.html
│   ├── 01-login.html
│   ├── 02-signup.html
│   ├── 03-home.html
│   ├── 04-qr-scan.html
│   ├── 05-qr-result.html
│   ├── 06-payment-complete.html
│   ├── 07-mypage.html              ← 입금지갑 있음/없음 상태 분기 (있음엔 [포인트 전환] 버튼)
│   ├── 08-change-password.html
│   ├── 09-notices.html
│   ├── 10-point-history.html
│   ├── 11-point-detail.html
│   ├── 12-notice-detail.html
│   ├── 13-convert-method.html      ← ⭐전환 방법 선택 + 직접 입금(전체주소+QR) 상태
│   └── 14-convert-metamask.html    ← ⭐메타마스크 수량 선택/입력 + 전환하기(딥링크)
└── (기기 프레임은 /frames/iphone-15-pro.html?screen=... 임베드)
```

- 공통 shadcn 토큰·컴포넌트 CSS는 각 화면 첫 `<style>`에 동일 블록 삽입(자기완결 파일)
- 화면당 ~1000줄 이하 · 기기 프레임은 **직접 다시 그리지 않고** 저장소 `/frames/iphone-15-pro.html?screen=...` 임베드

---

## 9. 인수 조건 (Acceptance checks)

- [ ] 15개 화면(0~14) 모두 존재하고 `index.html`에서 열람 가능
- [ ] 정의된 플로우 링크가 실제 클릭 이동 (0→1→3, 3→4→5→6, 3→7→8, 3→9→12, 3→10→11, **7→13→14**)
- [ ] 마이페이지 **입금지갑 있음/없음 두 상태** 확인 가능, **있음 상태에 [포인트 전환] 버튼** 존재
- [ ] [화면13] **메타마스크로 입금 / 직접 입금** 두 선택 제공, **직접 입금 = 입금지갑 전체주소 + QR + 복사**
- [ ] [화면14] 프리셋 6종(1,000~100,000) + 직접 입력, **[전환하기]가 메타마스크 딥링크로 이동**
- [ ] 딥링크 `uint256` = **수량×10^18**, **BigInt/문자열**로 생성(정밀도 손실 없음), `address`=입금지갑 주소
- [ ] 공지/이벤트 Chip 구분, 포인트 내역 사용/지급/차감 구분, 사용 상세에 가맹점/지점/상품 포함
- [ ] shadcn 토큰만 사용(원시 hex 없음), 액센트=블랙 화면당 1~2회, 8px radius, Geist/Fira Code
- [ ] 와이어프레임 충실도(구조 우선), focus-visible 링, 터치 타깃 44px+, iPhone 프레임 내 클리핑·오버플로 없음
- [ ] 가짜 통계 없음(라벨드 플레이스홀더), 약관은 `lorem ipsum`

---

## 10. 확정된 답변 (모두 확정 완료)

1. **충실도** — 와이어프레임 1차, 확인 후 고충실도로 재작업
2. **플랫폼** — 웹뷰 형태 → **iPhone(iOS) 프레임으로 통일**
3. **포인트** — 토큰 입금 → 비율 전환 → 단위 `P`, 소수점·네트워크 표기 불필요
4. **백엔드** — 없음, 목업 프로토타입
5. **로고** — 없음 → 워드마크/심볼 플레이스홀더 자동 생성
6. **결제 보안(PIN/생체)** — 이번엔 미포함(추후 추가 가능성 높음)
7. **약관** — `lorem ipsum`으로 길게 채움
8. **예시 카피** — 현실적 예시로 채움
9. **딥링크 `address`** — 지금은 **임의의 목업 주소** 사용. 사용자가 [지갑 생성하기]로 입금지갑 생성 시 그 주소가 대입됨
10. **전환 비율** — **1:1**(1 토큰 = 1 P)로 가정. 추후 관리자 페이지에서 설정

---

## 11. 다음 단계 (Next step)

모든 열린 질문이 확정되었습니다. **기획 문서 확정 완료 — 생성 준비 완료 상태**입니다.

1. 화면 목록·플로우·문구를 이 문서에서 마지막으로 검토·편집하세요.
2. 준비되면 **"이 기획서(`point-pay-app-plan.md`)대로 와이어프레임 만들어줘"** 라고 하시면, shadcn 기반 **15개 화면(0~14) HTML + `index.html` 런처**를 iPhone 프레임 안에 생성합니다.
