diff --git "a/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217--refactor--\354\275\224\353\223\234-\353\246\254\355\214\251\355\206\240\353\247\201.md" "b/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217--refactor--\354\275\224\353\223\234-\353\246\254\355\214\251\355\206\240\353\247\201.md"
new file mode 100644
index 0000000..d3fda40
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217--refactor--\354\275\224\353\223\234-\353\246\254\355\214\251\355\206\240\353\247\201.md"
@@ -0,0 +1,17 @@
+---
+name: "♻️ [refactor] 코드 리팩토링"
+about: 리팩토링을 위한 템플릿
+title: "♻️ [refactor] "
+labels: "♻️ refactor"
+assignees: ''
+
+---
+
+## 📝 개요
+- 자세한 개요 작성
+
+## ✔️ To-Do
+- [ ] 투두 내용 작성
+
+## 👀 ETC
+- 참고자료 등 기타 내용 작성
diff --git "a/.github/ISSUE_TEMPLATE/\342\234\250--feat--\352\270\260\353\212\245-\354\266\224\352\260\200.md" "b/.github/ISSUE_TEMPLATE/\342\234\250--feat--\352\270\260\353\212\245-\354\266\224\352\260\200.md"
new file mode 100644
index 0000000..4f9e935
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\342\234\250--feat--\352\270\260\353\212\245-\354\266\224\352\260\200.md"
@@ -0,0 +1,17 @@
+---
+name: "✨ [feat] 기능 추가"
+about: 새로운 기능 추가 템플릿
+title: "✨ [feat] "
+labels: "✨ feature"
+assignees: ''
+
+---
+
+## 📝 개요
+- 자세한 개요 작성
+
+## ✔️ To-Do
+- [ ] 투두 내용 작성
+
+## 👀 ETC
+- 참고자료 등 기타 내용 작성
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\220\233--fix--\353\262\204\352\267\270-\354\210\230\354\240\225.md" "b/.github/ISSUE_TEMPLATE/\360\237\220\233--fix--\353\262\204\352\267\270-\354\210\230\354\240\225.md"
new file mode 100644
index 0000000..c6d3346
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\360\237\220\233--fix--\353\262\204\352\267\270-\354\210\230\354\240\225.md"
@@ -0,0 +1,17 @@
+---
+name: "\U0001F41B [fix] 버그 수정"
+about: 버그 수정을 위한 템플릿
+title: "\U0001F41B [fix] "
+labels: "\U0001F41B fix"
+assignees: ''
+
+---
+
+## 📝 개요
+- 자세한 개요 작성
+
+## ✔️ To-Do
+- [ ] 투두 내용 작성
+
+## 👀 ETC
+- 참고자료 등 기타 내용 작성
diff --git "a/.github/ISSUE_TEMPLATE/\360\237\232\200--chore--\352\270\260\355\203\200-\353\263\200\352\262\275\354\202\254\355\225\255.md" "b/.github/ISSUE_TEMPLATE/\360\237\232\200--chore--\352\270\260\355\203\200-\353\263\200\352\262\275\354\202\254\355\225\255.md"
new file mode 100644
index 0000000..cdf8e54
--- /dev/null
+++ "b/.github/ISSUE_TEMPLATE/\360\237\232\200--chore--\352\270\260\355\203\200-\353\263\200\352\262\275\354\202\254\355\225\255.md"
@@ -0,0 +1,17 @@
+---
+name: "\U0001F680 [chore] 기타 변경사항"
+about: 기타 변경사항을 위한 템플릿
+title: "\U0001F680 [chore] "
+labels: "\U0001F680 chore"
+assignees: ''
+
+---
+
+## 📝 개요
+- 자세한 개요 작성
+
+## ✔️ To-Do
+- [ ] 투두 내용 작성
+
+## 👀 ETC
+- 참고자료 등 기타 내용 작성
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..387fcf5
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,26 @@
+## 📍 PR 타입 (하나 이상 선택)
+- [ ] 기능 추가
+- [ ] 버그 수정
+- [ ] 의존성, 환경 변수, 빌드 관련 코드 업데이트
+- [ ] 기타 사소한 수정
+
+## ❗️ 관련 이슈 링크
+Close #
+
+## 📌 개요
+-
+
+## 🔁 변경 사항
+
+## 📸 스크린샷 (선택)
+
+## 👀 기타 더 이야기해볼 점 (선택)
+
+## 💬 리뷰 요구사항 (선택)
+
+## ✅ 체크 리스트
+- [ ] PR 템플릿에 맞추어 작성했어요.
+- [ ] 변경 내용에 대한 테스트를 진행했어요.
+- [ ] 프로그램이 정상적으로 동작해요.
+- [ ] PR에 적절한 라벨을 선택했어요.
+- [ ] 불필요한 코드는 삭제했어요.
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..620511d
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,35 @@
+name: Build and Deploy on Self-Hosted Runner
+
+on:
+ push:
+ branches:
+ - develop
+
+jobs:
+ deploy:
+ runs-on: self-hosted
+
+ steps:
+ - name: 코드 가져오기 (pull)
+ run: |
+ cd /home/tokkit/Tokkit-Client
+ git pull origin develop
+
+ - name: Checkout Repository
+ uses: actions/checkout@v3
+
+ - name: Docker Compose Down
+ run: |
+ cd /home/tokkit
+ docker compose down
+
+ - name: Docker Compose Build
+ run: |
+ cd /home/tokkit
+ docker compose build
+
+ - name: Docker Compose Up
+ run: |
+ cd /home/tokkit
+ docker compose up -d
+
diff --git a/.gitignore b/.gitignore
index 5ef6a52..1a8392b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,7 +35,7 @@ yarn-error.log*
# vercel
.vercel
-
+.idea
# typescript
*.tsbuildinfo
next-env.d.ts
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..cd28328
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,13 @@
+
+FROM node:20 AS builder
+WORKDIR /app
+COPY . .
+RUN npm install
+RUN npm run build
+
+FROM node:20
+WORKDIR /app
+COPY --from=builder /app .
+EXPOSE 3000
+CMD ["npm", "run", "start"]
+
diff --git a/README.md b/README.md
index e215bc4..d0f35eb 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,75 @@
-This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+
+
+
+
“토큰이 있으면, 토킷이 있다.”
-## Getting Started
+# 🐰 Tokkit-Client
-First, run the development server:
+_“토큰이 있으면, 토킷이 있다.”_
+**Tokkit**은 우리은행 예금 토큰 기반의 전자지갑 서비스입니다.
+이 저장소는 **프론트엔드(Next.js)** 클라이언트 코드로, 사용자 인터페이스와 이벤트형 UX 구현을 담당합니다.
+
+---
+
+
+## ✨ 주요 기능 (프론트 주석 기준)
+
+- `/wallet` : 예금 토큰 잔액 확인 및 입출금 기능
+- `/store` : 바우처 카테고리별 탐색, 구매, 사용
+- `/mission` : 미션 달성 시 보상 시스템
+- `/admin` : 관리자용 바우처 및 사용자 관리
+- `/event` : 출석, 룰렛, 친구 초대 등 인터랙티브 이벤트
+
+---
-```bash
-npm run dev
-# or
-yarn dev
-# or
-pnpm dev
-# or
-bun dev
-```
-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
-You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+## 🌿 브랜치 규칙
-This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+- `main` : 배포 브랜치
+- `dev` : 개발 통합 브랜치
+- `feat/#{ISSUE_NUMBER}-작업 내용 (한글)` : 기능 개발 브랜치
+- `fix/#{ISSUE_NUMBER}-작업 내용 (한글)` : 버그 수정 브랜치
+- `hotfix/#{ISSUE_NUMBER}-작업 내용 (한글)` : 긴급 핫픽스
+
+---
+
+## 🧾 커밋 메시지 규칙
+
+```bash
+태그: 작업 내용 (한글)
+
+예:
+feat: 로그인 화면 UI 구현
+fix: 바우처 미표시 버그 수정
+```
-## Learn More
+---
-To learn more about Next.js, take a look at the following resources:
+### ✅ 주요 태그
-- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
-- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+| 태그 | 의미 |
+|------|------|
+| `Feat` | 기능 추가 |
+| `Fix` | 버그 수정 |
+| `Style` | 스타일, 포맷팅 |
+| `Refactor` | 코드 리팩토링 |
+| `Chore` | 설정, 의존성 작업 |
+| `Docs` | 문서 작업 |
+| `Test` | 테스트 코드 추가 |
-You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+---
-## Deploy on Vercel
+### 🏷️ 라벨 체계
-The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+| 라벨 | 설명 |
+|------|--------|
+| `FEAT` | 기능 추가 관련 PR/이슈 |
+| `FIX` | 버그 수정 관련 |
+| `STYLE` | UI/스타일/레이아웃 관련 |
+| `REFACTOR` | 리팩토링 관련 |
+| `CHORE` | 기타 설정 및 패키지 |
+| `TEST` | 테스트 코드 작업 |
+| `DOCS` | 문서/주석 관련 |
-Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+---
diff --git a/app/MainLayout.tsx b/app/MainLayout.tsx
new file mode 100644
index 0000000..c6ba113
--- /dev/null
+++ b/app/MainLayout.tsx
@@ -0,0 +1,14 @@
+"use client";
+
+import type React from "react"
+import { usePathname } from "next/navigation"
+
+// 클라이언트 컴포넌트
+export function MainLayout({ children }: { children: React.ReactNode }) {
+ "use client"
+
+ const pathname = usePathname()
+ const isAdmin = pathname.startsWith("/admin")
+
+ return
{children}
+}
\ No newline at end of file
diff --git a/app/dashboard/api/fetch-notice-preview.ts b/app/dashboard/api/fetch-notice-preview.ts
new file mode 100644
index 0000000..d10fc9c
--- /dev/null
+++ b/app/dashboard/api/fetch-notice-preview.ts
@@ -0,0 +1,41 @@
+import { getApiUrl } from "@/lib/getApiUrl";
+import { getCookie } from "@/lib/cookies";
+import {fetchWithAuth} from "@/lib/fetchWithAuth";
+
+const API_URL = getApiUrl();
+
+export interface NoticePreview {
+ id: number;
+ title: string;
+ content: string;
+ createdAt: string;
+ isNew: boolean;
+}
+
+export async function fetchNoticePreview(limit: number = 3): Promise {
+ const res = await fetchWithAuth(`${API_URL}/api/users/notice?page=0`, {
+ method: "GET",
+ credentials: "include",
+ });
+
+ if (!res.ok) throw new Error("공지사항을 불러오는 데 실패했습니다.");
+
+ const data = await res.json();
+ const raw = data.result.content || [];
+
+ const now = new Date();
+ const formattedNotices = raw.map((n: any) => {
+ const createdAt = new Date(n.createdAt);
+ const diffInDays = (now.getTime() - createdAt.getTime()) / (1000 * 60 * 60 * 24);
+
+ return {
+ id: n.id,
+ title: n.title,
+ content: n.content,
+ createdAt: n.createdAt,
+ isNew: diffInDays <= 3,
+ };
+ });
+
+ return formattedNotices.slice(0, limit);
+}
diff --git a/app/dashboard/api/fetch-recent-transactions.ts b/app/dashboard/api/fetch-recent-transactions.ts
new file mode 100644
index 0000000..cc6dcf9
--- /dev/null
+++ b/app/dashboard/api/fetch-recent-transactions.ts
@@ -0,0 +1,31 @@
+import { getApiUrl } from "@/lib/getApiUrl";
+import {fetchWithAuth} from "@/lib/fetchWithAuth";
+
+const API_URL = getApiUrl();
+
+export interface Transaction {
+ id: number;
+ type: string;
+ amount: number;
+ createdAt: string;
+ displayDescription?: string;
+}
+
+export async function fetchRecentTransactions(limit: number = 3): Promise {
+ const res = await fetchWithAuth(`${API_URL}/api/users/wallet/transactions`, {
+ credentials: "include",
+ });
+
+ if (!res.ok) {
+ throw new Error("거래내역 요청 실패");
+ }
+
+ const data = await res.json();
+ console.log(data);
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || "응답 실패");
+ }
+
+ return data.result.slice(0, limit);
+}
diff --git a/app/dashboard/api/wallet-info.ts b/app/dashboard/api/wallet-info.ts
new file mode 100644
index 0000000..0329602
--- /dev/null
+++ b/app/dashboard/api/wallet-info.ts
@@ -0,0 +1,21 @@
+import { getApiUrl } from "@/lib/getApiUrl";
+import { fetchWithAuth } from "@/lib/fetchWithAuth";
+import {getCookie} from "@/lib/cookies";
+
+const API_URL = getApiUrl();
+
+export async function fetchWalletInfo() {
+ const res = await fetchWithAuth(`${API_URL}/api/users/wallet/balance`, {
+ method: "GET",
+ credentials: "include",
+ });
+
+ if (!res.ok) throw new Error("지갑 정보를 불러오지 못했습니다.");
+
+ const data = await res.json();
+
+ const parsedResult =
+ typeof data.result === "string" ? JSON.parse(data.result) : data.result;
+
+ return parsedResult;
+}
diff --git a/app/dashboard/components/DashboardHeader.tsx b/app/dashboard/components/DashboardHeader.tsx
new file mode 100644
index 0000000..de1d73f
--- /dev/null
+++ b/app/dashboard/components/DashboardHeader.tsx
@@ -0,0 +1,40 @@
+"use client"
+
+import { Button } from "@/components/ui/button"
+import { Bell, User } from "lucide-react"
+import Image from "next/image"
+import { useRouter } from "next/navigation"
+
+export default function HeaderSection() {
+ const router = useRouter()
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/app/dashboard/components/NoticeSection.tsx b/app/dashboard/components/NoticeSection.tsx
new file mode 100644
index 0000000..6e6e721
--- /dev/null
+++ b/app/dashboard/components/NoticeSection.tsx
@@ -0,0 +1,102 @@
+"use client";
+
+import { motion, AnimatePresence } from "framer-motion";
+import { ChevronRight } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { useRouter } from "next/navigation";
+
+interface Notice {
+ id: number;
+ title: string;
+ content: string;
+ createdAt: string;
+ isNew: boolean;
+}
+
+interface NoticesSectionProps {
+ notices: Notice[];
+ currentNotice: number;
+ onNoticeChange: (index: number) => void;
+}
+
+export default function NoticesSection({
+ notices,
+ currentNotice,
+ onNoticeChange,
+}: NoticesSectionProps) {
+ const router = useRouter();
+ const now = new Date();
+
+ return (
+
+
+
+
+ )}
+
+ )
+}
diff --git a/app/merchant/signup/data/merchant-terms.ts b/app/merchant/signup/data/merchant-terms.ts
new file mode 100644
index 0000000..c712f71
--- /dev/null
+++ b/app/merchant/signup/data/merchant-terms.ts
@@ -0,0 +1,188 @@
+export interface Term {
+ id: string
+ title: string
+ required: boolean
+ content?: string
+}
+
+export const merchantTerms: Term[] = [
+ {
+ id: "merchant-service",
+ title: "가맹점 서비스 이용약관",
+ required: true,
+ content: `제1조 (목적)
+이 약관은 Tokkit(이하 "회사"라 함)이 제공하는 가맹점 서비스의 이용조건 및 절차, 회사와 가맹점 간의 권리, 의무, 책임사항 등을 규정함을 목적으로 합니다.
+
+제2조 (용어의 정의)
+1. "가맹점 서비스"란 회사가 제공하는 결제 서비스, 매출 관리 서비스 등을 의미합니다.
+2. "가맹점"이란 회사와 서비스 이용계약을 체결하고 회사가 제공하는 가맹점 서비스를 이용하는 사업자를 의미합니다.
+
+제3조 (약관의 효력 및 변경)
+1. 이 약관은 가맹점 서비스를 이용하고자 하는 모든 가맹점에게 적용됩니다.
+2. 회사는 필요한 경우 약관을 변경할 수 있으며, 변경된 약관은 서비스 내 공지사항에 게시하거나 기타 방법으로 가맹점에게 공지함으로써 효력이 발생합니다.
+
+제4조 (서비스의 내용)
+1. 회사가 제공하는 가맹점 서비스는 다음 각 호와 같습니다.
+ 가. 결제 서비스: 고객이 가맹점에서 상품이나 서비스를 구매할 때 결제를 처리하는 서비스
+ 나. 매출 관리 서비스: 가맹점의 매출 내역을 관리하고 분석하는 서비스
+ 다. 정산 서비스: 가맹점에 결제 금액을 정산하는 서비스
+ 라. 기타 회사가 정하는 서비스
+2. 회사는 서비스의 내용을 변경하거나 추가할 수 있으며, 이 경우 회사는 변경 또는 추가 내용을 서비스 내에 공지합니다.
+
+제5조 (서비스 이용 신청)
+1. 가맹점 서비스를 이용하고자 하는 사업자는 회사가 정한 양식에 따라 필요한 정보를 제공하고 이 약관에 동의함으로써 서비스 이용을 신청할 수 있습니다.
+2. 회사는 다음 각 호에 해당하는 경우 서비스 이용 신청을 승낙하지 않을 수 있습니다.
+ 가. 실명이 아니거나 타인의 명의를 이용한 경우
+ 나. 허위 정보를 기재하거나 회사가 요구하는 정보를 제공하지 않은 경우
+ 다. 관련 법령에 위배되거나 공서양속에 반하는 목적으로 신청한 경우
+ 라. 기타 회사가 정한 이용신청 요건이 충족되지 않은 경우
+
+제6조 (수수료)
+1. 회사는 가맹점에게 결제 금액의 일정 비율을 수수료로 부과할 수 있습니다.
+2. 수수료율은 가맹점의 업종, 매출 규모 등에 따라 차등 적용될 수 있으며, 구체적인 수수료율은 별도 계약에 따릅니다.
+3. 회사는 필요한 경우 수수료율을 변경할 수 있으며, 변경 시 적용일자 및 변경사유를 명시하여 가맹점에게 사전 통지합니다.
+
+제7조 (정산)
+1. 회사는 가맹점에게 결제 금액에서 수수료를 제외한 금액을 정산합니다.
+2. 정산 주기는 일별, 주별, 월별 중 가맹점이 선택할 수 있으며, 구체적인 정산 일정은 별도 계약에 따릅니다.
+3. 회사는 다음 각 호에 해당하는 경우 정산을 보류하거나 거절할 수 있습니다.
+ 가. 고객이 결제를 취소한 경우
+ 나. 고객이 결제에 대해 이의를 제기한 경우
+ 다. 가맹점이 이 약관을 위반한 경우
+ 라. 기타 회사가 정산을 보류하거나 거절할 필요가 있다고 판단한 경우`,
+ },
+ {
+ id: "merchant-privacy",
+ title: "개인정보 수집 및 이용 동의",
+ required: true,
+ content: `개인정보 수집 및 이용 동의
+
+Tokkit(이하 "회사"라 합니다)은 가맹점 서비스 제공을 위해 다음과 같이 개인정보를 수집 및 이용합니다. 내용을 자세히 읽으신 후 동의 여부를 결정하여 주시기 바랍니다.
+
+1. 수집하는 개인정보 항목
+- 필수항목: 사업자등록번호, 대표자명, 상호명, 사업장 주소, 연락처, 계좌정보
+- 선택항목: 이메일, 홈페이지 주소, 영업시간, 휴무일
+
+2. 개인정보 수집 및 이용 목적
+- 서비스 제공 및 계약 이행: 가맹점 서비스 제공, 본인 확인, 정산, 세금계산서 발행
+- 서비스 개선: 신규 서비스 개발, 기존 서비스 개선
+- 고객 관리: 고객 문의 응대, 공지사항 전달
+- 법령 준수: 관련 법령에 따른 의무 이행
+
+3. 개인정보의 보유 및 이용 기간
+- 회원 탈퇴 시까지 또는 법정 의무 보유기간까지
+- 전자금융거래법에 따라 전자금융거래에 관한 기록은 5년간 보관
+- 통신비밀보호법에 따라 접속 로그는 3개월간 보관
+
+4. 동의 거부권 및 거부 시 불이익
+- 필수항목에 대한 동의를 거부할 경우 서비스 이용이 제한됩니다.
+- 선택항목에 대한 동의를 거부하더라도 서비스 이용에 제한은 없으나, 일부 서비스 이용이 제한될 수 있습니다.
+
+5. 개인정보의 제3자 제공
+- 회사는 이용자의 개인정보를 원칙적으로 외부에 제공하지 않습니다.
+- 다만, 아래의 경우에는 예외로 합니다.
+ 1) 이용자가 사전에 동의한 경우
+ 2) 법령의 규정에 의거하거나, 수사 목적으로 법령에 정해진 절차와 방법에 따라 요청한 경우
+
+6. 개인정보의 안전성 확보 조치
+- 회사는 개인정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다.
+ 1) 관리적 조치: 내부관리계획 수립 및 시행, 정기적 직원 교육
+ 2) 기술적 조치: 개인정보처리시스템 접근 제한, 암호화 기술 적용, 접속 기록 보관
+ 3) 물리적 조치: 전산실, 자료보관실 등의 접근 통제
+
+7. 이용자의 권리와 행사 방법
+- 이용자는 언제든지 개인정보 열람, 정정, 삭제, 처리정지 요구 등의 권리를 행사할 수 있습니다.
+- 권리 행사는 회사에 대해 서면, 전화, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 회사는 이에 대해 지체 없이 조치하겠습니다.
+
+8. 개인정보 보호책임자 및 연락처
+- 개인정보 보호책임자: 홍길동
+- 연락처: privacy@tokkit.com, 02-123-4567`,
+ },
+ {
+ id: "merchant-financial",
+ title: "전자금융거래 이용약관 동의",
+ required: true,
+ content: `전자금융거래 이용약관
+
+제1조 (목적)
+이 약관은 Tokkit(이하 "회사"라 함)이 제공하는 전자금융거래 서비스의 이용조건 및 절차, 회사와 가맹점 간의 권리, 의무, 책임사항 등을 규정함을 목적으로 합니다.
+
+제2조 (용어의 정의)
+1. "전자금융거래"란 회사가 전자적 장치를 통하여 금융상품 및 서비스를 제공하고, 가맹점이 회사의 종사자와 직접 대면하거나 의사소통을 하지 아니하고 자동화된 방식으로 이를 이용하는 거래를 말합니다.
+2. "전자지급수단"이란 전자자금이체, 직불전자지급수단, 선불전자지급수단, 전자화폐, 신용카드, 전자채권 등 전자적 방법에 따른 지급수단을 말합니다.
+3. "전자적 장치"란 전자금융거래정보를 전자적 방법으로 전송하거나 처리하는데 이용되는 장치로서 현금자동지급기, 자동입출금기, 지급용단말기, 컴퓨터, 전화기 등을 말합니다.
+
+제3조 (약관의 효력 및 변경)
+1. 이 약관은 가맹점이 이에 동의함으로써 효력이 발생합니다.
+2. 회사는 필요한 경우 약관을 변경할 수 있으며, 변경된 약관은 서비스 내 공지사항에 게시하거나 기타 방법으로 가맹점에게 공지함으로써 효력이 발생합니다.
+3. 가맹점이 변경된 약관에 동의하지 않는 경우 서비스 이용을 중단하고 이용계약을 해지할 수 있습니다.
+
+제4조 (전자금융거래의 방법 및 절차)
+1. 가맹점은 회사가 정한 방법에 따라 전자금융거래를 이용할 수 있습니다.
+2. 회사는 가맹점의 전자금융거래 신청이 있는 경우 가맹점에게 약관에 정한 방법으로 승낙함으로써 전자금융거래가 성립합니다.
+3. 회사는 가맹점의 전자금융거래 신청에 대하여 가맹점이 약관에 정한 사항을 위반하는 경우 또는 기타 정당한 사유가 있는 경우에는 이를 승낙하지 않을 수 있습니다.
+
+제5조 (수수료)
+1. 회사는 가맹점에게 결제 금액의 일정 비율을 수수료로 부과할 수 있습니다.
+2. 수수료율은 가맹점의 업종, 매출 규모 등에 따라 차등 적용될 수 있으며, 구체적인 수수료율은 별도 계약에 따릅니다.
+3. 회사는 필요한 경우 수수료율을 변경할 수 있으며, 변경 시 적용일자 및 변경사유를 명시하여 가맹점에게 사전 통지합니다.
+
+제6조 (정산)
+1. 회사는 가맹점에게 결제 금액에서 수수료를 제외한 금액을 정산합니다.
+2. 정산 주기는 일별, 주별, 월별 중 가맹점이 선택할 수 있으며, 구체적인 정산 일정은 별도 계약에 따릅니다.
+3. 회사는 다음 각 호에 해당하는 경우 정산을 보류하거나 거절할 수 있습니다.
+ 가. 고객이 결제를 취소한 경우
+ 나. 고객이 결제에 대해 이의를 제기한 경우
+ 다. 가맹점이 이 약관을 위반한 경우
+ 라. 기타 회사가 정산을 보류하거나 거절할 필요가 있다고 판단한 경우
+
+제7조 (거래지시의 철회)
+1. 가맹점이 전자금융거래를 위한 거래지시를 한 경우 이를 철회할 수 없습니다.
+2. 제1항에도 불구하고 회사와 가맹점 간 별도 약정이 있는 경우에는 그 약정에 따릅니다.
+
+제8조 (전자금융거래의 기록 보존 및 제공)
+1. 회사는 전자금융거래의 내용을 추적, 검색하거나 그 내용에 오류가 발생한 경우에 이를 확인하거나 정정할 수 있는 기록을 생성하여 보존합니다.
+2. 제1항의 규정에 따라 회사가 보존하여야 하는 기록의 종류, 보존방법 및 보존기간은 전자금융거래법 제22조 및 같은 법 시행령 제12조에 따릅니다.
+
+제9조 (전자금융거래의 안전성 확보)
+1. 회사는 전자금융거래의 안전성과 신뢰성을 확보하기 위하여 전자금융거래의 종류별로 전자적 전송이나 처리를 위한 인력, 시설, 전자적 장치 등의 정보기술부문 및 전자금융업무에 관하여 금융위원회가 정하는 기준을 준수합니다.
+2. 회사는 가맹점의 전자금융거래 신청 및 이용 과정에서 가맹점이 제공한 정보를 보호하기 위하여 필요한 조치를 취합니다.`,
+ },
+ {
+ id: "merchant-marketing",
+ title: "마케팅 정보 수신 동의",
+ required: false,
+ content: `마케팅 정보 수신 동의
+
+Tokkit(이하 "회사"라 합니다)은 가맹점에게 유용한 서비스 및 이벤트 정보를 제공하기 위해 다음과 같이 마케팅 정보를 수신하는 것에 대한 동의를 받고 있습니다. 내용을 자세히 읽으신 후 동의 여부를 결정하여 주시기 바랍니다.
+
+1. 마케팅 정보 수신 동의 내용
+- 회사가 제공하는 서비스, 이벤트, 프로모션 등의 광고성 정보
+
+2. 마케팅 정보 수신 방법
+- 이메일, SMS, 푸시 알림, 우편 등
+
+3. 마케팅 정보 수신 목적
+- 신규 서비스 및 이벤트 안내
+- 맞춤형 혜택 및 프로모션 제공
+- 서비스 개선을 위한 의견 수렴
+
+4. 마케팅 정보 수신 동의의 효력 기간
+- 회원 탈퇴 시 또는 마케팅 정보 수신 동의 철회 시까지
+
+5. 동의 거부권 및 거부 시 불이익
+- 본 마케팅 정보 수신 동의는 선택사항으로, 동의를 거부하더라도 기본적인 서비스 이용에는 제한이 없습니다.
+- 다만, 동의하지 않을 경우 회사가 제공하는 각종 혜택 및 이벤트 정보를 받아보실 수 없습니다.
+
+6. 마케팅 정보 수신 동의 철회 방법
+- 가맹점은 언제든지 마케팅 정보 수신 동의를 철회할 수 있습니다.
+- 철회 방법: 서비스 내 '설정 > 알림 설정'에서 변경 또는 고객센터(1588-1234)로 요청
+
+7. 개인정보의 제3자 제공 및 위탁
+- 회사는 마케팅 활동을 위해 필요한 경우 이용자의 개인정보를 외부 전문업체에 위탁할 수 있습니다.
+- 위탁 업체 및 위탁 업무 내용은 회사 홈페이지의 '개인정보처리방침'에서 확인하실 수 있습니다.
+
+8. 마케팅 정보 관련 문의
+- 마케팅 정보 수신과 관련하여 문의사항이 있으신 경우, 고객센터(1588-1234) 또는 이메일(marketing@tokkit.com)로 문의해 주시기 바랍니다.`,
+ },
+]
\ No newline at end of file
diff --git a/app/merchant/signup/page.tsx b/app/merchant/signup/page.tsx
new file mode 100644
index 0000000..b5eeeeb
--- /dev/null
+++ b/app/merchant/signup/page.tsx
@@ -0,0 +1,158 @@
+ "use client"
+
+ import { useRouter } from "next/navigation"
+ import { motion } from "framer-motion"
+ import { useState, useEffect, useRef } from "react"
+ import { merchantTerms } from "./data/merchant-terms"
+ import MerchantTermsHeader from "./components/MerchantTermsHeader"
+ import TermsAlert from "./components/TermsAlert"
+ import TermsAgreementCard from "./components/TermsAgreementCard"
+ import TermsModal from "./components/TermsModal"
+
+ export interface Term {
+ id: string
+ title: string
+ required: boolean
+ content?: string
+ }
+
+ export default function MerchantSignupPage() {
+ const router = useRouter()
+ const [terms] = useState(merchantTerms)
+ const [agreedTerms, setAgreedTerms] = useState([])
+ const [allAgreed, setAllAgreed] = useState(false)
+ const [showAlert, setShowAlert] = useState(false)
+ const [currentTermIndex, setCurrentTermIndex] = useState(null)
+ const [viewedTerms, setViewedTerms] = useState([])
+ const [isAllTermsFlow, setIsAllTermsFlow] = useState(false)
+ const termRefs = useRef>({})
+ const initialRenderRef = useRef(true)
+
+ useEffect(() => {
+ if (initialRenderRef.current) {
+ initialRenderRef.current = false
+ return
+ }
+ const isAllAgreed = terms.every((term) => agreedTerms.includes(term.id))
+ setAllAgreed(isAllAgreed)
+ }, [agreedTerms, terms])
+
+ const handleToggleTerm = (termId: string) => {
+ if (!viewedTerms.includes(termId)) {
+ const termIndex = terms.findIndex((term) => term.id === termId)
+ if (termIndex !== -1) {
+ setCurrentTermIndex(termIndex)
+ setIsAllTermsFlow(false)
+ }
+ return
+ }
+ setAgreedTerms((prev) =>
+ prev.includes(termId) ? prev.filter((id) => id !== termId) : [...prev, termId]
+ )
+ }
+
+ const handleToggleAll = () => {
+ if (allAgreed) {
+ setAgreedTerms([])
+ } else {
+ const allViewed = terms.every((term) => viewedTerms.includes(term.id))
+ if (allViewed) {
+ setAgreedTerms(terms.map((term) => term.id))
+ } else {
+ setIsAllTermsFlow(true)
+ const firstNotViewed = terms.findIndex((term) => !viewedTerms.includes(term.id))
+ setCurrentTermIndex(firstNotViewed !== -1 ? firstNotViewed : 0)
+ }
+ }
+ }
+
+ const handleViewTerm = (termId: string) => {
+ const termIndex = terms.findIndex((term) => term.id === termId)
+ if (termIndex !== -1) {
+ setCurrentTermIndex(termIndex)
+ setIsAllTermsFlow(false)
+ }
+ }
+
+ const handleAgreeCurrentTerm = () => {
+ if (currentTermIndex !== null) {
+ const currentTerm = terms[currentTermIndex]
+ if (!viewedTerms.includes(currentTerm.id)) setViewedTerms((prev) => [...prev, currentTerm.id])
+ if (!agreedTerms.includes(currentTerm.id)) setAgreedTerms((prev) => [...prev, currentTerm.id])
+ if (isAllTermsFlow && currentTermIndex < terms.length - 1) {
+ setCurrentTermIndex(currentTermIndex + 1)
+ } else {
+ setCurrentTermIndex(null)
+ setIsAllTermsFlow(false)
+ if (isAllTermsFlow) setAgreedTerms(terms.map((term) => term.id))
+ }
+ }
+ }
+
+ const handleCloseTermModal = () => {
+ if (currentTermIndex !== null) {
+ const currentTerm = terms[currentTermIndex]
+ if (!viewedTerms.includes(currentTerm.id)) setViewedTerms((prev) => [...prev, currentTerm.id])
+ }
+ setCurrentTermIndex(null)
+ setIsAllTermsFlow(false)
+ }
+
+ const handleSubmit = () => {
+ const requiredTerms = terms.filter((term) => term.required).map((term) => term.id)
+ const allRequiredAgreed = requiredTerms.every((termId) => agreedTerms.includes(termId))
+ if (allRequiredAgreed) {
+ router.push("/merchant/signup/business")
+ } else {
+ setShowAlert(true)
+ setTimeout(() => setShowAlert(false), 3000)
+ }
+ }
+
+ return (
+
+ {/* 상단 고정 헤더 */}
+
+
+ {/* 헤더 제외 전체 중앙 정렬 영역 */}
+
+
+
+ ))}
+
+ );
+};
+
+export default TermsCardList;
\ No newline at end of file
diff --git a/app/merchant/signup/wallet/terms/data/walletTerms.ts b/app/merchant/signup/wallet/terms/data/walletTerms.ts
new file mode 100644
index 0000000..38f14f8
--- /dev/null
+++ b/app/merchant/signup/wallet/terms/data/walletTerms.ts
@@ -0,0 +1,196 @@
+export interface Term {
+ id: string
+ title: string
+ required: boolean
+ content: string
+}
+
+export const walletTerms: Term[] = [
+ {
+ id: "wallet-service",
+ title: "전자지갑 서비스 이용약관",
+ required: true,
+ content: `제1조 (목적)
+이 약관은 Tokkit(이하 "회사"라 합니다)이 제공하는 전자지갑 서비스(이하 "서비스"라 합니다)의 이용과 관련하여 회사와 이용자 간의 권리, 의무 및 책임사항, 기타 필요한 사항을 규정함을 목적으로 합니다.
+
+제2조 (용어의 정의)
+① "전자지갑"이란 이용자가 중앙은행 디지털 화폐(CBDC)를 안전하게 보관하고 이용할 수 있도록 회사가 제공하는 전자적 수단을 말합니다.
+② "이용자"란 이 약관에 따라 회사가 제공하는 서비스를 이용하는 자를 말합니다.
+③ "CBDC"란 중앙은행이 발행하는 디지털 형태의 법정 화폐를 말합니다.
+
+제3조 (약관의 효력 및 변경)
+① 이 약관은 서비스를 이용하고자 하는 모든 이용자에게 적용됩니다.
+② 회사는 관련 법령을 위배하지 않는 범위에서 이 약관을 개정할 수 있습니다.
+③ 회사가 약관을 변경할 경우에는 적용일자 및 변경사유를 명시하여 서비스 내에 공지합니다.
+
+제4조 (서비스의 내용)
+① 회사가 제공하는 서비스는 다음 각 호와 같습니다.
+ 1. CBDC 보관 및 관리 서비스
+ 2. CBDC 송금 및 결제 서비스
+ 3. 기타 회사가 정하는 서비스
+② 회사는 서비스의 내용을 변경하거나 추가할 수 있으며, 이 경우 회사는 변경 또는 추가 내용을 서비스 내에 공지합니다.
+
+제5조 (서비스 이용 제한)
+① 회사는 다음 각 호에 해당하는 경우 서비스 이용을 제한할 수 있습니다.
+ 1. 이용자가 이 약관을 위반한 경우
+ 2. 이용자가 법령을 위반한 경우
+ 3. 이용자가 타인의 명의나 정보를 도용한 경우
+ 4. 기타 회사가 정한 사유에 해당하는 경우
+② 서비스 이용이 제한된 경우, 회사는 이용자에게 그 사유 및 기간을 통지합니다.
+
+제6조 (이용자의 의무)
+① 이용자는 서비스 이용과 관련하여 다음 각 호의 행위를 하여서는 안 됩니다.
+ 1. 타인의 정보를 도용하거나 허위 정보를 제공하는 행위
+ 2. 회사의 서비스를 이용하여 불법적인 목적을 달성하고자 하는 행위
+ 3. 회사의 서비스를 이용하여 타인에게 피해를 주는 행위
+ 4. 기타 관련 법령에 위배되는 행위
+
+제7조 (회사의 의무)
+① 회사는 이용자가 안전하게 서비스를 이용할 수 있도록 개인정보 보호를 위한 보안 시스템을 갖추어야 합니다.
+② 회사는 이용자로부터 제기되는 의견이나 불만이 정당하다고 인정할 경우에는 신속히 처리하여야 합니다.
+
+제8조 (손해배상)
+① 회사는 서비스 제공과 관련하여 회사의 고의 또는 과실로 인해 이용자에게 손해가 발생한 경우, 관련 법령에 따라 손해를 배상합니다.
+② 이용자가 이 약관을 위반하여 회사에 손해를 입힌 경우, 이용자는 회사에 대하여 그 손해를 배상하여야 합니다.
+
+제9조 (분쟁해결)
+① 서비스 이용과 관련하여 회사와 이용자 간에 분쟁이 발생한 경우, 양 당사자는 분쟁의 해결을 위해 성실히 협의합니다.
+② 제1항의 협의에서 분쟁이 해결되지 않을 경우, 관련 법령에 따라 처리합니다.
+
+제10조 (약관의 해석)
+이 약관에 명시되지 않은 사항은 관련 법령 및 상관례에 따릅니다.`,
+ },
+ {
+ id: "personal-info",
+ title: "개인정보 수집 및 이용 동의",
+ required: true,
+ content: `개인정보 수집 및 이용 동의
+
+Tokkit(이하 "회사"라 합니다)은 전자지갑 서비스 제공을 위해 다음과 같이 개인정보를 수집 및 이용합니다. 내용을 자세히 읽으신 후 동의 여부를 결정하여 주시기 바랍니다.
+
+1. 수집하는 개인정보 항목
+- 필수항목: 성명, 생년월일, 성별, 휴대전화번호, 이메일 주소, 계좌정보, 주민등록번호(실명확인용)
+- 선택항목: 주소, 직업, 소득수준
+
+2. 개인정보 수집 및 이용 목적
+- 서비스 제공 및 계약 이행: 전자지갑 서비스 제공, 본인 확인, 거래 내역 관리
+- 서비스 개선: 신규 서비스 개발, 기존 서비스 개선
+- 고객 관리: 고객 문의 응대, 공지사항 전달
+- 법령 준수: 관련 법령에 따른 의무 이행
+
+3. 개인정보의 보유 및 이용 기간
+- 회원 탈퇴 시까지 또는 법정 의무 보유기간까지
+- 전자금융거래법에 따라 전자금융거래에 관한 기록은 5년간 보관
+- 통신비밀보호법에 따라 접속 로그는 3개월간 보관
+
+4. 동의 거부권 및 거부 시 불이익
+- 필수항목에 대한 동의를 거부할 경우 서비스 이용이 제한됩니다.
+- 선택항목에 대한 동의를 거부하더라도 서비스 이용에 제한은 없으나, 일부 서비스 이용이 제한될 수 있습니다.
+
+5. 개인정보의 제3자 제공
+- 회사는 이용자의 개인정보를 원칙적으로 외부에 제공하지 않습니다.
+- 다만, 아래의 경우에는 예외로 합니다.
+ 1) 이용자가 사전에 동의한 경우
+ 2) 법령의 규정에 의거하거나, 수사 목적으로 법령에 정해진 절차와 방법에 따라 요청한 경우
+
+6. 개인정보의 안전성 확보 조치
+- 회사는 개인정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다.
+ 1) 관리적 조치: 내부관리계획 수립 및 시행, 정기적 직원 교육
+ 2) 기술적 조치: 개인정보처리시스템 접근 제한, 암호화 기술 적용, 접속 기록 보관
+ 3) 물리적 조치: 전산실, 자료보관실 등의 접근 통제
+
+7. 이용자의 권리와 행사 방법
+- 이용자는 언제든지 개인정보 열람, 정정, 삭제, 처리정지 요구 등의 권리를 행사할 수 있습니다.
+- 권리 행사는 회사에 대해 서면, 전화, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 회사는 이에 대해 지체 없이 조치하겠습니다.
+
+8. 개인정보 보호책임자 및 연락처
+- 개인정보 보호책임자: 홍길동
+- 연락처: teamtokkit@gmail.com`,
+ },
+ {
+ id: "financial-info",
+ title: "금융정보 제공 동의",
+ required: true,
+ content: `금융정보 제공 동의서
+
+Tokkit(이하 "회사"라 합니다)은 전자지갑 서비스 제공을 위해 다음과 같이 금융정보를 수집 및 이용합니다. 내용을 자세히 읽으신 후 동의 여부를 결정하여 주시기 바랍니다.
+
+1. 금융정보 제공 동의 대상 기관
+- 금융결제원, 은행연합회, 신용정보집중기관, 신용정보회사, 금융회사(은행, 카드사 등)
+
+2. 금융정보 수집 항목
+- 계좌정보: 계좌번호, 은행명, 예금주명
+- 거래정보: 거래내역, 거래금액, 거래일시
+- 신용정보: 신용등급, 연체정보, 대출정보
+
+3. 금융정보 수집 및 이용 목적
+- 전자지갑 서비스 제공 및 운영
+- 금융사고 방지 및 조사
+- 법령상 의무 이행
+- 금융거래 내역 관리 및 분석
+
+4. 금융정보의 보유 및 이용 기간
+- 회원 탈퇴 시까지 또는 법정 의무 보유기간까지
+- 전자금융거래법에 따라 전자금융거래에 관한 기록은 5년간 보관
+- 금융실명거래 및 비밀보장에 관한 법률에 따른 거래기록은 5년간 보관
+
+5. 금융정보 제공 동의의 효력 기간
+- 서비스 이용계약 체결일로부터 서비스 해지일 또는 회원 탈퇴일까지
+- 단, 관련 법령에 따라 보존할 필요가 있는 경우 해당 법령에서 정한 기간까지
+
+6. 동의 거부권 및 거부 시 불이익
+- 본 금융정보 제공 동의는 전자지갑 서비스 이용을 위한 필수적 사항으로, 동의를 거부할 경우 서비스 이용이 제한됩니다.
+
+7. 금융정보의 파기 절차 및 방법
+- 회사는 금융정보 수집 및 이용 목적이 달성된 후에는 해당 정보를 지체 없이 파기합니다.
+- 전자적 파일 형태로 저장된 금융정보는 복구 불가능한 방법으로 영구 삭제하며, 종이에 출력된 금융정보는 분쇄기로 분쇄하거나 소각하여 파기합니다.
+
+8. 금융정보의 안전성 확보 조치
+- 회사는 금융정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다.
+ 1) 금융정보 접근 권한 제한 및 접근 통제 시스템 구축
+ 2) 금융정보 송수신 시 암호화 기술 적용
+ 3) 금융정보 처리 시스템에 대한 접속 기록 보관 및 위변조 방지 조치
+ 4) 금융정보 보호를 위한 내부관리계획 수립 및 시행
+
+9. 금융정보 보호책임자 및 연락처
+- 금융정보 보호책임자: 김철수
+- 연락처: teamtokkit@gmail.com`,
+ },
+ {
+ id: "marketing",
+ title: "마케팅 정보 수신 동의",
+ required: false,
+ content: `마케팅 정보 수신 동의
+
+Tokkit(이하 "회사"라 합니다)은 이용자에게 유용한 서비스 및 이벤트 정보를 제공하기 위해 다음과 같이 마케팅 정보를 수신하는 것에 대한 동의를 받고 있습니다. 내용을 자세히 읽으신 후 동의 여부를 결정하여 주시기 바랍니다.
+
+1. 마케팅 정보 수신 동의 내용
+- 회사가 제공하는 서비스, 이벤트, 프로모션 등의 광고성 정보
+
+2. 마케팅 정보 수신 방법
+- 이메일, SMS, 푸시 알림, 우편 등
+
+3. 마케팅 정보 수신 목적
+- 신규 서비스 및 이벤트 안내
+- 맞춤형 혜택 및 프로모션 제공
+- 서비스 개선을 위한 의견 수렴
+
+4. 마케팅 정보 수신 동의의 효력 기간
+- 회원 탈퇴 시 또는 마케팅 정보 수신 동의 철회 시까지
+
+5. 동의 거부권 및 거부 시 불이익
+- 본 마케팅 정보 수신 동의는 선택사항으로, 동의를 거부하더라도 기본적인 서비스 이용에는 제한이 없습니다.
+- 다만, 동의하지 않을 경우 회사가 제공하는 각종 혜택 및 이벤트 정보를 받아보실 수 없습니다.
+
+6. 마케팅 정보 수신 동의 철회 방법
+- 회원은 언제든지 마케팅 정보 수신 동의를 철회할 수 있습니다.
+- 철회 방법: 서비스 내 '설정 > 알림 설정'에서 변경 또는 고객센터(1588-1234)로 요청
+
+7. 개인정보의 제3자 제공 및 위탁
+- 회사는 마케팅 활동을 위해 필요한 경우 이용자의 개인정보를 외부 전문업체에 위탁할 수 있습니다.
+- 위탁 업체 및 위탁 업무 내용은 회사 홈페이지의 '개인정보처리방침'에서 확인하실 수 있습니다.
+
+8. 마케팅 정보 관련 문의
+- 마케팅 정보 수신과 관련하여 문의사항이 있으신 경우, 이메일(teamtokkit@gmail.com)로 문의해 주시기 바랍니다.`
+ }
+]
diff --git a/app/merchant/signup/wallet/terms/page.tsx b/app/merchant/signup/wallet/terms/page.tsx
new file mode 100644
index 0000000..d8bc5a0
--- /dev/null
+++ b/app/merchant/signup/wallet/terms/page.tsx
@@ -0,0 +1,40 @@
+import { Suspense } from "react"
+import { walletTerms } from "@/app/merchant/signup/wallet/terms/data/walletTerms"
+import TermsAgreementPage from "@/app/merchant/signup/wallet/terms/components/TermsAgreementPage"
+
+function TermsAgreementFallback() {
+ return (
+
+
+
+ ))}
+
+ );
+};
+
+export default TermsCardList;
\ No newline at end of file
diff --git a/app/signup/wallet/terms/data/walletTerms.ts b/app/signup/wallet/terms/data/walletTerms.ts
new file mode 100644
index 0000000..dc81ddd
--- /dev/null
+++ b/app/signup/wallet/terms/data/walletTerms.ts
@@ -0,0 +1,197 @@
+// data/terms/walletTerms.ts
+export interface Term {
+ id: string
+ title: string
+ required: boolean
+ content: string
+}
+
+export const walletTerms: Term[] = [
+ {
+ id: "wallet-service",
+ title: "전자지갑 서비스 이용약관",
+ required: true,
+ content: `제1조 (목적)
+이 약관은 Tokkit(이하 "회사"라 합니다)이 제공하는 전자지갑 서비스(이하 "서비스"라 합니다)의 이용과 관련하여 회사와 이용자 간의 권리, 의무 및 책임사항, 기타 필요한 사항을 규정함을 목적으로 합니다.
+
+제2조 (용어의 정의)
+① "전자지갑"이란 이용자가 중앙은행 디지털 화폐(CBDC)를 안전하게 보관하고 이용할 수 있도록 회사가 제공하는 전자적 수단을 말합니다.
+② "이용자"란 이 약관에 따라 회사가 제공하는 서비스를 이용하는 자를 말합니다.
+③ "CBDC"란 중앙은행이 발행하는 디지털 형태의 법정 화폐를 말합니다.
+
+제3조 (약관의 효력 및 변경)
+① 이 약관은 서비스를 이용하고자 하는 모든 이용자에게 적용됩니다.
+② 회사는 관련 법령을 위배하지 않는 범위에서 이 약관을 개정할 수 있습니다.
+③ 회사가 약관을 변경할 경우에는 적용일자 및 변경사유를 명시하여 서비스 내에 공지합니다.
+
+제4조 (서비스의 내용)
+① 회사가 제공하는 서비스는 다음 각 호와 같습니다.
+ 1. CBDC 보관 및 관리 서비스
+ 2. CBDC 송금 및 결제 서비스
+ 3. 기타 회사가 정하는 서비스
+② 회사는 서비스의 내용을 변경하거나 추가할 수 있으며, 이 경우 회사는 변경 또는 추가 내용을 서비스 내에 공지합니다.
+
+제5조 (서비스 이용 제한)
+① 회사는 다음 각 호에 해당하는 경우 서비스 이용을 제한할 수 있습니다.
+ 1. 이용자가 이 약관을 위반한 경우
+ 2. 이용자가 법령을 위반한 경우
+ 3. 이용자가 타인의 명의나 정보를 도용한 경우
+ 4. 기타 회사가 정한 사유에 해당하는 경우
+② 서비스 이용이 제한된 경우, 회사는 이용자에게 그 사유 및 기간을 통지합니다.
+
+제6조 (이용자의 의무)
+① 이용자는 서비스 이용과 관련하여 다음 각 호의 행위를 하여서는 안 됩니다.
+ 1. 타인의 정보를 도용하거나 허위 정보를 제공하는 행위
+ 2. 회사의 서비스를 이용하여 불법적인 목적을 달성하고자 하는 행위
+ 3. 회사의 서비스를 이용하여 타인에게 피해를 주는 행위
+ 4. 기타 관련 법령에 위배되는 행위
+
+제7조 (회사의 의무)
+① 회사는 이용자가 안전하게 서비스를 이용할 수 있도록 개인정보 보호를 위한 보안 시스템을 갖추어야 합니다.
+② 회사는 이용자로부터 제기되는 의견이나 불만이 정당하다고 인정할 경우에는 신속히 처리하여야 합니다.
+
+제8조 (손해배상)
+① 회사는 서비스 제공과 관련하여 회사의 고의 또는 과실로 인해 이용자에게 손해가 발생한 경우, 관련 법령에 따라 손해를 배상합니다.
+② 이용자가 이 약관을 위반하여 회사에 손해를 입힌 경우, 이용자는 회사에 대하여 그 손해를 배상하여야 합니다.
+
+제9조 (분쟁해결)
+① 서비스 이용과 관련하여 회사와 이용자 간에 분쟁이 발생한 경우, 양 당사자는 분쟁의 해결을 위해 성실히 협의합니다.
+② 제1항의 협의에서 분쟁이 해결되지 않을 경우, 관련 법령에 따라 처리합니다.
+
+제10조 (약관의 해석)
+이 약관에 명시되지 않은 사항은 관련 법령 및 상관례에 따릅니다.`,
+ },
+ {
+ id: "personal-info",
+ title: "개인정보 수집 및 이용 동의",
+ required: true,
+ content: `개인정보 수집 및 이용 동의
+
+Tokkit(이하 "회사"라 합니다)은 전자지갑 서비스 제공을 위해 다음과 같이 개인정보를 수집 및 이용합니다. 내용을 자세히 읽으신 후 동의 여부를 결정하여 주시기 바랍니다.
+
+1. 수집하는 개인정보 항목
+- 필수항목: 성명, 생년월일, 성별, 휴대전화번호, 이메일 주소, 계좌정보, 주민등록번호(실명확인용)
+- 선택항목: 주소, 직업, 소득수준
+
+2. 개인정보 수집 및 이용 목적
+- 서비스 제공 및 계약 이행: 전자지갑 서비스 제공, 본인 확인, 거래 내역 관리
+- 서비스 개선: 신규 서비스 개발, 기존 서비스 개선
+- 고객 관리: 고객 문의 응대, 공지사항 전달
+- 법령 준수: 관련 법령에 따른 의무 이행
+
+3. 개인정보의 보유 및 이용 기간
+- 회원 탈퇴 시까지 또는 법정 의무 보유기간까지
+- 전자금융거래법에 따라 전자금융거래에 관한 기록은 5년간 보관
+- 통신비밀보호법에 따라 접속 로그는 3개월간 보관
+
+4. 동의 거부권 및 거부 시 불이익
+- 필수항목에 대한 동의를 거부할 경우 서비스 이용이 제한됩니다.
+- 선택항목에 대한 동의를 거부하더라도 서비스 이용에 제한은 없으나, 일부 서비스 이용이 제한될 수 있습니다.
+
+5. 개인정보의 제3자 제공
+- 회사는 이용자의 개인정보를 원칙적으로 외부에 제공하지 않습니다.
+- 다만, 아래의 경우에는 예외로 합니다.
+ 1) 이용자가 사전에 동의한 경우
+ 2) 법령의 규정에 의거하거나, 수사 목적으로 법령에 정해진 절차와 방법에 따라 요청한 경우
+
+6. 개인정보의 안전성 확보 조치
+- 회사는 개인정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다.
+ 1) 관리적 조치: 내부관리계획 수립 및 시행, 정기적 직원 교육
+ 2) 기술적 조치: 개인정보처리시스템 접근 제한, 암호화 기술 적용, 접속 기록 보관
+ 3) 물리적 조치: 전산실, 자료보관실 등의 접근 통제
+
+7. 이용자의 권리와 행사 방법
+- 이용자는 언제든지 개인정보 열람, 정정, 삭제, 처리정지 요구 등의 권리를 행사할 수 있습니다.
+- 권리 행사는 회사에 대해 서면, 전화, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 회사는 이에 대해 지체 없이 조치하겠습니다.
+
+8. 개인정보 보호책임자 및 연락처
+- 개인정보 보호책임자: 홍길동
+- 연락처: teamtokkit@gmail.com`,
+ },
+ {
+ id: "financial-info",
+ title: "금융정보 제공 동의",
+ required: true,
+ content: `금융정보 제공 동의서
+
+Tokkit(이하 "회사"라 합니다)은 전자지갑 서비스 제공을 위해 다음과 같이 금융정보를 수집 및 이용합니다. 내용을 자세히 읽으신 후 동의 여부를 결정하여 주시기 바랍니다.
+
+1. 금융정보 제공 동의 대상 기관
+- 금융결제원, 은행연합회, 신용정보집중기관, 신용정보회사, 금융회사(은행, 카드사 등)
+
+2. 금융정보 수집 항목
+- 계좌정보: 계좌번호, 은행명, 예금주명
+- 거래정보: 거래내역, 거래금액, 거래일시
+- 신용정보: 신용등급, 연체정보, 대출정보
+
+3. 금융정보 수집 및 이용 목적
+- 전자지갑 서비스 제공 및 운영
+- 금융사고 방지 및 조사
+- 법령상 의무 이행
+- 금융거래 내역 관리 및 분석
+
+4. 금융정보의 보유 및 이용 기간
+- 회원 탈퇴 시까지 또는 법정 의무 보유기간까지
+- 전자금융거래법에 따라 전자금융거래에 관한 기록은 5년간 보관
+- 금융실명거래 및 비밀보장에 관한 법률에 따른 거래기록은 5년간 보관
+
+5. 금융정보 제공 동의의 효력 기간
+- 서비스 이용계약 체결일로부터 서비스 해지일 또는 회원 탈퇴일까지
+- 단, 관련 법령에 따라 보존할 필요가 있는 경우 해당 법령에서 정한 기간까지
+
+6. 동의 거부권 및 거부 시 불이익
+- 본 금융정보 제공 동의는 전자지갑 서비스 이용을 위한 필수적 사항으로, 동의를 거부할 경우 서비스 이용이 제한됩니다.
+
+7. 금융정보의 파기 절차 및 방법
+- 회사는 금융정보 수집 및 이용 목적이 달성된 후에는 해당 정보를 지체 없이 파기합니다.
+- 전자적 파일 형태로 저장된 금융정보는 복구 불가능한 방법으로 영구 삭제하며, 종이에 출력된 금융정보는 분쇄기로 분쇄하거나 소각하여 파기합니다.
+
+8. 금융정보의 안전성 확보 조치
+- 회사는 금융정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다.
+ 1) 금융정보 접근 권한 제한 및 접근 통제 시스템 구축
+ 2) 금융정보 송수신 시 암호화 기술 적용
+ 3) 금융정보 처리 시스템에 대한 접속 기록 보관 및 위변조 방지 조치
+ 4) 금융정보 보호를 위한 내부관리계획 수립 및 시행
+
+9. 금융정보 보호책임자 및 연락처
+- 금융정보 보호책임자: 김철수
+- 연락처: teamtokkit@gmail.com`,
+ },
+ {
+ id: "marketing",
+ title: "마케팅 정보 수신 동의",
+ required: false,
+ content: `마케팅 정보 수신 동의
+
+Tokkit(이하 "회사"라 합니다)은 이용자에게 유용한 서비스 및 이벤트 정보를 제공하기 위해 다음과 같이 마케팅 정보를 수신하는 것에 대한 동의를 받고 있습니다. 내용을 자세히 읽으신 후 동의 여부를 결정하여 주시기 바랍니다.
+
+1. 마케팅 정보 수신 동의 내용
+- 회사가 제공하는 서비스, 이벤트, 프로모션 등의 광고성 정보
+
+2. 마케팅 정보 수신 방법
+- 이메일, SMS, 푸시 알림, 우편 등
+
+3. 마케팅 정보 수신 목적
+- 신규 서비스 및 이벤트 안내
+- 맞춤형 혜택 및 프로모션 제공
+- 서비스 개선을 위한 의견 수렴
+
+4. 마케팅 정보 수신 동의의 효력 기간
+- 회원 탈퇴 시 또는 마케팅 정보 수신 동의 철회 시까지
+
+5. 동의 거부권 및 거부 시 불이익
+- 본 마케팅 정보 수신 동의는 선택사항으로, 동의를 거부하더라도 기본적인 서비스 이용에는 제한이 없습니다.
+- 다만, 동의하지 않을 경우 회사가 제공하는 각종 혜택 및 이벤트 정보를 받아보실 수 없습니다.
+
+6. 마케팅 정보 수신 동의 철회 방법
+- 회원은 언제든지 마케팅 정보 수신 동의를 철회할 수 있습니다.
+- 철회 방법: 서비스 내 '설정 > 알림 설정'에서 변경 또는 고객센터(1588-1234)로 요청
+
+7. 개인정보의 제3자 제공 및 위탁
+- 회사는 마케팅 활동을 위해 필요한 경우 이용자의 개인정보를 외부 전문업체에 위탁할 수 있습니다.
+- 위탁 업체 및 위탁 업무 내용은 회사 홈페이지의 '개인정보처리방침'에서 확인하실 수 있습니다.
+
+8. 마케팅 정보 관련 문의
+- 마케팅 정보 수신과 관련하여 문의사항이 있으신 경우, 이메일(teamtokkit@gmail.com)로 문의해 주시기 바랍니다.`
+ }
+]
diff --git a/app/signup/wallet/terms/page.tsx b/app/signup/wallet/terms/page.tsx
new file mode 100644
index 0000000..d40f34c
--- /dev/null
+++ b/app/signup/wallet/terms/page.tsx
@@ -0,0 +1,22 @@
+import { Suspense } from "react"
+import { walletTerms } from "@/app/signup/wallet/terms/data/walletTerms"
+import TermsAgreementPage from "@/app/signup/wallet/terms/components/TermsAgreementPage"
+
+function WalletTermsContent() {
+ return (
+
+ )
+}
+
+export default function WalletTermsPage() {
+ return (
+ Loading...