Quality-Gate System — Critical Assessment, Catalog and Replication Playbook (한국어)
1부 — 평가 및 성숙도 분류
섹션 제목: “1부 — 평가 및 성숙도 분류”종합 등급: A− / “고급”. 전체 프로젝트 중 상위 약 5~10%. 이 시스템은 업계에서 명시적으로 이름 붙인 여러 패턴을 독립적으로 구현하고 있습니다. 이는 가장 강력한 정합성의 신호입니다. 체크리스트를 그대로 모방한 것이 아니라 올바른 관행에 자연스럽게 도달했습니다.
| 참조 프레임워크 | 현재 수준 | 등급 |
|---|---|---|
| OWASP DSOMM (5개 수준, 5개 차원) | 견고한 레벨 3이며, _테스트 강도_와 _정적 분석 깊이_에서는 레벨 4에 도달하고 있습니다. 대부분의 조직은 레벨 1~2에 머뭅니다. | L3→L4 |
| OpenSSF Scorecard (18개 검사) | CI-Tests, Code-Review, Dependency-Update-Tool, Fuzzing, SAST, Signed-Releases(출처 증명), Token-Permissions, Vulnerabilities, Dangerous-Workflow를 통과합니다. 격차: main의 Branch-Protection이 꺼져 있으며, 일부 액션은 고정되어 있지 않습니다. |
~7–8/10 |
| SLSA (4개 수준) | npm publish --provenance + id-token: write + GitHub 호스팅 빌드 = L2이며, L3에 근접하고 있습니다. L3 이상에 필요한 강화된/밀폐형 빌더가 없습니다. |
L2→L3 |
| SonarQube “Clean as You Code” | 철학이 동일합니다. 래칫은 _비회귀_를 보장합니다(새 코드가 지표를 악화시키지 않음). 차이점: Sonar는 소수의 조건을 권장하지만, 당사 시스템에는 약 46개의 게이트가 있어 피로도 위험이 있습니다. | 정합, 단 주의 필요 |
| Quality-Ratchet 패턴 | 래칫 + dedicatedGate + tightenSlack + --require-tighten + 정상적 건너뛰기를 갖춘 참조 구현입니다. 대부분의 공개 예시보다 더 정교합니다. |
모범적 |
| DORA 2024 | 안정성 축에서 매우 강력합니다. 위험 요소: 과도한 게이트는 _리드 타임_을 늘릴 수 있습니다. 빠른 게이트 분리로 완화했지만, 커버리지 격차가 존재합니다(2부 참조). | 강력함(안정성) |
| OWASP LLM Top 10 (2025) | 런타임 가드 + promptfoo(평가) + garak(레드팀)을 통해 위험 #1(프롬프트 인젝션)을 다룹니다. 업계 표준 도구를 사용합니다. | 대응 완료 |
| 변이 테스트 | Stryker를 매일 밤 실행하며 임계값은 70/50이고, 8개의 핵심 모듈을 대상으로 합니다. 업계 합의(기존 코드 60% / 신규 코드 80%, 매일 밤 실행)를 상회합니다. 격차: 점수가 아직 래칫으로 관리되지 않습니다. | 거의 완성 |
파트 2 — 비판적 평가 (강점 + 솔직한 약점)
섹션 제목: “파트 2 — 비판적 평가 (강점 + 솔직한 약점)”강점 (평균 이상인 부분)
섹션 제목: “강점 (평균 이상인 부분)”- 다중 메트릭 래칫 엔진. 시스템의 핵심입니다.
quality-baseline.json에 24개의 메트릭이 있습니다.- 각각 방향(
up/down), 허용 오차(eps), 여유분 (tightenSlack),dedicatedGate플래그를 갖는 4개의 전용 기준선이 있습니다. 한 번 수정된 항목은 계속 수정된 상태로 유지됩니다. 즉, 코드베이스 엔트로피에 대한 해독제입니다.
- 각각 방향(
- 공급망을 위한 심층 방어. SAST(CodeQL/Sonar) + 시크릿(gitleaks 및
useDefault) + SCA(osv/npm-audit/Trivy/Dependabot) + 라이선스 + lockfile + SBOM + SLSA 출처 증명 + Scorecard + 워크플로 강화(zizmor). 이처럼 완전한 스택을 갖춘 코드베이스는 드뭅니다. - Goodhart의 법칙에 대한 해독제. 커버리지를 목표로 삼는 것은 전형적인 안티패턴입니다
(“측정 지표가 목표가 되는 순간, 더 이상 좋은 측정 지표가 아니다”). 여기에는 이를
보완하는 장치가 있습니다. 변이 테스트(테스트가 단순히 해당 줄을 실행하는지가 아니라
실제로 버그를 잡아내는지를 측정),
check-test-masking(통과를 위해 assert를 약화하는 행위를 차단), 모듈별 최소 커버리지(쉬운 부분뿐 아니라 고위험 코드도 테스트하도록 강제),check-pr-evidence(강제 규칙 #18). - 환각 방지 / 일관성 게이트. 드물지만 가치 있는 범주입니다.
check-known-symbols,check-fetch-targets,check-openapi-routes,check-docs-symbols는 문서, 명세, 문자열 디스패치가 실제로 존재하는 심볼을 가리키도록 보장합니다. lint/test로는 잡을 수 없는 “부패”를 탐지합니다. - 권고→차단 수명 주기. 새로운 게이트는 권고 수준으로 도입되어 성숙하는 동안에는 병합을 차단하지 않다가, 사이클 종료 시 차단 게이트로 전환됩니다. 상한선을 잃지 않으면서 마찰을 줄입니다.
- 인프라 누락 시 정상적인 건너뛰기. 스캐너(
--ratchet)는 바이너리/네트워크 실패 시exit 0으로 종료됩니다. 인프라 누락으로 인해 정당한 PR이 차단되는 일이 없습니다. 성숙한 엔지니어링입니다. - 문화의 코드화. 강제 규칙 +
trust-but-verify+ 만료된 allowlist + 증거 게이트를 통해 규율을 자동 검증 체계로 전환합니다.
솔직한 약점 (실질적인 공백)
섹션 제목: “솔직한 약점 (실질적인 공백)”- 🔴 fast-gates 분리에는 여전히 구조적 허점이 있습니다.
quality.yml(PR→release/**)은 이제 코드 PR에 대해 typecheck, 빠르고 결정론적인 테스트, 권고 수준의 프로덕션 빌드를 실행하지만, 여전히ci.yml의 전체 release-PR 범위(커버리지 래칫, 패키지 아티팩트, 통합 테스트, E2E, SonarQube)를 실행하지 않습니다. 동기(속도)는 타당하지만, 게이트는 병합이 일어나는 지점에 있어야 합니다(shift-left). 가장 큰 미해결 구조적 수정 사항입니다. - 🟠 게이트 난립/피로 위험. 약 46개의 게이트 + 25개의 job은 매우 많습니다. Sonar도 조건이 너무 많으면 “게이트 피로”와 우선순위 논쟁이 발생하고, 결국 게이트가 무시될 위험이 있다고 경고합니다. DORA는 과도한 게이트가 lead-time을 늘린다고 경고합니다. 권고 계층과 비절대적 래칫으로 이를 완화하고 있지만, 게이트별 정기적인 ROI 검토가 누락되어 있습니다(문서 동기화를 위한 일부 마이크로 게이트는 통합할 수 있습니다).
- 🟠 변이 점수는 아직 래칫이 아닙니다. 커버리지 조작에 대한 가장 강력한 해독제가 권고 수준에 머물러 있습니다. 이는 가장 가치가 높은 미해결 항목입니다(이미 90% 구현됨).
- 🟡 적절한 범위에서는 차단해야 하는 권고 항목.
osv(vulnCount)와oasdiff는 동결된 기준선이 있음에도 권고 수준입니다. osv를 권고 수준으로 두는 것은 타당합니다(기존 의존성에서 새로운 CVE가 발견되면 관련 없는 PR까지 차단될 수 있음). 하지만 중간 지점이 있습니다(Trivy에서 했던 것처럼 CRITICAL 이상이면서 수정 가능한 취약점만 차단). oasdiff가 권고 수준이라는 것은 계약을 깨뜨리는 변경도 통과할 수 있다는 의미입니다. - 🟡 런타임 보안은 nightly에서만 실행됩니다. schemathesis/garak/promptfoo/chaos/k6는 야간에 실행됩니다. 올바른 결정이지만(느리고 라이브 서버가 필요함), PR이 삽입 방어 회귀를 유발하더라도 다음 날 밤이 되어서야 탐지될 수 있습니다.
- 🟡
main의 브랜치 보호가 꺼져 있습니다.BRANCH_LOCK_TOKEN은 release 브랜치를 잠그지만main자체는 보호되지 않습니다. Scorecard/DSOMM에서 감점 요인입니다. 소유자의 조치가 필요합니다. - 🟡 CodeQL은 default-setup이며 semgrep은 코드화되지 않았습니다. default-setup은
정상적으로 작동하지만(알림 0건), 커밋된
codeql.yml을 사용하면 더 세밀하게 제어할 수 있습니다. semgrep은 저장소에서 버전 관리되지 않고 외부 클라우드 플랫폼을 통해 실행됩니다.
파트 3 — 품질 체크포인트 전체 카탈로그(이식 가능)
섹션 제목: “파트 3 — 품질 체크포인트 전체 카탈로그(이식 가능)”아래 12개 범주는 재사용 가능한 형태의 “품질 시스템”입니다. 각 범주에는 목표(보호할 대상), 사용 도구, 그리고 모든 스택에서 재현할 수 있는 도구 독립적 대안이 나열되어 있습니다.
1. 스타일 및 서식(결정적, 신속)
섹션 제목: “1. 스타일 및 서식(결정적, 신속)”- OmniRoute: lint-staged를 통한 Prettier + ESLint(pre-commit), 2칸 들여쓰기/큰따옴표/100열.
- 일반: 자동 수정 가능한 포매터 하나 + 린터 하나를 staged 파일에 대해 pre-commit에서 실행.
2. 타입
섹션 제목: “2. 타입”- OmniRoute:
typecheck:core(차단) +typecheck:noimplicit:core(권고) +type-coverage래칫 92.17% + 파일별 any 예산. - 일반: CI에서 엄격한 타입 검사 + 래칫 방식의 타입 커버리지 지표 + 파일별
any/탈출구 예산.
3. 테스트(강도)
섹션 제목: “3. 테스트(강도)”- OmniRoute: 서로 겹치지 않는 러너 2개(Node 네이티브 + vitest), 샤드 8개, 전역 커버리지 60/60/60/60 + 래칫 약 76% + 핵심 모듈을 위한 모듈별 하한 8개 + 야간 속성 기반 테스트 + 야간 변이 테스트.
- 일반: 테스트 러너 + 절대적 커버리지 하한(제로 방지) + 커버리지 래칫(회귀 방지) + 고위험 코드의 모듈별 하한(굿하트의 법칙 방지) + 순수 로직에 대한 속성 기반 테스트 + 테스트 품질의 실질적인 척도로서 야간 변이 테스트.
4. 테스트 정책(편법 방지)
섹션 제목: “4. 테스트 정책(편법 방지)”- OmniRoute:
pr-test-policy(프로덕션 코드에 테스트 요구),check-test-masking(약화된 어서션 차단),pr-evidence(성공 주장에 증거 블록 요구),test-discovery(모든 테스트가 러너에 의해 수집되도록 보장). - 일반: “새 코드 ⇒ 새 테스트” 게이트 + 제거된 어서션/항진식 탐지기 + 증거 요구사항(TDD 또는 현행 테스트) + glob 외부에 고립된 테스트가 없도록 보장.
5. 복잡도 및 코드 건전성(래칫)
섹션 제목: “5. 복잡도 및 코드 건전성(래칫)”- OmniRoute: ESLint 경고(3769↓), jscpd 중복(5.72%↓), 순환 복잡도+최대 줄 수 복잡도(1800↓), sonarjs 인지 복잡도(753↓), knip 미사용 코드/미사용 export(339↓), 파일별 파일 크기(동결, 축소만 허용), 순환 의존성(커스텀 Tarjan, 차단).
- 일반: 모든 건전성 지표(경고, 중복, 순환 및 인지 복잡도, 미사용 코드, 파일 크기, import 순환)에 래칫 적용. 방향은 항상 “악화 금지”.
6. 정적 보안(SAST + 비밀 정보)
섹션 제목: “6. 정적 보안(SAST + 비밀 정보)”- OmniRoute: CodeQL(알림 래칫 = 0), gitleaks(
[extend] useDefault=true— 매우 중요!), SonarQube, 커스텀 보안 규칙(public-creds, error-helper, route-guard-membership, route-validation). - 일반: 알림 래칫을 적용한 SAST(CodeQL/Sonar/semgrep) + 상속된 기본 규칙 세트를 사용하는 비밀 정보 스캐너(기본값을 재정의하는 커스텀 설정은 사각지대를 만듦) + 프로젝트별 Hard Rule 보안 게이트.
7. 공급망(의존성)
섹션 제목: “7. 공급망(의존성)”- OmniRoute: osv-scanner + npm-audit + Trivy + Dependabot(SCA), license-checker(SPDX 허용 목록), lockfile-lint(HTTPS+sha512+registry),
check-deps안티 슬롭스쿼팅(허용 목록 + 사용 기간 ≥72시간). - 일반: 다중 소스 SCA + 라이선스 허용 목록 + lockfile 무결성 검사 + 사용 기간/타이포스쿼팅 검사를 포함한 의존성 허용 목록 + 그룹화된 업데이트 봇.
8. 공급망(빌드 및 릴리스)
섹션 제목: “8. 공급망(빌드 및 릴리스)”- OmniRoute: SBOM(CycloneDX + syft), SLSA 출처 증명(
--provenance), OpenSSF Scorecard(매주), 워크플로 강화(zizmor: artipacked→persist-credentials:false, 캐시 포이즈닝, 토큰 권한). - 일반: 게시 시 SBOM 생성 + 서명된 출처 증명(SLSA L2+) + 예약 실행되는 Scorecard + 모든 워크플로 강화(최소 권한 토큰, 푸시하지 않는 checkout에서는 자격 증명 유지 금지, action을 SHA로 고정).
9. 계약 및 API
섹션 제목: “9. 계약 및 API”- OmniRoute: oasdiff(파괴적 OpenAPI 변경), schemathesis(야간 계약 퍼징), openapi-coverage(문서화된 route 비율, 래칫 38.3%), openapi-security-tiers(spec 대 route-guard).
- 일반: 파괴적 변경 계약 diff(oasdiff/buf) + spec에 대한 속성 기반 퍼징(schemathesis) + 래칫 방식의 문서화 커버리지 + spec↔코드 일관성.
10. 문서 및 i18n(부패 방지)
섹션 제목: “10. 문서 및 i18n(부패 방지)”- OmniRoute: docs-sync(미러링된 버전), docs-counts-sync(문서와 코드 간 수치), env-doc-sync, doc-links, fabricated-docs, cli-i18n, i18n-ui-coverage(
--threshold=65+ 래칫 80.1%). - 일반: 문서와 코드 간 버전/개수/환경 변수 동기화(신뢰가 아닌 게이트) + 내부 링크 검증 + 래칫 방식의 i18n 커버리지.
11. 환각 방지/일관성(드문 범주)
섹션 제목: “11. 환각 방지/일관성(드문 범주)”- OmniRoute: known-symbols(문자열 디스패치 ⇒ 현행 심볼), provider-consistency, fetch-targets(클라이언트 fetch ⇒ 실제 route), docs-symbols, db-rules(Hard Rules #2/#5), migration-numbering.
- 일반: 모든 “중복된 진실의 원천”(레지스트리, 문자열 디스패치, 계층 간 참조)에 대해 양쪽이 일치함을 입증하는 게이트 적용. 타입 검사/테스트가 잡지 못하는 부패를 탐지.
12. 복원력 및 도메인(제품별)
섹션 제목: “12. 복원력 및 도메인(제품별)”- OmniRoute: chaos(장애 주입), heap-growth(누수), k6(소크), promptfoo+garak(LLM 레드팀 OWASP LLM Top 10), 복원력 법칙 3가지(circuit-breaker/cooldown/lockout).
- 일반: 여러분의 도메인에 존재하는 장애 모드를 식별하고 각각에 대한 게이트를 마련합니다(야간 실행이어도 무방). AI 앱의 경우: 인젝션 레드팀. 분산 시스템의 경우: chaos + 누수 + 소크.
파트 4 — 모든 프로젝트에 적용 가능한 재현 계획
섹션 제목: “파트 4 — 모든 프로젝트에 적용 가능한 재현 계획”각각 독립적으로 가치를 제공하도록 단계별로 구축하세요. 12개 범주를 한꺼번에 모두 시도하지 마세요 — 그러면 파트 2에서 경고한 게이트 피로가 그대로 발생합니다. 모든 신규 게이트는 권고 상태로 도입하고, 안정화되면 차단 상태로 전환합니다.
재사용 가능한 핵심 요소: “래칫 게이트의 구조”
섹션 제목: “재사용 가능한 핵심 요소: “래칫 게이트의 구조””전체 시스템은 다음과 같은 3개 파일 패턴을 중심으로 작동합니다. 먼저 이 패턴을 복사하세요.
baseline.json— 고정된 메트릭 값 +direction(up/down) +eps(플레이크 방지) +tightenSlack+dedicatedGate.collect-metrics.<ext>— 도구를 실행하고 수치를 추출하여metrics.json에 기록합니다.check-ratchet.<ext>—metrics.json과baseline.json을 비교합니다.eps를 초과하여 회귀한 경우에만exit 1을 반환하고, 도구/인프라가 없으면exit 0을 반환하여 정상적으로 건너뜁니다.--require-tighten을 사용하면 기준선을 업데이트하지 않은 채 개선된 경우exit 1을 반환하여 개선 성과를 고정합니다.
이 패턴을 갖추면 모든 신규 메트릭(커버리지, 복잡도, 경고, SAST 경고, 번들 크기, 변이 점수 등)은 기준선에 한 줄만 추가하면 됩니다.
단계 0 — 기반 구축(1주 차)
섹션 제목: “단계 0 — 기반 구축(1주 차)”CI를 갖추고, 포매터 + 린터 + 타입 검사 + 테스트 러너 1개 + 절대적 커버리지 하한선 (예: 60%)을 설정합니다. 프리커밋에서는 빠르고 자동 수정 가능한 검사를 실행합니다. 결과: 어떤 PR도 기본 사항을 훼손하지 않습니다.
단계 1 — 래칫 엔진(2주 차) — 모든 것의 기반
섹션 제목: “단계 1 — 래칫 엔진(2주 차) — 모든 것의 기반”위의 3개 파일을 구현합니다. 경고, 커버리지, 복잡도, 중복, 데드 코드, 파일 크기에 대한 기준선을 고정합니다. 결과: 이제부터 코드베이스는 개선만 가능합니다.
단계 2 — 정적 분석 심화(3주 차)
섹션 제목: “단계 2 — 정적 분석 심화(3주 차)”경고 래칫을 적용한 SAST(CodeQL/Sonar/semgrep), 비밀 정보 스캐너(기본 규칙 세트를 상속), SCA(osv/Dependabot) + 라이선스 허용 목록 + lockfile-lint를 도입합니다. 결과: 알려진 취약점과 유출된 비밀 정보가 검사를 통과하지 못합니다.
단계 3 — 빌드 공급망(4주 차)
섹션 제목: “단계 3 — 빌드 공급망(4주 차)”게시 시 SBOM 생성 + 서명된 출처 증명(SLSA L2) + 예약 실행되는 Scorecard + 워크플로 강화 (zizmor: 최소 토큰, 자격 증명 미보존, 액션 버전 고정)를 적용합니다. 결과: 추적 가능하고 변조 방지된 릴리스가 만들어집니다.
단계 4 — 테스트 강도 강화(5~6주 차)
섹션 제목: “단계 4 — 테스트 강도 강화(5~6주 차)”유용하다면 두 번째 러너를 도입합니다. 핵심 모듈별 커버리지 하한선(굿하트의 법칙 방지),
순수 로직에 대한 속성 기반 테스트, 매일 밤 변이 테스트를 적용하고 → 첫 번째 점수가 나오면
mutationScore를 래칫으로 설정합니다. 결과: 커버리지가 허영 메트릭에 그치지 않으며, 테스트가 실제로 버그를 잡아낸다는 사실을 입증할 수 있습니다.
단계 5 — 계약 및 동적 분석(7주 차)
섹션 제목: “단계 5 — 계약 및 동적 분석(7주 차)”공개 API가 있다면 oasdiff(호환성을 깨는 변경, 차단) + schemathesis(매일 밤 퍼징)를 적용합니다. 도메인에 적합한 경우 DAST/레드팀 테스트를 매일 밤 실행합니다. 결과: 계약이 조용히 깨지지 않습니다.
단계 6 — 환각 방지 및 도메인 검사(8주 차)
섹션 제목: “단계 6 — 환각 방지 및 도메인 검사(8주 차)”프로젝트의 각 “중복된 진실”마다 일관성 게이트를 하나씩 둡니다. 도메인별 실패 모드 게이트도 적용합니다(AI의 경우 인젝션 레드팀 테스트). 결과: 구조적 부패와 도메인 실패에 대한 안전망이 생깁니다.
단계 7 — 거버넌스(지속적)
섹션 제목: “단계 7 — 거버넌스(지속적)”- 모든 신규 게이트에 권고→차단 주기를 적용합니다.
stale-allowlist: 모든 억제 항목에는 근거 + 이슈가 있어야 하며, 더 이상 필요하지 않은 억제 항목을 감지합니다.evidence-gate: PR의 성공 주장에는 증거(테스트 또는 지속적으로 유지되는 테스트)가 필요합니다.- 게이트별 분기별 ROI 검토를 수행합니다(효과가 없는 게이트는 폐지하거나 투자를 중단하여 피로를 방지합니다).
- 프로젝트의 강제 규칙을 실행 가능한 게이트로 승격합니다.
범분야 원칙(타협 불가)
섹션 제목: “범분야 원칙(타협 불가)”- 절대 기준이 아닌 래칫. 고정된 수치가 아니라 _비회귀_를 게이트 조건으로 삼습니다(0 방지 하한선 제외).
- 절대 하한선과 래칫을 함께 적용. 하한선은 붕괴를 방지하고, 래칫은 점진적인 저하를 방지합니다.
- 설계 단계부터 굿하트의 법칙 방지. 모든 목표 메트릭에는 균형추가 필요합니다(커버리지 ⇒ 변이 테스트 + 마스킹 방지, 테스트하기 어려운 코드도 반드시 테스트하도록 모듈별 하한선 적용).
- 정상적인 건너뛰기. 인프라가 없다는 이유로 차단하지 않으며, 실제 회귀만 차단합니다.
- 비용이 큰 메트릭에는
dedicatedGate사용. 외부 바이너리가 필요한 메트릭은 동기식 중앙 래칫 외부의 전용 스크립트로 처리하며, 건너뛰기 동작을 포함합니다. - 병합이 일어나는 지점에 게이트 배치. 빠른 게이트와 실제 병합 사이에 공백을 두지 마세요(빠른 게이트 분리에서 얻은 교훈).
- 차단 게이트는 적게, 신중하게 선택. Sonar/DORA가 보여주듯 조건이 너무 많으면 피로가 발생합니다. 차단 게이트의 장벽보다는 권고 + 래칫을 선호하세요.
파트 5 — 권장 개선 사항(우선순위별, 호환 가능)
섹션 제목: “파트 5 — 권장 개선 사항(우선순위별, 호환 가능)”P0 — 가장 높은 ROI, 거의 준비 완료
- 변이 점수 래칫(첫 번째 야간 Stryker 실행에서 값이 산출된 이후). 커버리지-굿하트 현상에 대응하는 핵심 수단이며, 약 90% 완료됨.
- 남아 있는 빠른 게이트의 허점 해소 — 권고 기간 1주가 지난 후
quality.yml의 프로덕션 빌드를 필수로 승격하고, 결정론적이며 릴리스 PR에만 적용되는 검사들을 계속 PR→release 경로로 이동. main브랜치 보호(소유자 설정) — Scorecard를 높이고 DSOMM 격차를 해소.
P1 — 가치 높음 4. osv/oasdiff → 적절한 범위에서 차단형으로 전환 — osv는 CRITICAL이면서 수정 가능한 항목만 차단(Trivy와 같은 2단계 방식); oasdiff는 호환성을 깨는 변경을 차단. 5. require-tighten → 차단형으로 전환(사이클 종료 시점) — 지표 개선을 고정. 6. ci-summary에서 게이트별 ROI/소요 시간 검토 — 느리거나 가치가 낮은 게이트를 찾아 제거.
P2 — 한계효용 감소 7. SLSA L3 — L2에서 상향하려는 경우 밀폐형/재현 가능한 빌더(GitHub SLSA generator) 도입. 8. 커밋된 CodeQL 설정 + 버전이 고정된 semgrep — 제어력/재현성 향상. 9. PR별 DAST 스모크 테스트 — 위험도가 가장 높은 엔드포인트에서 schemathesis/promptfoo의 빠른 하위 집합 실행(야간 실행에만 국한하지 않음). 10. 불안정성 대시보드 + DORA 지표 — 게이트가 속도를 저해하지 않는지 확인.
파트 6 — 구체적인 릴리스 교훈(Phase 9에 추가할 게이트)
섹션 제목: “파트 6 — 구체적인 릴리스 교훈(Phase 9에 추가할 게이트)”이 섹션은 릴리스 마감 과정에서 게이트가 누락되었던 실제 사고를 구체적인 증거 및 제안된 게이트와 함께 기록합니다. 각 항목은 파트 5의 후보입니다.
교훈 v3.8.27 (2026-06-17) — “빠른 게이트의 허점”으로 인해 결정론적 회귀가 릴리스 당일까지 유입됨
섹션 제목: “교훈 v3.8.27 (2026-06-17) — “빠른 게이트의 허점”으로 인해 결정론적 회귀가 릴리스 당일까지 유입됨”발생한 일. v3.8.27 /generate-release 과정에서 릴리스 PR(release/v3.8.27 → main)은
통합 사이클에서 전체 ci.yml 매트릭스가 실행된 첫 번째 시점이었습니다. 그 결과 한꺼번에 12건이 실패했습니다.
즉, 결정론적 테스트 3건 + 불안정성/환경 문제 약 9건이었습니다. 실제 제품 회귀는 없었지만,
사이클 PR은 전체 단위 테스트 스위트, pr-test-policy(테스트 마스킹), 전체 통합 테스트 스위트,
스키마 동등성 검사를 실행하지 않는 **Fast QG
(quality.yml)**를 통해 release/**에 들어가기 때문에 모두 감지되지 않았습니다. 결정론적 실패 3건은 다음과 같습니다.
- UI 변경으로 인해 오래된 테스트 —
permissions modal switch buttons declare button type: #4034에서 네 번째 스위치가 추가되었고(a11ytype="button"은 유지됨), 테스트의=== 3개수 조건이 오래된 상태가 되었습니다. 정적 분석이 #4034 PR에서 이를 감지했어야 합니다. - 패키징 변경으로 인해 오래된 테스트 —
findMissingArtifactPaths ... root runtime files:dist/http-method-guard.cjs가 적법한 필수 경로가 되면서 테스트의 예상 목록이 오래된 상태가 되었습니다. - 손실을 유발한 모듈화 불일치(가장 심각) —
settings schemas accept ... unprefixed toggle: 모듈화된updateSettingsSchema(#3988에서 생성된schemas/settings.ts)가 정본(settingsSchemas.ts)과 달라졌습니다. **45개 필드 대 85개 필드 — 40개 누락 + 6개 불일치(qdrant*)**였습니다. 이는 데드 코드였으므로(런타임은 정본을 사용함) 실제 영향은 없었지만, 직접 작성한 동등성 테스트만이 이를 감지했습니다. #4030은 #3988/#3993에서 발생한 유사한 누락 16건을 복원했지만, 이 사례는 빠져나갔습니다.
제안된 게이트(Phase 9):
- G1 — 빠른 게이트의 허점을 실제로 해소(P0 #2 확장).
quality.yml(PR→release/**)에서 typecheck + 영향받는 테스트뿐 아니라pr-test-policy(테스트 마스킹) + 전체 결정론적 단위 테스트 스위트도 실행합니다(또는 최소한 빠르고 불안정하지 않은 정적/동등성 파일 실행). 이렇게 하면 오래된 테스트와 assert 제거를 릴리스 당일이 아니라 해당 변경을 도입한 PR에서 감지할 수 있습니다. integration/e2e는 느리고 불안정하므로 제외하되, 결정론적 계층을 PR→main에만 두어서는 안 됩니다. - G2 — 모듈화 동등성 게이트(신규, 현재는 미지원). 모듈화된 배럴(
src/shared/validation/schemas/*,providerRegistry모듈 등)이 다시 내보내는 각 심볼에 대해 형태(z.object키, 레지스트리 항목)를 정본 소스와 비교하고 불일치(누락/추가 필드)가 있으면 실패시키는 검사입니다. #3988에서 발생한 40개 필드 누락을 바로 그 PR에서 감지할 수 있었을 것입니다. 이는 직접 작성한 동등성 테스트(누군가 작성해야 한다고 기억한 곳에만 존재함)를 일반화합니다. 비용도 낮습니다. 양쪽을 import한 뒤Object.keys(shape)를 비교하면 됩니다. - G3 — 결정론적 불안정성 분류(지원). LiveWS-startup 및 integration-combo/breaker
테스트는 로직이 아니라 CI의 서버 타임아웃/연쇄 실패(환경)로 인해 실패합니다. 이를
known-flaky(이슈와 함께 격리됨)로 표시하여 릴리스 PR의 실패 상태가 결정론적 회귀를 중간에서 가리는 노이즈가 아니라 실제 신호만 나타내도록 합니다.
원칙: 게이트는 병합이 일어나는 위치에서 실행되어야 합니다(Cross-cutting principles에 이미 명시됨).
v3.8.27 사고는 이 원칙이 lint/typecheck뿐 아니라 결정론적 테스트 계층에도 적용됨을 보여줍니다.
그렇지 않으면 오래된 테스트 + 손실을 유발한 모듈화로 인한 부채가 최악의 순간인 PR→main에서
한꺼번에 드러납니다.
출처(업계 모범 사례)
섹션 제목: “출처(업계 모범 사례)”- OWASP DevSecOps 성숙도 모델(DSOMM) — https://dsomm.owasp.org/about
- OpenSSF Scorecard / SLSA — https://openssf.org · https://slsa.dev
- SonarQube “코딩하면서 클린하게(Clean as You Code)” — https://docs.sonarsource.com/sonarqube-server/latest/user-guide/clean-as-you-code
- 품질 래칫(Quality Ratchets, LeadDev) — https://leaddev.com/software-quality/introducing-quality-ratchets-tool-managing-complex-systems
- 래칫을 활용한 지속적인 코드 개선(Continuous Code Improvement Using Ratcheting, Greiner) — https://robertgreiner.com/continuous-code-improvement-using-ratcheting/
- DORA 2024 DevOps 현황 — https://cloud.google.com/blog/products/devops-sre/announcing-the-2024-dora-report
- 변이 테스트 모범 사례(Stryker) — https://stryker-mutator.io
- 안티패턴으로서의 커버리지(Goodhart) — https://www.industriallogic.com/blog/code-coverage-complications/
- LLM 애플리케이션을 위한 OWASP Top 10(2025) — https://owasp.org/www-project-top-10-for-large-language-model-applications/
- 계약 테스트(oasdiff/schemathesis) — https://www.oasdiff.com · https://schemathesis.readthedocs.io
HagiCode
HagiCode는 구조화된 워크플로, 다중 에이전트 실행, Hero Dungeon 뷰를 갖춘 에이전트 코딩 작업 공간입니다.
더 스마트하고 빠르며 즐거운 에이전트 워크플로로 유용한 소프트웨어를 만드세요.

- Smart구조화된 워크플로는 의도를 아이디어부터 배포까지 실행 가능한 경로로 바꿉니다.
- Efficient다중 에이전트 워크플로로 조사, 구현, 검토를 병렬로 진행합니다.
- FunHero Dungeon은 긴 코딩 세션을 시각적이고 협업적인 경험으로 만듭니다.