대시보드 개발 기획서
초판 · 코드 조사 기반 문서 버전 v1.0

ABL 개발 기획서

아마추어 볼링 리그(ABL) 플랫폼을 어떤 구조로 지었는지 — 아키텍처 · 도메인 모델 · 데이터 흐름 · 인증 · 인프라를 개발자 관점에서 정리한 문서입니다. 내용은 실제 코드(백엔드 · 웹 · 모바일) 조사에 근거합니다.

문서 유형개발 기획서
대상ABL · 3-surface
스택 구성pnpm 모노레포
최종 갱신2026-07-21
01

개요

ABL(Amateur Bowl League)은 아마추어 볼링 리그를 운영하는 플랫폼입니다. 리그 편성 · 참가 신청 · 팀/레인 매칭 · 경기 점수 기록과 검증 · 결제와 정산 · 랭킹 · 게이미피케이션까지 리그 운영 전 과정을 다룹니다.

시스템은 백엔드 · 웹 · 모바일 3-surface로 구성된 pnpm 모노레포(apps/*)입니다. 백엔드가 단일 API를 제공하고, 웹(관리자·얼라이언스 매니저용)과 모바일(회원용)이 이 API를 공유합니다.

이 문서는 "무엇을 만드는가(요구사항)"가 아니라 "어떻게 지어져 있는가"를 개발자 관점에서 서술합니다. 모든 기술 서술은 실제 코드 조사에 근거하며, 확정되지 않은 부분은 §09에 "확정 대기 · 미확인"으로 따로 표시했습니다.

02

시스템 구성

웹과 모바일은 각각 독립된 클라이언트이고, 둘 다 백엔드가 제공하는 REST API(/api, JWT 인증)와 Socket.IO(실시간)로만 백엔드와 통신합니다. 백엔드는 PostgreSQL(도메인 데이터) · Redis(캐시·큐) · S3(파일) · 외부 결제(PG)에 연결됩니다.

웹 · apps/frontend Vue 3 · 관리자/얼라이언스 모바일 · apps/mobile Flutter · 회원 REST /api · JWT · Socket.IO apps/backend — NestJS 11 기능 모듈 48개 · RBAC PostgreSQL 16 Redis S3 결제(PG)

시스템 구성 개념도 — 실제 서버·네트워크 배치가 아닙니다.

apps/backendNestJS 11 + TypeORM 0.3 + PostgreSQL 16 (port 8217). 48개 기능 모듈로 리그 운영 도메인 전체를 담당. 단일 REST API + Socket.IO 제공.
apps/frontendVue 3 · Vite · Pinia · Tailwind (port 9217). 관리자와 얼라이언스 매니저용 웹 콘솔. 60여 개 페이지.
apps/mobileFlutter 3 · go_router · Riverpod · Dio. 회원용 앱(iOS · Android). 5탭 구조. 운영 API는 abl.plaq.co.kr/api.
03

도메인 모델

대회 구조는 Season → League → Round → MatchDay → Match → Game의 계층으로 내려갑니다. 회원의 참가는 MatchDay에 Participation으로 붙고, 실제 점수는 Match의 MatchPlayer 아래 GameScore로 기록됩니다.

Season League Round MatchDay match_sessions Match Game Participation → Payment · Settlement MatchPlayer → GameScore → ScoreVerification

대회 구조 계층과 참가 · 점수 분기 — 주요 엔티티만 표시.

엔티티는 도메인 그룹으로 나뉩니다. 아래는 코드에서 확인된 핵심 그룹입니다.

주체Member(members) · Alliance · BowlingCenter · Admin/Role/Permission.
대회 구조Season → League → Round → MatchDay(match_sessions) → Match → Game.
참가 · 결제Participation ─1:1─ Payment · Settlement. 대기(waitlist) · 듀오(duoPartner) · 고스트(isGhost) 개념 포함.
점수 · 검증MatchPlayer → GameScore ─1:1─ ScoreVerification, ─1:N─ ScoreEditHistory. GameScore에 낙관적 락(@VersionColumn).
실력TierDefinition · MemberTier · RpHistory + 평균(average) 집계.
게이미피케이션뱃지 · 미션 · 마스터리 · 시즌패스 · ABL 포인트 · 출석 로그.
04

데이터 흐름 — 참가부터 정산까지

회원이 경기일에 참가 신청을 하면 (참가비가 있으면) 결제가 이어지고, 신청 마감 후 매칭이 팀(A/B)과 레인을 배정합니다. 경기가 진행되며 점수가 제출되고, 사진 증빙 검증을 거친 뒤 정산이 이루어집니다.

참가 신청 결제 매칭 팀 · 레인 경기 · 점수 검증 정산

참가 → 결제 → 매칭 → 경기·점수 → 검증 → 정산.

매칭마감 후 매칭이 팀 A/B와 레인을 배정. 정원 초과 시 대기(waitlistOrder), 밸런스용 고스트(isGhost), 듀오는 함께 배정.
점수 · 검증제출된 점수는 SUBMITTED 상태로 저장 → 사진 증빙 검증 후 정산 대상이 됨. 핸디캡은 (상대 평균 − 본인 평균) × 0.8 로 자동 적용.
정산경기 확정 후 참가별 Settlement 생성(상금 규칙 적용). settledAt 이후에는 SettlementGuard로 수정이 잠김.
05

인증 · 권한

인증은 JWT + refresh 토큰 방식입니다. 접근 토큰은 짧게(1시간) 유지하고, refresh 토큰은 해시해 DB에 저장하며 family 기반 회전으로 재사용 공격을 막습니다.

권한은 RBAC(역할–권한, resource:action 쌍)로 관리하고, JwtAuthGuard · RolesGuard · @RequirePermission() · ProfileCompletionGuard 등의 가드로 강제합니다. @Public()이 붙은 라우트만 인증을 건너뜁니다.

웹(관리자)ADMIN · ALLIANCE_MANAGER 이중 로그인 — 경로(/admin vs /alliance)로 구분하고 서로 다른 토큰을 사용. 접근 토큰은 메모리, refresh는 localStorage.
모바일(회원)이메일/비밀번호 + 소셜 로그인(Google · Kakao · Naver). 토큰은 flutter_secure_storage(키체인/암호화 저장소)에 보관.
06

API 구조

백엔드는 하나의 REST API(/api)를 제공하며, 컨트롤러는 공개 · 회원 · 관리자 · 웹훅 그룹으로 나뉩니다. 개발용 문서는 /api/docs(Swagger)로 제공되며 운영에서는 비활성입니다.

그룹예시 경로권한
공개GET /api/leagues, /api/members/:id/public, /api/stats/leaderboard, /api/health@Public()
회원POST /api/match-days/:id/apply, /api/my/participations, /api/payments/my, /api/settlements/myJWT
관리자/api/admin/leagues(+generate-schedule), /api/admin/scores(verify), /api/matching/batch-generate, /api/lanes/:id/assignJWT + RBAC
웹훅POST /api/webhooks/payment/pg-callback공개(HMAC 검증)
07

인프라 · 운영

Redis캐시 · rate limit(Throttler) · BullMQ 작업 큐 · 로그인 상태·블랙리스트.
S3점수 증빙 사진 업로드(presigned URL 방식).
Socket.IO실시간 업데이트(경기 상태 · 알림 · 정원 변화 등). 재연결 시 캐시 무효화.
스케줄러cron 작업(자동 알림 · 정산 처리 등)을 ScheduleModule로 오케스트레이션.
결제(PG)외부 결제 게이트웨이 연동. 결과는 웹훅(pg-callback)으로 수신하며 HMAC로 검증.
기타Winston(구조화 로그) · Helmet(보안 헤더).
08

DB 규약

synchronize:false가 원칙입니다(2026-03-27 데이터 손실 사고 이후 코드에 고정). 스키마 변경은 반드시 마이그레이션으로만 반영하며, 현재 69개가 존재합니다.

마이그레이션MigrationInterface 구현, up()/down() 모두 작성(되돌리기 가능). raw SQL은 queryRunner.query()로.
datasource앱 로딩용(database.config.ts, autoLoadEntities:true)과 CLI 전용(typeorm.datasource.ts)을 분리.
규칙NOT NULL 추가 시 DEFAULT 필수, enum 컬럼도 명시적 type. 비트랜잭션 DDL(예: CREATE INDEX CONCURRENTLY)은 -t none으로 실행.
09

확정 대기 · 미확인

아래는 코드 조사에서 확정되지 않았거나 확인되지 않은 항목입니다. 사실처럼 서술하지 않고 따로 표시합니다.

staging API 도메인
모바일 코드에 staging-api.abl.kr가 언급되나 배포 여부 미확인. 운영은 abl.plaq.co.kr/api.
모바일 인증서 고정(cert pinning)
api_client.dart에 TODO로 남아 있음(운영 SSL 검증 강화 예정).
일부 실시간 이벤트/응답 스키마
주요 이벤트명은 확인되나 전체 WebSocket 이벤트·응답 스키마는 원본 코드 재확인 필요.
참고 소셜 로그인 앱키 등 비밀값이 코드에 하드코딩된 부분이 확인되었으나, 이 공개 문서에는 싣지 않습니다. 해당 사안은 별도 보안 항목으로 다룹니다.
10

변경 이력

이 문서가 바뀐 내용을 시간순으로 남깁니다. 내용이 바뀌면 본문에서 옛 문장은 취소선으로, 새 내용은 추가 표시로 남기고, 아래에 날짜·요약을 덧붙입니다.

2026-07-21 문서 신규 작성 — ABL 백엔드 · 웹 · 모바일 3-surface 코드 조사를 근거로 초판 작성(아키텍처 · 도메인 모델 · 데이터 흐름 · 인증 · 인프라 · DB 규약).
본 문서(v1.0)는 실제 코드 조사에 근거한 개발자용 초판입니다. 코드가 바뀌어 문서를 갱신할 때는 본문에 취소선·추가로 변경을 표시하고 §10에 항목을 남깁니다.