성능·안정성 종합 진단 — eval-system-premium

전체 코드 인벤토리 분석 + 최적 개선 로드맵 (10 영역)
jodal-eval-ai · 2026-05-22 · 5/22 데모 + 운영 안정화 + 장기 확장
요약: 헥사고날 아키텍처와 데이터 파이프라인은 견고합니다 (33/33 LLM 파싱 성공, MinIO 영구화, e2e 10+ pass). 단 (a) 운영 가시성·재시작 안전성이 부족하고 (b) LLM 응답 변동성이 일부 결과 quality 를 떨어뜨리며 (c) 보안 비밀 관리가 데모 수준입니다. P0(데모 직전) 5건, P1(2주) 8건, P2(분기) 9건 — 총 22 개선안.
분석 영역
10
개선안
22
P0 (즉시)
5
P1 (2주)
8
P2 (분기)
9
머지된 PR
14
e2e Pass
10+
실측 proposals
33

① hwpx-intelligence 파이프라인

외부 단일 인스턴스 (호스트 uvicorn), in-memory JobManager. 33 proposals 처리 검증됨.

영역현재 / 위험개선안우선
JobManagerin-memory dict — 서버 재시작 시 jobs 휘발. PR #9 의 fs glob 으로 file fallback 만 부분 해결SQLite 또는 Redis 영속화 (Job + status), 재시작 시 진행중 job restoreP1
LLM stuck지투파워 hwp 88K paragraph → 27분 stuck (OpenRouter rate limit + 큰 prompt)(a) zone text length cap 8000 → adaptive chunk + summarize-then-extract
(b) per-zone 5분 timeout + skip with status='partial'
P0
structured outputchat_json — 일부 응답에 bullets 빈 결과 (30%). 단순 prompt 기반OpenRouter response_format=json_schema 강제. retry on missing field. function calling 명시 schemaP1
동시 처리uvicorn single worker — 큐 직렬uvicorn workers=2~4 + asyncio.Semaphore (LLM 동시호출 제한) — OpenRouter rate 안배P1
VLMskip_vlm=1 일괄 → 이미지 caption 손실이미지 zone 만 별도 worker 에서 비동기 처리. VLM 결과 partial update 지원P2

② BFF (eval-system-premium) — 헥사고날

domains/ports/adapters 분리 양호. apply_extraction use case + ProductRecord deduped + image_storage + cert_repository 일관 트랜잭션.

영역현재 / 위험개선안우선
self-pollerFastAPI BackgroundTasks — 컨테이너 재시작 시 진행중 polling 손실. 90초 deadlineDramatiq (Redis broker — docker-compose 에 이미 있음) actor 로 이전 + at-least-once 재시도P1
hwpx_client timeout30s — 큰 hwp upload 시 stream 단계 부족 (지투파워 5.8MB)upload=120s, status/result=30s, file=60s — endpoint 별 분리P0
migrate-images endpointPOST /api/admin/migrate-images 인증 없음 (admin 라벨만)PORTAL_JWT 미들웨어로 admin scope 강제 + rate limitP1
file_storage_key업로드 시 + lazy fetch 시 두 번 DB write — race 시 마지막 winsSELECT FOR UPDATE 또는 optimistic locking (proposal.updated_at)P2
OpenAPI parse한국어 본문 control char 로 Python JSON parse 실패 (앞서 jq 사용으로 우회)JSONResponse 직접 사용 — strict serialization 확인. tests/test_openapi_json.py 추가P2

③ Frontend (Next.js 16 / standalone)

standalone build + .env.production inline. AuthorComparePanel + ImageGallery + Modal 다 동작.

영역현재 / 위험개선안우선
data fetchinguseEffect + fetch — refetch / cache / stale-while-revalidate 부재TanStack Query 도입 — useQuery({queryKey: ['proposals']}, ...) + invalidate on mutationP1
JSONB read 부하/api/proposals 가 모든 proposal 의 raw (extraction) 동봉 — 33 × ~50KB = 1.6MB / fetchlist 응답에 raw 제외 + GET /api/proposals/{id}/detail 만 raw 포함. 또는 raw.image_assets 제외P1
HashTag 분류frontend keyword classifier — false positive (LPVT/AI 너무 broad)BFF 의 ClassifyFeatureGroups use case + LLM 분류 (zero-shot or fine-tuned). 캐시 처음만P1
Image lazyloading=lazy + decoding=async OK. 단 next/image 미사용 (srcset 없음)BFF 가 ?w=320 thumbnail 변형 endpoint 추가 + next/image remotePatternsP2
e2e Playwrightimage-gallery / cert-info / krds-a11y / file-download / feature-groups 등 ≥10 spec유지 CI 에서 매 PR 자동 run + axe-core a11yP1

④ DB · Migration

영역현재 / 위험개선안우선
JSONB extraction 크기33 proposal × 평균 50KB (image_assets storage_key 적용 후 다이어트됨)JSONB GIN 인덱스 (raw → patents → number) — 특허번호 검색 가속P2
migration head 단일현재 OK. 다만 분기 시 entrypoint alembic upgrade head 가 exit 255CI 에서 alembic heads | wc -l ≤ 1 보장 (lint step)P1
backup 부재postgres_data volume 만 — 백업 정책 없음매일 pg_dump → MinIO bucket=pps-backups + 7일 retentionP0

⑤ MinIO · Storage

영역현재 / 위험개선안우선
presigned URL hostendpoint=minio:9000 — 브라우저 접근 불가. BFF stream proxy 만 동작(a) docker-compose 의 minio_endpoint_public env 추가 — 외부 hostname:9000 분리
(b) nginx proxy 또는 Cloudflare R2 (S3 호환) 대체
P1
bucket 권한모두 같은 bucket — public 분리 없음eval-premium-images (private) vs eval-premium-public-thumbs 분리P2
replicationsingle node — 단일 실패점MinIO erasure coding 또는 R2 백업 syncP2

⑥ Docker · Infra

영역현재 / 위험개선안우선
resource limitsBFF cpus=2.0/mem=1G, frontend cpus=1.0/mem=768M — 명시됨유지
read_only + tmpfsBFF/frontend 적용됨 — 보안 ✓유지
portal-nginxprod 경로 (/app/eval-premium-api) 의존 — 데모 환경 미부팅dev/prod profile 분리 — docker-compose profile=prod, dev 는 직접 hostportP2
healthcheckBFF /api/health + frontend / + postgres pg_isready + minio /minio/health/live유지

⑦ 보안

영역현재 / 위험개선안우선
OpenRouter API key평문 노출 (메일 + .env.onprem). PR #173 분석 페이지에도 노출됨즉시 revoke + 새 키 발급 + Infisical / Doppler 시크릿 매니저 이전P0
PORTAL_JWT_SECRET"dev-jodal-eval-2026-replace-in-onprem" — 약한 dev 키 prod 위험onprem 부팅 시 openssl rand -hex 64 자동 생성 + .env.onprem 갱신P0
HTTPShttp://localhost — 데모는 OK, 온프렘은 inner TLS 필요portal-nginx 에 self-signed 또는 사내 CA 인증서 mountP1
admin endpoint 인증POST /api/admin/migrate-images — 인증 없음PORTAL_JWT admin scope 미들웨어 강제P1

⑧ 운영 · 가시성 (Observability)

영역현재 / 위험개선안우선
로깅print + log.info — 구조화 없음 (BFF requirements.txt 에 structlog 있으나 미적용)structlog JSON formatter + correlation_id (proposal_id) middlewareP1
에러 트래킹없음 — 단순 stdoutSentry 도입 (BFF + Frontend) — 무료 tier 충분. dsn env 주입P0
메트릭없음prometheus_client + /metrics endpoint (LLM 호출 count/latency/error)P2
대시보드없음Grafana + Prometheus + Loki (로컬 docker stack) — 또는 외부 SaaS (Datadog free)P2

⑨ LLM · AI 안정성

영역현재 / 위험개선안우선
모델 분기.env.example dev=OpenRouter / .env.onprem prod=vLLM — 이미 분리 ✓유지
rate limitOpenRouter 큰 batch 시 429 가능성 — 직렬 처리라 일부 mitigationtenacity exponential backoff + circuit breakerP1
비용 가시성OpenRouter 호출 cost 추적 없음LLM port 에 usage callback (prompt_tokens / completion_tokens / cost_usd) → DB 적재P2
response quality30% bullets 빈응답retry once with explicit "" reinforcementP1

⑩ 데이터 무결성 · 진실성

영역현재 / 위험개선안우선
source_refhwpx 가 추출한 source_ref (section, para_start/end, file) 포함 — 추적성 OK유지
dedupProductRecord.deduped() + Certification dedup (5-tuple) — 이중 보호유지
checksum업로드 파일 sha256 미저장UploadProposal 시 hashlib.sha256 → proposal.file_sha256 컬럼. 중복 업로드 사전 차단P2

P0 — 데모 직전 (즉시, 6시간 이내)

1. OpenRouter API 키 revoke + 재발급

메일·.env.onprem 평문 노출. 데모 후 즉시 폐기 → 새 키 발급 → Infisical/Doppler

2. hwpx-intelligence LLM stuck 방지

큰 hwp 27분 stuck → per-zone 5분 timeout + status='partial' fallback. 데모 데드락 방지

3. hwpx_client timeout 분리

현재 30s 단일 → upload=120s, status/result=30s, file=60s

4. Sentry 도입 (BFF + Frontend)

에러 가시성 0 → 무료 tier dsn 주입만으로 5분 셋업. 데모 운영 안전

5. postgres pg_dump 백업 cron

데모 도중 사고 시 복구 불가 → 30분마다 dump → MinIO pps-backups bucket

P1 — 운영 안정화 (2주 이내)

  1. self-poller → Dramatiq actor 이전 — 재시작 시 진행중 job 자동 복구
  2. LLM structured output schema 강제 — response_format=json_schema + retry on missing field
  3. hwpx-intelligence JobManager 영속화 — SQLite or Redis
  4. BFF /api/proposals 목록 raw 제외 — 1.6MB → 50KB
  5. structlog JSON + correlation_id (proposal_id) — 로그 통합 추적
  6. HashTag 분류 backend 이전 — ClassifyFeatureGroups use case + LLM 기반
  7. TanStack Query 도입 — refetch/cache/stale-while-revalidate
  8. admin endpoint JWT 인증 — PORTAL_JWT scope 강제

P2 — 장기 확장성 (분기)

  1. JSONB GIN 인덱스 — 특허번호 검색 가속
  2. MinIO 외부 endpoint + presigned URL public — BFF 부하 0
  3. thumbnail variant on-the-fly — ?w=320 BFF resize + next/image srcset
  4. VLM 비동기 별도 worker — partial update + 진행률 UX
  5. Prometheus + Grafana — LLM 호출 메트릭 대시보드
  6. checksum sha256 — 중복 업로드 차단
  7. bucket 권한 분리 — private/public 분리
  8. file_storage_key optimistic locking — race 안전성
  9. OpenAPI JSON serialization 강건화 — control char escape

실측 데이터 (현재 검증된 사실)

처리 proposals
33/33
unique 회사
33
unique 특허
269
quality_tests rows
136
image_assets
665
MinIO 영구화
~720
HashTag 매칭 0
0
Playwright
10+ pass

참고