Files
a301_client/docs/superpowers/specs/2026-03-23-wallet-ui-design.md
2026-03-23 13:24:52 +09:00

7.2 KiB

지갑 UI 설계

작성일: 2026-03-23 상태: 승인됨


개요

웹 클라이언트(a301_client)에 블록체인 지갑 UI를 추가한다. 단일 /wallet 페이지에 4개 탭(지갑, 자산, 인벤토리, 마켓)으로 구성하고, HomePage에 잔액 요약 카드를 배치한다.


페이지 구조

라우팅

경로 컴포넌트 인증
/wallet WalletPage 로그인 필수

/wallet 라우트 보호: 기존 AdminRoute 패턴을 참고하여 PrivateRoute 컴포넌트를 새로 만든다. 로그인하지 않은 유저는 /login으로 리다이렉트.

기존 라우트는 변경하지 않는다.

HomePage 변경

  1. 헤더: "지갑" 링크 추가 → /wallet로 이동
  2. 지갑 요약 카드: DownloadSection 위에 배치
    • TOL 잔액 (큰 글씨)
    • 보유 자산 수, 장착 아이템 수
    • 클릭 시 /wallet로 이동
    • 로그인하지 않은 상태에서는 숨김
    • 3개 API (/balance, /assets, /inventory) Promise.all로 병렬 호출
    • 일부 API 실패 시: 실패한 항목만 "--"로 표시, 카드 자체는 숨기지 않음

탭 1: 지갑

잔액, 지갑 주소, 키 내보내기.

잔액 카드

  • TOL 잔액을 크게 표시
  • API: GET /api/chain/balance

지갑 정보

  • 공개키: 축약 표시 (a3b4...e1f2) + 복사 버튼
  • 주소: 축약 표시 (a3b4c5d6...e9f0a1b2) + 복사 버튼
  • 클릭 시 클립보드에 전체 값 복사, 토스트로 "복사됨" 알림
  • API: GET /api/chain/wallet

키 내보내기

  • 비밀번호 입력 필드 + "내보내기" 버튼
  • 성공 시: 개인키 hex를 화면에 표시 (복사 버튼 포함)
  • 경고 텍스트: "개인키를 안전하게 보관하세요"
  • 탭 이탈 시(다른 탭 클릭): 개인키 표시 상태 초기화 (보안)
  • 에러 처리:
    • HTTP 401 → "비밀번호가 올바르지 않습니다"
    • 기타 에러 → 토스트로 서버 에러 메시지 표시
  • API: POST /api/chain/wallet/export (body: {"password": "..."})

탭 2: 자산

보유 NFT 목록 + 상세 정보.

자산 목록

  • 아이템 이름, 템플릿 이름, 자산 ID 표시
  • 거래 가능 여부 표시 ("거래 가능" / "거래 불가")
  • API 호출 전략:
    1. GET /api/chain/assets → 자산 ID 배열 반환
    2. 각 ID에 대해 GET /api/chain/asset/:idPromise.all병렬 호출
    3. 개별 자산 로드 실패 시: 해당 자산만 "로드 실패" 표시, 나머지는 정상 표시
  • 로딩 중: 스피너 표시

자산 상세 (클릭 시 펼치기)

  • 속성(properties) 키-값 표시
  • 거래 가능 여부
  • 마켓 등록 상태 (등록됨 / 미등록)
  • 마켓 등록 버튼: 미등록 + 거래 가능한 자산에만 표시
    • 클릭 시: 가격 입력 인라인 UI (펼쳐진 상세 영역 내 숫자 input + "등록" 버튼)
    • POST /api/chain/market/list (body: {"asset_id": "...", "price": N})
    • Idempotency-Key 헤더 필요
    • 성공 시: 토스트 알림 + 해당 자산 상태 갱신 (마켓 등록됨으로 변경)

탭 3: 인벤토리

장착 슬롯 현황 (조회 전용).

슬롯 목록

  • 슬롯 이름 + 장착된 아이템 이름/ID 표시
  • 빈 슬롯은 "비어있음"으로 표시 (점선 테두리)
  • 장착/해제 버튼 없음 (게임 내에서만 조작)
  • API: GET /api/chain/inventory

탭 4: 마켓

NFT 마켓플레이스. 구매, 판매(자산 탭에서 연결), 취소.

리스팅 목록

  • 전체 / 내 리스팅 필터 토글
  • 각 리스팅: 아이템 이름, 판매자 (축약 주소), 가격
  • "내 리스팅" 필터: 클라이언트에서 /api/chain/wallet으로 가져온 내 지갑 주소와 리스팅의 seller 필드를 비교하여 필터링
  • API: GET /api/chain/market → 활성 리스팅 목록
  • 미사용: GET /api/chain/market/:id — 리스팅 상세 모달이 없으므로 의도적으로 사용하지 않음

구매

  • 타인의 리스팅에 "구매" 버튼
  • 확인 다이얼로그 (useConfirm 사용): "화염 활을 500 TOL에 구매하시겠습니까?"
  • POST /api/chain/market/buy (body: {"listing_id": "..."})
  • Idempotency-Key 헤더 필요
  • 성공 시: 토스트 알림 + 리스팅 목록 새로고침 + 잔액 갱신
  • 실패 시: 서버 응답 에러 메시지를 토스트로 표시 (잔액 부족 등 포함)

내 리스팅 취소

  • 내 리스팅에 "취소" 버튼 (빨간 테두리)
  • 확인 다이얼로그: "리스팅을 취소하시겠습니까?"
  • POST /api/chain/market/cancel (body: {"listing_id": "..."})
  • Idempotency-Key 헤더 필요
  • 성공 시: 토스트 알림 + 리스팅 목록 새로고침

API 호출 패턴

기존 API 클라이언트 활용

  • 모든 API 호출은 src/api/client.jsapiFetch() 경유
  • JWT 토큰 자동 첨부, 401 시 자동 refresh
  • 새 파일: src/api/chain.js — 체인 관련 API 래퍼 함수 모음

Idempotency-Key

  • 마켓 구매/등록/취소 등 트랜잭션 API에는 Idempotency-Key 헤더 필요
  • chain.js 래퍼 내부에서 crypto.randomUUID()로 자동 생성하여 헤더에 추가
  • 호출 측(컴포넌트)에서는 Idempotency-Key를 신경 쓸 필요 없음

에러 처리

  • API 에러 시 토스트로 에러 메시지 표시
  • 네트워크 에러 시 기존 retry 로직 적용 (GET만)

로딩 상태

  • 각 탭 진입 시: 중앙 스피너 표시 (기존 로딩 패턴과 동일)
  • WalletSummary: 인라인 스피너 또는 "--" placeholder

컴포넌트 구조

src/
├── api/
│   └── chain.js              # 체인 API 래퍼 (신규)
├── pages/
│   ├── WalletPage.jsx        # /wallet 페이지 (탭 컨테이너) (신규)
│   └── WalletPage.css        # 지갑 페이지 스타일 (신규)
├── components/
│   └── wallet/               # 지갑 관련 컴포넌트 (신규)
│       ├── WalletTab.jsx     # 탭 1: 잔액, 주소, 키 내보내기
│       ├── AssetsTab.jsx     # 탭 2: 자산 목록 + 상세
│       ├── InventoryTab.jsx  # 탭 3: 인벤토리 조회
│       ├── MarketTab.jsx     # 탭 4: 마켓
│       └── WalletSummary.jsx # HomePage 요약 카드

스타일링

  • 기존 프로젝트 패턴 그대로: plain CSS, dark 테마
  • 색상: 기존 #BACDB0 (sage green) accent 사용
  • 탭 UI: 하단 보더로 활성 탭 표시
  • 복사 버튼: 클릭 시 navigator.clipboard.writeText() + 토스트
  • 반응형: @media (max-width: 768px) 대응

변경 파일 요약

파일 변경
src/api/chain.js 신규: 체인 API 래퍼 (Idempotency-Key 자동 생성)
src/pages/WalletPage.jsx 신규: 지갑 페이지 (탭 컨테이너)
src/pages/WalletPage.css 신규: 지갑 페이지 스타일
src/components/wallet/WalletTab.jsx 신규: 잔액/주소/키 내보내기
src/components/wallet/AssetsTab.jsx 신규: 자산 목록 + 상세 + 마켓 등록
src/components/wallet/InventoryTab.jsx 신규: 인벤토리 조회
src/components/wallet/MarketTab.jsx 신규: 마켓 (구매/취소)
src/components/wallet/WalletSummary.jsx 신규: 홈 요약 카드
src/App.jsx 수정: /wallet 라우트 + PrivateRoute 추가
src/pages/HomePage.jsx 수정: 헤더 링크 + WalletSummary 추가