AI 분석 문제 컨텍스트 풍부화 — 그룹 분석 문제 정보 주입 + Programmers 크롤러 모듈
요약AI 분석 시 코드만 주입하던 페인포인트 해소 — 두 가지 접근으로 문제 컨텍스트 풍부화. Wave B: ai-analysis 그룹 분석 엔드포인트에서 Problem Service를 조회해 problem_title/problem_description을 <problem_context> 블록으로 주입 (ADR-030 S-5 개별 분석 패턴 재활용). Wave A: problem 서비스에 Programmers 크롤러 모듈 신설 — 문제 등록 시 sourceUrl 기반 description 자동 보완 (axios+cheerio 경량 크롤링, Playwright 불필요). 크롤링 스파이크(Scout)에서 BOJ는 2026-04-28 서비스 완전 종료 확인, Programmers는 SSR로 httpx만으로 파싱 가능 확인. Critic 게이트 Findings 3건(P1 SSRF·P2 envelope unwrap·P2 catch 핸들러) 수정 후 CLEAN. multer CVE-2026-5079(HIGH) 보안 패치 병행. PR #462 머지 커밋 a7731fa. AI Analysis 353 passed·커버리지 99% / Problem 205 passed·커버리지 96.47%.
날짜
영향도높음
목표
- AI 분석 시 사용자 코드만 제공되어 AI가 문제를 역추론해야 하는 페인포인트를 해소한다.
- Wave B: 그룹 분석에 문제 정보(제목·설명)를 주입해 다중 제출 비교 분석 정확도를 높인다.
- Wave A: 문제 등록 시
sourceUrl기반 Programmers 자동 크롤링으로description을 채워 AI 컨텍스트 부재를 근본 해결한다. - Wave S (스파이크): httpx+BeautifulSoup4만으로 Programmers 파싱 가능 여부를 실증한다.
배경
- Sprint 238(ADR-030 S-5)에서 개별 분석의
<problem_context>주입 패턴을 구현했으나, 그룹 분석(/group-analysis)에는 동일 패턴이 적용되지 않았다. Problementity에sourceUrl/sourcePlatform필드가 이미 존재(Sprint 97 Programmers 이전 산물)했으나, 등록 시 자동 크롤링 로직이 없어description이 대부분 비어 있었다.- 플랫폼별 크롤링 가능성은 스파이크 선행 없이 가정할 수 없어 Scout 스파이크를 Wave A 전에 배치했다.
결정
D1. 그룹 분석 문제 정보 주입 전략 — Problem Service 내부 API 조회
- 그룹 분석 요청 수신 시
problem_id를 이용해 Problem ServiceGET /internal/{problem_id}를 조회한다. - 조회 실패(네트워크 오류, 문제 없음) 시 fallback: 경고 로그 후 문제 정보 없이 계속 진행 — 서비스 가용성 우선.
<problem_context>블록은 ADR-030 S-5 개별 분석 패턴과 동일하게 격리 — 프롬프트 인젝션 방어 포함.GROUP_SYSTEM_PROMPT/GROUP_SQL_SYSTEM_PROMPT양 쪽 모두에 인젝션 방어 지침 추가.- 아웃바운드 키:
INTERNAL_KEY_PROBLEM(CLAUDE.md Sprint 239 Q-5 SSOT 준수).
D2. 크롤링 범위 — Programmers 전용, 등록 시 1회 비동기
- Scout 스파이크 결과: BOJ는 2026-04-28 채점 서비스 종료, Programmers는 SSR로 JS 렌더링 불필요 확인.
- Playwright 금지 (Docker 이미지 ~400MB 증가, 브라우저 런타임 불허) →
axios+cheerio경량 크롤링. - 문제 등록 시
description미입력 +sourceUrl/sourcePlatform있으면 비동기 backfill (fire-and-forget, 응답 블로킹 없음). ALLOWED_HOSTS화이트리스트:school.programmers.co.kr단독 +https:전용 검증 → SSRF 방어.
D3. 스파이크 선행 원칙 재확인
- 크롤링 기술 가능성 확인을 Wave A 착수 전 Wave S(Scout)로 선행하는 패턴이 올바름을 실증.
- BOJ 종료 같은 운영 변경 사항은 코드 분석이 아닌 실제 HTTP 요청으로만 확인 가능.
구현
Wave S — 크롤링 스파이크 (Scout)
- BOJ
acmicpc.net/problem/1000: HTTP 200이나 "채점 서비스 준비 중" 페이지 반환 — 서비스 종료 확인. - Programmers
school.programmers.co.kr/learn/courses/30/lessons/{N}: SSR 확인, httpx 직접 요청으로 HTML 수신 성공.- 파싱 가능 필드: 제목(
.challenge-title)·난이도([data-challenge-level])·설명(div.markdown)·제한사항·입출력 예 테이블.
- 파싱 가능 필드: 제목(
Wave B — 그룹 분석 문제 컨텍스트 주입 (ai-analysis, Oracle 직접)
| 파일 | 변경 내용 |
|---|---|
src/config.py | problem_service_url / problem_service_key 환경변수 추가 |
src/prompt.py | build_group_user_prompt() — problem_title/problem_description 파라미터 추가, <problem_context> 블록 격리 주입 |
src/main.py | /group-analysis 엔드포인트 — Problem Service 조회 + fallback 로직 |
tests/test_prompt.py | 신규 5건 (컨텍스트 주입·미주입·sanitize 검증) |
tests/test_main.py | 신규 2건 (Problem Service 성공·실패 시나리오) |
- 커밋:
bd33185 - 테스트: 353 passed / 커버리지 TOTAL 99%
Wave A — Programmers 크롤러 모듈 (problem, Oracle 직접)
| 파일 | 변경 내용 |
|---|---|
src/crawler/crawler.service.ts | ALLOWED_HOSTS 화이트리스트 + axios+cheerio Programmers SSR 파싱 |
src/crawler/crawler.module.ts | 모듈 등록 |
src/problem/problem.module.ts | CrawlerModule 임포트 |
src/problem/problem.service.ts | create() 후 비동기 크롤링 트리거 (.catch() 포함) |
src/crawler/crawler.service.spec.ts | 신규 5건 (플랫폼 판별·파싱·오류 처리·SSRF 차단) |
src/problem/problem.service.spec.ts | 신규 2건 (크롤링 트리거·조기 반환) |
- 커밋:
d7b11ef,20a4f28(커버리지 수정) - 테스트: 198 → 205 passed / 커버리지 Branches 96.47% / Functions 98.61%
Critic 게이트 Findings 수정 (커밋 95e34dd)
| 등급 | 내용 | 수정 |
|---|---|---|
| P1 SSRF | crawler.service.ts — sourceUrl 무검증 외부 요청 | ALLOWED_HOSTS 화이트리스트 + https: 전용 검증 |
| P2 envelope unwrap | main.py — Problem Service 응답 {data: problem} 직접 필드 접근 | prob_data.get("data", prob_data) unwrap 후 추출 |
| P2 catch 핸들러 | problem.service.ts — fire-and-forget unhandled rejection | .catch() 추가 + Error/비-Error 2케이스 분기 |
보안 패치 (커밋 86cc2ec)
multer2.1.1 → 2.2.0 — CVE-2026-5079 (HIGH) 보안 패치.
검증
- CI: 38 checks PASS / 0 FAIL
- AI Analysis: 353 passed · TOTAL 99% · main.py 100% · prompt.py 100%
- Problem: 205 passed · Statements 99.02% · Branches 96.47% ≥ 96% · Functions 98.61% ≥ 98% · Lines 99.13%
- ESLint: Errors 0 (tsc pre-existing baseUrl deprecation은 Sprint 246 확인 기지 이슈, 변경 범위 무관)
- Critic (Codex gpt-5.5): Findings 3건(P1·P2·P2) 전부 수정 → CLEAN
- PR #462 squash merge →
a7731fa(2026-07-15T08:58:11Z)