v1 (Spring Boot + PostgreSQL) — 기술설계서 v1.6 모델을 실 백엔드로 구현. 아래 v0.5 문단은 프론트 프로토타입(mock) 설명이며 화면은 그대로 유지된다.
- 백엔드
server/— Spring Boot 3(Java 17) · JdbcTemplate · Flyway · PostgreSQL 16domain순수 자바(요율계산·통합 랭킹·자격 게이트·승자확정) — 협의(NEGOTIATED)를 표준 룰로 일원화, 조회구분(lookup_key)·세션 축 포함batch배정판 3층 산출(전체 재산출·일 delta·수시 증분)이 승자확정 로직 한 벌을 공유- 배정판은 우대(이벤트·협의) 승자만 저장(§1.4) — 기본은 미저장, 원장 조회 미스 시 기본수수료 직접 적용
- 프론트
src/— 화면 대부분이 실 API 기반: 대시보드·이벤트 등록(구간표 저작)·승인함·협의 신청/승인·협수 관리·수수료 결정 흐름 + "백엔드 연동" 라이브 탭.src/api/가 클라이언트·라벨 - 국내 옵션 구간표(BANDS): 이벤트 등록에서 가격 구간별 정률+정액+최소수수료를 저작 → 지배관계 검증(구간 교차 시 실패 지점 표시) → 승인 → 배정 → 결정 흐름에서 매칭 구간 하이라이트
- 테스트: 서버 JUnit+Testcontainers 82, 프론트 vitest 143
# 1) PostgreSQL (Podman 또는 Docker) — 호스트 5433
podman run -d --name fees-pg -e POSTGRES_DB=fees -e POSTGRES_USER=fees -e POSTGRES_PASSWORD=fees -p 5433:5432 postgres:16
# 또는: docker compose up -d (docker-compose.yml)
# 2) 백엔드 (:8080) — Flyway가 스키마 V1 + 시드 V2 자동 적용
cd server && ./gradlew bootRun
# 3) 프론트 (:5173) — /api 는 :8080 으로 프록시
npm install && npm run dev첫 접속 후 "백엔드 연동" 탭에서 전체 재산출 실행 → 계좌 8041-2237-01 배정판(선물=협의 T2, ETF=이벤트)·이력·원장 조회 시뮬을 확인할 수 있다.
cd server && ./gradlew test # 서버 (Testcontainers, Podman/Docker 필요)
npm test # 프론트증권사에서 상품군(국내주식/해외주식/국내파생/해외파생/금현물)별로 BASE 수수료 위에 EVENT(일괄 이벤트)와 NEGOTIATED(개별 협의수수료) 룰을 등록·검증·승인하고, 계좌별로 어떤 룰이 실제 적용되는지와 그 근거를 투명하게 확인할 수 있도록 만든 수수료 이벤트 정형화 플랫폼의 프로토타입입니다. 룰 등록 시 지배관계(기존 요율 대비 항상 저렴한지) 검증과 역마진(회사부담이 자사 수취분보다 큰지) 경고를 자동 계산하고, 체결 시점에 계좌×feeKey별 활성 룰 중 최저가를 **해석(resolve)**하는 과정을 화면으로 시연합니다. 백엔드 없이 mock 데이터와 클라이언트 상태(zustand)만으로 동작합니다.
v0.5에서 전량 바인딩 테이블(계좌×품목 materialize)을 완전히 폐기했습니다. 국내 1위 리테일 증권사 규모
(수천만 계좌 × 수천 품목)에서는 계좌×품목 전량 전개가 물리적으로 불가능하기 때문입니다. 대신 해석형
코어로 전환했습니다: 룰은 룰로만 저장하고, 체결 시점에 resolve(계좌, feeKey)가 협의 예외 → 이벤트 후보 →
BASE 순으로 후보를 모아 최저가를 즉석 계산하며, 그 결과를 resolved_cache(계좌, feeKey)에 read-through로
캐시합니다. 룰 승인/기간연장/배치 변경 시에는 전량 재계산 대신 영향받는 scope/계좌의 캐시만 무효화해
다음 조회에서 lazy하게 재해석되도록 합니다.
feeKey는 상품군마다 입도가 다릅니다 — 주식은 품목을 붕괴시켜 (거래소·세션·채널)만으로 해석하고(카디널리티가
가장 큰 곳에서 품목을 없애는 것이 규모의 핵심), 파생만 품목을 유지합니다. 채널은 전 상품군 공통 축으로
HTS/MTS/API/ARS/센터/반대매매 6종을 둡니다. 계좌번호는 12자리 숫자 문자열(110000001001)로 통일했습니다.
협의수수료는 계좌ID로 인덱싱된 예외(overlay) 테이블(nego_exception)로만 존재해 전체 계좌 수와 무관하게
작습니다.
v0.1에서 추가된 것: 대시보드의 룰 검색·필터와 행 클릭 상세 조회, 위저드 입력 보존(탭 전환 시), 품목 0건 상신 차단과 지배관계 실패 사유 구체화(어느 가격에서 얼마나 비싼지), CSV 자동 반영, 예상 감면 누계 카드.
v0.3에서 추가된 것: 수수료 결정 흐름 탭 — 계좌×품목을 고르면 신규 플랫폼(To-Be)의 수수료 결정
로직을 ①대상 판정 → ②후보 룰 수집(탈락 룰·사유 포함) → ③최저가 경쟁 → ④확정 바인딩 → ⑤체결→금액
계산의 5단계로, 각 단계가 참조하는 실제 데이터 행과 함께 단계별로 시연합니다(v0.5부터는 도메인의
resolve()/resolveFee() 결과를 그리며, ④는 캐시 적중/미스를 보여줍니다 — 아래 v0.5 문단 참고). 화면이
보여주는 흐름이 곧 엔진의 실제 결정 과정입니다. 이 흐름에서 등급은 수수료 결정에 쓰이지 않습니다
— 등급표 직조회(As-Is)를 벗어나, 각 룰이 스스로 선언한 대상(전체·지정계좌·조건·신청·휴면복귀)에 이 계좌가
걸리는지로 후보를 모아 최저가로 경쟁시킵니다. 협의수수료 자격은 신청 + 조건(예: 6개월평균자산 ≥ 5억)입니다.
v0.4에서 추가된 것: 배치 플로우 탭 — [배치 실행] 버튼 하나로 이 플랫폼을 실제 운영하는 정형화 배치 6종 (①룰 발효/만료 → ②지표 재산정 → ③종목 동기화 → ④협수 조건 평가 → ⑤재해석 → ⑥지배관계 재검증)을 실제 엔진 호출로 순차 실행하고, 각 잡의 진행·결과와 잡 클릭 시 before→after 변경 내역을 보여줍니다. 배치는 실제 상태(store)를 변경하므로 실행 후 대시보드·계좌 조회·협수 관리가 진짜로 갱신됩니다. 대표 시연은 트리거 캐스케이드입니다: ②가 A-1002(110000001002)의 6개월평균자산을 문턱(5억) 위로 올리면 → ④가 협수 조건 충족으로 판정·자동연장하고(그 계좌의 캐시를 무효화) → ⑤ 재해석에서 A-1002의 해외주식이 BASE → 협의수수료로 바뀝니다.
v0.5에서 추가/전환된 것: 전량 바인딩 테이블 완전 폐기. 배치 잡은 더 이상 계좌×품목을 전량 재전개하지
않고 영향받는 scope/계좌의 캐시만 무효화합니다 — ①룰 발효/만료와 협의수수료 승인/기간연장은 해당 룰의
scope로 invalidateByScope, ②지표 재산정과 ④협수 grant 부여/해지는 해당 계좌로 invalidateAccount를
호출합니다. ⑤(구 "바인딩 재계산")는 전량 재전개 대신 계좌마다 resolveFee를 다시 호출해 캐시를 재적재하는
재해석 잡입니다. 배치 잡은 전부 도메인/스토어의 실제 함수(classifyLifecycle·nudgeMetrics·
evalCondition·resolve/resolveFee·ResolveCache.invalidateByScope/invalidateAccount·
revalidateDominance)를 호출하므로 화면이 보여주는 것이 곧 실제 로직입니다.
npm install
npm run devnpm testnpm run build- 대시보드에서 활성 룰 목록과 진행률, 수수료 해석 캐시 현황(적중/미스 건수), 역마진 경고 룰 수, 예상
감면 누계를 확인합니다. 계좌×품목 전량 바인딩 테이블은 없다는 것과, 예외(협의)만 저장하고 나머지는
해석·캐시한다는 점이 카드 문구로 표시됩니다. 룰 행을 클릭하면 상세 요건(적용범위·요율표 구성요소·검증
배지·대상·변경 이력)이 그 자리에 펼쳐집니다. 상단 검색창에 룰 이름·ID·품목코드(예:
6A)를 입력하거나 유형/상태/상품군 필터로 목록을 좁힐 수 있습니다(기본 상태 필터는 '활성'이며, '전체'로 바꾸면 승인대기·반려·종료 룰도 보입니다). - 종목 마스터에서 코드/이름으로 종목을 검색해 존재를 확인하고, CSV 등록 패널에 신규 종목 한 줄
(
코드,이름,상품군,거래소,통화)을 붙여넣어 [등록]합니다. 이어서 [원장 동기화] 버튼을 눌러 결정적으로 유입되는 신규 상장 종목 목록과 건수를 확인합니다. 신규 품목은 아직 어떤 계좌도 조회한 적이 없으므로 캐시에 없고, 무효화도 필요 없습니다(다음 조회 시 자연히 해석됩니다). - 이벤트 등록 위저드에서 CME 6A를 대상으로 하는 신규 EVENT를 6단계 순서로 등록합니다.
- 기본정보 (이름/유형/적용형태/기간/상품군 — v0.2부터 상품군을 1단계에서 선택합니다)
- 적용범위 (상품군에 따라 조건부 렌더: 국내주식은 거래소(KRX/NXT) 체크박스가 있으나, 국내파생·금현물은 거래소가 KRX로 고정되어 거래소 선택 UI 자체가 없습니다. 세션 체크박스 + 종목 선택은 듀얼 패널 픽커(좌측 검색/거래소·상태 필터/전체 선택, 우측 선택·제외 chip)로 대량 종목을 다룹니다)
- 요율표 (구성요소 이름/부담주체/방식/값 입력)
- 대상 (전체 또는 계좌 CSV)
- 시뮬레이션 (지배관계·역마진 판정 + 대상 계좌 수·예상 감면액 확인)
- 상신 (기안자 확인 후 제출 → 상태: 승인대기)
- 승인함에서 방금 상신한 룰의 검증 배지(지배관계 ✓, 역마진 경고 여부)와 시뮬레이션 결과를 확인한 뒤 승인합니다.
- 대시보드로 돌아와 활성 이벤트 수와 예상 감면 누계가 증가한 것을 확인하고, 검색창에
6A를 입력해 방금 등록한 이벤트를 찾아 클릭해 상세를 확인합니다. - 계좌 조회에서 CME 6A를 보유한 계좌(예:
110000001001)를 선택해 feeKey 해석기로 (거래소·세션·채널· 품목)을 지정하면 해석 출처 룰이 새로 승인한 이벤트로 바뀌었는지 확인하고, 우측 체결 시뮬레이터에 가격·수량을 입력해 수수료가 인하되었는지 확인합니다. 화면 상단의 협의 예외 패널에서는 이 계좌에 걸린 협의수수료 overlay(있는 경우)를 계좌ID로 직접 조회합니다 — 전량 바인딩 목록을 뒤지는 대신 예외만 봅니다. - 협수 관리에서 협의수수료 조건 게이지(충족/미충족)를 확인하고, 조건을 충족한 건에 대해 원클릭으로 기간을 연장합니다(연장 시 해당 scope의 캐시가 무효화됩니다).
- 수수료 결정 흐름에서 계좌(예:
110000001001)와 feeKey(품목/세션/채널)를 고른 뒤 [해석]으로 ①→⑤를 짚어 갑니다. ①에서 조회 컨텍스트(feeKey)가, ②에서 후보 룰과 탈락 룰·사유가, ③에서 후보별 평균 고객부과액 경쟁과 최저가 승자가, ④에서 해석 결과가 캐시 적중(재계산 없음)인지 캐시 미스(계산 후 저장) 인지가, ⑤에서 체결가·수량으로 즉석 계산된 수수료 구성요소 분해가 나옵니다. 같은 계좌·feeKey를 다시 해석하면 ④가 캐시 적중으로 바뀌는 것을 볼 수 있습니다. 팀장 보고용 포인트: 후보 경쟁으로 최저가가 정해지고, 원장은 (캐시 적중 시) 저장된 답 한 행만 조회하며, 금액은 저장하지 않고 요율표×체결로 즉석 계산한다는 해석형 흐름을 실제 로직(그림이 아니라resolve/resolveFee산출)으로 보여줍니다. - 배치 플로우에서 [배치 실행]을 눌러 정형화 배치 6종이 순차로 도는 것을 봅니다. ②지표 재산정에서
110000001002의 6개월평균자산이 4.9억 → 5.14억으로 오르고(그 계좌 캐시 무효화), ④협수 조건 평가가 이를 충족으로 판정하며(다시 캐시 무효화), ⑤재해석 카드를 클릭하면110000001002의 해외주식이 BASE → 협의수수료로 바뀐 행이 드릴다운에 보입니다(트리거 캐스케이드 — 전량 재전개가 아니라 무효화된 캐시가 다음 조회 때 재해석되는 것). 실행 후 대시보드로 이동하면 캐시 통계·활성 룰 수가 실제로 갱신돼 있습니다. [초기화]로 mock 상태를 되돌릴 수 있습니다.
- 백엔드가 없는 mock 데이터 기반이며, 모든 상태는 클라이언트 메모리(zustand)에만 존재합니다.
- Excel 업로드 대신 CSV 붙여넣기로 대체되어 있습니다.
- 기준일(
TODAY)이2026-07-04로 고정되어 있어 실제 날짜와 무관하게 동작합니다. - 새로고침 시 모든 상태가 초기 mock 데이터로 리셋됩니다. 단, 탭 전환 시에는 위저드 작성 중 입력이 보존됩니다.
- 위저드로 등록한 협의수수료 룰은 Enrollment 생성 UI가 없어 협수 관리 화면에 나타나지 않음 (mock Enrollment만 표시)
- 종목 마스터는 가상 생성 데이터 ~2,858건(원장 연동 시뮬레이션)이며, 실제 원장 시스템과 연동되어 있지 않습니다. [원장 동기화]는 결정적으로 정해진 신규 상장 풀에서 유입되는 것을 흉내 낸 시뮬레이션입니다.
- 배치 플로우의 각 잡은 실제 도메인/스토어 함수를 호출하지만, 원장·체결 로그가 없어 일부 입력은 시뮬레이션입니다. 특히 ②지표 재산정은 6개월 지표를 결정적으로 +5% 증분(실제 체결 재집계가 아님)하고, 잡 순차 실행은 스케줄러 없이 화면에서 400ms 간격으로 연출합니다. 실패·롤백·재시도·이력 영속화는 범위 밖입니다.
- 캐시 무효화는 즉시 삭제(eager)입니다. 설계 문서(v0.5 spec §6)가 제안하는 lazy 무효화(다음 조회 시 stale 표시 후 재계산)가 아니라, 영향받는 scope/계좌의 캐시 엔트리를 그 자리에서 즉시 제거합니다. 4계좌 규모 프로토타입에서는 차이가 없지만, 실제 규모(수천만 계좌)에서는 무효화 자체의 비용(그 scope에 걸리는 캐시 엔트리 스캔)이 커질 수 있어 별도 검토가 필요합니다.
resolve()의 최저가 경쟁은 후보 요율표에서 몇 개 표본 가격을 뽑아 평균 고객부과액을 비교하는 방식이라, 실제 체결가 분포에 따라 미세하게 달라질 수 있는 근사(표본 기반) 로직입니다. 배치 ②지표 재산정과 마찬가지로 실 체결 로그 기반이 아닌 시뮬레이션 입력입니다.
- v0 설계: docs/superpowers/specs/2026-07-04-fee-event-platform-design.md
- v0.1 증분 설계: docs/superpowers/specs/2026-07-04-v01-dashboard-detail-search-design.md
- v0.2 증분 설계: docs/superpowers/specs/2026-07-04-v02-instrument-master-design.md
- v0.3 증분 설계: docs/superpowers/specs/2026-07-04-v03-fee-decision-trace-design.md
- v0.4 증분 설계: docs/superpowers/specs/2026-07-04-v04-batch-pipeline-design.md
- v0.5 증분 설계: docs/superpowers/specs/2026-07-04-v05-scalable-fee-architecture-design.md
- 실 시스템 이관 문서: docs/table-design.md, docs/business-process.md