프로그래머스 데이터셋 + Gateway 외부 연동
날짜
영향도높음
주요 결정
사전 JSON 번들링 > 실시간 파싱/비공식 API
공식 API 부재 + Cloudflare JA3 차단(Sprint 83 전례) + 문제 풀 유한(373건)/갱신 빈도 낮음. 운영 안정성 우선
검색 API도 번들링 기반
Solvedac과 대칭되는 search 엔드포인트를 인-메모리로 제공 → Sprint 96 프런트에서 동일 UX로 구현 가능
Lv.1~5 ↔ BRONZE~DIAMOND 1:1 매핑
디자인 토큰 0줄 수정으로 기존 Difficulty enum·스타일 토큰 재사용
DTO `@IsIn` 강화
자유 문자열 허용에서 허용값 제한으로 전환. 입력 경계에서 불일치를 조기 차단. DB는 VARCHAR 유지 → Expand-Contract 불필요
브랜치 규율 복원
architect가 실수로 main에 직접 커밋했던 adad5cf를 feat/gateway-programmers-dataset 브랜치로 이동 후 main 리셋(로컬 단계). "main 직접 push 금지" 규칙 준수
배경
백준(BOJ) 서비스 종료로 AlgoSu 문제 제공처를 프로그래머스로 전환해야 한다. 전환 범위가 백엔드·프런트·제출 파이프라인에 걸쳐 광범위해 3개 스프린트 로드맵(Sprint 95 백엔드 인프라 → Sprint 96 프런트 UX → Sprint 97 제출·문서)으로 분할. 본 스프린트는 1단계로, 사용자 가시 변화 0을 원칙으로 백엔드 인프라만 선행 구축한다.
목표
- 프로그래머스 문제 메타데이터를 사전 큐레이션된 JSON으로 번들링해 검색 UX의 외부 API 의존성 제거
- Gateway에 BOJ(Solved.ac)와 대칭 구조의 외부 엔드포인트
/api/external/programmers/*구축 - DTO
sourcePlatform을@IsIn(['BOJ','PROGRAMMERS'])로 강화해 플랫폼 허용값 명시 - 기존 BOJ 경로 회귀 0건 보장
작업 요약
| 커밋 | 담당 | 내용 |
|---|---|---|
adad5cf | architect | ProgrammersService/Controller 골격 + external.module.ts 등록 + DTO @IsIn 확장 |
60b7925 | librarian | 데이터 번들링 결정 ADR (docs/adr/topics/sprint-95-programmers-dataset.md) |
e460b79 | postman | Playwright 기반 크롤러 스크립트 + 초기 데이터셋 373건 |
18b3932 | curator | 데이터 품질 QA 리포트 (PROGRAMMERS-QA.md) |
2578ae0 | gatekeeper | 검증 리포트 + 749 tests PASS + BOJ 회귀 무결성 |
aff4b47 | Oracle | DTO 타입 강화 후속: problem.service.spec.ts 동기화 |
수정 내용
Gateway 외부 모듈
services/gateway/src/external/programmers.service.ts— JSON 기동 시 로드,Map<problemId, Info>메모리 캐시,fetchProblem/searchProblem— SolvedacService 인터페이스 대칭services/gateway/src/external/programmers.controller.ts—GET /problem/:problemId,GET /search?query=&page=, SwaggerExternal — Programmersservices/gateway/src/external/external.module.ts— ProgrammersService/Controller 등록(기존 Solvedac 병치)- 데이터 봉투 구조:
{ version: ISO8601, items: ProgrammersProblemInfo[] }+ 레거시 배열 하위 호환(isDataEnvelope()타입 가드)
크롤러 & 데이터셋
services/gateway/scripts/fetch-programmers-problems.ts— Playwright chromium headless로/learn/challenges?levels=N&order=acceptance_desc&page=M순회. 레벨별 독립 페이지네이션, 신규 항목 0개 시 종료, 300~500ms 랜덤 딜레이, HTML/URL 로깅 금지services/gateway/data/programmers-problems.json— 373건 수집 (Lv.1:95 / Lv.2:132 / Lv.3:95 / Lv.4:31 / Lv.5:20), 42840(모의고사) 포함, zod 런타임 검증 통과
DTO
services/problem/src/problem/dto/create-problem.dto.ts—SOURCE_PLATFORMS = ['BOJ','PROGRAMMERS'] as const추출,@IsIn(SOURCE_PLATFORMS)적용, 타입 export- DB 마이그레이션 불필요 (기존
source_platformVARCHAR(50) 유지)
테스트 동기화
services/problem/src/problem/problem.service.spec.tsL156/L414/L422 — 가상 플랫폼 리터럴('LeetCode','Codeforces') →'PROGRAMMERS'교체. sourceUrl은@IsUrl검증만 걸려있어 최소 변경 원칙으로 유지
검증 결과
| 항목 | 결과 |
|---|---|
| Gateway 유닛 테스트 (50 suites / 749 tests) | ✅ PASS |
Gateway tsc --noEmit | ✅ 오류 0 |
| Gateway ESLint (src + scripts) | ✅ 오류 0 |
Problem problem.service.spec.ts (35 tests) | ✅ PASS (초기 FAIL → Oracle 수정) |
BOJ 회귀 (solvedac.{service,controller}.ts diff) | ✅ 0줄 변경 |
| 데이터 품질(중복/누락/인코딩/대표문제 포함) | ✅ 6 PASS / 1 WARN(tags) |
결정
- 사전 JSON 번들링 > 실시간 파싱/비공식 API: 공식 API 부재 + Cloudflare JA3 차단(Sprint 83 전례) + 문제 풀 유한(373건)/갱신 빈도 낮음. 운영 안정성 우선
- 검색 API도 번들링 기반: Solvedac과 대칭되는
search엔드포인트를 인-메모리로 제공 → Sprint 96 프런트에서 동일 UX로 구현 가능 - Lv.1
5 ↔ BRONZEDIAMOND 1:1 매핑: 디자인 토큰 0줄 수정으로 기존Difficultyenum·스타일 토큰 재사용 - DTO
@IsIn강화: 자유 문자열 허용에서 허용값 제한으로 전환. 입력 경계에서 불일치를 조기 차단. DB는 VARCHAR 유지 → Expand-Contract 불필요 - 브랜치 규율 복원: architect가 실수로 main에 직접 커밋했던 adad5cf를
feat/gateway-programmers-dataset브랜치로 이동 후 main 리셋(로컬 단계). "main 직접 push 금지" 규칙 준수