인포그랩
경력근거: DevOps 지표(DORA Metrics) 측정 및 개선 활동
※ 실제 산출물이 아닌 AI 예측 시안입니다.
인포그랩 FDE 의 업무에는 고객사 DevOps 환경 구축·운영과 나란히 'DevOps 지표(DORA Metrics) 측정 및 개선 활동' 이 그대로 적혀 있고, 회사 스스로를 GitLab 중심의 AI 네이티브 DevOps 서비스회사로 소개하며 모든 반복은 자동화한다고 밝힙니다. 남의 회사 파이프라인에 들어가 성숙도를 숫자로 말해야 하는 자리이므로, 처음 보는 실행 기록과 `.gitlab-ci.yml` 만으로 네 지표를 계산하고 근거를 들어 병목 잡을 짚어 고객사에 넘길 문서를 뽑는 도구가 첫 주 업무 그 자체가 됩니다. 특히 파이프라인 밖에서 손으로 나간 배포를 수치에 섞지 않고 확인 질문으로 빼내는 부분은, 성숙도가 낮은 조직일수록 기록이 비어 있다는 현장 사정을 전제로 만든 것입니다. 다만 이 포트폴리오가 보여주는 것은 실행 기록 파싱과 지표 계산, 규칙 판정과 근거 추적, 컨테이너 배포라는 기술 역량이며, 금융권 대규모 SI·SM 운영 도메인 전문성은 주장하지 않습니다. 모르는 도메인 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
인포그랩 FDE 의 핵심 업무에 DORA 지표 측정과 개선 활동이 그대로 적혀 있습니다. 현장에서 '느린 것 같다' 가 아니라 지금 배포 빈도가 얼마고 어느 잡이 리드타임을 먹는지 숫자와 근거로 말해야 다음 개선 과제가 합의됩니다.
GitLab 을 중심으로 CI/CD 파이프라인을 구축하고 개발 프로세스를 개선하는 자리입니다. 남의 회사 파이프라인을 짧은 시간에 읽어내는 일이라, 설정 파일과 실행 기록을 같이 놓고 원인을 짚는 도구가 첫 주에 가장 많이 쓰입니다.
인포그랩은 AI 네이티브 DevOps 서비스회사로 모든 반복을 자동화한다고 밝히고, 업무에 Best Practice 전파와 기술 교육이 들어 있습니다. 진단이 한 번 쓰고 버리는 수작업이 아니라 명령 한 줄로 반복되고 그 결과가 고객사에 전달할 문서로 나와야 합니다.
고객사에서 내보낸 파이프라인 실행 이력 파일과 배포·장애 기록, 그리고 `.gitlab-ci.yml` 을 인자로 받아 `dora-scan report --since 2026-05-01` 한 줄로 도는 CLI입니다. 배포 빈도, 변경 리드타임, 변경 실패율, 복구 시간을 실행 기록에서 직접 계산하고, 재시도 비율이 높은 잡·앞 단계를 기다리느라 놀고 있는 잡·캐시를 못 받아 매번 처음부터 도는 잡을 규칙으로 골라냅니다. 각 진단에는 그 판단을 만든 실행 건 번호와 `.gitlab-ci.yml` 의 줄 번호를 붙여, 고객사 담당자가 리포트를 열고 바로 그 자리를 열어볼 수 있게 합니다. 파이프라인을 거치지 않고 손으로 나간 배포나 복구 시각이 비어 있는 장애처럼 기록만으로 판단이 안 되는 건 수치에 섞지 않고 '확인이 필요한 항목' 으로 따로 빼서 묻습니다. 결과물은 터미널 요약과 고객사에 그대로 넘길 마크다운 리포트 파일입니다.
고객사에 이번 주 처음 들어간 DevOps 엔지니어입니다. 킥오프에서 '배포가 느리고 자꾸 깨진다' 는 말만 듣고 나왔는데, 그 회사 파이프라인을 제가 만든 게 아니라 어디부터 봐야 할지 모릅니다. 실행 이력을 내려받아 스프레드시트로 열어 보면 몇천 줄이라 눈으로 세다가 이틀이 갑니다. 다음 주 보고에서 '체감상 느립니다' 대신 지금 배포 빈도가 주 몇 회이고 어느 잡이 리드타임의 절반을 먹는지 숫자로 말하고 싶고, 그 숫자를 의심받으면 근거가 된 실행 건을 바로 열어 보여주고 싶은 사람입니다.
DORA 네 지표와 파이프라인 병목은 사람 판단이 아니라 실행 기록에서 계산되는 값이라고 보고 규칙으로 먼저 다 계산한 다음, 모델에게는 그 수치가 어디서 생긴 지연인지를 고객사 담당자가 읽을 한국어 문장으로 요약하는 일만 맡기면, (a) 처음 보는 파이프라인도 명령 한 줄로 지표와 병목 순위가 나오고, (b) 모든 진단에 실행 건 번호와 설정 줄 번호가 붙어 재현 확인이 가능하며, (c) 기록만으로 못 정하는 항목은 수치에 섞이지 않고 확인 질문으로 분리되어 숫자가 과장되지 않습니다.
파이프라인 실행 이력 파일과 배포·장애 기록, `.gitlab-ci.yml` 을 받아 배포 빈도·변경 리드타임·변경 실패율·복구 시간을 계산하고, 규칙 여섯 개로 병목 잡을 골라 근거 실행 건 번호와 설정 줄 번호를 붙인 마크다운 리포트를 내보내는 CLI입니다. 고객사 GitLab 서버에 직접 붙는 API 연동은 사내망 접근 권한이 필요하므로 1주차에는 내보내기 파일 입력만 가정하고, 실시간 수집은 3주차로 미룹니다.
프로젝트마다 다르게 적힌 잡 이름과 실패 사유 문구를 정규화 규칙으로 묶어 같은 잡이 따로 세어지지 않게 하고, DevOps 엔지니어 2명이 각 진단이 실제로 손댈 만한 지적인지 라벨링해 판정 일치도를 붙이는 단계입니다.
도구를 컨테이너로 감싸 쿠버네티스 CronJob 으로 주 1회 돌리고, Terraform 으로 실행 환경을 코드로 남겨 다른 고객사에 같은 구성을 그대로 세우는 단계입니다. 이때부터 내보내기 파일 대신 GitLab API 로 직접 수집합니다.
함정을 섞은 3개 프로젝트분 실행 기록을 넣으면 배포 빈도·변경 리드타임·변경 실패율·복구 시간 네 숫자와 병목 잡 순위가 나오고, 각 진단을 펼치면 근거가 된 실행 건 번호와 설정 파일 줄 번호가 붙어 있는 걸 시연합니다
계산된 네 지표를 같은 기록에서 손으로 센 값과 대조해 100% 자동 판정합니다. 일부러 만든 함정 4종(파이프라인 밖 수동 배포, 복구 시각 결측, 재시도로 중복된 실행, 프로그램에 없는 잡)이 지표를 오염시키지 않고 확인 항목으로 빠지는지 매번 검사합니다
실행 이력 파일이 반쯤 깨진 30종(열 누락·시각 역전·인코딩 깨짐·빈 파일)을 넣어도 도구가 죽지 않고 무엇이 왜 안 읽혔는지 말하고 끝납니다
생성된 리포트 문장이 계산 결과에 없는 수치를 지어내지 않았는지, 문장 속 숫자와 근거 번호를 계산 결과와 기계로 대조해 검사합니다. 단, 기록과 정답을 모두 작성자가 만든 합성 기준이라 '성능 검증' 이 아니라 '데모 조건에서의 동작' 입니다
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 전부 작성자가 만든 더미데이터 자가채점이며, 실제 진단이 고객사에서 손댈 만한 지적인지는 인포그랩이 들어간 고객사의 실제 파이프라인 기록과 그 회사 담당자 검수가 있어야 측정 가능합니다. 이 도구는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다
이건 고객·실데이터 없이 혼자 만든 포트폴리오입니다. 모든 수치는 합성 데이터 자가채점이라 '성능 증명' 이 아닙니다. 면접에서 '데모 조건 동작 + 실검증 준비 상태' 로 솔직히 설명합니다
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 파이프라인 밖 수동 배포·복구 시각 결측·재시도 중복·잡 이름 표기 흔들림 같은 경계 케이스를 더미에 섞어, 쉬운 문제만 푸는 게 아님을 보여줍니다
DORA 네 지표는 파이프라인에 남은 기록만 봅니다. 손으로 나간 배포가 많은 조직에서는 배포 빈도가 실제보다 낮게 나오므로, 숫자를 성숙도 점수처럼 단정하지 말고 '기록에 남은 범위' 라는 단서를 리포트 첫 줄에 함께 씁니다
병목 규칙은 파이프라인 성격을 타서, 원래 오래 걸리는 게 맞는 잡(전체 회귀 테스트)을 병목으로 잘못 지목할 수 있습니다. 규칙마다 왜 걸렸는지를 함께 내보내 담당자가 기각할 수 있게 만듭니다
실제 도입 시 달라지는 것을 명시합니다. GitLab API 직접 수집 권한, 고객사 보안 정책에 맞춘 사내 배포, 담당자 검수로 교체해야 하며 이는 도입 단계 과제로 분리합니다
이 포트폴리오가 보여주는 것은 파이프라인 실행 기록 파싱과 지표 계산, 규칙 판정과 근거 추적, 컨테이너 배포라는 기술 역량이며, 금융권 대규모 SI 운영 도메인 전문성은 주장하지 않습니다. 모르는 도메인 깊이는 면접에서 모른다고 말하는 편이 낫습니다
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사가 내보낸 파이프라인 실행 이력입니다. 잡 하나의 한 번 실행이 한 행이고, 지표 계산과 병목 판정이 모두 이 표에서 나옵니다. 종료 시각 결측, 잡 이름 표기 흔들림, 재시도로 중복된 실행, 러너 태그 미등록, 극단적인 소요 시간을 일부러 섞어 쉬운 데이터만 푸는 게 아님을 보여줍니다.
| run_id | project | pipeline_id | trigger | stage | job_name_raw | job_key | status | started_at | finished_at | duration_sec | retry_index | runner_tag | cache_hit | failure_reason | commit_sha |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| j0001 | core-api | p101 | merge_request | build | build-image | build_image | success | 2026-05-04T09:12:00+09:00 | 2026-05-04T09:18:20+09:00 | 380 | 0 | docker-large | true | — | a1b2c3d4e5f6 |
| j0002 | core-api | p101 | merge_request | test | unit_test | unit_test | failed | 2026-05-04T09:18:40+09:00 | 2026-05-04T09:41:10+09:00 | 1,350 | 0 | docker-large | false | 테스트 3건 실패 | a1b2c3d4e5f6 |
| j0003 | core-api | p101 | merge_request | test | Unit-Test | unit_test | success | 2026-05-04T09:52:00+09:00 | 2026-05-04T10:15:30+09:00 | 1,410 | 1 | docker-large | false | — | a1b2c3d4e5f6 |
| j0004 | core-api | p101 | merge_request | deploy | deploy-stg | deploy_stg | success | 2026-05-04T10:16:00+09:00 | 2026-05-04T10:19:00+09:00 | 180 | 0 | shell-deploy | — | — | a1b2c3d4e5f6 |
| j0005 | core-api | p102 | push | build | build image | build_image | canceled | 2026-05-06T14:02:00+09:00 | — | — | 0 | docker-large | false | 사용자 취소 | b2c3d4e5f6a1 |
아래 JSON Schema 에 맞는 고객사 파이프라인 실행 이력 120건을 JSON 배열로 생성. 프로젝트는 core-api, web-front, batch-etl 세 개로 하고 pipeline_id 하나에 여러 잡 행이 묶이도록.
{"type":"object","properties":{"run_id":{"type":"string"},"project":{"type":"string"},"pipeline_id":{"type":"string"},"trigger":{"enum":["push","merge_request","schedule","manual"]},"stage":{"type":"string"},"job_name_raw":{"type":"string"},"job_key":{"type":["string","null"]},"status":{"enum":["success","failed","canceled","skipped"]},"started_at":{"type":"string"},"finished_at":{"type":["string","null"]},"duration_sec":{"type":["number","null"]},"retry_index":{"type":"number"},"runner_tag":{"type":["string","null"]},"cache_hit":{"type":["boolean","null"]},"failure_reason":{"type":["string","null"]},"commit_sha":{"type":"string"}},"required":["run_id","project","pipeline_id","trigger","stage","job_name_raw","status","started_at","retry_index","commit_sha"]}
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. (1) status 가 canceled 라 finished_at 과 duration_sec 이 null 인 행 8~10건, (2) 같은 잡을 대소문자·하이픈·언더바·공백만 다르게 적은 job_name_raw 표기 흔들림(build-image / build_image / Build Image / build image) 10쌍 이상, (3) 같은 pipeline_id 안에서 retry_index 가 1 이상으로 올라가며 반복된 실행 12건 이상(그중 절반은 마지막에 success), (4) job_key 가 null 이라 어느 잡인지 매핑되지 않는 행 5건, (5) runner_tag 가 null 인 행 8건, (6) duration_sec 이 0 인 skipped 행과 6000 이상인 야간 회귀 테스트 행을 함께, (7) 같은 원인을 다른 문장으로 적은 failure_reason(타임아웃 / 시간 초과로 중단 / 제한 시간 경과) 6건 이상. run_id 는 j0001 부터 연속으로. JSON 배열만.{"type":"object","properties":{"run_id":{"type":"string"},"project":{"type":"string"},"pipeline_id":{"type":"string"},"trigger":{"enum":["push","merge_request","schedule","manual"]},"stage":{"type":"string"},"job_name_raw":{"type":"string"},"job_key":{"type":["string","null"]},"status":{"enum":["success","failed","canceled","skipped"]},"started_at":{"type":"string"},"finished_at":{"type":["string","null"]},"duration_sec":{"type":["number","null"]},"retry_index":{"type":"number"},"runner_tag":{"type":["string","null"]},"cache_hit":{"type":["boolean","null"]},"failure_reason":{"type":["string","null"]},"commit_sha":{"type":"string"}},"required":["run_id","project","pipeline_id","trigger","stage","job_name_raw","status","started_at","retry_index","commit_sha"]}고객사의 운영 배포와 장애·복구 기록입니다. 배포 빈도와 변경 실패율, 복구 시간을 계산하는 축이며 파이프라인 실행 기록과 run_id 로 이어집니다. 파이프라인을 거치지 않고 손으로 나간 배포와 복구 시각이 비어 있는 장애를 일부러 섞어, 그 행들이 지표에 섞이지 않고 확인 항목으로 빠지는지 검사하게 합니다.
| event_id | project | kind | env | occurred_at | pipeline_run_id | commit_sha | restored_at | caused_by_event_id | note |
|---|---|---|---|---|---|---|---|---|---|
| e001 | core-api | 배포 | prod | 2026-05-12T16:47:00+09:00 | j0014 | f6a1b2c3d4e5 | — | — | — |
| e002 | batch-etl | 배포 | prod | 2026-05-09T10:14:00+09:00 | j0013 | e5f6a1b2c3d4 | — | — | — |
| e003 | batch-etl | 장애 | prod | 2026-05-09T11:20:00+09:00 | — | e5f6a1b2c3d4 | 2026-05-09T12:05:00+09:00 | e002 | 적재 잡이 빈 파일을 덮어써 조회가 비었습니다 |
| e004 | core-api | 배포 | prod | 2026-05-14T21:30:00+09:00 | — | — | — | — | 긴급 수정으로 담당자가 서버에 직접 올렸습니다 |
| e005 | core-api | 장애 | prod | 2026-05-14T22:10:00+09:00 | — | — | — | e004 | 복구 시각을 아무도 적지 않았습니다 |
아래 JSON Schema 에 맞는 고객사 배포·장애 기록 50건을 JSON 배열로 생성. project 는 core-api, web-front, batch-etl 세 개, pipeline_run_id 는 pipeline_runs 의 run_id 와 이어지도록.
{"type":"object","properties":{"event_id":{"type":"string"},"project":{"type":"string"},"kind":{"enum":["배포","장애"]},"env":{"enum":["prod","stg"]},"occurred_at":{"type":"string"},"pipeline_run_id":{"type":["string","null"]},"commit_sha":{"type":["string","null"]},"restored_at":{"type":["string","null"]},"caused_by_event_id":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["event_id","project","kind","env","occurred_at"]}
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. (1) pipeline_run_id 가 null 인 손으로 나간 배포 8건 이상(배포 빈도 계산에 섞이면 안 되고 확인 항목으로 빠져야 한다), (2) kind='장애' 인데 restored_at 이 null 인 미복구 행 5건(복구 시간 평균에 섞이면 안 된다), (3) pipeline_run_id 가 실행 이력에 없는 번호를 가리키는 행 3건, (4) commit_sha 를 8자리 짧은 해시로 적어 12자리와 표기가 어긋나는 행 5건, (5) caused_by_event_id 가 배포가 아니라 다른 장애를 가리키거나 null 인 행을 섞어 변경 실패율 분모 판단이 필요하게, (6) 같은 pipeline_run_id 로 두 번 기록된 중복 배포 2건. event_id 는 e001 부터 연속으로. JSON 배열만.{"type":"object","properties":{"event_id":{"type":"string"},"project":{"type":"string"},"kind":{"enum":["배포","장애"]},"env":{"enum":["prod","stg"]},"occurred_at":{"type":"string"},"pipeline_run_id":{"type":["string","null"]},"commit_sha":{"type":["string","null"]},"restored_at":{"type":["string","null"]},"caused_by_event_id":{"type":["string","null"]},"note":{"type":["string","null"]}},"required":["event_id","project","kind","env","occurred_at"]}| Python | 실행 이력 파싱, DORA 네 지표 계산, 병목 규칙 판정, 마크다운 리포트 생성을 구현하는 대표 언어 |
| GitLab CI | 진단 대상이자 도구 자신의 검증 수단으로, `.gitlab-ci.yml` 을 파싱해 잡 의존 관계와 캐시 설정을 읽고 도구 자체도 GitLab CI 로 테스트를 돌립니다 |
| Jenkins | 잡 이름·단계·실행 시각만 남은 최소 공통 형태로 입력 스키마를 잡아, Jenkins 빌드 이력 내보내기도 같은 파서로 읽히게 합니다 |
| Docker | 고객사마다 다른 파이썬 버전에 영향받지 않도록 도구를 이미지 한 장으로 배포하고, 현장 노트북에서 `docker run` 한 줄로 돌게 합니다 |
| Linux | 실행 기록 파일을 다루는 셸 파이프라인과 컨테이너 런타임 운영 환경 |
| Kubernetes | 3주차에 진단을 CronJob 으로 정기 실행하고 리포트를 아티팩트로 남기는 실행 기반 |
| Terraform | 3주차에 진단 실행 환경(CronJob·네임스페이스·시크릿)을 코드로 정의해 다음 고객사에 같은 구성을 세웁니다 |
| Ansible | 고객사 현장 서버에 도구를 설치하고 실행 계정·경로를 맞추는 절차를 플레이북 한 벌로 고정합니다 |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우고 싶으면 2주차(잡 이름 정규화·사람 검수)로 진행합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(쿠버네티스 정기 실행·Terraform 환경 코드화)로 진행합니다.
한 번의 진단 실행 결과입니다. DORA 네 지표와 각 수치를 만든 행 번호, 병목 진단 목록(규칙 번호·계산 수치·근거 실행 건·설정 파일 줄 번호·담당자가 읽을 문장), 그리고 기록만으로 못 정해 확인이 필요한 항목을 담습니다. 수치와 근거가 어긋난 출력을 기계로 걸러낼 수 있게 필드를 나눠 둡니다.
{"type":"object","properties":{"scan_id":{"type":"string"},"period":{"type":"object","properties":{"since":{"type":"string"},"until":{"type":"string"}},"required":["since","until"]},"dora":{"type":"object","properties":{"deploy_frequency_per_week":{"type":"number"},"lead_time_hours_p50":{"type":["number","null"]},"change_failure_rate":{"type":["number","null"]},"restore_hours_p50":{"type":["number","null"]},"counted_event_ids":{"type":"array","items":{"type":"string"}},"excluded_event_ids":{"type":"array","items":{"type":"string"}}},"required":["deploy_frequency_per_week","counted_event_ids","excluded_event_ids"]},"findings":{"type":"array","items":{"type":"object","properties":{"rule_id":{"enum":["retry_heavy","stage_wait","cache_miss","serial_bottleneck","flaky_job","long_tail_job"]},"job_key":{"type":"string"},"measured":{"type":"object"},"evidence_run_ids":{"type":"array","items":{"type":"string"}},"ci_config_lines":{"type":"array","items":{"type":"number"}},"message_ko":{"type":"string"},"severity":{"enum":["high","mid","low"]}},"required":["rule_id","job_key","measured","evidence_run_ids","message_ko","severity"]}},"needs_confirmation":{"type":"array","items":{"type":"object","properties":{"reason":{"enum":["pipeline_없는_배포","복구시각_결측","실행번호_불일치","잡_매핑_실패"]},"ref_ids":{"type":"array","items":{"type":"string"}},"question_ko":{"type":"string"}},"required":["reason","ref_ids","question_ko"]}}},"required":["scan_id","period","dora","findings","needs_confirmation"]}[
{
"input_ref": "core-api·web-front·batch-etl 3개 프로젝트, 2026-05-01 ~ 2026-05-31 실행 기록",
"output": {
"scan_id": "scan-2026-05",
"period": {
"since": "2026-05-01",
"until": "2026-05-31"
},
"dora": {
"deploy_frequency_per_week": 1.2,
"lead_time_hours_p50": 4.3,
"change_failure_rate": 0.4,
"restore_hours_p50": 0.75,
"counted_event_ids": [
"e001",
"e002",
"e006",
"e007"
],
"excluded_event_ids": [
"e004",
"e009",
"e010",
"e012"
]
},
"findings": [
{
"rule_id": "retry_heavy",
"job_key": "e2e",
"measured": {
"runs": 3,
"retry_ratio": 0.67,
"wasted_sec": 5040
},
"evidence_run_ids": [
"j0007",
"j0008",
"j0009"
],
"ci_config_lines": [
41,
42,
43
],
"message_ko": "web-front 의 e2e 잡이 같은 커밋에서 세 번 돌았고 앞의 두 번은 시간 초과로 버려졌습니다. 버려진 시간만 84분이라, 재시도를 늘리기 전에 제한 시간과 병렬 분할을 먼저 보는 편이 좋습니다.",
"severity": "high"
},
{
"rule_id": "cache_miss",
"job_key": "npm_build",
"measured": {
"runs": 1,
"cache_hit_ratio": 0,
"avg_sec": 1360
},
"evidence_run_ids": [
"j0006"
],
"ci_config_lines": [
22,
23
],
"message_ko": "web-front 의 npm_build 는 캐시를 한 번도 받지 못해 매번 처음부터 설치합니다. 설정에 cache 키가 브랜치 이름으로 잡혀 있어서 브랜치가 바뀔 때마다 캐시가 새로 생깁니다.",
"severity": "mid"
}
],
"needs_confirmation": [
{
"reason": "pipeline_없는_배포",
"ref_ids": [
"e004",
"e010"
],
"question_ko": "파이프라인을 거치지 않고 서버에 직접 올라간 배포가 두 건 있습니다. 배포 빈도에 포함할까요, 예외로 둘까요?"
},
{
"reason": "복구시각_결측",
"ref_ids": [
"e005"
],
"question_ko": "5월 14일 장애의 복구 시각이 비어 있어 복구 시간 평균에서 뺐습니다. 실제 복구 시각을 아시는 분이 있을까요?"
}
]
}
},
{
"input_ref": "batch-etl 단독, 실행 이력에 없는 번호를 가리키는 배포가 섞인 경우",
"output": {
"scan_id": "scan-batch-05",
"period": {
"since": "2026-05-01",
"until": "2026-05-31"
},
"dora": {
"deploy_frequency_per_week": 0.25,
"lead_time_hours_p50": 1.7,
"change_failure_rate": 1,
"restore_hours_p50": 0.75,
"counted_event_ids": [
"e002"
],
"excluded_event_ids": [
"e010"
]
},
"findings": [
{
"rule_id": "flaky_job",
"job_key": "deploy_prod",
"measured": {
"runs": 2,
"fail_ratio": 0.5,
"same_commit": true
},
"evidence_run_ids": [
"j0012",
"j0013"
],
"ci_config_lines": [
58
],
"message_ko": "batch-etl 의 deploy_prod 가 같은 커밋에서 한 번 권한 오류로 실패한 뒤 수동 실행으로 성공했습니다. 배포 계정 권한이 파이프라인 러너에 없어서 매번 사람이 다시 눌러야 하는 상태로 보입니다.",
"severity": "high"
}
],
"needs_confirmation": [
{
"reason": "pipeline_없는_배포",
"ref_ids": [
"e010"
],
"question_ko": "5월 20일 배포는 실행 기록에 짝이 없습니다. 야간에 수동으로 반영한 건이 맞을까요?"
}
]
}
},
{
"input_ref": "잡 이름이 매핑되지 않은 야간 회귀 테스트가 섞인 경우",
"output": {
"scan_id": "scan-nightly",
"period": {
"since": "2026-05-08",
"until": "2026-05-08"
},
"dora": {
"deploy_frequency_per_week": 0,
"lead_time_hours_p50": null,
"change_failure_rate": null,
"restore_hours_p50": null,
"counted_event_ids": [],
"excluded_event_ids": []
},
"findings": [
{
"rule_id": "long_tail_job",
"job_key": "nightly-regression",
"measured": {
"runs": 1,
"avg_sec": 6000,
"share_of_pipeline": 1
},
"evidence_run_ids": [
"j0010"
],
"ci_config_lines": [],
"message_ko": "야간 회귀 테스트가 100분 걸리며 그 파이프라인의 거의 전부를 차지합니다. 야간에만 돌아 배포를 직접 막지는 않으므로, 줄이기보다 실패했을 때 누가 보는지를 먼저 정하는 편이 낫습니다.",
"severity": "low"
}
],
"needs_confirmation": [
{
"reason": "잡_매핑_실패",
"ref_ids": [
"j0010"
],
"question_ko": "nightly-regression 잡이 설정 파일에서 찾아지지 않습니다. 다른 저장소에서 불러오는 잡일까요?"
}
]
}
}
][자동 검증: 결정론, 사람 없이 통과] 1. JSON Schema 통과: dora·findings·needs_confirmation 의 필수 필드와 rule_id·reason 열거값을 기계로 검사해 형식이 깨진 출력을 거릅니다. 2. 지표 재계산 대조: dora 의 네 수치를 같은 기록으로 독립 계산한 값과 비교합니다. counted_event_ids 와 excluded_event_ids 의 합이 기간 내 전체 배포 건과 일치하는지도 함께 봅니다. 3. 근거 실재 확인: findings 의 evidence_run_ids 가 모두 pipeline_runs 에 있는 run_id 인지, needs_confirmation 의 ref_ids 가 실재하는 행인지 집합으로 대조합니다. 4. 문장 속 숫자 대조: message_ko 에 등장하는 숫자를 뽑아 measured 값과 맞춰, 모델이 없는 수치를 지어낸 출력을 거릅니다. 5. 제외 규칙 강제: pipeline_run_id 가 null 인 배포와 restored_at 이 null 인 장애가 counted 에 들어가면 실패로 처리합니다. 지표가 과장되지 않게 하는 핵심 검사입니다. [수동 검증: 사람 필요] 1. DevOps 엔지니어 2명이 각 finding 이 실제로 손댈 만한 지적인지(원래 오래 걸리는 게 맞는 잡을 병목으로 지목하지 않았는지) 라벨링하고, 두 사람 판정 일치도를 Cohen's κ(두 사람 답이 우연 이상으로 일치하는 정도, 0~1)로 봅니다. 2. message_ko 의 어조가 고객사에 그대로 전달해도 될 수준인지 5% 표본을 사람이 읽습니다.