← 목록
프로젝트 2026-04-16 22KB 읽기 25분

crowny-project — 플렉시블팩토리 v3.0 (실사용 사이클 + 추천보상 + 라이프 그래프 + 12년 최적화 + 프로덕션 고도화)

개요

project.crowny.org:9730 — 플렉시블팩토리(고객) → 광주 은광교회 LED Wall 설치 건을 실제로 굴릴 수 있도록 PM 사이클 전반을 보강. 동시에 사용자 템플릿 생태계와 크로스-프로젝트 통합 시각화(라이프 그래프)를 추가.

무엇을 했는가

A. 프로젝트 라이프사이클 (실사용 기능)

테이블/엔드포인트 추가 (server.js):

  • user_templates (id, ownerId, sourceProjectId, name, visibility, teamId, tree, depth, tags, price, downloads, icon, createdAt)
  • checklists (id, projectId, nodePath, items, updatedBy, updatedAt)
  • snapshots (id, projectId, label, note, root, meta, description, createdBy, createdAt)
  • template_votes (id, templateId, userId, at)
엔드포인트:
  • DELETE /api/projects/:id — 프로젝트 삭제 (manager↑)
  • POST /api/projects/:id/rename — 이름 변경
  • POST /api/projects/:id/clone — 복제 (트리 깊은 복사)
  • POST /api/projects/:id/to-template — 템플릿 변환
  • visibility: personal | team | public
  • public + price>0 → 배포 시 사용료 자동 송금
  • GET /api/user-templates?scope=&sort= — 사용자 템플릿 갤러리
  • scope: all | mine | team | public
  • sort: new | popular | recommends | downloads
  • GET/DELETE /api/user-templates/:id
  • POST /api/user-templates/:id/deploy
  • POST /api/user-templates/:id/recommend — 토글식 추천 (1인 1표)
  • stripTreeForTemplate() 헬퍼 — 트리 → 템플릿 변환 시 PII/상태 제거 (assignee/managers/log/tiomtaum 초기화)

    UI: 프로젝트 상세 패널에 5개 버튼 추가 (체크리스트/스냅샷/이름수정/템플릿화/삭제)

    B. 체크리스트 + 8종 프리셋

    CHECKLIST_PRESETS 상수 (8종, 59항목): install-pre / install-active / install-post / contract / site-survey / claim / delivery / meeting

    엔드포인트:

    • GET /api/checklist-presets / GET /api/checklist-presets/:id
    • GET/PUT /api/projects/:id/checklist?path= — 노드별 체크리스트
    • GET /api/projects/:id/checklists — 전체 요약
    • POST /api/projects/:id/checklist/apply — 프리셋 일괄 적용
    UI: renderChecklistPanel() — 노드 트리에서 체크리스트 보기 + 프리셋 다중선택 prompt

    C. 스냅샷/롤백

    엔드포인트:

    • POST /api/projects/:id/snapshots — 현재 상태 캡처 (label, note)
    • GET /api/projects/:id/snapshots
    • GET /api/projects/:id/snapshots/:sid
    • POST /api/projects/:id/snapshots/:sid/rollback — 복원 (실행 전 자동 백업 스냅샷 생성)
    UI: renderSnapshotPanel() — 생성/롤백/삭제

    D. 추천/보상 시스템 (사용자 템플릿 생태계)

    핵심 로직:

    • 1인 1표 토글 (재호출 시 취소)
    • 자기 템플릿 추천 차단
    • 비공개 템플릿 추천 차단, 팀 템플릿은 팀원만
    • 공개 템플릿 첫 추천 시 작성자에게 +1맘 자동 지급 (system 발신, rewards 테이블 기록)
    • 응답: { ok, voted, recommends, mamPaid }
    인기도 정렬: popularity = recommends × 3 + downloads × 2

    UI: 템플릿 카드에 ♥ 추천 버튼 (voted 상태 색상 변화), 정렬 토글 4종 (최신/인기/추천/다운로드)

    검증:

    • tester2 → tester1 템플릿 추천 → tester1 지갑 mam +1 확인
    • 자기 추천 시도 → 400 "자기 템플릿에는 추천 불가"
    • 토글 재추천 → recommends 0으로 감소

    E. 라이프 그래프 (크로스-프로젝트 시각화)

    GET /api/life-graph 신규:

    • timeline: 프로젝트별 진행률/비용/노드수/생성일
    • rewardTimeline: 30일 일별 맘/포네 적립
    • activityHeatmap: 30일 일별 활동 건수
    • health: 전체 평균 건강도
    • wallet, totalProjects, totalRewards
    UI: 환영 대시보드에 "라이프 그래프" 카드:
  • KPI 4개 (건강도 색상등급 / 진행중 프로젝트 / 맘 보유 / 30일 적립)
  • Chart.js 차트 3개:
  • 프로젝트 진행률 수평 바 (80%↑녹/40~80주황/↓40빨)
  • 보상 추이 라인차트 (맘 노란/포네 보라)
  • 활동 히트맵 바차트 (밀도별 투명도)
  • F. 안정성/보안 강화 (감사 후 보강)

    서버 감사로 발견된 5개 이슈 모두 해결:

    1. /api/life-graph try/catch — 손상 노드 있어도 다른 프로젝트 집계 계속
    2. /api/projects/:id/gantt try/catch + canReadProject — 권한 없는 사용자 차단 + 손상 노드 안전 처리
    3. GET /api/teams/:id — 팀원만 조회 가능 (이전엔 누구나)
    4. /api/projects/:id/contacts — canReadProject 필요 (이전엔 누구나)
    5. /api/projects/:id/split — 자식 이름 3개 필수 + 공백 거절 + sanitizeStr

    G. share.html 데이터 활용 보강

    공개 API가 description/meta/tree 모두 반환했지만 share.html에서 미사용이던 부분 모두 표시:

    • 설명 본문 (white-space:pre-wrap)
    • 메타 태그 (유형/규모/고객/비전 — 칩 형태)
    • 프로젝트 구조 (최대 20개 노드, TOAU 색상 + 진행바)
    • 총 범주 수 (헤더에 표시)

    검증 결과 (curl 스모크)

    [life-graph]    GET  /api/life-graph                      → projects=2, health=20, timeline OK ✅
    [split 검증]    POST /api/projects/:id/split (빈이름)     → 400 "공백일 수 없음"             ✅
    [gantt 권한]    GET  /api/projects/:id/gantt (외부유저)   → 403 "읽기 권한 없음"             ✅
    [추천 보상]     POST /api/user-templates/:id/recommend     → +1맘 작성자 자동 지급           ✅
    [자기 추천]     POST /api/user-templates/:id/recommend     → 400 "자기 템플릿에는 추천 불가" ✅
    [정렬 인기순]   GET  /api/user-templates?sort=popular     → recommends×3+downloads×2 정렬   ✅
    [공유 페이지]   GET  /api/public/projects/:id            → name/desc/meta/tree 전부 반환   ✅
    

    관련 파일

  • /Users/ef/crowny-project/server.js (4400+ 줄)
  • db.table('user_templates'/'checklists'/'snapshots'/'template_votes') — 312~315
  • CHECKLIST_PRESETS 8종 — search "install-pre"
  • stripTreeForTemplate() — 3533
  • POST /api/projects/:id/to-template — 3543
  • GET /api/user-templates (sort/voted/popularity) — 3585
  • POST /api/user-templates/:id/recommend (보상지급) — 3637
  • GET /api/life-graph — 3099
  • splitMatch 입력검증 — 4120
  • ganttMatch try/catch + canReadProject — 4064
  • teamMatch GET 팀원체크 — 2688
  • contactsMatch canReadProject — 3020
  • /Users/ef/crowny-project/public/js/app.js
  • renderLifeGraph() Chart.js — 369
  • openUserTemplatePanel(scope, sort) 정렬+추천 — 2680
  • 5개 버튼 (rename/clone/template/snapshot/checklist/delete) — renderProject 내부
  • /Users/ef/crowny-project/public/share.html
  • 설명/메타태그/트리 표시 추가
  • 추가 완료 (2026-04-16 후속)

    H. 인쇄 가능 견적서 페이지 ✅

  • GET /api/public/estimates/:id — 공개 견적 JSON (estimate + project + issue 정보)
  • GET /estimate/:id — 정적 HTML 리라이트 (server.js:2518)
  • public/estimate.html — A4 인쇄 친화 견적서 (회사/고객/항목표/합계/조건)
  • @media print → 브라우저 Ctrl+P → PDF 저장
  • 도구바(PDF 저장 버튼) + 헤더(견적번호/발행일) + 공급자/고객 블록
  • 항목표(역할/인원/일수/단가/가산/금액) + 공급가액/VAT/합계
  • 견적 조건 5항 + 직인란
  • share.html 제출 결과에 "📄 견적서 보기 / PDF 저장 →" 링크 표시
  • 검증: curl /api/public/estimates/e7b30282... → projectName=광주은광교회2026, total=13915000, lines=4
  • I. 간트 차트 직접 진입 ✅

    • renderGantt() / loadGantt() (app.js:2447) 이미 구현되어 있었음 (분석 모달 내부)
    • 프로젝트 패널 툴바에 "간트" 버튼 추가 → 독립 카드 #ganttCard
    • 접기/펼치기 별도 핸들러 (ganttExpandAll2 / ganttCollapseAll2)
    • 검증: /api/projects/:id/gantt → items=4, 트리 분할 후 L0/L1 계층 표시 정상

    잔여 이슈 / 다음 단계

    채울 수 있는 기능 — 우선순위 순:

    1. 체크리스트 카운트 사이드바 노출 — 미완료 체크 항목 수를 프로젝트 카드/사이드바에
    배지로 표시 (현재는 패널 열어야 보임).

    1. 스냅샷 diff 뷰 — 두 스냅샷 비교 (어떤 노드가 변경됐는지). 현재는 롤백만 가능.
    1. 워크플로우 자동 트리거 검증/api/workflow-presets 5종 + 커스텀 룰은
    생성/조회만 됨. 실제 trigger 발동 코드 경로 추적 + 알림 연결 필요.

    1. 실시간 라이프 그래프 갱신 — 현재는 페이지 진입 시 1회 fetch.
    WebSocket으로 reward/activity 이벤트 수신 시 차트 partial update.

    1. 템플릿 가격 송금 검증 — public + price>0 deploy 시 송금 코드 있으나
    잔액 부족/수신자 미존재 케이스 E2E 미검증.

    1. 공유 링크 만료/조회제한 — 현재 /share/:id 무한 공개. 만료일/조회수
    제한/특정 사용자만 옵션 필요 (shares 테이블 확장).

    1. 모바일 반응형 — 환영 대시보드 grid 4열이 좁은 화면에서 깨질 수 있음.
    라이프 그래프 차트 2열 → 1열 폴백.

    1. 백엔드 일괄 try/catch 확장 — life-graph/gantt 외에도 analytics, insight,
    tiomtaum-batch 등 트리 순회 엔드포인트 전체에 동일 패턴 적용.

    1. 간트 일정 편집 UX 개선 — 날짜 미설정 노드의 📅 버튼은 있으나 드래그로 바 조정은 미지원.
    좌/우 핸들 드래그 → startDate/endDate 업데이트 (PATCH /schedule) 연결 필요.

    다음 세션 참고

    • 추천 시스템 보상은 공개 템플릿 첫 추천만 지급 (반복 토글 시 0맘).
    RPS 폭증 우려 시 template_votes insert 전 rate limit 추가 검토.

    • 라이프 그래프 활동 히트맵은 db.select('activity', a => a.at >= day30) 풀스캔.
    활동 테이블 커지면 인덱스 (at desc) 필요.

    • canReadProject(pj, uname) / hasPermission(pj, uname, role) 헬퍼는
    모든 신규 프로젝트 엔드포인트에 의무 적용.

    • 사용자 템플릿 추가/수정 후 db.compact('user_templates') 호출 누락 케이스 체크 필요.

    J. 초대 시스템 (2026-04-16 추가)

    권한 지정 초대 링크/SMS 발송 → 수락 시 자동 가입·로그인·프로젝트 입장.

    백엔드

  • invites 테이블 추가 (token 24 hex, 일회용, TTL 기본 14일 · 최대 90일)
  • 매니저↑ 엔드포인트:
  • POST /api/projects/:id/invite — {inviteeName, inviteeContact, permission, ttlDays, note} → {invite, link, smsText}
  • GET /api/projects/:id/invite — 목록
  • DELETE /api/projects/:id/invites/:iid — 회수
  • 공개 엔드포인트 (무인증):
  • GET /api/invite/:token — 초대 정보 + 프로젝트 프리뷰 + 통계
  • POST /api/invite/:token/accept — {mode:'register'|'login', username, password, displayName}
  • - crownyAuth로 유저 생성/로그인 → shares 로우에 permission 반영 → used=true - 반환 {token, user, projectId, projectName, permission}

    프론트

  • /invite/:tokenpublic/invite.html (무인증, 신규가입/기존로그인 탭)
  • 수락 성공 시 localStorage.setItem('pj_token', r.token) + pj_autoOpenProject=projectId/ 이동 → 자동 진입
  • 메인 앱 헤더 초대하기 버튼 → 모달 (이름/연락처/권한/유효일/메시지)
  • 발송 후 [링크 복사] / [문자 복사] / [SMS 앱 열기] 3버튼
  • 초대 이력 리스트 (대기/사용됨/만료, 회수 버튼)
  • E2E 스모크 통과

    1. PM 로그인 · 초대 생성 (JSON newline escape 정상)
    2. 초대 토큰으로 프리뷰 조회
    3. mode:'register' 로 신규 유저 kimyoungsang 가입 + share 생성
    4. 신규 토큰으로 프로젝트 읽기 성공
    5. 같은 토큰 재사용 → 이미 사용된 초대 (410 차단)

    L. LED 카탈로그 + 설계 엔진 (2026-04-17 추가, DOT-QUOTE v45 포팅)

    DOT-QUOTE v45 (4958줄 JSX)에서 핵심 로직 포팅:

    카탈로그 DB (3테이블)

    • led_modules: 16종 (P1.25 COB ~ P5.95 Outdoor), USD/㎡ 원가, priceMode(b2b/margin/custom)
    • led_processors: 27종 (Novastar H2~H20, VX400~2000, TU15~4K, Pixelhue P10/P20/P80/Q8/U5, Media Server)
    • led_cabinets: 4종 (640x480, 640x640, 960x480, 960x640)
    • led_settings: usdRate(1500), b2bMulti(1.6), remitExtraPct(5)
    • mergeDB 패턴: 사용자 편집 보존 + 신규 기본값 자동 병합

    설계 엔진 (5함수)

    • calcAutoLayout(dW, dH, fitMode) — 640mm 캐비닛 + 320mm 잔여 패킹
    • calcVerticalMix(dH, fitMode) — 480/640 높이 혼합 최적화
    • calcLedSize(dW, dH, mode, presetId, fit) — 캐비닛/모듈 모드, over/under
    • getResolution(pitch, cols, rows, cabW, cabH, rowLayout) — 픽셀 해상도
    • calcPortGuide(totalPx, cabTotalPx, redundant) — VX/H/TU 프로세서 추천
    • calcSparesAuto(sqm) — ≤10㎡:10개 / ≤20㎡:20개 / >20㎡:ceil(sqm)

    API

    • GET /api/catalog/{modules,processors,cabinets,settings,cost-defaults,status-map}
    • PUT /api/catalog/{modules,processors,cabinets,settings} — 카탈로그 편집
    • POST /api/projects/:id/design/calc — {desiredW, desiredH, fitMode, cabinetPreset, moduleId} → layout+resolution+portGuide+spares
    • POST /api/projects/:id/design — 설계 저장

    자재/노무 기본 단가 (₩/㎡)

    항목₩/㎡
    Steel Frame47,000
    Steel Labor80,000
    HF-IX Cable18,000
    CAT6 Cable10,000
    PSU64,000
    Harmonic Filter40,000
    Installation250,000
    Scaffold30,000
    EWP25,000
    Dismantling20,000
    Conduit15,000

    M. 현장 파악 (Site Survey) 모듈 (2026-04-17 추가)

    DOT-QUOTE에 없는 신규 기능.

    데이터 모델

    survey: {
      id, projectId, surveyDate, surveyedBy, location,
      photos: [{id, url, annotation, timestamp}],
      dimensions: {wallW, wallH, ceilingH, powerKw, hangPoints, accessRoute},
      environment: {indoorOutdoor, lighting, temperature, humidity},
      notes
    }
    

    API

    • POST /api/projects/:id/survey — 현장 조사 저장
    • GET /api/projects/:id/survey — 목록
    • PUT /api/projects/:id/survey/:sid — 수정
    • DELETE /api/projects/:id/survey/:sid — 삭제

    현장→설계 자동 전이

    벽면 W/H 입력 → "→ 설계로 전이" 버튼 → LED 설계 패널에 desiredW/desiredH 자동 채움

    N. 하드웨어 견적 (BOM 기반) (2026-04-17 추가)

    DOT-QUOTE의 calcPricing() 포팅 — 기존 인건비 견적(buildEstimate)과 별도.

    가격 엔진 (calcHwPricing)

    1. 모듈 비용: moduleGroups[] → USD원가 × 패킹비(5%) × 환율 × ㎡
    2. 프로세서: 원가(krw) / 판가(marginPct% 기본 20%, 만원 올림)
    3. 자재 5종: ㎡ auto 또는 직접입력
    4. 노무 6종: ㎡ auto 또는 직접입력
    5. 마진: B2B(20%) / B2C(30%) / PUBLIC(15%) / CUSTOM
    6. VAT 10% → 할인% → 만원 절사

    API

    • POST /api/projects/:id/hw-estimate — 견적 생성 (result 자동 계산)
    • GET /api/projects/:id/hw-estimate — 이력
    • POST /api/projects/:id/hw-estimate/calc — 계산만 (저장 없이)

    프론트 3버튼 워크플로우

    현장조사 → (벽면 W/H) → LED설계 → (모듈+㎡+프로세서) → 하드웨어견적 각 단계에서 "→ 다음 단계" 버튼으로 데이터 자동 전이

    스모크 테스트 결과 (P1.86 GOB, 3200×1920mm)

    • 실제 크기: 3200×1920mm = 6.14㎡
    • 캐비닛: 5×4 = 20ea, 모듈 120ea
    • 해상도: 1720×1032 = 1,775,040px
    • 포트: 3 → VX400Pro × 1
    • 견적: 원가 ₩10,909K → 최종 ₩21,360K (마진 79.4%)

    K. 튜토리얼 페이지 (2026-04-16 추가)

    • /tutorial, /help, /tutorial/*, /help/*public/tutorial.html
    • 메인 앱 헤더에 📖 가이드 링크 추가 (신규 탭)
    • 10개 섹션 + 사이드바 스크롤-스파이:
    1. TOAU 개요 2. 프로젝트 생성 3. 범주 3분할 4. TOAU 상태전이 5. 초대/권한 6. 체크리스트 7. 견적서 8. 간트 9. 라이프 그래프 10. 스냅샷/템플릿
    • 권한표(읽기/쓰기/관리자) · FAQ 포함

    O. 12년 운영 최적화 (v2.6, 2026-04-16)

    보안

    • escHtml() 전역 XSS 방어 (pj.name, description, comments, notifications 등 전부)
    • sanitizeStr() 강화: on\w+=, javascript:, data: 패턴 차단
    • Rate Limiting 4등급: auth(10/min), write(30/min), export(5/min), general(120/min)
    • 분할 깊이 제한(6단계), 이미 분할된 노드 재분할 차단
    • CSV 내보내기 10,000노드 상한

    CellDB 내구성

    • WAL CRC32 체크섬: 각 WAL 행에 crc32|payload 형식, 손상 행 자동 스킵
    • 3세대 백업 로테이션: .bak.bak.1.bak.2 순차 회전
    • 크래시 복구: 메인 파일 손상 시 3세대 백업 순차 시도, JSON 구조 검증

    안정성

    • _fatalCount 50회 초과 시 자동 셧다운 (좀비 프로세스 방지)
    • unhandledRejection 비치명 WARN 처리
    • TTL 자동정리: 세션(24h), 활동(90d), 알림(60d+읽음), 만료 초대 — 매시간
    • O(n²) → O(1): 프로젝트 목록 중복 제거 Set 기반

    프론트엔드

    • 탭 비활성 시 알림 폴링 + WS 재연결 억제 (배터리/대역폭 절약)
    • beforeunload: interval 해제, WS 정리 close
    • 탭 복귀 시 즉시 알림 갱신 + WS 재연결

    P. 프로덕션 고도화 (v2.7, 2026-04-17)

    보안 헤더

    • HSTS: max-age=31536000 (1년), includeSubDomains
    • CSP: default-src 'self', script/style/img/connect/font 세분화
    • Permissions-Policy: 카메라/마이크/위치 차단

    공격 방어

    • 경로 탐색: path.resolve() + publicDir 경계 체크, ../../ 요청 차단
    • Slow Loris: server.setTimeout(30s), headersTimeout(15s), requestTimeout(30s)
    • parseBody: 10초 타임아웃 (느린 클라이언트 소켓 강제 해제)
    • 헤더 인젝션: Content-Disposition RFC 5987 인코딩

    성능

    • gzip: JSON 응답(1KB 이상) + 정적 파일(html/css/js/svg) 자동 압축
    • 캐시: 정적 에셋 max-age=30일+immutable, HTML no-cache

    안정성

    • Graceful shutdown: server.close() → HTTP 드레인(10초) → DB 저장 → exit
    • 503 응답: 셧다운 중 새 요청에 Retry-After:30 반환
    • 세션: 슬라이딩 갱신(5분마다 lastLogin 갱신) + 절대 만료 30일

    Q. 옵저버빌리티 + 방어 심화 (v2.8, 2026-04-17)

    구조화 로깅

    • JSON 형식: {"t":"ISO","l":"info","m":"req","rid":"hex16","method":"GET","path":"/api/...","status":200,"ms":5}
    • 레벨 필터: LOG_LEVEL=debug|info|warn|error|fatal 환경변수
    • 서버 에러, 셧다운, uncaughtException 등 핵심 경로 전환

    옵저버빌리티

    • Request ID: 요청마다 crypto.randomBytes(8).hex, X-Request-Id 응답 헤더
    • /api/metrics: 총 요청수, 메서드별, 상태코드별, 에러 수, 지연시간 avg/max, RSS/heap MB, PID
    • /api/healthz: liveness (프로세스 생존)
    • /api/ready: readiness (테이블 5개↑ + 디스크 여유)

    보안

    • 프로토타입 오염 차단: stripProto()__proto__/constructor/prototype 키 재귀 제거
    • WS 메시지 검증: 타입 화이트리스트(3종), 4KB 제한, 5분마다 토큰 재검증 → 만료 시 auth_expired 전송 후 소켓 종료

    데이터 보호

    • 자동 백업: 6시간마다 전체 DB JSON → data/auto-backups/backup-YYYY-MM-DDTHH-MM-SS.json
    • 최대 28개 유지 (7일 분), 초과 시 오래된 순 삭제
    • 멱등성 키: Idempotency-Key 헤더 → 1시간 캐시, 동일 키 재요청 시 캐시된 응답 반환

    API 개선

    • 페이지네이션: /api/activity, /api/projects/:id/activity?limit=N&offset=M (max 200)
    • 응답: { items: [], total, limit, offset }

    R. 데이터 무결성 + 접근성 (v2.9, 2026-04-18)

    데이터 무결성

    • 지갑 뮤텍스: per-user async 락 (Promise 기반 큐) → 동시 reward/tip 레이스 방지
    • 금액 검증: Number.isFinite(amount) && 0 < amount <= 1,000,000 + Math.floor()
    • 프로젝트 삭제 캐스케이드: 9개 테이블(comments/activity/shares/invites/designs/surveys/hw_estimates/snapshots/checklists) 연쇄 정리
    • 팀 삭제 고아 정리: 프로젝트 teamId→null + 팀 대상 shares 삭제

    DB 성능

    • 인덱스 추가: sessions.username, invites.token, activity.projectId
    • 기존: projects.id, sessions._key, logic.id, teams.id

    프론트엔드 복원력

    • API 함수: 네트워크 오류 + 503 에 지수 백오프(1s, 2s) 최대 2회 재시도
    • JSON 파싱 실패 안전 처리

    접근성 (ARIA)

    • 트리: role="tree", role="treeitem", tabindex="0", aria-expanded, aria-label
    • 모달: role="dialog", aria-modal="true", 첫 입력 요소 자동 포커스

    S. 입력 검증 강화 + WS 방어 + 증분 동기화 (v3.0, 2026-04-17)

    수치 입력 검증 강화

    • safeNum(): 범위 클램핑 유틸 (Number.isFinite + min/max/fallback)
    • 적용: calcCostLine 인원/일수(1~9999), 팁 금액(1~1000), HW견적 discPct/customMargin/truncUnit
    • HW견적 타입 검증: screens 배열 강제, materials/labor 객체 강제, bizType/status 화이트리스트
    • 팁 타입 화이트리스트: mam/phone만 허용 (tip + tip-message 양쪽)

    에러 메시지 정보 유출 차단

    • 지갑 잔액 노출 제거 (맘 잔액 부족 → 보유액 미표시)
    • 수신자 username 노출 제거
    • 공격자에게 내부 상태 추론 불가

    WebSocket 방어

    • IP당 연결 제한: 최대 5개 (wsPerIp Map, connect/disconnect/ping sweep 동기화)
    • 프레임 방어: 64비트 길이(len=127) → 즉시 연결 종료, 4KB 초과 → 연결 종료
    • 불완전 프레임: buf.length < 4 체크 (len=126 경우)

    WS 증분 동기화

    • TOAU/assign 이벤트에 nodePath + node 데이터 포함
    • 프론트: findNodeByPath() 유틸 → 로컬 트리 노드 병합 → renderTree() 재렌더링
    • 병합 불가 시 폴백으로 openProject() 전체 reload
    • auth_expired WS 이벤트 → 세션 만료 토스트