980d7d905d
Stack
- Next.js 15 (App Router) + TypeScript + Drizzle ORM + postgres-js
- Node scrypt for password hashing; PBKDF2 verifier for legacy gnuboard5 hashes
- pnpm workspace monorepo: apps/web + packages/{db,auth,themes,...}
Themes (admin-selectable at /admin/themes)
- basic : 그누보드 default reproduction (light, blue accent)
- eyoom : eb4_maga_005 매거진 reproduction (dark, orange accent, ranking sidebar)
- amina : Aminam Builder reproduction (light, violet gradient, card grid)
- youngcart : 영카트 shop reproduction (red accent, search bar, category nav)
DB
- New schema (12 tables) pushed to PG via drizzle-kit: members, sessions, boards,
posts, point_ledger, app_settings, bacara_*, lottery_tickets, roulette_spins,
game_points, board_groups
- Legacy data still readable from inspection2 schema via @slot/db/legacy
Verified end-to-end against the migrated DB on localhost:3000:
- Home renders with active theme tokens injected as CSS variables
- /free lists 442K real posts from inspection2.g5_write_free
- Login (testlogin/test1234) issues session cookie, header switches to
"테스트님 환영합니다 / 로그아웃"
- Switching app_settings.theme.global from eyoom → amina swaps colors,
layout, and Korean nav labels site-wide on next request
Migration docs added: 03-migration-plan, 04-theme-architecture,
05-local-dev-setup, 06-feature-inventory.
10 KiB
10 KiB
Migration Plan — gnuboard5(PHP) → Next.js + Node.js + PostgreSQL
0. 목표
- 기존 사이트(
slot-ss.com) 의 모든 기능을 보존한 채 stack 을 PHP → Node/Next 로 전면 교체. - 테마 시스템을 빌트인 — 운영자가 관리자 화면에서 다음 4종 중 하나를 선택할 수 있어야 함:
- 기본 (그누보드 default 룩앤필 reproduction)
- 이윰빌더 (현재 운영 중인
eb4_maga_005매거진 레이아웃 reproduction) - 아미나빌더 (그누보드5 의 또 다른 인기 빌더 — Aminam Builder 룩앤필)
- 영카드(YoungCart) (영카트 쇼핑몰 중심 레이아웃)
- PostgreSQL 단일 DB. 회원/포인트/게시판/쇼핑/베팅/검수 모두 통합.
- 운영 가능한 마이그레이션 체크리스트와 롤백 경로 제공.
1. 신규 스택
| 레이어 | 기술 | 용도 |
|---|---|---|
| Web | Next.js 15 (App Router, RSC) + TypeScript | SSR/ISR 페이지, SEO, 4테마 라우팅 |
| API | Hono (Node.js, edge-friendly) — 또는 Next.js Route Handler | REST + WebSocket 핸들러 |
| Auth | NextAuth.js v5 (Auth.js) | 세션, 소셜 로그인, 2FA |
| ORM | Drizzle ORM | TypeScript 타입 안전, raw SQL 친화 |
| DB | PostgreSQL 17 | 메인 데이터 |
| Cache | Redis 7 | 세션, 캐시, 랭킹, rate-limit |
| Queue | BullMQ (Redis backed) | 27개 그누보드 cron 대체 |
| Object Storage | S3 호환 (R2/MinIO/AWS) | 첨부파일·에디터 이미지 (현재 82.5GB) |
| Search | Meilisearch (옵션) 또는 PostgreSQL FTS | 게시판/회원 검색 (현재 sphinx 대체) |
| Realtime | Socket.IO 또는 Pusher | 바카라/룰렛 실시간, 알림 |
| 결제 | KCP / 이니시스 / LG U+ Node SDK 또는 직접 통신 | 기존 4종 게이트웨이 대응 |
| 본인인증 | OK-Name / iNICert / Naver/Kakao API | 본인인증 |
| Deploy | Docker + (Vercel | Coolify |
2. 모노레포 레이아웃 (pnpm workspace + Turborepo)
slot/
├── apps/
│ ├── web/ # Next.js 사용자 사이트
│ ├── admin/ # Next.js 관리자 (별도 호스트 권장)
│ └── api/ # Hono API + WebSocket
├── packages/
│ ├── db/ # Drizzle 스키마, 마이그레이션, 시드
│ ├── auth/ # 인증 헬퍼, PBKDF2 검증, NextAuth 설정
│ ├── themes/ # 4종 테마 — basic/eyoom/amina/youngcart
│ ├── ui/ # 공통 컴포넌트 (Button, Modal, Editor, Pagination, ...)
│ ├── shop/ # 쇼핑몰 도메인 (item, cart, order, payment)
│ ├── games/ # 바카라/룰렛/복권/슬롯 코어 + Swiun API
│ ├── shared/ # 타입, 유틸, validators (zod)
│ └── workers/ # BullMQ 워커 (rank-up, point-dist, sms)
└── ops/
├── docker/ # docker-compose, Dockerfiles
└── pgloader/ # 기존 db/migrate_*.load 이전
3. URL 호환성
기존 .htaccess 가 정의한 라우트는 그대로 유지 (SEO 보존):
| 기존 패턴 | Next.js 라우트 |
|---|---|
/ |
/ |
/<bo_table> |
/[boTable] |
/<bo_table>/<wr_id> |
/[boTable]/[wrId] |
/group/<gr_id> |
/group/[grId] |
/page/<pid> |
/page/[pid] |
/content/<co_id> |
/content/[coId] |
/mypage/<t> |
/mypage/[tab] |
/shop/list-<ca_id> |
/shop/list/[caId] |
/shop/brand-<br_cd> |
/shop/brand/[brCd] |
/shop/<it_id> |
/shop/[itId] |
/adm/* |
/admin/* (별도 admin 앱) |
/bbs/login.php |
/login |
/bbs/board.php?bo_table=X |
/[X] (rewrite) |
기존 PHP URL(/bbs/board.php?bo_table=free)에 대해서는 미들웨어에서 새 URL로 301 리다이렉트.
4. 데이터 마이그레이션
- 이미 완료: MariaDB → PostgreSQL (pgloader, schema=
inspection2) - 다음 단계:
packages/db/schema/*.ts— Drizzle 스키마를 신규 design 으로 정의g5_member→membersg5_board+g5_write_<bo_table>→boards+ 단일posts테이블 +comments테이블 (write 테이블 분리 구조 폐기)g5_point→point_ledgerg5_visit→visits(또는 ClickHouse / 시계열 DB 로 분리)- 게임/베팅/검수/SMS/챗봇 → 각각 도메인 패키지로
packages/db/migrate-from-legacy/*.ts— 운영 데이터를 신규 스키마로 ETL 변환- 36개
g5_write_<X>테이블을 단일posts로 union (board_id 컬럼 추가) - PBKDF2 비밀번호 해시는 그대로 유지 (legacy verifier로 검증, 첫 로그인 시 bcrypt 로 자동 업그레이드)
- 36개
- 검증 쿼리: 기존 vs 신규 row count, sum(mb_point), 활성 회원 수 등 매칭 확인.
5. 인증·세션 호환
5.1 비밀번호 호환
- 그누보드 PBKDF2 (sha256, 12000 iter, 24-byte salt+hash) 검증을 그대로 구현 (
packages/auth/legacy-pbkdf2.ts) - 신규 가입자는
bcrypt또는argon2id사용 - 기존 회원이 첫 로그인 성공 시: PBKDF2 검증 후 신규 알고리즘으로 rehash (자동 업그레이드)
5.2 세션
- NextAuth Database session (PostgreSQL) — Redis 캐시
mb_id기반 식별자 → 신규user_id(UUID v7) 매핑 테이블 유지
5.3 2FA (ask-otp 호환)
- 관리자 admin 계정 OTP 그대로 유지
- TOTP (Google Authenticator) 표준 사용
6. 게시판 모델 통합
기존: 게시판마다 g5_write_<bo_table> 테이블 분리 (스키마는 동일)
신규: 단일 posts 테이블 + board_id 외래키
CREATE TABLE posts (
id UUID PRIMARY KEY,
board_id TEXT NOT NULL REFERENCES boards(slug),
parent_id UUID, -- 댓글이면 부모 글 id
reply_path TEXT, -- gnuboard wr_reply 호환 (중첩 댓글 정렬)
num BIGINT, -- 글 그룹 (정렬용, gnuboard wr_num 호환)
author_id UUID REFERENCES members(id),
author_name TEXT, -- 비회원 글
subject TEXT,
content TEXT,
attachments JSONB, -- file metadata
hit INTEGER DEFAULT 0,
good INTEGER DEFAULT 0,
bad INTEGER DEFAULT 0,
is_comment BOOLEAN DEFAULT FALSE,
is_secret BOOLEAN DEFAULT FALSE,
ip INET,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ
);
CREATE INDEX posts_board_idx ON posts (board_id, created_at DESC);
CREATE INDEX posts_board_thread_idx ON posts (board_id, num, reply_path);
CREATE INDEX posts_author_idx ON posts (author_id, created_at DESC);
PostgreSQL 의 partial index, partition, JSONB 활용으로 36개 테이블 union 후에도 성능 무리 없음.
초대형 게시판 (free 442K rows) 은 list partition 으로 분할 가능.
7. 슬롯/카지노 도메인
7.1 게임 포인트
point_ledger(append-only) — 적립/사용/환전 내역- 트리거 또는 워커가 회원 잔고 (
members.point_balance) 갱신 - 운영 DB 의
g5_point(594만 행) → 그대로 임포트
7.2 슬롯 베팅 (Swiun API 브릿지)
apps/api/src/routes/swiun.ts가 외부 슬롯사 API 와 통신- WebSocket 으로 실시간 베팅 결과 푸시
- 기존
plugin/swiunApi/*.php의 로직 1:1 이전
7.3 바카라
- 기존
plugin/bacara/*.php→packages/games/bacara/* - 게임 룰: 베팅(Player/Banker/Tie) + 결과 + 정산
- 실시간 페이지: Socket.IO 룸 단위 브로드캐스트
7.4 룰렛
- 기존
roulette/index.php의 휠 게임 → React + Framer Motion 으로 재구현 - 회당 1회 무료 + 추가 회전은 포인트 차감
7.5 복권
g5_write_lottery_ticket(45,724건) →lottery_tickets테이블- 응모 → 추첨 (cron) → 당첨자 통보 → 포인트 지급
7.6 블랙리스트 / 검수
blacklist_table(193K),check_table(359K) →blacklist,inspection_logs- 관리자가 ID/IP/회원명 검색 가능
7.7 챗봇
chatbot_conversations,chatbot_feedback→chatbot_*- OpenAI 또는 Anthropic API 연동 (
apps/api/src/routes/chatbot.ts)
8. 27개 cron → BullMQ 워커
기존 plugin/cron/auto.*rankup.php 27개 + 기타 cron 들을:
// packages/workers/jobs/rank-update.ts
queue.add('rank.update', { game: 'bacara' }, { repeat: { cron: '*/5 * * * *' } });
queue.add('rank.update', { game: 'slots' }, { repeat: { cron: '*/5 * * * *' } });
// ...
9. 첨부파일 (총 82.5GB)
- 운영 서버에서 R2/S3 로 일회성 업로드 (rclone)
- 신규 시스템은 처음부터 직접 S3 에 저장
- DB 의
g5_board_file.bf_file경로 → S3 키로 변환 - 마이그레이션 동안 폴백: missing key → 운영 서버 origin 으로 프록시
10. 단계별 마일스톤
| 단계 | 산출물 | 검증 |
|---|---|---|
| M0 (완료) | 소스 + DB 로컬 사본, PostgreSQL 셋업, 로컬 PHP 정상 가동 | 로그인 OK |
| M1 (1~2주) | 모노레포 스캐폴딩, Drizzle 스키마, ETL 첫 패스 (members, posts, point), Next.js 기본 셸, 4-테마 토글 | 회원 로그인 (PBKDF2), 자유게시판 list/view |
| M2 (3~4주) | 게시판 글쓰기·댓글·첨부, 마이페이지, 포인트, 회원 가입/탈퇴, 알림 | 핵심 게시판 동작 |
| M3 (5~6주) | 슬롯/바카라/룰렛/복권 기능 이전, BullMQ 워커, 챗봇 | 게임 정상 동작 |
| M4 (7~8주) | 영카트 쇼핑몰, 결제 게이트웨이, SMS, 본인인증, 소셜 로그인 | 주문/결제/SMS 검증 |
| M5 (9~10주) | 관리자 페이지 (회원/게시판/베팅/룰렛 관리, 테마 선택 UI) | 관리자 OTP 로그인 + 일상 운영 가능 |
| M6 (11~12주) | 첨부파일 S3 이전, SEO 검사, 성능 부하 테스트 (k6), 보안 감사 | Lighthouse 90+ / 부하 테스트 통과 |
| M7 (13주) | 카나리 트래픽 5% → 50% → 100% 절체. DNS cutover. | 라이브 |
11. 롤백
- 신규 시스템 cutover 후 6주간 기존 PHP 인프라 hot-standby 유지
- DNS TTL 60s → 4시간 단계 상승
- DB는 신규(PostgreSQL) → 구(MariaDB) 역방향 CDC (Debezium 또는 logical replication adapter) — 비상 시 MariaDB 로 1시간 내 복귀 가능
12. 보안 사항
- 운영 DB 패스워드 (
iiOii5*^^*), 토큰 키 (ac57f676fe741f0ab3471d81dbee3bf1), 서버 root 패스워드 모두 신규 시스템 첫 배포 시 회전 - ENV 변수는
.env.local또는 Doppler / 1Password / AWS Secrets Manager 로 관리 - 결제·본인인증 키는 운영 환경에만 주입
- WAF (Cloudflare) + rate-limit (Hono middleware + Redis)