NestJS + Next.js 기반의 커뮤니티 플랫폼 보일러플레이트입니다. 인증, 게시판, 알림, 검색, 신고/제재, 관리자 기능을 포함한 프로덕션 수준의 모노레포 구조로 설계되었습니다.
- 이메일/비밀번호 로그인·회원가입 — JWT HttpOnly 쿠키 기반, 이메일 인증
- OAuth 소셜 로그인 — Google, Kakao, Naver (native fetch, passport 미사용)
- 2단계 인증(2FA) — TOTP(Google Authenticator 등) + 이메일 OTP 선택 지원
- 계정 잠금 — 로그인 5회 실패 시 15분 잠금
- 세션 관리 — 기기별 RefreshToken 조회·삭제, 비밀번호 변경 시 전체 세션 무효화
- 이메일 변경 — 새 이메일로 인증 코드 발송 후 적용
- OAuth 연결 관리 — 연동된 소셜 계정 목록 조회·해제
- 게시판·게시글·댓글 CRUD — Keyset 기반 무한스크롤 페이지네이션
- 대댓글 — 4단계 중첩, 소프트 삭제
- 파일 첨부 — 게시글 생성·수정 시 파일 업로드/삭제
- 고정 게시글 — 쿼리 분리 패턴으로 상단 고정
- 카테고리 필터링 — 게시판별 카테고리 탭/칩 UI
- 리액션 시스템 — 이모지 기반 게시글 반응
- SSE 실시간 알림 — 로그인 상태에서 댓글·답글·리액션 즉시 수신
- 웹 푸시 알림 — Service Worker + VAPID 기반, 브라우저 닫힌 상태에서도 수신
- 알림 목록 — 미읽음 카운트, 전체/개별 읽음 처리
- 통합 검색 — 게시글·댓글 전문 검색, 키워드 하이라이팅
- 필터링 — 작성자, 날짜 범위, 게시판별 필터
- URL 상태 관리 — 검색 조건이 URL에 유지되어 공유 가능
- 다형 신고 — 게시글·댓글·사용자·미디어 신고
- 콘텐츠 제재 — 숨김·삭제, 동일 대상 신고 자동 처리
- 사용자 제재 — 경고·정지(기간 지정), 로그인 시 정지 계정 차단
- 권한 분리 — Moderator(숨김·경고) / Operator(삭제·정지) / Admin(전체)
- 대시보드 — 사용자·게시글·댓글 통계
- 사용자 관리 — 목록 조회, 정지·복구
- 콘텐츠 관리 — 게시글·댓글 관리
- 권한 그룹 — 그룹별 세분화된 퍼미션 관리
- 신고 처리 — 신고 목록 조회·처리·기각
- 이메일 템플릿 — 발송 템플릿 내용·활성화 관리
- 시스템 설정 — KV 스토어 기반 런타임 설정 변경
- 약관 관리 — 버전 관리·동의 이력
- 파일 업로드 — Local 또는 S3(MinIO 호환) 드라이버 전환 가능
- 썸네일 생성 — sharp 기반 자동 리사이징
- 이메일 — SMTP, DB 템플릿 관리, 발송 로그·재발송
- Rate Limiting — 전역 + 민감 엔드포인트별 강화 적용
| 영역 | 기술 |
|---|---|
| Backend | NestJS, TypeScript, Prisma ORM |
| Frontend | Next.js 16 (App Router), TypeScript, Tailwind CSS |
| Database | PostgreSQL |
| Auth | JWT (HttpOnly Cookie), Passport 없이 native OAuth |
| Realtime | SSE (Server-Sent Events), Web Push (VAPID) |
| Storage | Local / AWS S3 (환경변수로 전환) |
| Nodemailer (SMTP) | |
| Docs | Swagger / OpenAPI |
| CI | ESLint, Prettier, Husky pre-commit |
weaver2/
├── apps/
│ ├── core-backend/ # NestJS API 서버 (port 4000)
│ │ ├── prisma/schema/ # 도메인별 분리된 Prisma 스키마
│ │ └── src/
│ │ ├── core/ # 프레임워크 기능 (auth, user, notification, permission, terms)
│ │ ├── features/ # 비즈니스 기능 (board, report, search)
│ │ ├── infrastructure/# 인프라 (email, upload, config)
│ │ └── system/ # 관리자 API
│ └── core-frontend/ # Next.js 웹 앱 (port 3000)
│ └── src/
│ ├── app/ # 라우팅 (App Router)
│ ├── features/ # 도메인별 슬라이스
│ ├── shared/ # 공통 UI 컴포넌트
│ └── infrastructure/# ApiClient, Providers
├── libs/
│ ├── common/ # 전역 데코레이터, 인터셉터, 필터
│ ├── email/ # Nodemailer 래퍼
│ ├── pagination/ # Keyset 페이지네이션 라이브러리
│ ├── prisma/ # PrismaService 모듈
│ ├── shared/ # PERMISSIONS 상수, hasPermission() (백·프론트 공용)
│ └── upload/ # StorageProvider 인터페이스, Local/S3 구현
└── scripts/ # 유틸리티 스크립트
cp apps/core-backend/.env.example apps/core-backend/.env
cp apps/core-frontend/.env.example apps/core-frontend/.env.localapps/core-backend/.env 필수 항목:
DATABASE_URL=postgresql://<user>:<password>@localhost:5432/<db>
JWT_SECRET=<secret>
SMTP_HOST=smtp.gmail.com
SMTP_USER=<email>
SMTP_PASS=<app-password>
# Web Push (VAPID) — node -e "const wp=require('web-push'); console.log(wp.generateVAPIDKeys())"
VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY=
VAPID_SUBJECT=mailto:admin@example.com
# 파일 저장 방식: local(기본) | s3
STORAGE_DRIVER=localpnpm install# 마이그레이션 실행
DATABASE_URL=<your-url> npx prisma migrate deploy --schema apps/core-backend/prisma/schema
# 시드 데이터 생성 (권한 그룹, 기본 관리자 계정, 이메일 템플릿 등)
pnpm db:seed# 백엔드 (port 4000)
pnpm dev
# 프론트엔드 (port 3000) — 별도 터미널
cd apps/core-frontend && pnpm dev| 서비스 | URL |
|---|---|
| 프론트엔드 | http://localhost:3000 |
| API 서버 | http://localhost:4000 |
| Swagger | http://localhost:4000/docs |
기본 관리자 계정은 시드 실행 후 apps/core-backend/prisma/seed 참조.
모든 경로는 /v1 프리픽스를 포함합니다.
POST /v1/auth/sign-in # 로그인
POST /v1/auth/sign-up/email # 이메일 회원가입
POST /v1/auth/refresh # 토큰 갱신
POST /v1/auth/sign-out # 로그아웃
POST /v1/auth/password/request-reset # 비밀번호 재설정 요청
POST /v1/auth/password/reset # 비밀번호 재설정
GET /v1/auth/oauth/:provider # OAuth 시작 (google|kakao|naver)
POST /v1/auth/2fa/authenticate # 2FA 최종 인증
POST /v1/auth/2fa/email/send # 이메일 OTP 발송
GET /v1/auth/sessions # 세션 목록
DELETE /v1/auth/sessions/:id # 세션 삭제
GET /v1/auth/oauth/connections # OAuth 연결 목록
DELETE /v1/auth/oauth/connections/:provider # OAuth 연결 해제
GET /v1/users/me # 본인 정보 조회
PATCH /v1/users/me # 프로필 수정
PATCH /v1/users/me/password # 비밀번호 변경
DELETE /v1/users/me # 계정 탈퇴
POST /v1/users/me/email # 이메일 변경 요청
POST /v1/users/me/email/confirm # 이메일 변경 확인
GET /v1/boards # 게시판 목록
GET /v1/boards/:id/posts # 게시글 목록 (categoryId 필터 지원)
POST /v1/boards/:id/posts # 게시글 생성 (파일 첨부 가능)
GET /v1/boards/:id/posts/:postId # 게시글 조회
PATCH /v1/boards/:id/posts/:postId # 게시글 수정
DELETE /v1/boards/:id/posts/:postId # 게시글 삭제
POST /v1/boards/:id/posts/:postId/reactions/:emojiId # 리액션 추가
GET /v1/notifications # 알림 목록
GET /v1/notifications/unread-count # 미읽음 수
GET /v1/notifications/stream # SSE 실시간 스트림
PATCH /v1/notifications/:id/read # 개별 읽음 처리
PATCH /v1/notifications/read-all # 전체 읽음 처리
GET /v1/notifications/push-subscription/public-key # VAPID 공개키
POST /v1/notifications/push-subscription # 웹 푸시 구독 저장
DELETE /v1/notifications/push-subscription # 웹 푸시 구독 해제
GET /v1/search?q=키워드&type=post|comment&boardId=...&authorId=...
POST /v1/reports # 신고 생성
GET /v1/reports # 신고 목록 (관리자)
PATCH /v1/reports/:id/action # 신고 처리 (숨김·삭제·경고·정지)
PATCH /v1/reports/:id/reject # 신고 기각
POST /v1/upload # 파일 업로드
GET /v1/upload/:id/file # 파일 서빙 (302 redirect)
GET /v1/upload/:id/thumbnail # 썸네일 서빙 (302 redirect)
Controller → Service → Repository (*.query.ts | *.command.ts) → Prisma
읽기(Query)와 쓰기(Command)를 파일 레벨에서 분리합니다.
EventEmitter2 (notification.created)
→ NotificationListener
→ DB 저장 (notifications 테이블)
→ InMemoryNotificationEmitter → SSE 발송
→ PushSubscriptionService → 웹 푸시 발송
NOTIFICATION_EMITTER 심볼 토큰으로 추상화되어 있어, Redis 기반 Emitter로 교체 시 NotificationModule provider만 변경하면 됩니다.
STORAGE_DRIVER=local → LocalStorageProvider (uploads/ 디렉토리)
STORAGE_DRIVER=s3 → S3StorageProvider (AWS S3 / MinIO)
환경변수 하나로 드라이버를 전환할 수 있습니다.
역할(Role) 대신 권한 그룹(PermissionGroup) 기반으로 동작합니다.
- 사용자는 여러 권한 그룹에 속할 수 있습니다.
PERMISSIONS상수와hasPermission()함수가@weaver2/shared에 정의되어 백엔드·프론트엔드가 공유합니다.- 와일드카드 지원:
board:*는board:read,board:write등을 모두 포함합니다.
- JWT: HttpOnly 쿠키 기반, Access Token 15분 / Refresh Token 최대 30일
- CSRF: 뮤테이션 요청마다
x-csrf-token헤더 검증 - Rate Limiting: 전역 60초/100회, 로그인 60초/10회, 2FA 60초/3~5회
- 계정 잠금: 로그인 5회 실패 → 15분 잠금
- 보안 헤더: Helmet, HSTS(프로덕션), X-Frame-Options 등
# 개발 서버
pnpm dev # 백엔드 개발 모드
pnpm build # 백엔드 프로덕션 빌드
# 데이터베이스
pnpm db:migrate # 마이그레이션 실행
pnpm db:seed # 시드 데이터 생성
pnpm db:reset # DB 초기화 + 시드
# 코드 품질
pnpm lint # ESLint 검사
pnpm format # Prettier 포맷팅
pnpm test # 유닛 테스트
pnpm test:integration # 통합 테스트 (Postgres 필요)
# E2E (Playwright) — 풀 스택 시나리오, 자세한 안내는 apps/core-frontend/e2e/README.md
pnpm --filter core-frontend e2e:install # 1회: chromium 브라우저 설치
pnpm --filter core-frontend e2e # e2e 실행 (자동으로 frontend·backend 기동)
pnpm --filter core-frontend e2e:ui # Playwright UI 모드 (시나리오 작성/디버깅)# 개발 환경 (PostgreSQL + 앱)
docker-compose up -d
# 프로덕션
docker-compose -f docker-compose.prod.yml up -d.github/workflows/에 4개의 워크플로우가 포함되어 있습니다.
| 워크플로우 | 트리거 | 역할 |
|---|---|---|
ci.yml |
PR / main push / 수동 | install → secret-files / lint / unit / integration / backend build / web build / prisma check → CI success 단일 게이트 |
security.yml |
매주 월 09:00 KST / 수동 / 의존성 PR | pnpm audit --audit-level=high, 발견 시 자동 Issue 생성 + PR fail |
pr-checks.yml |
PR 이벤트 | Conventional Commits 제목 검사, 경로별 자동 라벨, 500줄 이상 PR 경고 |
codeql.yml |
PR / main push / 매주 월 14:00 KST / 수동 | GitHub CodeQL 정적 분석 (security-extended 쿼리) |
설계 포인트:
ci-success단일 aggregator job — Branch protection에서 이 하나만 require하면 충분.HUSKY=0+--ignore-scripts+ 명시적prisma generate— pnpm v11의approve-builds프롬프트와 husky postinstall 노이즈 회피.- Postgres 16 service container + healthcheck로 integration test 격리.
concurrency.cancel-in-progress— 동일 PR 새 push 시 진행 중 run 자동 취소.
저장소 Settings → Rules → Rulesets → New branch ruleset:
- Target: default branch
- Restrict deletions ✅
- Block force pushes ✅
- Require a pull request before merging ✅
- Required approvals: 1
- Dismiss stale approvals: ✅
- Require conversation resolution: ✅
- Require status checks to pass ✅
CI success단일 등록 (워크플로우가 한 번 실행된 후에 검색 가능)Analyze (javascript-typescript)추가 (CodeQL — public 또는 Team/Enterprise plan)
이 보일러플레이트를 private repo로 복제하셨다면 plan에 따라 ruleset 일부 항목을 빼야 합니다:
| 기능 | Public Free | Private Free | Private Team/Enterprise |
|---|---|---|---|
| Code Scanning (CodeQL) | ✅ | ❌ → ruleset에서 제거 | ✅ |
Restrict file paths (Push ruleset) |
✅ | ❌ → 본 보일러플레이트의 3-layer guard 사용 | ✅ |
| Dependabot | ✅ | ✅ | ✅ |
| Branch ruleset | ✅ | ✅ | ✅ |
Private + Free 환경에서 CodeQL 워크플로우 자체는 두어도 무방하지만 결과를 Security 탭에서 볼 수 없으며, ruleset의
Require code scanning results는 영원히 만족되지 않으므로 반드시 제거해야 합니다.
이 보일러플레이트는 GitHub plan 의존 없이 secret 유출을 방어합니다:
.gitignore—.env*(단,.env.example제외),*.pem/.key/.p12/.pfx/.crt/.cer,id_rsa*,id_ed25519*.husky/pre-commit— staged 파일 검사 후 차단 (커밋 자체가 실패)ci.yml의secret-filesjob — 트리 전체 검사 (push에서 차단)
세 단계가 동일 패턴을 공유하므로 어느 하나가 뚫려도 다음에서 잡힙니다.
| 문서 | 역할 |
|---|---|
README.md |
프로젝트 소개, 기술 스택, API, CI 가이드 (이 문서) |
CHARTER.md |
보일러플레이트 헌장 — 미션, IN/OUT 범위, 설계 원칙, 확장 포인트, 분기 체크리스트 |
CONTRIBUTING.md |
일상 개발 워크플로우, 브랜치/커밋/PR/리뷰/마이그레이션/테스트/FAQ |
SECURITY.md |
보안 취약점 보고·대응 정책, 빌트인 보안 레이어 정리 |
ROADMAP.md |
향후 권장 작업 (Dependabot, e2e, 운영 등) |
CLAUDE.md |
코딩 철학·EDGE 방법론·금지 사항 (AI 협업 규칙 포함) |
apps/core-frontend/CLAUDE.md |
프론트엔드 디렉토리 구조·스킨 시스템·import 규칙 |
docs/audits/ |
종합 감사 보고서 archive |