← RLSC — 컴포넌트의 일생

공급 라인 · 오프라인

Garbage Collector

풀에 남을 자격을 심사하는 기계 게이트. 린터·유효성이 «규격에 맞는가»를 물었다면 GC 는 «쓸 만한가»를 묻는다. 판정은 컴포넌트마다 키 하나로 남고, 하류가 유효로 보는 것은 두 키의 교집합이다.

현행 키 GCv1.0 세트 4.0.0 판정 전략 v1 갱신 2026-09-13
§0

왜 컬럼을 갈랐나

이 절이 답하는 질문 — 이미 유효성 키가 있는데 왜 판정을 하나 더 두나?
유효성 (린터)

규격에 맞는가 — 자격

기록: design_objects.valid_component_meta
허용목록: VALID_COMPONENT_VERSIONS

GC 게이트

쓸 만한가 — 품질

기록: design_objects.garbage_collector_meta
등록부: garbage_collector_gate_config

둘을 한 컬럼에 병기하지 않은 이유가 셋이다. 판 갈이 주기와 소유가 다르고, GC 키가 유효성 허용목록 검증에 섞여 들어가는 사고를 컬럼 경계가 차단하며, 죽은 키 청소가 컬럼 단위로 단순해진다.

허용목록에 넣었으면 기본 판정 버전이 바뀌었다

VALID_COMPONENT_VERSIONS 는 화면과 서버 zod 스키마가 배열 마지막 원소를 기본값으로 쓴다. GC 키를 거기에 넣었으면 기본 판정 버전이 GC 키로 바뀌었을 것이다. 이 주의가 src/common/gc-gate-key.ts 주석에 그대로 박혀 있다.

§1

키 세 상태와 교집합

이 절이 답하는 질문 — 어떤 행이 서빙 대상인가? 키가 비어 있는 행은 어떻게 읽나?
유효성 키 true GC 키 true 서빙 대상 두 키가 모두 true 인 행만 키 값의 의미 — 세 상태 true 통과 풀에 잔류한다 false 걸림 풀 소속이 해제된다 키 없음 미판정 통과가 아니다 기록은 jsonb 병합이다 — 기존 키를 지우지 않고 더한다 {"GCv0.1": true, "GCv1.0": false}
현행 GC 키가 무엇인지는 garbage_collector_gate_config 의 active 행이 가리킨다.

키가 없는 행은 두 부류다

판정 원장과 조인하면 갈린다. 심리했지만 결론을 못 낸 것과 애초에 심리 대상이 아니었던 것은 다르다.

원장에 completed run 행이뜻다음에 할 일
있다심리 후 보류 — 판정 불가상류 재료(페이지 RLSC 등)가 채워지면 재판정 대상
없다애초에 그 키의 판정 대상이 아니었다풀 밖이거나 옛 시드다 — 할 일 없음

판정 불가는 무죄 추정하지 않는다

제거 기준에 속한 규칙이 페이지 문맥 부재 등으로 돌아가지 못하면 키를 적지 않고 미판정으로 남긴다. «조용히 통과»를 만들지 않기 위해서다. 단, 못 돌린 규칙이 제거 기준 밖이면 판정은 그대로 성립한다.

§2

소유는 세 곳으로 갈린다

이 절이 답하는 질문 — «이 값을 바꾸려면 어디를 고쳐야 하나»의 답이 왜 셋인가?
① 버전 정의 — 코드

GATE_VERSIONS

GcGate/gateVersions.ts

키 → 세트 버전 + 전략 버전. 그리고 현행 포인터 CURRENT_GATE_KEY. 채택·롤백은 이 파일의 PR 이고, 리뷰와 git 이력이 곧 변경 기록이다.

② 등록부 — DB

gate_config

garbage_collector_gate_config

버전마다 행이 쌓이고(삭제 없음) active 한 행이 현행이다. 사람이 직접 쓰지 않는다 — syncGateConfig 가 코드 정의를 멱등 동기화한다.

③ 파라미터 — 호출

runGcGate(options)

GcGate/runGcGate.ts

gateKey(필수) · validComponentVersion · target · writeGcMeta · 실험용 오버라이드(setVersion·removalLabels).

판단 세트의 실체는 packages/component-gc 의 버전별 파일이다. 새 세트 파일이 저장소에 들어와도 GATE_VERSIONS 에 그 세트를 쓰는 키가 등록되기 전까지 아무 일도 일어나지 않는다.

현재 등록된 키

GcGate/gateVersions.ts
export const GATE_VERSIONS: Record<string, { setVersion: string; strategyVersion: string }> = { // 세트 3.4 시대의 키. 4.0 개정에서 3.4 술어가 교체되어 이 키로는 더 이상 판정을 돌릴 수 없다 'GCv0.1': { setVersion: '3.4.0', strategyVersion: 'v0' }, // 세트 4.0 + 판정 전략 v1 (2층: always / conditional + scoreCut 60) 'GCv1.0': { setVersion: '4.0.0', strategyVersion: 'v1' }, }; export const CURRENT_GATE_KEY = 'GCv1.0';
키세트전략지금
GCv0.13.4.0v0 (한글 라벨 1층)은퇴 — 3.4 술어가 교체되어 재실행 불가. meta 에 적힌 값의 의미만 남는다
GCv1.04.0.0v1 (코드 2층 + 컷 60)현행 — 기록이 허용되는 유일한 키

키가 뜻하는 것 — 불변식 하나

같은 키로 적힌 값들은 전부 같은 판정 체계로 매겨졌다. 이것이 깨지는 변경이면 새 키를 등록한다 — 세트 채택, 전략 개정, 결과를 유의미하게 바꾸는 문턱 변경이 그렇다. 버그 수정이나 근거 문장 개선처럼 결과가 사실상 안 변하는 것만 키를 유지한다.

새 키로 올릴 때 기존 키의 값은 보존한 채 병기한다. 그래서 롤백이 CURRENT_GATE_KEY 를 되돌리는 한 줄 PR 로 끝나고 재판정이 필요 없다. 한 번 배포된 이름은 meta 에 남아 있으므로 개명·삭제하지 않는다.

§3

판정 두 층 — 전략 v1

이 절이 답하는 질문 — 어떤 결함이 걸리면 버리나? 왜 어떤 것은 혼자서, 어떤 것은 점수와 함께 보나?

판정기(packages/component-gc)가 내는 것은 «무엇이 걸렸나»까지다. 그것을 어떤 기준으로 자를지가 게이트의 판정 전략이고, 기준이 바뀌면 그 파일만 바뀐다. 세트 개정과 전략 개정을 따로 굴리려고 나눠 둔 자리다.

판정 결과 findings · score 가드 기준이 비었나? 기준 규칙을 못 돌렸나? 예 null · 미판정 아니오 1층 · always 코드만 걸려도 탈락 단독 정밀도 ≥ 90% 걸림 2층 · conditional 코드 그리고 점수 ≥ 60 단독 정밀도 70~90% 걸림 false · 걸림 그 밖 true · 통과
computeVerdict() 의 실제 분기 순서다. 위에서 걸리면 아래는 보지 않는다.
1층 — always

걸리면 바로 탈락

NO_EFFECTIVE_VISUAL ORPHAN_ORDINAL PAGE_BACKPLATE.COVER PAGE_BACKPLATE.BOTH SPARSE_BOX.CERTAIN DETACHED_FRAGMENT.STRONG
2층 — conditional · scoreCut 60

걸리고 동시에 점수가 컷 이상일 때만

MISSING_NODE.BOTH MISSING_NODE.TWIN MISSING_NODE.SLOT PAGE_BACKPLATE.EDGE SPARSE_BOX.STRONG

2층을 둔 이유는 단독으로는 못 믿을 정밀도라도 «다른 결함이 겹쳤다»는 신호가 붙으면 지울 만하다는 것이다. 점수 컷은 그 «겹쳤다»를 재는 대리 지표다.

컷 60 은 «둘이 겹치면 넘고 하나만 걸리면 못 넘는다»는 자리다

MISSING_NODE.BOTH(감점 .440) 단독은 44.0 이고, 거기에 PAGE_BACKPLATE.EDGE(.400)가 겹치면 66.4 다.

셋을 요구하는 컷 70 으로 두면 2층이 사실상 발화하지 않는다. 반대로 컷 50 으로 내리면 기준에서 기각한 코드들이 점수를 밀어 올려 2층을 통과시키는 사고가 시작된다 — 지금 컷에서는 MISSING_NODE.BOTH + SPARSE_BOX.MODERATE + DETACHED_FRAGMENT.MODERATE 를 다 겹쳐도 59.4 라 못 넘는다.

기준은 규칙이 아니라 코드 단위로 준다

SPARSE_BOX 를 넣으면 전 구간을 덮고 SPARSE_BOX.CERTAIN 은 그 구간만 덮는다. 같은 규칙인데 구간별 정밀도가 96.6% 와 12.5% 로 갈리기 때문에, 이 구분이 없으면 기준을 세울 수가 없다.

대조는 접두사 매칭이다

PAGE_BACKPLATE 는 bare 코드로 발화하는 일이 없고 .COVER·.EDGE·.BOTH 로만 나온다. 정확 일치로 두면 그 규칙이 통째로 무력해진다 — 배치 전량 실측에서 제거 대상 1,765 건 중 395 건(22.4%)을 놓쳤다.

예외 하나 — DETACHED_FRAGMENT.STRONG

정밀도 91.8% 로 판정선을 넘는데 라벨 n 이 49 라 한 장이 모자란다. 한 장이면 판정이 서는 자리라 1층에 넣고 라벨 채우기를 후속으로 뒀다. n 조건만 면제한 것이고 정밀도 조건은 면제하지 않았다.

1층·2층 코드 목록과 컷 근거는 GcGate/verdictStrategy.ts 의 VERDICT_STRATEGY_V1(2026-09-10) 과 그 주석에서 그대로 옮겼다. 판정선과 코드별 정밀도 실측의 정본은 mordor-wiki rat-pod/garbage-collector/removal-criteria.md 다 — 이 페이지에서는 확인하지 않았다.

세트 4.0 이 재는 규칙

세트 4.0 부터 밖으로 나가는 것은 한글 라벨이 아니라 규칙 id 와 finding 코드다. 한글 라벨은 «표시 이름»으로 내려갔고, 강도 구간이 코드 접미사에 실린다.

규칙 id표시 라벨
NO_EFFECTIVE_VISUAL단독 완결성_유효시각요소없음
BG_DEPENDENT배경 의존도
MISSING_NODE단독 완결성_노드누락
ORPHAN_ORDINAL맥락 의존도_순번조각
PAGE_BACKPLATE시각적 균형
DETACHED_FRAGMENT단독 완결성_조각분리
SPARSE_BOX크기 적정성
DECOR_SCATTERED스타일 교체 용이성_장식산발

구간 접미사는 형상(BOTH·TWIN·SLOT·BULK·COVER·EDGE)과 강도(CERTAIN·STRONG·MODERATE·WEAK) 두 축이 같은 자리에 들어온다. 강도 구간 이름은 등급(GcGrade)과 글자가 같지만 같은 값이 아니다 — 코드 주석의 경고다. EMPTY_SLOT 은 id 만 예약돼 있고 위 목록에 없다. 출처는 packages/component-gc/src/types.ts · src/rulesets/gc-4-0.ts.

§4

대상 세 모드, 실험과 운행

이 절이 답하는 질문 — 무엇을 모수로 잡고 도나? 실수로 서빙을 흔들지 않으려면?

모수는 전부 DB 에 있는 사실로 정의된다. 사람 기억이나 별도 목록에 의존하는 모드는 없다.

기본

unjudged

기준 유효성 키는 통과했으나 해당 GC 키가 아직 없는 것. 정규 운행과 첫 가동의 모드다. 이미 판정된 것은 키가 있어 자연히 빠진다.

gateKey 지정

judged

그 키가 적혀 있는 것 전부. 재판정의 모드다. 모수가 «직전 판정 집합»으로 고정되므로, 그 사이 풀이 변해도 같은 무리를 다시 본다.

runId 지정

run

그 run 이 판정했던 참가 명단 그대로. 세트 비교 실험 전용이라 차이가 순수하게 세트·기준 탓이 된다. meta 기록과 조합하면 거부된다.

왜 run 모드는 기록을 막나

옛 명단에는 그 사이 풀에서 이미 빠진 DO 가 섞일 수 있다. 그것을 지금 풀의 판정으로 적으면 원장과 meta 가 어긋난다. 그래서 코드가 시작 전에 세운다.

실험과 운행을 가르는 것은 writeGcMeta 하나다

writeGcMeta하는 일서빙 영향
생략 (기본 · 드라이런) 판정하고 원장에만 쌓는다. design_objects 는 건드리지 않는다. 등록된 아무 키나 허용한다 무풍 — 규칙이 결정론적이라 재실행이 안전하고, 원장이 덮어쓰지 않는 구조라 재실행이 무해하다
true run 이 completed 로 확정된 뒤에만 meta 기록 배치가 돈다. active 키만 허용 — 아니면 400 서빙 풀이 바뀐다. 도중에 죽은 run 은 키를 하나도 안 남기고, 재실행은 이어달리기가 아니라 처음부터다

gateKey 는 필수다

어떤 버전으로 판정·기록할지는 항상 호출이 명시한다. 판정 실행은 active 를 조건으로 삼지 않는다 — 같은 요청은 언제 보내도 같은 버전으로 돈다. 미등록 키는 400 이고, 등록은 GATE_VERSIONS 정의 + 배포로만 이루어진다.

기록을 active 키로 제한하는 이유는, 파이프라인이 옛 버전 설정을 못 고친 채 부르는 사고가 조용히 옛 키로 적히는 대신 에러로 드러나게 하기 위해서다.

실행 전 검증은 생략할 수 없다

제거 기준은 문자열이고 코드의 정본은 세트라, 세트 개정으로 규칙이 개명·은퇴되면 교집합이 항상 비어 전원 통과가 되는데 에러는 나지 않는다. 그 무증상 오작동을 시끄러운 실패로 바꾸려고, assertRemovalCodesInSet 이 기준 코드가 세트의 코드 집합에 들어 있는지 먼저 본다. 3.4 → 4.0 에서 시각요소없음·시각요소1개 가 하나로 통합된 것이 실제 사례다.

버전 채택 절차

채택은 사람의 결정이고, 그 표현은 PR 이다. 이후는 배포와 정규 실행이 잇는다.

  1. 세트 PR — 새 세트 파일 + RULESETS 등록. 필요하면 오버라이드 드라이런으로 후보를 검증하고 걸림 건수 변화를 PR 에 남긴다
  2. 채택 PR — GATE_VERSIONS 에 새 키 정의 추가 + CURRENT_GATE_KEY 전환. 게이트 담당 리뷰 필수
  3. 배포 — 서버 기동 시 syncGateConfig 가 등록부에 새 행을 만들고 active 를 옮긴다. 게이트 호출을 기다리지 않는다
  4. 파이프라인 안내 — 다음 풀부터 새 gateKey 로 호출. 정규 실행이 곧 판정값 생성이라 별도 백필이 없다
  5. 소비처 전환 — 서빙 필터의 현행 키 전환 시점은 새 풀 발행과 묶는다

기준·세트를 바꾼 뒤의 첫 실행은 드라이런으로 제거율과 라벨 분포를 직전 completed run 과 대조한 뒤 기록을 잇는다. 어느 경로든 실험 run 자체는 승격되지 않는다 — 채택 후 정식 run 을 새로 돌리고 그 run 이 정본이 된다.

§5

문서와 코드가 갈리는 자리

이 절이 답하는 질문 — 정책 문서를 읽었는데 코드와 다르다. 어느 쪽이 맞나?

이 구간은 지금도 움직이고 있다. 2026-09-13 에 읽었을 때 코드가 정책 문서보다 며칠 앞서 있었다. 아래 셋은 코드를 적은 것이다.

항목정책 문서코드 (이쪽이 현재다)
현행 키 GCv0.1 = 세트 3.4.0 + 1차 6라벨
gate-key.md · 2026-09-09
CURRENT_GATE_KEY = 'GCv1.0' = 세트 4.0.0 + 전략 v1
gateVersions.ts · 2026-09-10
키 정의의 모양 키 → 세트 버전 + removal_labels
gate-operation.md · 2026-09-10
키 → { setVersion, strategyVersion }. 기준은 verdict_strategy 참조로 나갔다
커밋 ab9183273 · 2026-09-10
기준의 단위 한글 라벨 여섯을 1층으로 매칭 ASCII 코드 단위 2층 + 점수 컷. 한글 라벨은 표시 이름으로 내려갔다

저장소 안에 뒤처진 상수가 하나 남아 있다

src/common/gc-gate-key.ts 의 GC_GATE_KEY = 'GCv0.1' 은 2026-09-07 자로 멈춰 있고, 같은 저장소의 CURRENT_GATE_KEY 는 'GCv1.0' 이다. 어느 쪽을 읽는 코드인지 확인하고 쓸 것. 현행 포인터로서 의미를 갖는 것은 CURRENT_GATE_KEY 쪽이다 — 등록부 active 행을 정하는 것이 그것이기 때문이다.

아직 열려 있는 것

정책 문서 둘 다 상태가 «검토중»이고 확인 대기 중이다(2026-09-09·09-10 기준). GC 판정에 대한 사람 오버라이드 경로는 아직 없다 — 필요해지면 유형 온보딩 오버라이드 계약의 방식(원장 불변, 오버라이드 별도 원장)을 따라 설계한다고 적혀 있다. 소비처 API 들은 버전 파라미터를 유효성 허용목록으로 검증하므로, GC 키를 받으려면 각 API 의 파라미터 계약을 따로 열어야 한다.

§6

코드 지도

하는 일파일
버전 정의 · 현행 포인터src/app/pipeline/common/service/GcGate/gateVersions.ts
판정 전략 실물 (v1)GcGate/verdictStrategy.ts
전략 등록 · DB 동기화 모양GcGate/verdictStrategies.ts
게이트 실행 (대상 선정 · run · 기록)GcGate/runGcGate.ts
등록부 멱등 동기화GcGate/syncGateConfig.ts
판정 키 상수 (뒤처짐 — §5)src/common/gc-gate-key.ts
판단 세트 — 규칙 · 술어 · 임계packages/component-gc/src/
세트 4.0 정의packages/component-gc/src/rulesets/gc-4-0.ts
규칙 id 타입packages/component-gc/src/types.ts

DB

테이블 · 컬럼담는 것
design_objects.garbage_collector_metajsonb 평평한 키 — 판정 요약
garbage_collector_gate_config버전 등록부. active 행이 현행
garbage_collector_judgement_runsrun 원장 — gate_key·상태·run_meta 스냅샷
garbage_collector_judgement_results판정 원장 — DO 별 결과
garbage_collector_verdict_strategy전략 정의 동기화 대상

마이그레이션은 supabase/migrations/ 에 있다 — 20260903094604_create_garbage_collector_judgement_tables.sql, 20260904132527_create_garbage_collector_gate_config.sql, 20260907105215_add_design_objects_garbage_collector_meta.sql. run 마다 gate_key · set_version · 전략 스냅샷이 자동 기록되어 «그때 뭘로 돌았나»는 원장이 증명한다.