프로그래머스 검색 장애 수정 — Dockerfile data/ 번들 누락

Sprint 98 — 프로그래머스 검색 장애 수정

배경

Sprint 95~97에서 프로그래머스 전환을 완료하였으나, 운영 환경에서 프로그래머스 문제 검색이 전면 NotFoundException 을 반환하는 장애가 발생했다. 서비스 자체 로직은 정상이었고, 원인은 인프라 (Dockerfile) 계층의 파일 번들 누락이었다.

증상

  • GET /external/programmers/search?q=모의고사404 NotFoundException
  • Gateway 서비스 로그: 프로그래머스 JSON 로드 실패 (빈 캐시로 기동): Error: ENOENT: no such file or directory
  • 부팅 시 cache.size = 0 → 모든 검색/조회 불가

근본 원인 분석

경로 추적

CMD: node dist/src/main.js
컴파일 위치: src/external/programmers.service.ts → dist/src/external/programmers.service.js
__dirname @ runtime: /app/dist/src/external/

join(__dirname, '..', '..', 'data', 'programmers-problems.json')
= /app/dist/src/external/../../data/programmers-problems.json
= /app/dist/data/programmers-problems.json   ← 컨테이너 내 기대 경로

Dockerfile 분석

Dockerfile
# production stage (수정 전)
COPY --from=builder /app/dist ./dist      # dist/ 복사
# data/ 미포함 → /app/dist/data/ 존재하지 않음
  • Builder 단계: COPY . .data/programmers-problems.json 존재 (/app/data/)
  • npm run build (nest build): data/dist/ 자동 복사 없음
  • Production stage: dist/ 만 복사, data/ 누락
  • 결과: /app/dist/data/programmers-problems.json 미존재 → ENOENT → 빈 캐시

ts-node 환경과의 차이

로컬 개발(nest start:dev, ts-node) 환경에서는 __dirname = /app/src/external/ 이므로 join('..', '..', 'data') = /app/data/ (프로젝트 루트) → 파일 존재하여 정상 작동. 컴파일 환경에서만 경로가 dist/ 기준으로 이동하므로 개발 중 재현 불가했던 이유다.

수정 내용

1. nest-cli.json — assets 자동 번들 (NestJS 관례)

JSON
"compilerOptions": {
  "deleteOutDir": true,
  "assets": [
    {
      "include": "../data/**",
      "outDir": "./dist/data",
      "watchAssets": false
    }
  ]
}

npm run build 실행 시 data/dist/data/ 자동 복사. 로컬 컴파일 빌드에서도 재현 방지.

2. Dockerfile — 명시적 COPY (안전망)

Dockerfile
COPY --from=builder /app/data ./dist/data

nest-cli assets 경로 해석의 버전 의존성과 무관하게, 반드시 /app/dist/data/에 파일이 존재함을 보장하는 안전망. CI 빌드 시 이 레이어가 없으면 이미지 빌드 실패로 조기 발견 가능.

수정된 파일

파일작업설명
services/gateway/Dockerfile수정production stage에 COPY --from=builder /app/data ./dist/data 추가
services/gateway/nest-cli.json수정compilerOptions.assets 추가로 nest build 시 자동 복사

검증 결과

단위 테스트

Test Suites: 50 passed, 50 total
Tests:       754 passed, 754 total  (programmers 관련 43건 포함)
  • programmers.service.spec.ts: 기존 loadFromFilefetchProblem 성공 검증으로 충분
  • cache.size > 0 검증은 fetchProblem(42840) 성공으로 동치 → 신규 테스트 불필요

E2E 테스트 (programmers-full-flow.spec.ts)

Test Suites: 1 passed, 1 total
Tests:       13 passed, 13 total
  [A] GitHub Worker 파일 경로 생성 — 4건 PASS
  [B] MQ 이벤트 계약 sourcePlatform 검증 — 4건 PASS
  [C] AI 프롬프트 플랫폼 맥락 주입 — 3건 PASS (Python 3.9.6 로컬: empty-pass, CI Python 3.12에서 완전 실행)
  [D] 외부 API 격리 검증 — 2건 PASS

타입/린트

tsc --noEmit: PASS (오류 없음)
eslint: 0 errors, 6 warnings (pre-existing, programmers 수정과 무관)
  - structured-logger.service.ts: _t, _r unused vars
  - public-profile.controller.spec.ts, public-share.controller.spec.ts: fetch/모킹 unused vars

회귀 방지

  • Dockerfile 안전망: COPY --from=builder /app/data ./dist/data 레이어로 빌드 시 data/ 존재 보장
  • nest-cli.json assets: npm run build 로컬 환경에서도 dist/data/ 자동 생성
  • CI 검증 권장 (이월): .github/workflows/dist/data/programmers-problems.json 존재 검증 step 추가

2차 증상 — 레벨 0 문제 미크롤링

증상

Dockerfile 수정 후에도 프로그래머스 레벨 0(코딩기초트레이닝) 문제를 검색하면 404 NotFoundException.

원인

크롤러 services/gateway/scripts/fetch-programmers-problems.ts:55LEVELS = [1, 2, 3, 4, 5] 상수가 레벨 0을 명시적으로 제외하여 JSON 스냅샷(373건)에 코딩기초트레이닝 문제가 전혀 포함되지 않음.

수정 내용

  1. 크롤러 LEVELS 확장[0, 1, 2, 3, 4, 5] 로 변경 (커밋 266c8c5)
  2. levelToDifficulty(0) — 기존 ?? null 분기가 이미 레벨 0을 null 매핑으로 처리 → 로직 변경 없이 주석만 0~5 범위로 갱신
  3. 회귀 테스트programmers.service.spec.ts에 레벨 0 봉투 형식 로드 + difficulty=null 검증 1건 추가 (총 44건 PASS)
  4. 운영 반영 — 코드 수정 후 데이터 재수집 필요: cd services/gateway && npm run fetch-programmers (Playwright 네트워크 작업, 수 분 소요)

검증 결과

  • programmers.*.spec.ts: 44건 PASS (+1건 신규)
  • tsc --noEmit: PASS

데이터 재수집 완료 (커밋 1bc2a0a)

npm run fetch-programmers 실행 결과:

레벨건수비고
Lv0 (코딩기초트레이닝)240신규
Lv195기존
Lv2132기존
Lv395기존
Lv431기존
Lv520기존
총계613 (+240)373 → 613
  • 버전: 2026-04-20T04:35:10.312Z
  • Zod 검증 PASS
  • 샘플: 120583 "중복된 숫자 개수", 120585 "머쓱이보다 키 큰 사람", 120802 "두 수의 합 구하기"
  • 검증 문제 42840 모의고사 존재 확인
  • 소요 시간: 70초 (단일 패스)

3차 보강 — Dockerfile 빌드시점 검증 (커밋 cfb19ec)

COPY --from=builder /app/data ./dist/data 직후 RUN test -f /app/dist/data/programmers-problems.json 추가. 향후 경로 변경/삭제 시 docker build 자체가 실패하여 조기 발견. CI에 별도 step 추가 불필요한 inline 방어.

이월 항목

#항목우선순위
1레벨 0 문제 tags 보강 — fetch-programmers-tags.ts 2차 패스 실행 (240건 태그 빈 배열)보통
2H5 UX 개선 — 키워드 검색 연동 (프론트 검색창 → Gateway searchByQuery API 호출)낮음
3ESLint pre-existing 경고 6건 정리 (structured-logger, public-profile/share spec)낮음

커밋

SHA메시지
79b6dbbfix(gateway): Dockerfile에 programmers JSON 데이터 번들 추가
bf794a5docs(adr): Sprint 98 — 프로그래머스 검색 장애 수정 기록
b84c481docs(adr): Sprint 98 — Postman 검증 결과 보완
266c8c5fix(gateway): 프로그래머스 레벨 0 문제 크롤링 대상 포함
9e78e19docs(adr): 레벨 0 크롤링 누락 2차 증상 기록
1bc2a0achore(data): 프로그래머스 문제 레벨 0 포함 재수집 (373 → 613건)
cfb19ecfix(gateway): Dockerfile 빌드시점 data/ 번들 누락 검증 추가