Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions week-06/dev/CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,26 @@

### Smart Contract

- [ ] Solidity 0.8.26 이상 사용
- [ ] 최소 1개 이상의 상태 변수
- [ ] 최소 2개 이상의 public/external 함수
- [x] Solidity 0.8.26 이상 사용
- [x] 최소 1개 이상의 상태 변수
- [x] 최소 2개 이상의 public/external 함수
- [ ] 모든 상태 변경 함수에 이벤트 발생
- [ ] Foundry 테스트 작성 (최소 5개 테스트)
- [ ] CEI 패턴 또는 ReentrancyGuard 적용 (해당 시)
- [x] CEI 패턴 또는 ReentrancyGuard 적용 (해당 시)

### Frontend

- [ ] Next.js App Router 사용
- [ ] wagmi + RainbowKit으로 지갑 연결
- [ ] 컨트랙트 상태 읽기 (useReadContract)
- [ ] 컨트랙트 상태 쓰기 (useWriteContract)
- [ ] 트랜잭션 대기 상태 표시 (pending indicator)
- [ ] 에러 처리 및 사용자 피드백
- [x] Next.js App Router 사용
- [x] wagmi + RainbowKit으로 지갑 연결
- [x] 컨트랙트 상태 읽기 (useReadContract)
- [x] 컨트랙트 상태 쓰기 (useWriteContract)
- [x] 트랜잭션 대기 상태 표시 (pending indicator)
- [x] 에러 처리 및 사용자 피드백

### Deployment

- [ ] Sepolia 테스트넷에 배포
- [ ] 배포된 컨트랙트 주소 README에 기재
- [x] Sepolia 테스트넷에 배포
- [x] 배포된 컨트랙트 주소 README에 기재
- [ ] Etherscan에서 컨트랙트 검증 (선택)

---
Expand All @@ -36,17 +36,17 @@

### User Flow

- [ ] 지갑 연결 기능
- [ ] 메인 기능 1개 이상 (예: 토큰 전송, 투표, 기록 저장)
- [ ] 사용자 잔액 또는 상태 표시
- [ ] 트랜잭션 히스토리 또는 결과 표시
- [x] 지갑 연결 기능
- [x] 메인 기능 1개 이상 (예: 토큰 전송, 투표, 기록 저장)
- [x] 사용자 잔액 또는 상태 표시
- [x] 트랜잭션 히스토리 또는 결과 표시

### UX/UI

- [ ] 반응형 레이아웃 (모바일/데스크톱)
- [ ] 로딩 상태 표시
- [ ] 에러 메시지 표시
- [ ] 한국어 UI (선택)
- [x] 반응형 레이아웃 (모바일/데스크톱)
- [x] 로딩 상태 표시
- [x] 에러 메시지 표시
- [x] 한국어 UI (선택)

---

Expand Down
82 changes: 82 additions & 0 deletions week-06/dev/WEEK06-README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Kamin

> One wallet, every cafe spend in one view.
<img width="2480" height="1652" alt="image" src="https://github.com/user-attachments/assets/8068d096-4312-4976-bf9d-520073ac7a04" />

Kamin은 여러 프랜차이즈 카페에 흩어진 주문 기록과 소비 데이터를 한곳으로 모아 보여주는 카페 소비 aggregator입니다. 사용자는 브랜드별 앱을 오가며 내역을 확인하지 않아도, Kamin에서 자신의 카페 소비 흐름을 통합적으로 관리할 수 있습니다.

이 프로젝트는 단순한 주문 인터페이스가 아니라, 프랜차이즈별 소비 데이터를 공통 포맷으로 수집하고 연결해 누적 지출, 브랜드별 이용 패턴, 주문 히스토리를 하나의 사용자 경험으로 재구성하는 데 초점을 둡니다. 여기에 온체인 리워드를 결합해, 실제 소비 활동이 곧 디지털 보상으로 이어지는 구조를 제공합니다.

사용자는 지갑을 연결한 뒤 브랜드와 메뉴를 선택하고, 백엔드에서 주문 정보를 생성한 다음, 스마트 컨트랙트에 주문을 확정하는 트랜잭션을 전송합니다. 주문이 성공하면 CafeMarket에 기록이 남고, 보상으로 KAMIN 토큰이 민팅됩니다.

## Problem

기존 카페 소비 경험은 브랜드별 앱, 멤버십, 포인트 체계가 서로 분리되어 있어 사용자가 자신의 전체 소비 흐름을 통합해서 보기 어렵습니다. 주문 내역은 흩어져 있고, 포인트는 브랜드 안에 갇혀 있으며, 사용자는 실제로 얼마나 소비했고 어떤 브랜드를 얼마나 이용하는지 직관적으로 파악하기 어렵습니다.

## Solution

Kamin은 여러 프랜차이즈 카페의 주문 기록과 소비 데이터를 하나의 인터페이스로 연결하는 카페 소비 aggregator입니다. 사용자는 Kamin 안에서 카페를 선택하고 주문을 생성한 뒤, 동일한 흐름으로 거래를 확정할 수 있고, 이 과정에서 주문 데이터는 백엔드와 데이터베이스에 정리되며 온체인에서는 주문 기록과 보상 민팅이 함께 처리됩니다.

Kamin이 제공하는 핵심 가치는 다음과 같습니다.
- 여러 프랜차이즈 카페의 소비 데이터를 한곳에서 조회하는 통합 경험
- 브랜드별 주문 내역과 누적 소비를 같은 기준으로 비교할 수 있는 구조
- 실제 주문 기록을 온체인 활동과 연결하는 투명한 리워드 흐름
- 주문, 기록, 보상 적립이 분리되지 않는 일관된 사용자 여정

## Business Model

Kamin은 주문과 소비 데이터를 통합하는 인터페이스일 뿐 아니라, 브랜드 제휴를 확장할 수 있는 리워드 네트워크의 기반이 됩니다. 제휴 브랜드가 늘어날수록 사용자는 하나의 공통 보상 체계 안에서 더 많은 카페 경험을 연결할 수 있고, 브랜드는 신규 유입과 재방문을 유도할 수 있는 공동 혜택 채널을 확보할 수 있습니다.

특히 공통으로 적립되는 KAMIN 토큰은 향후 브랜드 제휴 구조에 따라 각 브랜드의 자체 포인트처럼 사용하거나 교환할 수 있는 형태로 발전할 수 있습니다. 이는 브랜드별로 단절된 포인트 시스템을 연결하는 공통 정산 및 리워드 레이어로 작동할 수 있으며, 사용자에게는 더 높은 활용성을, 브랜드에게는 제휴 기반의 락인과 마케팅 효율을 제공합니다.

## Growth Vision

Kamin의 장기 목표는 단순한 카페 주문 서비스가 아니라, 분산된 오프라인 소비 경험을 통합하고 디지털 자산화하는 소비 aggregation layer가 되는 것입니다. 더 많은 프랜차이즈와 로컬 카페를 연결하고, 개인화된 리워드, 브랜드 간 혜택 연동, 온체인 멤버십까지 확장함으로써 카페 소비 전반을 아우르는 공통 인프라로 발전할 수 있습니다.

프로젝트는 세 부분으로 구성됩니다.
- `frontend`: Next.js App Router 기반 사용자 인터페이스
- `backend`: NestJS, Prisma, PostgreSQL 기반 주문 API와 서명 생성 서버
- `contracts`: Foundry 기반 스마트 컨트랙트와 배포 스크립트

## Architecture

```mermaid
flowchart LR
U[User]
FE[Frontend<br/>Next.js + wagmi + RainbowKit]
BE[Backend<br/>NestJS + Prisma]
DB[(PostgreSQL)]

subgraph CHAIN[Sepolia]
K[Kamin Contract]
M[CafeMarket Contracts<br/>Starbucks / Twosome / Mega / Hollys]
end

U -->|wallet connect / order request| FE
FE -->|GET menus / history / grass / summary| BE
BE -->|read / write| DB
BE -->|orderId, rewardAmount, signature| FE
FE -->|confirmOrder| K
K -->|recordOrder| M
K -->|mint KAMIN| U
```

## Order Flow

```mermaid
sequenceDiagram
participant U as User
participant FE as Frontend
participant BE as Backend
participant DB as PostgreSQL
participant K as Kamin
participant M as CafeMarket

U->>FE: 주문 요청
FE->>BE: POST /order
BE->>DB: 주문 저장
BE-->>FE: orderId, rewardAmount, signature
FE->>K: confirmOrder
K->>M: recordOrder
K-->>U: KAMIN mint
```
6 changes: 6 additions & 0 deletions week-06/dev/backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DATABASE_URL="postgresql://postgres:password@localhost:5432/kamin"
PRIVATE_KEY=YOUR_SIGNER_PRIVATE_KEY
STARBUCKS_MARKET_ADDRESS=0xb80A6060e3611a0A8A410E2db76B91dC08a5F9b9
TWOSOME_MARKET_ADDRESS=0xB4e6d4c228e5bfd99271eC2E4D664092a429fA4F
MEGA_MARKET_ADDRESS=0x85F1cA2B89C26fe613a010b83456594C4a742C53
HOLLYS_MARKET_ADDRESS=
58 changes: 58 additions & 0 deletions week-06/dev/backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# compiled output
/dist
/node_modules
/build

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# temp directory
.temp
.tmp

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

/generated/prisma
4 changes: 4 additions & 0 deletions week-06/dev/backend/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}
147 changes: 147 additions & 0 deletions week-06/dev/backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Backend

카페 리워드 dApp의 NestJS 백엔드입니다.

## 주요 역할

- 메뉴 데이터 조회
- 주문 요청 처리
- 주문 서명 생성
- 주문 히스토리 조회
- 잔디판 데이터 집계
- 브랜드별 주문 수 집계
- PostgreSQL + Prisma 연동

## Tech Stack

- NestJS
- Prisma
- PostgreSQL
- ethers.js

## 환경변수

`.env`에 아래 값이 필요합니다.

```env
DATABASE_URL="postgresql://postgres:password@localhost:5432/kamin"
PRIVATE_KEY=YOUR_SIGNER_PRIVATE_KEY
STARBUCKS_MARKET_ADDRESS=0xb80A6060e3611a0A8A410E2db76B91dC08a5F9b9
TWOSOME_MARKET_ADDRESS=0xB4e6d4c228e5bfd99271eC2E4D664092a429fA4F
MEGA_MARKET_ADDRESS=0x85F1cA2B89C26fe613a010b83456594C4a742C53
HOLLYS_MARKET_ADDRESS=0x70e98e267f365137157C0E7e5AdD36318Db5502B
KAMIN_ADDRESS=0x8911C397ABc19635fe0b6B7bD93071d463e67573
```

## 실행 방법

### 1. 의존성 설치

```bash
npm install
```

### 2. Prisma 준비

```bash
npx prisma migrate dev --name init
npx prisma db seed
```

### 3. 개발 서버 실행

```bash
npm run start:dev
```

기본 포트는 `3001`입니다.

## Architecture

```mermaid
flowchart LR
U[User]
FE[Frontend<br/>Next.js + wagmi + RainbowKit]
BE[Backend<br/>NestJS + Prisma]
DB[(PostgreSQL)]

subgraph CHAIN[Sepolia]
K[Kamin Contract]
M[CafeMarket Contracts<br/>Starbucks / Twosome / Mega / Hollys]
end

U -->|wallet connect / order request| FE
FE -->|GET menus / history / grass / summary| BE
BE -->|read / write| DB
BE -->|orderId, rewardAmount, signature| FE
FE -->|confirmOrder| K
K -->|recordOrder| M
K -->|mint KAMIN| U
```

## Order Flow

```mermaid
sequenceDiagram
participant U as User
participant FE as Frontend
participant BE as Backend
participant DB as PostgreSQL
participant K as Kamin
participant M as CafeMarket

U->>FE: 주문 요청
FE->>BE: POST /order
BE->>DB: 주문 저장
BE-->>FE: orderId, rewardAmount, signature
FE->>K: confirmOrder
K->>M: recordOrder
K-->>U: KAMIN mint
```

## API

### `GET /order/menus?brand=starbucks`

브랜드별 메뉴 조회

### `POST /order`

주문 생성 및 서명 응답

요청 예시:

```json
{
"user": "0xUserAddress",
"market": "0xMarketAddress",
"menuName": "Americano"
}
```

응답 예시:

```json
{
"orderId": 1742470000,
"rewardAmount": 120,
"signature": "0x..."
}
```

### `GET /order/history?user=0x...`

사용자 주문 히스토리 조회

### `GET /order/grass?user=0x...`

잔디판용 날짜별 주문 수 조회

### `GET /order/summary?user=0x...`

브랜드별 주문 수 요약 조회

## 참고

- 프론트는 직접 서명을 만들지 않고, backend가 생성한 `signature`를 받아 `confirmOrder`를 호출합니다.
- 브랜드/메뉴 데이터는 Prisma seed를 통해 초기화됩니다.
Loading