설계로그 목록
ADR-0040 공통 설계중

출처에 닿지 못한 검증 — 확인 불가는 실패와 같은 코드로 끝나지 않는다

작성 2026-08-10 · 최종 갱신 2026-08-10

검증 게이트가 인용이 틀렸다고 판정한 것과 출처에 아예 닿지 못한 것을 같은 종료코드(1)로 끝내고 있었다. 두 상태는 다음 행동이 정반대인데(전자는 인용 정정, 후자는 재실행) 게이트는 문구로만 구분하고 코드로는 구분하지 않았다. 이 로그의 다른 게이트 두 개는 이미 그 구분을 갖고 있다. 세 상태(통과·검증 실패·확인 불가)를 종료코드 0·1·75 로 나누고, 출처 도달 여부를 먼저 확인해 닿지 않으면 예산을 태우지 않고 끝내며, 확인 불가도 게시는 막되 데이터 결함으로 표시하지 않는다. 확인 불가 상태에서 그 게이트와 무관한 산출물의 게시를 함께 막을지는 이 기록에서 정하지 않고 현재 그렇게 되고 있다는 사실만 적는다.

이 문서는 연구·설계 기록이며, 현재 제공 중인 서비스가 아닙니다. 각 항목의 상태 표기를 함께 확인해 주세요.

맥락

2026-08-10 설계로그 인용 게이트가 두 번 연속 실패했다. 11:16 실행은 541초, 13:02 실행은 541초를 쓰고 각각 조회 32건이 전부 '조회 실패 — urlopen error timed out' 이었다. 성공한 조회가 한 건도 없다.

같은 날 10:54 실행은 같은 러너 환경에서 36초에 끝났고 일치 19건·문언 동일 65건·조회 실패 0건이었다. 두 실패 사이 다른 환경에서 같은 OC 키로 같은 엔드포인트를 호출하면 1.2초에 응답했다.

즉 데이터에는 아무 문제가 없고 러너에서 출처에 닿지 못한 것이다. 게이트의 출력 문구는 그 구분을 이미 적고 있었다 — '이 실행은 드리프트 없음을 뜻하지 않는다 — 재실행할 것'.

그러나 종료코드는 1 이다. 인용이 틀렸을 때와 같은 코드이고, 워크플로는 둘을 구분하지 않는다. 배포 스텝은 두 경우 모두 skipped 로 끝난다.

이 저장소의 다른 게이트 두 개는 이 구분을 이미 갖고 있다. scripts/_r15c_build.py 는 검증 전에 출처 도달을 핑으로 확인하고 닿지 않으면 본검증을 생략한 채 게시 보류(EX_TEMPFAIL)로 끝낸다. scripts/generate_daily_tip.py 는 EX_TEMPFAIL=75 를 상수로 두고 확인 불가일 때 그 코드를 반환한다.

러너에서 law.go.kr 도달이 막히는 것은 새로 발견된 사정이 아니다. scripts/register_missing_precedents.py 와 scripts/register_casenote_assets.py 는 'GitHub 러너는 law.go.kr 접속이 막혀(EX_TEMPFAIL) 실시간 검증이 안 되는' 상황을 전제로 만들어진 도구다.

2026-08-06 에 이 게이트의 실행 예산을 워크플로 timeout 이 아니라 코드로 옮긴 이유도 같은 축이었다 — 점검이 돌지 않은 것과 결과가 깨끗한 것을 구분해야 한다는 것이다. 그때 구분한 것은 출력 문구였고 종료코드는 그대로 두었다.

예산 상향은 이 상태에 대한 처방이 아니다. 느려서 못 끝낸 것이 아니라 한 건도 되지 않았기 때문이며, 예산을 두 배로 늘리면 실패를 두 배 느리게 확인할 뿐이다.

검토한 대안

대안 A기각

실행 예산을 올린다.

성공한 조회가 0건이므로 시간 문제가 아니다. 예산은 실패를 늦출 뿐이고, 워크플로 timeout 아래에 예산을 두기로 한 2026-08-06 의 구조도 흔들린다.

대안 B기각

확인 불가를 통과로 처리해 배포를 진행시킨다.

확인하지 못한 것을 확인된 것으로 적는 것이며 이 로그의 fail-closed 를 정면으로 무너뜨린다. ADR-0008 이 적은 '관측할 수 없는 것을 이상 없음으로 적지 않는다'와 같은 지점이다.

대안 C채택

세 상태를 종료코드로 구분하고, 출처 도달 여부를 먼저 확인한다.

다음 행동이 다른 두 상태를 코드로 나눈다. 이 저장소의 다른 게이트 두 개가 이미 쓰는 형태이므로 새 규약을 만드는 것이 아니라 빠진 곳을 맞추는 것이다. 도달 확인을 먼저 하면 예산을 태우지 않고 끝난다.

대안 D보류

설계로그 인용 게이트를 배포 워크플로에서 떼어 내 사후 탐지로만 돌린다.

2026-07-27 이 이 게이트를 배포 스텝 앞에 인라인으로 둔 이유가 있다 — 형제 워크플로로 분리하면 같은 push 에서 병렬 실행되어 차단이 아니라 사후 경보가 된다. 예방 게이트를 탐지로 바꾸는 것은 이 사고와 별개의 판단이므로 여기서 하지 않는다.

결정

  1. 외부 출처에 도달해야 성립하는 검증 게이트는 결과를 세 상태로 구분한다 — 통과, 검증 실패(자료에 결함이 있다), 확인 불가(출처에 닿지 못했거나 예산을 넘겼다). 종료코드를 각각 0·1·75 로 하고 세 상태의 출력 문구를 다르게 낸다. 75 는 sysexits.h 의 EX_TEMPFAIL 이며 이 저장소의 다른 게이트 두 개가 이미 쓰는 값이다.
  2. 검증을 시작하기 전에 출처에 닿는지 먼저 확인한다. 닿지 않으면 본검증을 돌리지 않고 즉시 확인 불가로 끝낸다. 닿지 않는 상태에서 예산을 전부 태우는 것은 확인을 더 하는 것이 아니라 진단을 늦추는 것이다.
  3. 확인 불가도 게시를 막는다. 다만 그것을 검증 실패로 표시하지 않는다. 경보 제목과 로그 첫 줄에 데이터 결함이 아니라는 것과 다음 행동이 재실행이라는 것을 적는다.
  4. 확인 불가가 연속 2회 지속되면 사람에게 알린다. 조용한 재시도만 반복하지 않는다. 2회라는 값의 출처는 ADR-0039 결정 1 의 ③(이 로그가 명시적으로 정한 값)이며 법령이나 관측에서 나온 값이 아니다.
  5. 확인 불가 상태에서 그 게이트와 무관한 산출물의 게시를 함께 막을지는 이 기록에서 정하지 않는다. 다만 현재 함께 막히고 있다는 사실과 그 비용을 여기에 적어 둔다 — 스텝 순서 때문에 그렇게 되고 있을 뿐이고 그렇게 하기로 정한 기록은 어디에도 없다. 정하지 않았다는 것을 적는 것이 정한 것처럼 보이게 두는 것보다 낫다(ADR-0039 와 같은 취급).

근거

이 결정이 만드는 한계

폐기 조건

근거자료

인용 법령

인용한 법령이 없습니다.

참고자료

외부 참고자료를 부착하지 않았다. 이 결정의 근거는 전부 이 프로젝트 자신의 산출물이다 — 2026-08-10 의 실행 로그 세 건(성공 1·실패 2, 각 소요와 조회 성공 건수), scripts/_r15c_build.py 와 scripts/generate_daily_tip.py 의 EX_TEMPFAIL 처리, scripts/register_missing_precedents.py 의 전제 서술, .github/workflows/deploy-hosting.yml 의 스텝 순서. ADR-0016 결정 1 의 등급으로는 자체자료에 해당하며, 외부 문헌으로 뒷받침되는 주장은 이 기록에 없다.

산출물·측정 기록

작성 기록

이 기록의 조사·실측 정리·선택지 정리와 문장 초안은 사람이 지시하고 LLM(Claude)이 작성했다. 채택안(선택지 C)과 결정 1~5 의 문장은 운영자가 2026-08-10 에 검토해 채택했다. 결정 5 를 비워 두는 것도 운영자의 선택이며, LLM 초안은 그 항목을 '별도로 정한다'로 제시했다. 작성 주체를 적는 형식은 ADR-0035 에서 신설했다.

담당 리더

마디 리더

마디L60

시스템 총괄 · Master API Gateway

사람의 자격이나 직위에 대응하지 않는 시스템 역할 표기다. 이 로그의 설계 결정은 사람이 작성하고 사람이 책임진다(ADR-0004·ADR-0016).

설계로그는 특정 법률 분야가 아니라 시스템 전체의 설계 결정을 다룬다. 개별 도메인 리더가 아니라 60명 리더 시스템의 운영·품질을 총괄하는 리더가 담당한다.

리더 프로필 보기

관련 기록

ADR-0016ADR-0039ADR-0010ADR-0015ADR-0008