THE IRUM(더이룸) — academy.crowny.org 멀티테넌트 에듀테크 플랫폼 빌드
개요
academy.crowny.org(:9865)를
B2B SaaS 멀티테넌트 학원 플랫폼으로 확장. 사용자 RFP("더이룸 통합 사이트 구성") + 직접 설계한 THE IRUM 관리자 SPA(navy/amber) 기반. 기존 28모듈 백엔드를 전부 재사용하고, RFP 4대 킬러기능을 신규 6모듈로 추가.
더이룸 = 플래그십 테넌트 1호점, 추가 가맹학원은 화이트라벨(
--brand-color/CNAME)로 자기 브랜드 운영.
울트라 병렬(Workflow 11에이전트, 1.07M토큰) 빌드 → opus 적대 감사 → 메인 세션이 라이브 결선·검증.
무엇을 했는지
신규 6모듈 (기존 28 → 34 라우터)
| 모듈 | prefix | RFP 킬러기능 |
|---|
| tenant | /api/tenant | 멀티테넌시 + 화이트라벨 (lib/tenant.js 해석기 동반) |
| dual-ai | /api/dualai | IB·STEAM Dual-AI 교차분석 (radar 5축 + IB Criterion A/B/C/D + STEAM + SDGs배지 + 처방전) |
| clinic | /api/clinic | 오답클리닉 (문항별 오답 매트릭스 + 문제은행 유사문항 자동생성) |
| compare | /api/compare | 다중원생 Grouped Bar 교차비교 + 시계열 History |
| portal | /api/portal | 난수URL 프라이빗 학부모 포털 (4-in-1: 출결/과제/점수/코멘트 + 배지지갑) |
| automation | /api/auto | 보강 D-Day · 평일보정 수납 · 학년 자동진급 · 다중클래스 enroll |
Gemini Mock-우선 어댑터
adapters/gemini.js: GEMINI_MODE=mock(기본, 규칙기반 결정론)|live(실 API, 키없으면 mock 폴백, throw 금지). exports: analyzeCross/generateComment/varyQuestion/healthCheck.
- 한선씨 동반
지능교차.한선 — IB Criterion 환산·처방전 셀코어 룰.
프론트엔드
public/theirum/index.html + theirum.js — 사용자 설계 THE IRUM 관리자 SPA(navy #0f172a/amber #f59e0b) 충실 포팅, mock→라이브 API 결선. 부팅 시 GET /api/tenant/current로 브랜드 CSS변수 주입. API 실패 시 mock 폴백(데모 안전).
public/portal/report.html — 무인증 모바일 학부모 포털 (/report/:token).
public/theirum/onboard.html — 가맹학원 화이트라벨 온보딩(크라우니 베이지+골드 플랫폼 셸).
- 테넌트 시드
데이터/_테넌트/theirum.json (navy/amber, geminiLinks 6/netlifyApps 3/sns — URL은 PLACEHOLDER).
한선씨 동반 7파일 (전부 hanseonc_high COMPILE OK)
멀티테넌시 · 지능교차 · 교차분석 · 오답클리닉 · 비교분석 · 학부모포털 · 업무자동화. PostToolUse 훅이 자동 학습.
라이브 결선 (메인 세션 직접)
- server.js: MODULE_ROUTES +6라인,
req.academyId = resolveTenant(req) 주입(securityCheck 직후), /theirum·/theirum/ 디렉토리 라우트. (portal 에이전트가 /report/:token 서빙 추가)
- lib/auth.js:
/api/tenant/current 공개(EXACT), GET /api/portal/:token 정밀 공개 규칙(list/issue 제외→토큰 유출 차단).
- lib/config.js:
.env 값을 process.env에도 주입(dotenv처럼) — ADMIN_TOKEN/GEMINI_MODE 적용 버그 수정.
.env 신규: ADMIN_TOKEN(랜덤 생성), GEMINI_MODE=mock, OCR_PROVIDER=mock.
감사가 잡은 실버그 (수정완료)
업무자동화.한선: 인라인 세미콜론 주석이 컴파일러 문장종결자로 오인 → COMPILE FAIL. 감사 에이전트가 제거·재컴파일 검증.
- 학년 자동진급:
birthYear 미존재 시 "초5"+1="초51" 문자열 concat 버그 → 생년월일 파싱 + numToGrade("초N/중N/고N") 포맷으로 수정.
- 포털 무인증 화이트리스트:
/api/portal/ prefix가 매칭로직(prefix+'/')과 불일치 + list 유출위험 → 정밀 정규식 규칙으로 교체.
검증 (라이브 HTTP 엔드투엔드)
- 34/34 라우터 로드,
academy.crowny.org/api/tenant/current 200(공개), /theirum 8443 SNI 200.
- Dual-AI: radar{기초85·문해82·성향65·문제해결87·표현79} IB{A6·B5.4·C5.8·D5.8} STEAM85 추천반·배지4·처방전.
- 평일보정: dueDate 2026-07-05(일)→scheduledAlertDate 2026-07-02. 보강 D-Day 정상.
- 오답클리닉: 유사문항 3문항 생성, 취약매트릭스.
- 학부모포털: 무인증 GET 200(4-in-1 완전),
/list 401 보호유지.
- 다중비교: 김지훈[85,75,90]/이서연[70,92,65] grouped-bar.
- 학년: 초5→초5(포맷정상).
관련 파일 경로
- 서비스 루트:
/Users/ef/crowny-academy/
- 신규 모듈:
modules/{tenant,dual-ai,clinic,compare,portal,automation}/routes.js
- 어댑터/lib:
adapters/gemini.js, lib/tenant.js
- 한선씨:
한선씨/{멀티테넌시,지능교차,교차분석,오답클리닉,비교분석,학부모포털,업무자동화}.한선
- 프론트:
public/theirum/{index.html,theirum.js,onboard.html}, public/portal/report.html
- 테넌트 시드:
데이터/_테넌트/theirum.json
- 결선 수정:
server.js, lib/auth.js, lib/config.js, .env
- 원자료:
~/Downloads/index.html(THE IRUM 설계 SSOT), ~/Downloads/더이룸학원 통합 사이트 구성 - Google Gemini.pdf(RFP 8p)
- ADMIN_TOKEN:
/Users/ef/crowny-academy/.env
접속
- 관리자 SPA:
academy.crowny.org/theirum
- 학부모 포털:
academy.crowny.org/report/{난수토큰}
- 가맹 온보딩:
academy.crowny.org/theirum/onboard.html
잔여 이슈
- Gemini 링크/SNS URL = PLACEHOLDER — 실 URL을
PUT /api/tenant/theirum(Bearer ADMIN_TOKEN)로 갱신 필요. (RFP의 6 Gemini 공유링크 + 3 Netlify 앱 + 네이버블로그/플레이스/인스타)
- GEMINI_MODE=mock — 실 Dual-AI는 GEMINI_MODE=live + GEMINI_API_KEY 주입 시 활성(현재는 규칙기반 결정론).
- clinic/portal 테넌트격리 미적용 — aid 계산은 하나 데이터경로 T() 접두 미사용. theirum(루트)은 무해, 추가 가맹점 등록 시 dual-ai/compare 패턴(
const T=s=>aid==='theirum'?s:aid+'/'+s) 복사 필요.
- 테넌트 토큰 스코핑 — 현재 전역 ADMIN_TOKEN. 멀티테넌트 프로덕션 시 테넌트별 토큰 스코핑 권장.
- 프론트 색상 — navy/amber가 Tailwind 클래스 하드코딩 + CSS변수 병행. 완전한 테넌트 동적 색상은 CSS변수 경로만 반영.
후속 (2026-06-22): 학생등록 + 셀코어 수정
- 학생등록: SPA에 로그인게이트+학생관리뷰(등록/목록/삭제)+동적select 추가, theirum 원장계정(won@theirum.kr), DELETE /api/students/:id 추가. 풀사이클 HTTP PASS.
- 셀코어: 신규 한선씨가 주석으로만 흉내내던 것 적발→룰중심 3파일(지능교차42/업무자동화18/오답클리닉15 룰) 실제 셀코어 재작성, COMPILE OK. 배열순회/문자열파싱은 셀코어 부적합 명기 후 절차적 유지.
QA 패스 (2026-06-22): 학부모포털 검증 + 전체 시각검증 + 기능체크
- 기능 체크(라이브 HTTP): tenant/dual-ai(IB준거)/clinic(매트릭스·유사문항)/compare(grouped-bar·history)/automation(평일보정·D-Day·학년·enroll)/기존(MBTI·scoring) 전항목 ✓.
- 학부모 포털 4-in-1 검증: 김지훈 실데이터 시드(채점·출결·숙제·배지·진단) → /report/:token 무인증 조회 = 출결 출석률100%·과제 제출1·성적77.5%·배지3·코멘트(처방전) 전부 렌더 ✓.
- 전체 시각검증(CDP 8뷰): dashboard/students/strategy/upload/links/report/portal/onboard 스크린샷. 동적 student-select(실 학생 표시)·report 풀플로우(레이더+IB+STEAM+처방전+클로징)·links(실 6 Gemini+3 Netlify+SNS)·portal·onboard(골드 화이트라벨 셸) 모두 정상.
- 이번 패스 수정: 포털 출결 카운트 한글/영문 관용 매칭(0→정상), 코멘트 없으면 Dual-AI 처방전 폴백.
- 플래그(사용자 확인 필요): ① 대시보드/리포트에 "KBS미래인재교육과정" 외부 브랜드 거명 — 자체브랜드 원칙 대상(실제 제휴면 유지, 아니면 일반화). ② scoring/upload mock이 0/0점 레코드 생성 → 포털 최근채점 0/0 표시(평균은 정확). 시드품질 이슈.
차별점 보강 4종 (2026-06-22) — 38 라우터
참고학원(aca2000행정/통통통·랠리즈소통/어나더클래스UI) 대비 AI·크라우니 차별. 울트라병렬 5에이전트(4빌더+opus감사)+메인 결선.
| 차별점 | 모듈 | 핵심 |
|---|
| 이탈예측 & 리텐션 | retention(/api/retention) | 셀코어16룰 4상 위험점수(성취하락·출결·상담경과·과제)→관리필요 학생+개입제안 |
| AI 월간 성장리포트 | growth(/api/growth) | 채점·진단·출결·배지 집계→서술형 성장스토리(셀코어5룰 하이라이트)+카톡발송 |
| 블록체인 학습이력 인증서 | cert(/api/cert) | 성취 SHA-256→크라우니체인(9729) 실앵커(POST /api/block, 3초타임아웃+로컬해시체인폴백)+무인증 verify.html. 셀코어13룰 등급 |
| 셀코어 No-code 운영자동화 | ops(/api/opsrules) | 원장이 규칙 정의(metric/op/value/action)→셀코어6룰 엔진 자동실행 |
- 한선씨 4종 전부 실제 셀코어 룰(16/20/13/6)+COMPILE OK. 프론트 feat-{name}.js 4종, nav 4버튼(navy통일), 뷰 4컨테이너(view-section).
- 감사가 잡은 실버그(수정): retention 라우트 경로 strip오류(/scores→/api/retention/scores, 100%404), listJson 객체배열을 id로 오용(빈데이터), 테넌트 항등 하드코딩, feat-retention apiFetch를 parsed JSON 오인. growth 라우트 경로. cert는 /anchor(없음)→/api/block 실앵커로 수정.
- 검증: 38/38 라우터, 4기능 HTTP PASS, CDP 4뷰 스크린샷 정상. cert chainOk=true 실앵커.
멀티테넌시 하드닝 + 가맹 2호점 데모 (2026-06-23)
B2B SaaS 화이트라벨 실증.
가맹점B(samplb) 생성(teal 브랜드 #0f766e/#14b8a6, 원장 won-b@samplb.kr).
- 테넌트 자동식별: resolveTenant 우선순위0 = 인증된 academyToken의 academyId(헤더/쿼리 위조 차단). 원장이 자기 토큰으로 로그인하면 그 학원으로 스코프 고정.
- 코어 데이터 격리: server.js _handleAPI에
TS() 추가, 학생/반/수납/상담 19개 호출 + DELETE unlink를 테넌트 접두(theirum=루트, 가맹점={aid}/).
- clinic 격리: 오답/클리닉지 T() 적용, 문제은행은 공유(플랫폼 콘텐츠).
- 화이트라벨 색상: tailwind.config irum-* 색상을
var(--brand-primary/accent/dark) 참조로 변경 → injectBrand가 테넌트별 색 주입 시 전체 UI 자동 전환(사이드바·로고·강조).
- 검증: 양방향 격리(theirum 3명 김지훈/이서연/박도윤 ↔ samplb 2명 최민준/한소율, 교차누수0), retention/clinic도 격리, 디스크 분리(데이터/samplb/{학생,오답}). CDP: B 로그인→teal 사이드바+B 학생만(tenantB_students.png).
- 잔여: portal/ops는 폴더 공유(포털토큰·운영룰 로그)이나 studentId/룰id 고유키라 내용 누수 없음 — 완전 격리는 후속 T() 적용. 출결은 모듈레벨 헬퍼라 미스코프(killer 모듈은 자체 T()로 읽음).
3종 마무리 (2026-06-23): 완전격리 + 실 OCR + 셀프서비스 온보딩
- T() 완전격리: portal(헬퍼에 테넌트 래핑 jsonFile/listJson 주입+issue 학생조회 T()), ops(액션실행 alarm/보강/리포트/실행로그 T()), server.js 출결(출결기록/조회 TS 전달), clinic(오답/클리닉지 T·문제은행 공유). 검증: 출결 theirum2일/samplb1일, ops 실행로그 samplb 3건, portal B최민준/theirum김지훈 격리.
- 실제 시험지 OCR: Tesseract 5.5.2(brew, kor+eng) + adapters/ocr.js 배선. scoring/upload에 OCR 분기(이미지+OCR_PROVIDER≠mock→readPrintedText→키워드 채점). 함정수정: ①launchd PATH에 brew 없음→TESSERACT_BIN 절대경로(.env) ②launchd /tmp 샌드박스 불일치→os.tmpdir() 사용. 검증: 한글 시험지 이미지→86자 추출→키워드 채점(광합성/엽록체).
- 셀프서비스 온보딩: POST /api/auth/signup(공개) = 테넌트+원장계정 동시생성→즉시 토큰. onboard.html에 원장이메일/비번 필드+가입→자동로그인→/theirum 리다이렉트. 검증: 무인증 가입201→samplc(purple) 격리(정하준)→디스크 데이터/samplc/학생, 중복 academyId 409.
- 3테넌트 라이브: theirum(navy,3명)·samplb(teal,2명)·samplc(purple,셀프가입). 양방향+삼방향 격리 누수0.