410 lines
15 KiB
Markdown
410 lines
15 KiB
Markdown
# chocoadmin 프로젝트 계획서
|
|
|
|
## 1. 프로젝트 개요
|
|
|
|
| 항목 | 내용 |
|
|
|---|---|
|
|
| 서비스명 | chocoadmin |
|
|
| 목적 | chocomae 웹 서비스(mouse-typing)의 관리자 기능을 독립 웹 서비스로 분리 |
|
|
| 기존 서비스 경로 | `/Users/jisangs/work/jinaju/git/web/mouse-typing` |
|
|
| 신규 서비스 경로 | `/Users/jisangs/work/jinaju/git/web/chocoadmin` |
|
|
| 기존 운영 환경 | AWS EC2 t3a.medium 인스턴스 1개 |
|
|
| 사용 DB | 기존 chocomae DB (MariaDB 10.11.13) 그대로 사용 |
|
|
|
|
---
|
|
|
|
## 2. 핵심 개발 방향
|
|
|
|
1. 기존 `chocomae` DB를 그대로 사용한다. DB 스키마는 임의로 변경하지 않는다.
|
|
2. 관리자 UI와 서버 로직만 신규 Next.js 서비스로 분리한다.
|
|
3. 기존 테이블 구조와 상태값을 그대로 존중하고, 기존 서비스의 비즈니스 규칙을 먼저 파악한 뒤 동일하게 구현한다.
|
|
4. 승인·취소·거절 같은 변경 액션은 반드시 서버 사이드에서 권한 검증 → 트랜잭션 → 로그 기록 순서로 처리한다.
|
|
5. 중복 처리 방지를 위해 모든 변경 API는 현재 상태를 조건으로 먼저 검사한다.
|
|
|
|
---
|
|
|
|
## 3. 기술 스택
|
|
|
|
| 구분 | 기술 |
|
|
|---|---|
|
|
| 런타임 | Node.js LTS |
|
|
| 프레임워크 | Next.js 16 (App Router) |
|
|
| 언어 | TypeScript |
|
|
| 패키지 매니저 | pnpm |
|
|
| UI / CSS | Tailwind CSS + shadcn/ui |
|
|
| Table | TanStack Table (React Table v8) |
|
|
| DB 클라이언트 | mysql2 (MariaDB 호환) |
|
|
| ORM | Prisma (기존 DB introspection 방식으로 활용) |
|
|
| 유효성 검증 | Zod |
|
|
| 인증 | NextAuth.js v5 (기존 `admin` 테이블 활용) |
|
|
| 환경변수 관리 | `.env.local` / `.env.production` |
|
|
|
|
### Prisma vs Drizzle ORM
|
|
|
|
두 가지 모두 MariaDB를 지원하나, 이 프로젝트에서는 **Prisma**를 우선 사용한다.
|
|
|
|
- 기존 DB를 `prisma db pull`(introspection)로 그대로 가져올 수 있어 초기 세팅이 빠름
|
|
- TypeScript 타입 자동 생성으로 컬럼명 오타를 컴파일 단계에서 잡을 수 있음
|
|
- 세밀한 SQL 제어가 필요한 구간은 `$queryRaw` / `$executeRaw`로 raw SQL 병행 가능
|
|
|
|
---
|
|
|
|
## 4. 개발 환경 및 배포 환경
|
|
|
|
### 4-1. 개발 환경
|
|
|
|
| 항목 | 내용 |
|
|
|---|---|
|
|
| 하드웨어 | 맥미니 |
|
|
| IDE | IntelliJ IDEA |
|
|
| 실행 방법 | IntelliJ 터미널에서 `pnpm dev` |
|
|
| 포트 | 3001 (기존 mouse-typing과 충돌 방지) |
|
|
| DB (개발) | 맥미니 로컬 MariaDB (`localhost:3306`) |
|
|
| 환경변수 | `.env.local` |
|
|
|
|
### 4-2. 배포(서비스) 환경
|
|
|
|
| 항목 | 내용 |
|
|
|---|---|
|
|
| 플랫폼 | Synology NAS DS920+ (RAM 20GB) — Docker |
|
|
| 실행 방법 | Synology Container Manager (`docker compose up`) |
|
|
| DB (서비스) | AWS EC2 MariaDB (chocomae 서비스와 동일 인스턴스) |
|
|
| DB 접근 | AWS 보안그룹에서 NAS 외부 IP만 3306 포트 허용 |
|
|
| DB 계정 | chocoadmin 전용 최소 권한 계정으로 분리 |
|
|
| 환경변수 | `.env.production` 또는 Container Manager 환경변수 설정 |
|
|
|
|
### 4-3. Dockerfile / docker-compose (배포용)
|
|
|
|
```dockerfile
|
|
# Dockerfile
|
|
FROM node:22-alpine
|
|
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
WORKDIR /app
|
|
COPY package.json pnpm-lock.yaml ./
|
|
RUN pnpm install --frozen-lockfile
|
|
COPY . .
|
|
RUN pnpm build
|
|
CMD ["pnpm", "start"]
|
|
```
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
chocoadmin:
|
|
build: .
|
|
ports:
|
|
- "3001:3000"
|
|
environment:
|
|
- NODE_ENV=production
|
|
- DATABASE_URL=mysql://USER:PASS@AWS_EC2_HOST:3306/chocomae
|
|
- NEXTAUTH_SECRET=...
|
|
- NEXTAUTH_URL=http://NAS_IP:3001
|
|
restart: unless-stopped
|
|
```
|
|
|
|
---
|
|
|
|
## 5. 데이터베이스 주요 테이블
|
|
|
|
스키마: [`docs/db-reference/schema.sql`](./db-reference/schema.sql)
|
|
|
|
| 테이블 | 설명 | 주요 컬럼 |
|
|
|---|---|---|
|
|
| `maestro` | 마에스트로(강사) 계정 | `MaestroID`, `Name`, `Email`, `AccountType`, `ActivateStatus`, `AvailableActivateDateTime`, `PlayerCount` |
|
|
| `maestro_extension` | 연장 신청 | `MaestroExtensionID`, `MaestroID`, `AccountType`, `RequestedDateTime`, `Status` |
|
|
| `maestro_upgrade` | 업그레이드 신청 | `MaestroUpgradeID`, `MaestroID`, `RegisteredAccountType`, `RequestedAccountType`, `RequestedDateTime`, `Status` |
|
|
| `maestro_log` | 처리 이력 로그 | `MaestroLogID`, `MaestroID`, `Type`, `LogDateTime`, `Remark` |
|
|
| `admin` | 관리자 계정 | `AdminID`, `Name`, `Password`, `Email`, `AccountType`, `ActivateStatus` |
|
|
| `player` | 학생 계정 | `PlayerID`, `MaestroID`, `Name`, `EnterCode` |
|
|
|
|
### 상태값 코드표
|
|
|
|
**`maestro.AccountType`**
|
|
|
|
| 값 | 의미 |
|
|
|---|---|
|
|
| 1 | 20명 (1만원) |
|
|
| 2 | 50명 (2만원) |
|
|
| 3 | 100명 (3만원) |
|
|
| 4 | 500명 (4만원) |
|
|
| 5 | 1,000명 (5만원) |
|
|
|
|
**`maestro.ActivateStatus`**
|
|
|
|
| 값 | 의미 |
|
|
|---|---|
|
|
| 0 | 미승인 |
|
|
| 1 | 체험 |
|
|
| 2 | 활성화 |
|
|
| 100 | 등록취소 |
|
|
|
|
**`maestro_extension.Status` / `maestro_upgrade.Status`**
|
|
|
|
| 값 | 의미 |
|
|
|---|---|
|
|
| 1 | 요청 |
|
|
| 2 | 취소/거절 |
|
|
| 3 | 적용 완료 |
|
|
|
|
---
|
|
|
|
## 6. 애플리케이션 구조
|
|
|
|
```
|
|
chocoadmin/
|
|
├── app/
|
|
│ ├── (auth)/
|
|
│ │ └── login/
|
|
│ │ └── page.tsx # 관리자 로그인
|
|
│ ├── (admin)/
|
|
│ │ ├── layout.tsx # 공통 사이드바 + 헤더
|
|
│ │ ├── maestros/
|
|
│ │ │ ├── page.tsx # 마에스트로 전체 목록
|
|
│ │ │ └── [maestroId]/
|
|
│ │ │ └── page.tsx # 마에스트로 상세 정보
|
|
│ │ ├── extension-requests/
|
|
│ │ │ └── page.tsx # 연장 신청 목록 + 승인/취소
|
|
│ │ └── upgrade-requests/
|
|
│ │ └── page.tsx # 업그레이드 신청 목록 + 승인/거절
|
|
│ └── api/
|
|
│ ├── auth/[...nextauth]/
|
|
│ ├── maestros/
|
|
│ │ ├── route.ts # GET: 목록
|
|
│ │ └── [id]/route.ts # GET: 상세, PATCH: 상태 변경
|
|
│ ├── extension-requests/
|
|
│ │ └── [id]/route.ts # PATCH: 승인/취소
|
|
│ └── upgrade-requests/
|
|
│ └── [id]/route.ts # PATCH: 승인/거절
|
|
├── components/
|
|
│ ├── ui/ # shadcn/ui 컴포넌트
|
|
│ ├── data-table/ # TanStack Table 공통 컴포넌트
|
|
│ ├── layout/ # 사이드바, 헤더
|
|
│ ├── forms/
|
|
│ └── dialogs/ # 승인/취소 확인 다이얼로그
|
|
├── lib/
|
|
│ ├── db.ts # Prisma Client 싱글톤
|
|
│ ├── auth.ts # NextAuth 설정
|
|
│ ├── validators/ # Zod 스키마
|
|
│ └── constants.ts # 상태값 상수
|
|
├── types/
|
|
│ └── index.ts
|
|
├── prisma/
|
|
│ └── schema.prisma # DB introspection 결과
|
|
├── docs/
|
|
│ └── db-reference/
|
|
│ └── schema.sql
|
|
├── .env.local # gitignore
|
|
├── .env.production # gitignore
|
|
├── Dockerfile
|
|
└── docker-compose.yml
|
|
```
|
|
|
|
---
|
|
|
|
## 7. 주요 화면 기능 명세
|
|
|
|
### 7-1. 공통 레이아웃
|
|
|
|
- 로그인 화면 (미인증 시 `/login` 리다이렉트)
|
|
- 좌측 사이드바 네비게이션
|
|
- 공통 확인 다이얼로그 (승인/취소/거절 전 확인)
|
|
- 처리 성공/실패 토스트 알림
|
|
|
|
### 7-2. 마에스트로 전체 목록 (`/maestros`)
|
|
|
|
- 전체 목록 조회 (서버 사이드 페이지네이션)
|
|
- 검색: 이름, 이메일, MaestroID
|
|
- 필터: ActivateStatus (미승인 / 체험 / 활성화 / 등록취소)
|
|
- 필터: AccountType (20명 / 50명 / 100명 / 500명 / 1,000명)
|
|
- 정렬: 가입일, 사용 가능일
|
|
- 컬럼: ID / 이름 / 이메일 / 계정 유형 / 상태 / 학생 수 / 사용 가능일 / 상세보기
|
|
|
|
### 7-3. 마에스트로 상세 정보 (`/maestros/[maestroId]`)
|
|
|
|
- 기본 계정 정보 (이름, 이메일, 계정 유형, 상태, 만료일, 약관 동의일)
|
|
- 플레이어(학생) 목록 요약
|
|
- 연장 신청 이력
|
|
- 업그레이드 신청 이력
|
|
- 관리자 처리 로그 (`maestro_log`)
|
|
|
|
### 7-4. 연장 신청 목록 (`/extension-requests`)
|
|
|
|
- 연장 신청 목록 조회 (기본 필터: Status=1 요청 건)
|
|
- 필터: 상태 (요청 / 취소 / 적용)
|
|
- 컬럼: 신청ID / 마에스트로명 / 계정유형 / 신청일시 / 상태 / 승인 / 취소
|
|
|
|
**승인 처리 순서:**
|
|
1. `maestro_extension.Status = 1` 인지 확인 (중복 처리 방지)
|
|
2. 트랜잭션 시작
|
|
3. `maestro_extension.Status → 3`
|
|
4. `maestro.AvailableActivateDateTime` 연장, `maestro.ActivateStatus → 2` (기존 서비스 로직 확인 후 동일 적용)
|
|
5. `maestro_log` 기록
|
|
6. 트랜잭션 커밋
|
|
|
|
**취소 처리 순서:**
|
|
1. `maestro_extension.Status = 1` 인지 확인
|
|
2. 트랜잭션 시작
|
|
3. `maestro_extension.Status → 2`
|
|
4. `maestro_log` 기록
|
|
5. 트랜잭션 커밋
|
|
|
|
### 7-5. 업그레이드 신청 목록 (`/upgrade-requests`)
|
|
|
|
- 업그레이드 신청 목록 조회 (기본 필터: Status=1 요청 건)
|
|
- 필터: 상태 (요청 / 취소 / 적용)
|
|
- 컬럼: 신청ID / 마에스트로명 / 현재등급 / 요청등급 / 신청일시 / 상태 / 승인 / 거절
|
|
|
|
**승인 처리 순서:**
|
|
1. `maestro_upgrade.Status = 1` 인지 확인 (중복 처리 방지)
|
|
2. 트랜잭션 시작
|
|
3. `maestro_upgrade.Status → 3`
|
|
4. `maestro.AccountType → RequestedAccountType` 변경 (PlayerCount 갱신 정책은 기존 서비스 확인 후 동일 적용)
|
|
5. `maestro_log` 기록
|
|
6. 트랜잭션 커밋
|
|
|
|
**거절 처리 순서:**
|
|
1. `maestro_upgrade.Status = 1` 인지 확인
|
|
2. 트랜잭션 시작
|
|
3. `maestro_upgrade.Status → 2`
|
|
4. `maestro_log` 기록
|
|
5. 트랜잭션 커밋
|
|
|
|
---
|
|
|
|
## 8. 보안 고려사항
|
|
|
|
1. 관리자 로그인은 NextAuth 서버 세션 기반으로 구현한다.
|
|
2. 기존 `admin.Password` 저장 방식(평문/해시/자체 암호화)을 먼저 확인한 뒤 동일 방식 또는 해시 업그레이드를 결정한다.
|
|
3. 승인·취소·거절 액션은 Next.js Server Action 또는 API Route에서 CSRF 보호를 적용한다.
|
|
4. DB 연결 정보는 `.env` 파일로 관리하며 Git에 커밋하지 않는다.
|
|
5. 배포 DB 계정은 chocoadmin 전용 계정으로 분리하고 최소 권한(SELECT + 필요한 테이블만 UPDATE)을 적용한다.
|
|
6. AWS EC2 보안그룹에서 MariaDB 포트(3306)를 NAS 외부 IP로만 허용한다.
|
|
7. 관리자 전용 서비스이므로 외부에 직접 노출하지 않고 VPN 또는 NAS 내부 포트로 접근하는 방식을 권장한다.
|
|
|
|
---
|
|
|
|
## 9. 개발 단계 계획
|
|
|
|
### Phase 0. 기존 관리자 코드 조사
|
|
|
|
> 기존 서비스 비즈니스 규칙을 파악해 재현 실수를 방지한다.
|
|
|
|
- [x] `mouse-typing` 관리자 라우트와 처리 로직 분석 (`src/web/admin/`, `src/web/server/admin/`)
|
|
- [x] 연장 승인 시 `AvailableActivateDateTime` 계산 방식 확인 (몇 개월/년 연장인지)
|
|
- [x] 업그레이드 승인 시 `PlayerCount` 갱신 여부 확인
|
|
- [x] `admin.Password` 검증 방식 확인 (평문 / MD5 / bcrypt 등)
|
|
- [x] `maestro_log` 기록 형식(`Type`, `Remark`) 확인
|
|
- [x] 조사 결과를 `docs/business-rules.md`에 문서화
|
|
|
|
### Phase 1. 프로젝트 초기화
|
|
|
|
- [x] `pnpm create next-app` (Next.js 16, TypeScript, App Router, Tailwind CSS)
|
|
- [x] shadcn/ui 초기화 (`pnpm dlx shadcn@latest init`)
|
|
- [x] TanStack Table 설치 (`pnpm add @tanstack/react-table`)
|
|
- [x] Prisma 설치 및 MariaDB introspection (`pnpm add prisma`, `prisma db pull`)
|
|
- [x] Zod 설치 (`pnpm add zod`)
|
|
- [x] NextAuth.js v5 설치
|
|
- [x] ESLint / Prettier 설정
|
|
- [x] `.env.local` 환경변수 구조 정의
|
|
- [x] IntelliJ에서 `pnpm dev` 실행 확인
|
|
|
|
### Phase 2. DB 연결 및 타입 구성
|
|
|
|
- [x] Prisma Client 싱글톤 (`lib/db.ts`)
|
|
- [x] 상태값 상수 정의 (`lib/constants.ts`)
|
|
- [x] 공통 유틸 작성 (날짜 포맷, AccountType 레이블 변환 등)
|
|
- [x] 개발 DB에서 `maestro`, `maestro_extension`, `maestro_upgrade` 조회 확인
|
|
|
|
### Phase 3. 인증 및 공통 레이아웃
|
|
|
|
- [x] 로그인 페이지 (`/login`)
|
|
- [x] NextAuth Credentials Provider — `admin` 테이블 기반 검증
|
|
- [x] 인증 프록시 (`proxy.ts`) — 비로그인 시 `/login` 리다이렉트
|
|
- [x] 공통 레이아웃 (`(admin)/layout.tsx`) — 사이드바, 헤더
|
|
- [x] 로그아웃 기능
|
|
|
|
### Phase 4. 마에스트로 목록 및 상세
|
|
|
|
- [x] 마에스트로 목록 API (`GET /api/maestros`) — 검색·필터·페이지네이션 포함
|
|
- [x] TanStack Table 기반 목록 화면
|
|
- [x] 마에스트로 상세 API (`GET /api/maestros/[id]`)
|
|
- [x] 상세 화면 — 연장/업그레이드 이력, 학생 목록 요약, 처리 로그
|
|
|
|
### Phase 5. 연장 신청 관리
|
|
|
|
- [x] 연장 신청 목록 API + 화면
|
|
- [x] 승인/취소 확인 다이얼로그
|
|
- [x] 승인 API (`PATCH /api/extension-requests/[id]`) — 트랜잭션 + maestro_log
|
|
- [x] 취소 API — 트랜잭션 + maestro_log
|
|
|
|
### Phase 6. 업그레이드 신청 관리
|
|
|
|
- [x] 업그레이드 신청 목록 API + 화면
|
|
- [x] 승인/거절 확인 다이얼로그
|
|
- [x] 승인 API (`PATCH /api/upgrade-requests/[id]`) — 트랜잭션 + maestro_log
|
|
- [x] 거절 API — 트랜잭션 + maestro_log
|
|
|
|
### Phase 7. 배포 환경 구성
|
|
|
|
- [x] Dockerfile (프로덕션용 pnpm 빌드)
|
|
- [x] `docker-compose.yml` (Synology NAS Container Manager용)
|
|
- [ ] AWS EC2 보안그룹 인바운드 설정 (NAS IP → 3306 허용) — `docs/deployment.md`에 절차 문서화
|
|
- [ ] Synology NAS에서 컨테이너 실행 테스트 — `docs/deployment.md`에 절차 문서화
|
|
- [ ] 운영 DB read-only 리허설 후 변경 기능 활성화 — `docs/deployment.md`에 절차 문서화
|
|
|
|
### Phase 8. 검증 및 안정화
|
|
|
|
- [ ] 승인/취소/거절 트랜잭션 시나리오 테스트
|
|
- [ ] 중복 처리 방지 (이미 처리된 건 재처리 시 에러 확인)
|
|
- [ ] 비인증 접근 차단 확인
|
|
- [ ] Docker 재시작 후 정상 동작 확인
|
|
|
|
---
|
|
|
|
## 10. MVP 범위
|
|
|
|
**1차 MVP (위 Phase 0~7):**
|
|
- 관리자 로그인
|
|
- 마에스트로 전체 목록 및 상세 정보 보기
|
|
- 연장 신청 목록 / 승인 / 취소
|
|
- 업그레이드 신청 목록 / 승인 / 거절
|
|
- 관리자 액션 로그 기록
|
|
- Docker 기반 NAS 배포
|
|
|
|
**MVP 이후 확장 후보:**
|
|
- 마에스트로 상태 직접 변경
|
|
- 학생 목록 상세 관리
|
|
- 데이터 내보내기 (CSV)
|
|
- 처리 통계 대시보드
|
|
- 감사 로그 전용 화면
|
|
- 관리자 계정 관리
|
|
|
|
---
|
|
|
|
## 11. 구현 전 확인이 필요한 사항
|
|
|
|
기존 `mouse-typing` 서비스 코드(`src/web/server/admin/`)에서 반드시 확인해야 할 비즈니스 규칙들이다.
|
|
|
|
1. 연장 승인 시 `AvailableActivateDateTime`을 정확히 얼마나 연장하는가? (1년? 계정 유형별 다름?)
|
|
: 계정 유형별로 차이 없고, 1년 연장
|
|
2. 업그레이드 승인 시 `PlayerCount`를 즉시 변경하는가, 아니면 `AccountType`만 변경하는가?
|
|
: AccountType만 변경
|
|
3. `admin.Password`는 어떤 방식으로 저장/검증되는가? (평문, MD5, bcrypt 등)
|
|
: MariaDB의 Password() 함수를 이용해서 저장/검증됨
|
|
4. `maestro_log`의 `Type` 컬럼에 어떤 값들이 사용되는가? (`Remark` 형식 포함)
|
|
: 다음과 같은 값들이 사용됨
|
|
* add_maestro
|
|
* update_maestro
|
|
* add_maestro
|
|
* register_maestro
|
|
* update_maestro_name
|
|
* extension_maestro
|
|
* upgrade_maestro
|
|
* update_maestro_email
|
|
5. 운영 MariaDB가 외부 IP 접속을 허용할 수 있는 네트워크 구조인가?
|
|
: 외부 IP 접속을 허용하고 있다
|
|
|
|
---
|
|
|
|
*작성일: 2026-05-26*
|