검증 레인이 지적 다섯 건을 들고 돌아왔다. 순서대로 훑으며 고치려던 손을 한 번 멈췄다. 넷째 지적까지는 "몇 번째 줄에 이런 문제가 있다"는 형태였는데, 원본을 열어 그 줄을 직접 읽어 보니 지목한 위치가 실제와 조금씩 어긋나 있었다. 검증자가 틀렸다는 확신이 서기 전까지는 아무것도 고치지 않기로 했다.

앞서 세운 교차 검증 레인(교차 검증을 처음 세우는 법)은 작성자와 다른 벤더가 계획·테스트·구현을 단계마다 검증하도록 순서를 정해 준다. 다만 그 레인이 정해 주는 건 누가, 언제 검증하느냐까지다. 검증자가 돌려준 지적 자체가 맞는지는 레인을 아무리 잘 세워도 저절로 확인되지 않는다. 지적을 받은 다음, 그 지적이 정말 맞는지는 결국 사람이 다시 봐야 했다.

검증 레인이 돌아왔다는 것과 검증 레인이 옳다는 것은 다른 얘기다. 지적 목록을 그대로 수정 지시로 받아들이면 검증자의 실수가 내 실수 위에 한 겹 더 쌓인다. 반대로 모든 지적을 의심하면 검증을 세운 의미가 없어진다. 그 사이 어딘가에서 건별로 판정하는 절차가 필요했다.

이 습관이 생기기 전에는 검증자가 돌려준 목록을 권위 있는 문서처럼 대했다. 작성자보다 뒤에 서서 검토만 하는 쪽이니 지적 하나하나가 더 정확할 거라 넘겨짚었다. 실제로는 검증자도 같은 방식으로 추론하는 모델이다. 근거가 흐릿한 자리에서는 검증자도 작성자만큼 헷갈린다. 그 사실을 두 번의 사건을 겪고 나서야 몸으로 받아들였다.

위치를 짚은 지적, 실제 위치는 달랐다

검증 호출 하나가 지적 다섯 건을 반환했다. 앞의 네 건은 전부 같은 형태였다. "이 파일 이 줄 근처에 이런 문제가 있다"는 위치 지정형 지적이었다. 형식만 보면 신뢰도가 가장 높아 보이는 부류다. 줄 번호까지 짚었으니 원본을 대조하면 바로 확인될 텐데, 마침 그게 함정이었다.

원본을 열어 지목된 줄을 하나씩 읽었다. 네 건 모두 실제 위치가 조금씩 밀려 있었다. 어떤 건 한두 줄, 어떤 건 대여섯 줄 어긋났다. 코드가 리팩터링되면서 줄 번호가 밀렸는데, 네 건 모두 어긋난 방향이 같았다. 왜 그런 방향으로 어긋났는지는 바깥에서 알 방법이 없었다. 지적한 문제 자체가 없다는 게 아니라, 그 문제가 있다고 주장한 자리에 실제로는 다른 코드가 있었다는 뜻이다. 그대로 그 줄을 고쳤다면 엉뚱한 코드를 건드릴 뻔했다.

네 건 전부 지목된 좌표가 원본과 어긋났고, 반영하지 않았다. 반증이라는 말이 거창하게 들릴 수 있는데, 이 자리에서 반증되는 건 지목된 좌표뿐이다. 원본 파일을 열어 그 줄을 눈으로 읽으면 위치가 맞는지는 몇 초 만에 갈린다. 지적 전체를 버릴지는 별개의 문제다. 지적이 짚은 문제 자체가 어디에도 없을 때만 그 지적을 버린다. 그 몇 초를 아끼려고 위치 지정형 지적을 곧바로 믿었다면 코드는 고쳐지지 않고 줄 번호만 뒤섞였을 것이다.

처음엔 이 정도 오차는 그냥 반올림해서 근처 줄을 찾아 고치면 되지 않느냐고 생각했다. 그런데 "근처"라는 말이 문제였다. 대여섯 줄 어긋난 지적을 감으로 근처까지 좁혀 고치면, 검증자가 실제로 본 게 맞는지 아니면 내가 짐작으로 끼워 맞춘 건지 구분이 안 된다. 위치가 틀린 지적은 반올림 대상이 아니라 반증 대상이라는 걸 이때 정리했다.

같은 호출의 다섯째 지적은 결이 달랐다. "문서 안에서 두 군데가 서로 다른 전제를 깔고 있다"는 논리 지적이었다. 위치를 짚을 필요가 없는 지적이라 원본을 열어 줄을 셀 필요도 없었다. 문서 두 곳을 나란히 놓고 읽으면 모순이 그대로 보였다. 이건 타당해서 받아들였다. 같은 응답 안에서도 지적마다 신뢰도가 달랐다는 게 이 경험에서 가장 남는 부분이다. 하나의 호출이 통째로 맞거나 틀리는 게 아니라 건별로 갈렸다.

검토자가 여러 장의 메모지를 손에 든 채 문서 위에서 살피고, 그중 세 장은 폐기함에 버려지고 한 장만 책상에 남아 있는 모습을 그린 일러스트
지적 목록을 받으면 먼저 건별로 나눠 보고, 원본과 어긋나는 항목은 버리고 남는 것만 수정에 반영하는 걸 표현한 그림.

없다고 잘라 말한 지적이, 실제로는 있었다

다른 계획서 검증에서는 더 위험한 패턴을 만났다. 이번엔 위치 오류가 아니라 존재 자체를 부정하는 지적이었다. 계획서가 연속으로 반려됐고, 반려 사유 중 두 건이 사실이 아니었다.

하나는 "그 도구 트리에는 특정 확장 표면이 없다"는 주장이었다. 없다고 잘라 말했으니 계획에서 그 부분을 아예 빼는 쪽으로 고치려던 참이었다. 확인 삼아 해당 설정 파일을 직접 열어 봤다. 그 확장 표면은 없기는커녕 이미 활성 상태로 등록돼 있었다. 검증자가 존재하지 않는다고 단정한 자리에 실제로는 살아 있는 설정이 앉아 있었던 셈이다.

다른 하나는 "그 자원은 심링크가 아니라 비용이 0"이라는 주장이었다. 심링크가 아니라니 정리하지 않아도 되는 자원으로 분류하고 넘어갈 뻔했다. 실제로 확인해 보니 심링크였다. 원본과 이어져 있어 방치하면 원본 쪽 변경이 그대로 새는 통로였다. "비용이 0"이라는 판단은 "심링크가 아니다"라는 잘못된 전제 위에서만 성립하는 결론이었다.

두 지적 모두 "무엇이 있다"가 아니라 "무엇이 없다"는 형태였다. 없다는 주장은 확인 비용이 가장 싸다는 게 역설적이다. 파일 하나를 열어 항목이 등록돼 있는지, 심링크인지 아닌지만 보면 끝난다. 그런데 그 싼 확인을 건너뛰면 손해는 가장 크다. 만약 두 전제를 그대로 받아들였다면 실제로 필요했던 정리 작업 하나가 계획에서 통째로 빠진 채 통과됐을 것이다. 잘못된 반려는 잘못된 통과만큼 위험하다. 반려당하고도 억울해서 넘어가지 못한 게 아니라, 반려 사유 자체가 틀려서 다시 확인한 것뿐이다.

왜 부재 주장이 유독 잘 틀릴까 생각해 봤다. 무언가가 있다고 말하려면 그 자리를 정확히 짚어야 하니 근거가 남는다. 반대로 없다고 말하는 건 근거 없이도 그럴듯하게 들린다. "찾아봤는데 없더라"는 문장은 실제로 찾아봤는지, 어디까지 찾아봤는지를 검증자 스스로도 구분하지 못한 채 나올 수 있다. 그래서 부재 주장은 다른 어떤 지적보다 확신에 찬 어조와 실제 근거의 격차가 크다.

지적을 세 종류로 나눠 다르게 다룬다

두 사건을 겪고 나서 지적을 하나로 뭉뚱그려 다루지 않기로 했다. 지적은 성격이 다른 세 종류로 갈리고, 종류마다 확인 방법과 신뢰할 수 있는 정도가 다르다.

첫째는 위치·인용·수치를 짚는 지적이다. "몇 번째 줄", "이 문서의 이 구절", "이 값은 몇이어야 한다"처럼 구체적인 좌표를 댄다. 구체적이라서 믿음직해 보이지만 앞선 사건이 보여주듯 좌표 자체가 틀릴 수 있다. 원본을 직접 열어 대조하기 전에는 절대 반영하지 않는다.

둘째는 부재를 주장하는 지적이다. "~가 없다", "~는 하지 않는다"처럼 무언가의 부재를 단정한다. 확인 비용이 가장 싸면서 틀렸을 때 손해가 가장 크다는 게 이 종류의 특징이다. 없다고 믿고 그 부분을 계획에서 빼 버리면, 나중에야 있었다는 게 드러나도 되돌리기 번거롭다. 그래서 부재 주장은 반드시 직접 확인한다.

셋째는 논리·모순·일관성을 짚는 지적이다. "이 부분과 저 부분의 전제가 서로 다르다"처럼 글이나 계획 안에서 자체 모순을 찾아낸다. 원본 밖의 사실을 끌어올 필요가 없어서 대조 없이도 문서 안에서 판정할 수 있다. 근거를 따라가 보고 실제로 모순이면 받아들이고, 견강부회면 버린다.

위치·인용·수치 지적, 부재 주장 지적, 논리·모순 지적 세 종류를 나란히 놓고 각각에 필요한 확인 방법을 표시한 다이어그램
지적을 세 종류로 나누면 위치·인용·수치는 원본 대조, 부재 주장도 직접 확인, 논리·모순은 문서 안에서 근거로 판정한다는 구분이 선다.

세 종류를 가르는 기준은 결국 하나다. 원본을 열지 않고도 판정할 수 있느냐, 아니면 반드시 열어야 하느냐다. 앞의 두 종류는 열어야 하고 세 번째는 열지 않아도 된다. 이 구분이 없으면 모든 지적을 같은 무게로 취급하게 되고, 그러면 위치가 틀린 지적도 논리가 타당한 지적도 똑같이 "검증자가 그렇다니까" 받아들이는 쪽으로 쏠린다.

지적 종류 예시 표현 필요한 조치
위치·인용·수치 몇 번째 줄, 이 구절, 이 값 원본을 열어 직접 대조하기 전엔 반영 금지
부재 주장 ~가 없다, ~는 하지 않는다 확인 비용이 가장 싸므로 반드시 직접 확인
논리·모순·일관성 이 부분과 저 부분이 다르다 원본 대조 없이 문서 안 근거로 판정 가능

세 종류로 정확히 안 갈리는 지적도 있었다. "이 부분(3번째 문단)의 수치가 다른 부분(6번째 문단)의 전제와 안 맞는다"처럼 위치와 논리가 한 문장에 섞인 경우다. 이런 건 쪼갰다. 위치를 짚은 절반은 원본 대조 대상으로, 모순을 짚은 절반은 논리 지적으로 따로 판정했다. 하나의 지적이라고 해서 반드시 하나의 판정으로 끝낼 필요는 없었다.

채택 게이트: 번호를 매기고, 원본을 열고, 반증을 기록한다

분류만으로는 부족하다. 분류를 실제 절차로 옮겨야 다음에도 같은 방식으로 반복할 수 있다. 지적 목록을 받으면 가장 먼저 건별로 번호를 붙인다. 다섯 건이면 다섯 개의 독립된 항목으로 쪼갠다. 하나의 응답을 통째로 채택하거나 통째로 버리는 판단을 막기 위해서다.

번호를 붙인 다음 각 건을 세 종류 중 하나로 분류한다. 위치·인용·수치 지적과 부재 주장 지적은 원본을 열어 대조한 결과를 함께 적는다. "실제 위치 확인함, 일치" 또는 "실제 위치 확인함, 불일치"처럼 확인 결과를 짧게 남긴다. 논리·모순 지적은 문서 안에서 근거를 따져 수용 여부를 적는다.

기록은 길게 쓰지 않는다. 번호, 종류, 원본 대조 결과, 판정 네 칸이면 충분하다. 예를 들면 "3번 · 부재 주장 · 원본 확인: 설정 파일에 항목 등록돼 있음 · 판정: 반증됨"처럼 한 줄로 끝낸다. 짧게 남기는 이유는 나중에 다시 훑어볼 사람이 그 판정을 몇 초 안에 이해할 수 있어야 하기 때문이다. 판정 근거를 길게 풀어 쓰면 다음에 읽을 때 오히려 무엇이 핵심 근거였는지 흐려진다.

대조 결과 좌표가 어긋난 건은 그 자리에서 곧장 버리지 않는다. 어긋난 건 지목된 위치일 뿐, 지적이 짚은 문제 자체까지 없다는 뜻은 아니어서다. 지목된 자리 주변을 좁게 다시 훑어 같은 문제가 근처에 있는지부터 확인하고, 문제 자체가 어디에도 없을 때만 "반증됨"으로 표시하고 버린다. 버렸다고 그 기록을 지우지는 않는다. 살아남은 지적에만 수정을 적용하고, 반증된 지적은 왜 반증됐는지까지 남겨 둔다. 이 기록이 다음 라운드에서 값어치를 한다. 검증을 다시 돌렸을 때 같은 지적이 또 올라오면, 그사이 원본이 바뀌지 않았을 때만 처음부터 다시 조사하지 않고 이전 반증 기록을 그대로 참조한다. 원본이 바뀌었다면 그 기록은 무효로 보고 처음부터 다시 확인한다.

지적 목록에서 분류를 거쳐 원본 대조로 이어지고, 그 결과가 반증됨으로 버려지거나 채택돼 수정으로 넘어가는 흐름과 검증 레인 상태 표시 분기를 함께 나타낸 다이어그램
지적 목록을 분류하고 원본과 대조해 반증된 항목은 버리고 채택된 항목만 수정으로 넘기며, 레인이 정상인지 대체인지도 함께 표시하는 게이트 흐름.

이 절차를 몇 번 돌리고 나니 규칙이 하나 더 생겼다. 수정은 살아남은 지적에만 적용한다. 반증된 지적을 "혹시 몰라서" 절반만 반영하는 타협은 하지 않는다. 절반만 반영하면 나중에 왜 그렇게 고쳤는지 근거를 다시 못 찾는다. 채택과 폐기 둘 중 하나로만 끝맺는다.

레인이 막히면 통과의 값도 달라진다

채택 여부를 정하는 건 지적 하나하나의 신뢰도만이 아니다. 그 지적을 낸 검증이 어떤 조건에서 나왔는지도 같은 판단에 들어간다. 레인이 정상으로 돌았는지 아예 막혔는지에 따라 똑같은 "통과"라는 말도 무게가 다르고, 채택 판단에 반영하는 비중이 그만큼 달라진다. 한 세션에서는 벤더 하나의 텍스트 검증 레인이 계정 유형 문제로 요청 자체를 거부했다. 다른 대체 레인이 없어서 그 벤더 쪽 검증은 그 세션에서 통째로 비었다.

다른 벤더는 정도가 달랐다. 원래 쓰던 모델이 한도 소진으로 막히자 같은 레인 안에서 대체 모델로 넘어갔다. 검증 자체는 진행됐지만 평소 쓰던 모델과는 다른 모델이 판정을 내렸다. 여기서 중요한 건 "검증을 건너뛰었다"고 뭉뚱그리는 게 아니라 "어떤 조건에서 누가 검증했는지"를 결과에 표시로 남기는 것이다.

폴백으로 받은 통과는 정상 통과와 같은 값으로 기록하지 않는다. 통과라는 단어만 보면 둘은 똑같아 보이지만 속은 다르다. 하나는 원래 계획한 검증자가 정식으로 내린 판정이고, 다른 하나는 대체 검증자가 대신 내린 판정이다. 나중에 문제가 생겨 되짚어볼 때 "그때 통과했잖아"라는 말만으로는 부족하다. 어떤 조건의 통과였는지가 남아 있어야 그 통과를 얼마나 믿을지도 다시 판단할 수 있다.

레인이 아예 막힌 경우는 더 단순하게 처리한다. 막힌 레인은 없는 셈 치고 넘어가지 않는다. 그 벤더 자리는 비어 있다고 명시적으로 남기고, 레인이 복구되면 같은 산출물을 다시 그 벤더에 돌린다. 검증을 건너뛴 채 "통과로 간주"하는 건 결국 자기 판단으로 자기 작업을 승인하는 것과 다르지 않다.

표시를 남기는 습관이 없던 시절에는 통과 기록만 보고 안심했다가 나중에 곤란했던 적이 있다. 어떤 산출물이 통과됐다는 사실만 남아 있고 그 통과가 정상 레인에서 나왔는지 대체 레인에서 나왔는지 구분이 안 됐다. 결국 신뢰도를 다시 매기려면 그 시점의 기억에 의존해야 했다. 표시 한 줄 남기는 비용이 나중에 그 기억을 되짚는 비용보다 훨씬 쌌다.

이 절차에도 한계는 분명하다. 검증자를 의심하는 절차 자체가 시간을 쓴다. 모든 지적을 하나하나 원본과 대조하면 애초에 검증을 세운 이점이 사라진다. 그래서 위 세 분류로 대조 대상을 좁히는 걸 타협점으로 삼았다. 위치·인용·수치와 부재 주장만 반드시 열어 보고, 논리·모순은 문서 안에서 판정해 대조 부담을 줄인다. 그리고 이 분류 자체가 내가 겪은 범위에서 나온 경험칙이지, 측정으로 검증된 규칙은 아니다. 다른 작업이나 다른 검증 레인에서는 세 분류의 경계가 다르게 그어질 수도 있다.

비슷한 실전 기록은 LLM 활용에 모아 둔다. 남은 숙제는 이 분류를 사람이 매번 손으로 하지 않고 절반이라도 자동으로 걸러내는 일이다. 위치·인용·수치 지적이라면 원본 대조 스크립트를 먼저 돌려 어긋난 좌표부터 자동으로 걸러내는 식으로 다음 단계를 그려보고 있다.

자주 묻는 질문

검증자가 지적한 내용은 원칙적으로 다 반영해야 하지 않나?

아니다. 검증자도 위치를 잘못 짚거나 있는 것을 없다고, 없는 것을 있다고 잘못 판단할 수 있다. 지적을 그대로 수정 지시로 받아들이면 검증자의 실수가 원본 위에 그대로 얹힌다. 건별로 나눠 확인한 뒤 살아남은 지적만 반영해야 한다.

모든 지적을 다 원본과 대조해야 하나?

그러면 검증을 세운 이점이 사라진다. 위치·인용·수치 지적과 부재 주장 지적처럼 원본을 열어야만 판정 가능한 종류로 대조 대상을 좁힌다. 논리·모순 지적은 문서 안에서 근거만 따지면 되므로 원본 대조 없이도 수용 여부를 정할 수 있다.

왜 부재를 주장하는 지적을 가장 조심해야 하나?

확인 비용이 가장 싼데 틀렸을 때 손해가 가장 크기 때문이다. 파일 하나만 열어 보면 확인이 끝나지만, 없다는 전제를 그대로 믿고 넘어가면 실제로 필요했던 작업이 통째로 계획에서 빠질 수 있다. 잘못된 반려는 잘못된 통과만큼 위험하다.

검증 레인 하나가 막히면 검증을 건너뛰어도 되나?

아니다. 막힌 레인은 비어 있다고 명시적으로 남기고 복구되면 다시 돌린다. 대체 모델로 폴백해 받은 통과도 정상 통과와 같은 값으로 기록하지 않고 어떤 조건에서 누가 검증했는지를 결과에 표시로 남긴다.