Suman 의 PR #162 — 비개발자용 작업 요약

17 commits / +4,166 줄 / -81 줄 / 48 파일 — 무엇을, 왜, 어떻게

작성: 2026-05-21 저자: Suman Paudel (Kathmandu) 리뷰어: Cheolhee Lee (Seoul) 대상: jodal-eval-ai/pps-mono-repo

1한눈에 요약

Suman 이 만든 것: 조달청 우수제품 평가 시스템의 백엔드(BFF) + 프론트엔드 + AI 챗봇 + 비교 매칭 + 배포 인프라까지 한 번에. 크게 8개 작업 묶음 (T1~T8 + UI 4개)으로 분류.
묶음무엇비개발자 설명상태
T1~T3기존 코드 스타일 정리들여쓰기 / 띄어쓰기 정돈 (실제 동작 변경 없음)유지
T4 (5건)AI 챗봇 시스템평가위원이 AI에게 질문하고 답변받는 챗 화면제거됨
T5 (1건)AI 모델 등록한국어 잘하는 AI (Qwen3-32B) + 의미 검색 모델 (KURE-v1) 시스템 등록유지
T6 (2건)업체 비교 매칭업체 제안서 ↔ RFP 요구사항 자동 비교 점수 산출유지
T7 (1건)배포 인프라Docker 컨테이너 + Nginx 설정 (서버에 띄울 준비)유지
T8 (1건)자동 통합 테스트브라우저 시뮬레이션으로 전체 흐름 자동 점검유지
Docs (1건)인수인계 문서다음 사람이 이어받을 때 보는 가이드 (HANDOFF.md)유지
UI (4건)업로드 + 비교 화면HWPX 다중 업로드 + 진행률 + 추출 결과 표 + 비교 화면 분리유지

2커밋 17개 — 한국어 풀이

📐 T1~T3 — 기존 코드 정리

d4b62f1코드 들여쓰기 / 띄어쓰기 자동 정돈
한 일: ruff 라는 자동 포매터로 기존 T1~T3 파일의 띄어쓰기·줄바꿈을 표준에 맞춤.
왜: 다음 커밋들이 diff(변경 비교) 가 깨끗하게 보이도록 사전 정돈. 실제 동작 변경 0.

💬 T4 — AI 챗봇 시스템 (5 commits) 데모에서 제거됨

1dcab27T4-c1 챗 메시지 저장 DB 테이블 설계
한 일: 평가위원과 AI의 대화 기록 저장용 데이터베이스 테이블 2개 (chat_message, chat_feedback) 추가.
왜: 챗 기록을 영구 저장해야 평가위원이 나중에 다시 볼 수 있음. 피드백은 AI 답변 개선용.
→ alpha 에서 제거 (데모 scope 외)
b7f923eT4-c2 AI 서버와 통신하는 모듈 + 취소 신호 처리
한 일: BFF 가 AI 엔진 서버(ai_engine)에 챗 요청을 보내는 HTTP 모듈 + 사용자가 "취소" 누르면 진행 중 요청을 끊을 수 있게 하는 Redis 기반 신호 처리.
왜: BFF 가 직접 LLM(GPT 같은 거)을 호출하지 않고 별도 AI 엔진을 거치는 구조. 보안 + 비용 통제.
→ alpha 에서 chat 부분은 제거. 하지만 같은 모듈을 다른 LLM 호출에 재활용 가능 (summary endpoint 등).
0ba52c7T4-c3 챗 API 6개 + 서버 부팅 로직
한 일: 챗 관련 API 6개 (메시지 보내기 / 가져오기 / 취소 / 피드백 / 메시지 삭제 / 세션 삭제) + 서버 시작 시 DB 연결·AI 엔진 어댑터·Redis 초기화 코드.
왜: 프론트엔드가 호출할 챗 endpoint 노출. lifespan 은 서버 부팅·종료 시 자원 정리 표준 패턴.
→ alpha 에서 6 API 모두 제거. lifespan 은 유지 (다른 endpoint 가 같은 자원 씀).
1fffbd0T4-c4 챗 API 자동 테스트 14개 + 가짜 어댑터
한 일: 챗 6 API 가 정상 동작하는지 자동 검증하는 테스트 14건 + 외부 의존성을 흉내 내는 "가짜 어댑터" (외부 서버 없이도 테스트 가능).
왜: 외부 LLM / 데이터베이스 / Redis 없이도 챗 로직 자체를 검증. CI(자동 빌드 시스템) 에서 빠르게 실행.
→ alpha 에서 chat 테스트 14건 제거. 나머지 14건 (업로드 / 추출 / 웹훅) 은 유지.
6dfc63cT4-c5 Redis 없을 때 메모리 임시 사용
한 일: 서버 부팅 시 Redis 가 연결 안 되면 자동으로 메모리 내 임시 저장소로 fallback. Redis 가 없는 dev / 테스트 환경에서도 챗이 동작.
왜: Redis 안 떠 있는 환경에서 챗 cancel 기능이 graceful 하게 동작하도록.
→ alpha 에서 chat 자체는 제거. fallback 로직은 evidence / summary endpoint 에서 재활용.

🤖 T5 — AI 모델 등록 유지

5b79eefT5 한국어 AI + 의미 검색 모델 등록
한 일: AI 엔진에 모델 슬롯 4개 등록 — (1) Qwen3-32B (한국어 평가 챗봇용), (2) Qwen3-VL-8B (이미지 분석용), (3) Qwen3-32B (RFP 구조 분석용), (4) KURE-v1 (한국어 의미 검색 임베딩용). 개발에선 OpenRouter (외부 LLM 게이트웨이), 온프렘에선 H100 GPU 위 vLLM 으로 같은 모델 서빙.
왜: 한 모델로 모든 작업 하기보다 "용도별 최적 모델" 분리. eval-chatbot 과 rfp-structure 가 같은 Qwen3-32B 라도 prompt / config 가 다름. 한국어 평가 도메인 + 시험성적 이미지 분석 + 의미 검색까지 한 번에 커버.

⚖️ T6 — 업체 비교 매칭 (2 commits) 유지

dab4495T6-c1 매칭 점수 저장 테이블 3개
한 일: 업체 제안서가 RFP 요구사항·평가항목·주요기술 각각과 얼마나 매칭되는지 LLM 점수를 저장할 테이블 3개 신설 (tech_match / requirement_match / eval_item_match). 각 row 는 [proposal_id, bid_id, item_id, score, rationale, evidence, model_name] 7개 필드.
왜: 평가위원이 한 입찰에 10개 업체 제안서를 검토할 때 (10 업체 × 20 항목 = 200 매칭) 점수를 영구 저장해서 다시 계산 안 해도 됨. evidence(JSON) 는 근거 인용용.
f88d433T6-c2 비교 API 3개 + 분석 파이프라인 연결 + evidence 지연 로딩
한 일: 프론트엔드가 호출할 비교 API 3개 (주요기술 / 요구사항 / 평가항목) + 업로드 완료 시 비동기 분석 작업 자동 시작 + 각 셀 클릭 시에만 LLM 으로 evidence 가져오는 lazy 로딩 + Redis 24시간 캐시.
왜: 미리 모든 evidence 를 계산하면 비싸므로 (LLM 호출 = 토큰 비용), 평가위원이 실제로 셀 클릭할 때만 호출. 한 번 본 evidence 는 24시간 캐시.
알려진 제약: matcher (분석 파이프라인) 가 다른 DB(knowledge_hub)로 점수를 쓰는데 BFF 는 자기 DB만 읽음 → 현재는 fixture(가짜 점수) 로 표시 중. 데모일 시연엔 영향 없으나 진짜 LLM 매칭 결과는 후속 보강 필요.

📦 T7 — 배포 인프라 유지

7adfbebT7 Docker / Nginx / DB 초기화 / 외부 통신 차단 안전장치
한 일: (1) Docker 컨테이너 정의 (frontend + BFF), (2) Nginx 설정 (외부 URL 을 내부 서비스로 라우팅 + 챗 SSE 안전 통과), (3) PostgreSQL 데이터베이스 초기화 스크립트, (4) ai_engine 의 일부 mock 경로 차단 (실서버에서 실수로 mock 응답이 노출되지 않도록).
왜: 코드만 있어선 서버에 못 띄움. 컨테이너화 + 리버스 프록시 + DB 시드 = "한 명령으로 배포 가능" 상태로.

🧪 T8 — 자동 통합 테스트 유지

e5c46f8T8 브라우저 자동 테스트 + 데모 데이터 시드 + CI 워크플로우
한 일: (1) Playwright (실제 Chrome 띄워서 자동 클릭·검증) "real-bff" 프로젝트 셋업, (2) 데모용 입찰 1건 + 제안서 3건 + 매칭 21건 자동 시드 스크립트 (멱등 — 여러 번 실행해도 안전), (3) GitHub Actions CI 워크플로우 (PR 올리면 자동 실행).
왜: 사람이 매번 손으로 테스트하기엔 너무 많은 시나리오. 브라우저 자동화로 한 번에 50+ 케이스 검증. PR 마다 자동 실행 → 회귀 차단.

📝 Docs — 인수인계 문서 유지

40735edHANDOFF.md — 다음 담당자에게 넘기는 인수인계 문서
한 일: 17 commit 의 역할 표 + 알려진 미해결 갭 7건 + 후속 작업 우선순위 정리.
왜: Suman 이 카트만두에 있고 Cheolhee 는 서울. 시차·언어 차이로 구두 인수인계 어려움. 문서로 컨텍스트 보존.

📤 UI — 업로드 + 비교 화면 (4 commits) 유지

ebb266bUI 1 — Mock 데이터 → 실제 데이터로 전환 (Upload + Place end-to-end)
한 일: 이전엔 화면에 박혀있던 가짜 회사명·점수를 BFF 의 실제 API 응답으로 교체. 업로드한 HWPX 가 곧바로 화면에 추출 결과로 표시되는 흐름 완성.
왜: 데모 → 실서비스 진입을 위해 가짜 데이터 제거 필수.
ae4786eUI 2 — 다중 업로드 병렬 + 실시간 진행률 + 15분 타임아웃
한 일: 여러 HWPX 파일을 동시에 업로드 + 각 파일별 % 진행률 / 단계명 / 메시지 실시간 표시 + 무거운 파일 (5분+) 도 충분히 처리할 수 있게 deadline 900초 (15분).
왜: 평가위원은 5~10개 제안서를 동시 업로드. 순차 처리하면 너무 느림. 진행률 안 보이면 사용자가 멈춤 의심.
8925781UI 3 — /upload + /eval 페이지 분리 + 비교 대상 멀티 선택
한 일: 한 페이지에 다 있던 업로드와 비교를 별도 페이지로 분리 (/upload + /eval). 비교 페이지에서 "비교 대상 선택" 드롭다운으로 여러 업체 동시 선택 → side-by-side 컬럼으로 자동 표시.
왜: UX 명확성 — 업로드는 업로드, 비교는 비교. 한 화면에 다 넣으면 산만함.
bbee0afUI 4 — 2단계 폴링 (파싱 중엔 hwpx만 / 완료 후 BFF만)
한 일: 이전엔 프론트가 hwpx + BFF 양쪽을 2초마다 폴링 → BFF 가 "아직 안 됐어요(409)" 응답을 수십 번 받음. 이제 1단계는 hwpx 만 폴링하다 done 신호 받으면 2단계로 BFF만 한 번 확인.
왜: BFF 로그에 "409 spam" 이 데모 시 추하게 보임. 네트워크 부하도 감소.

🔧 Cheolhee (보조) — CI fix

6d2c408pytest 의존성 추가 + Playwright 잡 advisory 처리
한 일: CI 가 pytest 자체를 못 설치해서 fail. requirements 에 추가. Playwright 잡은 hwpx-intelligence(외부 repo)가 CI 환경에 없어 fail → "fail 해도 머지 차단 안 함" 으로 처리.
왜: 머지를 빠르게 풀어내기 위함. 후속 PR 에서 정식 fix.

3총량 통계

항목수치
Commits17 (Suman 16 + Cheolhee 1)
추가 라인+4,166
삭제 라인-81
변경 파일48
작업 기간약 1주 (2026.05.13 ~ 2026.05.20)
리뷰 PR 번호#162
현재 상태#162 일단 머지 → 회귀 발견 → #163 으로 revert → alpha 브랜치에 chat 제거 + 매핑 fix 적용

4현재(alpha) 와 Suman 원본 #162 의 차이

영역Suman 원본현재 alpha이유
AI 챗봇 (T4)5 commits / 6 API / DB 테이블 2개 / 테스트 14건제거데모 scope 외 + H100 LLM 미검증 리스크 + AdminGuard 회귀 유발
매칭 매핑 버그제품 지정번호(designation_no)를 입찰 ID(bid_id) 로 잘못 사용수정같은 입찰의 N 제안서가 N개의 다른 bid_id 로 분산되는 버그
proposal_id 비멱등매번 새 uuid 발급 → webhook 과 list 경로에서 다른 idspec_id 그대로 사용 (결정성)같은 파일이 두 경로에서 다른 id 로 나오는 일관성 깨짐
요약 endpoint없음POST /api/proposals/{id}/summary 추가chat 제거하면서 ai_engine 어댑터를 더 가치 있게 재활용
upload 시 bid 컨텍스트BFF 가 받을 곳 없음bid_id Form 필드 추가 + frontend 동봉다중 입찰 환경 대비
docker-compose 의존postgres + redis + ai-engine + knowledge-hub 4개 필수postgres + redis 만 필수, 나머지 optional외부 LLM 없이도 데모 4 기능 동작 (graceful degrade)
chat dead-link/admin/usage/chatbot 등 잔존/admin/usage 리다이렉트 fixchat 제거 후 404 페이지 노출 방지
총 변화: +221 / -2,829 = 순 -2,608 줄. Suman 의 작업 위에 chat 만 빼내고 매핑 버그 fix + summary endpoint 추가.

5Suman 이 잘 한 점 / 아쉬운 점

👍 잘 한 점

항목설명
구조화된 commits17개 commit 이 T4-c1 ~ T8 같은 명확한 단위로 분리 — 리뷰·revert·cherry-pick 모두 쉬움
테스트 동봉chat API 14건 + corpus e2e 42건 + Playwright 등 자동 테스트가 일관되게 포함
HANDOFF.md 인수인계알려진 갭 7개를 명시 — Cheolhee 가 빠르게 컨텍스트 파악
graceful fallbackRedis 없을 때 memory fallback / matcher cross-DB 시 fixture fallback — 외부 의존 실패가 전체를 깨뜨리지 않게 설계
2-phase pollingBFF "아직 안 됐어요" 스팸을 제거하는 클리어한 UX 개선

👎 아쉬운 점 (alpha 에서 보강)

항목문제
bid_id 매핑 버그제품 번호(designation_no)를 입찰 ID 로 잘못 사용 — 같은 입찰의 비교가 깨짐
proposal_id 비멱등매번 uuid 새로 발급 — 같은 파일이 두 경로에서 다른 id 로 나옴
chat scope데모일 D-1 에 LLM 의존 챗봇은 검증 부담 큰데 PR 에 포함
AdminGuard SSR 회귀로딩 텍스트 "인증 확인 중..." 가 SSR 응답에 안 나옴 — 테스트 3건 fail
cross-DB writematcher 가 다른 DB로 결과를 써서 BFF 가 못 읽음 — fixture fallback 으로 우회

6다음 단계 (alpha → 데모일)

  1. H100 GPU 서버에서 vLLM 부팅 확인 — Suman 은 OpenRouter (외부 LLM 게이트웨이) 로만 테스트. on-prem 의 Qwen3-32B 실서빙은 미검증.
  2. 임베딩 차원 검증 — 설정 1536 vs KURE-v1 native 1024 불일치 가능성. curl :8306/embed 한 번이면 됨.
  3. matcher cross-DB write 보강 (선택) — fixture 가 아닌 진짜 LLM 매칭 점수 표시.
  4. 비교 insight endpoint 추가 (선택) — LLM 이 비교 표 자동 생성.