요약: 헥사고날 아키텍처와 데이터 파이프라인은 견고합니다 (33/33 LLM 파싱 성공, MinIO 영구화, e2e 10+ pass).
단 (a) 운영 가시성·재시작 안전성 이 부족하고
(b) LLM 응답 변동성 이 일부 결과 quality 를 떨어뜨리며
(c) 보안 비밀 관리 가 데모 수준입니다.
P0(데모 직전) 5건, P1(2주) 8건, P2(분기) 9건 — 총 22 개선안.
① hwpx-intelligence 파이프라인
외부 단일 인스턴스 (호스트 uvicorn), in-memory JobManager. 33 proposals 처리 검증됨.
영역 현재 / 위험 개선안 우선
JobManager in-memory dict — 서버 재시작 시 jobs 휘발. PR #9 의 fs glob 으로 file fallback 만 부분 해결 SQLite 또는 Redis 영속화 (Job + status), 재시작 시 진행중 job restore P1
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 output chat_json — 일부 응답에 bullets 빈 결과 (30%). 단순 prompt 기반 OpenRouter response_format=json_schema 강제. retry on missing field. function calling 명시 schema P1
동시 처리 uvicorn single worker — 큐 직렬 uvicorn workers=2~4 + asyncio.Semaphore (LLM 동시호출 제한) — OpenRouter rate 안배 P1
VLM skip_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-poller FastAPI BackgroundTasks — 컨테이너 재시작 시 진행중 polling 손실. 90초 deadline Dramatiq (Redis broker — docker-compose 에 이미 있음) actor 로 이전 + at-least-once 재시도 P1
hwpx_client timeout 30s — 큰 hwp upload 시 stream 단계 부족 (지투파워 5.8MB) upload=120s, status/result=30s, file=60s — endpoint 별 분리 P0
migrate-images endpoint POST /api/admin/migrate-images 인증 없음 (admin 라벨만) PORTAL_JWT 미들웨어로 admin scope 강제 + rate limit P1
file_storage_key 업로드 시 + lazy fetch 시 두 번 DB write — race 시 마지막 wins SELECT 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 fetching useEffect + fetch — refetch / cache / stale-while-revalidate 부재 TanStack Query 도입 — useQuery({queryKey: ['proposals']}, ...) + invalidate on mutation P1
JSONB read 부하 /api/proposals 가 모든 proposal 의 raw (extraction) 동봉 — 33 × ~50KB = 1.6MB / fetch list 응답에 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 lazy loading=lazy + decoding=async OK. 단 next/image 미사용 (srcset 없음) BFF 가 ?w=320 thumbnail 변형 endpoint 추가 + next/image remotePatterns P2
e2e Playwright image-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 255 CI 에서 alembic heads | wc -l ≤ 1 보장 (lint step) P1
backup 부재 postgres_data volume 만 — 백업 정책 없음 매일 pg_dump → MinIO bucket=pps-backups + 7일 retention P0
⑤ MinIO · Storage
영역 현재 / 위험 개선안 우선
presigned URL host endpoint=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
replication single node — 단일 실패점 MinIO erasure coding 또는 R2 백업 sync P2
⑥ Docker · Infra
영역 현재 / 위험 개선안 우선
resource limits BFF cpus=2.0/mem=1G, frontend cpus=1.0/mem=768M — 명시됨 유지 —
read_only + tmpfs BFF/frontend 적용됨 — 보안 ✓ 유지 —
portal-nginx prod 경로 (/app/eval-premium-api) 의존 — 데모 환경 미부팅 dev/prod profile 분리 — docker-compose profile=prod, dev 는 직접 hostport P2
healthcheck BFF /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
HTTPS http://localhost — 데모는 OK, 온프렘은 inner TLS 필요 portal-nginx 에 self-signed 또는 사내 CA 인증서 mount P1
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) middleware P1
에러 트래킹 없음 — 단순 stdout Sentry 도입 (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 limit OpenRouter 큰 batch 시 429 가능성 — 직렬 처리라 일부 mitigation tenacity exponential backoff + circuit breaker P1
비용 가시성 OpenRouter 호출 cost 추적 없음 LLM port 에 usage callback (prompt_tokens / completion_tokens / cost_usd) → DB 적재 P2
response quality 30% bullets 빈응답 retry once with explicit "" reinforcement P1
⑩ 데이터 무결성 · 진실성
영역 현재 / 위험 개선안 우선
source_ref hwpx 가 추출한 source_ref (section, para_start/end, file) 포함 — 추적성 OK 유지 —
dedup ProductRecord.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주 이내)
self-poller → Dramatiq actor 이전 — 재시작 시 진행중 job 자동 복구
LLM structured output schema 강제 — response_format=json_schema + retry on missing field
hwpx-intelligence JobManager 영속화 — SQLite or Redis
BFF /api/proposals 목록 raw 제외 — 1.6MB → 50KB
structlog JSON + correlation_id (proposal_id) — 로그 통합 추적
HashTag 분류 backend 이전 — ClassifyFeatureGroups use case + LLM 기반
TanStack Query 도입 — refetch/cache/stale-while-revalidate
admin endpoint JWT 인증 — PORTAL_JWT scope 강제
P2 — 장기 확장성 (분기)
JSONB GIN 인덱스 — 특허번호 검색 가속
MinIO 외부 endpoint + presigned URL public — BFF 부하 0
thumbnail variant on-the-fly — ?w=320 BFF resize + next/image srcset
VLM 비동기 별도 worker — partial update + 진행률 UX
Prometheus + Grafana — LLM 호출 메트릭 대시보드
checksum sha256 — 중복 업로드 차단
bucket 권한 분리 — private/public 분리
file_storage_key optimistic locking — race 안전성
OpenAPI JSON serialization 강건화 — control char escape
generated 2026-05-22 · ai-real-estate-service workspace · jodal-eval-ai