직접 해 본 결과만 적습니다 · 확인하지 못한 것은 그렇게 표시합니다
써 AI 마케팅 적용기 시중에 나온 AI 방법을 실무에 넣어 보고, 쓸만한지 남깁니다
콘텐츠 만들기

규칙을 지나지 않는 길이 네 군데 있었습니다 — 자동화 게이트 우회로 찾는 법

계속 쓴다2026-08-10·읽기 6분·글 써봄

자동화에 AI검수 게이트를 붙여 두고 안심하고 있었습니다. 문서에도 "조건을 못 채우면 중단" 이라고 적어 뒀습니다.

확인해 보니 코드가 달랐습니다. 게이트를 지나지 않는 경로가 네 군데 있었습니다.

1. 게이트 함수를 부르는 곳을 전부 나열합니다 2. 발행하는 함수를 부르는 곳도 전부 나열합니다 3. 둘을 대조해 게이트를 안 지나는 경로를 찾습니다 4. 찾은 것을 실제로 재현해서 확인합니다

자동화 점검 증상 — 문서와 코드가 다릅니다

증상 — 문서에 적힌 대로 동작할 거라고 믿고 있는데, 실제로는 다르게 돕니다.

문서에 적힌 동작과 실제 코드가 어긋난 예를 정리한 터미널 화면

문서 코드
근거 자료가 없으면 중단 경고만 남기고 계속 진행
점수 미달이면 발행 안 함 점수를 못 재면 그냥 통과
민감 항목은 사람 승인 뒤에 특정 경로에서는 승인 없이 나감
강제 옵션은 한 가지만 연다 나머지 게이트까지 같이 열림

원인 — 문서를 먼저 쓰고 코드를 나중에 고치면서 둘이 벌어졌습니다. 저는 문서를 기준으로 생각하고 있었는데, 실제로 도는 것은 코드입니다.

특히 두 번째가 무섭습니다. "미달이면 막는다" 와 "못 재면 막는다" 는 다른 말입니다. 채점기가 죽으면 점수가 없고, 없으면 미달도 아닙니다.

AI검수 게이트 우회로를 찾는 법

해결 — 코드를 읽어 내려가지 말고, 부르는 쪽을 전부 나열합니다.

게이트 함수와 발행 함수를 부르는 곳을 나열해 대조하는 방법을 정리한 터미널 화면

# 게이트를 부르는 곳
grep -rn "check_gates\|safety_check" --include="*.py" .

# 발행을 부르는 곳
grep -rn "publish\|deploy_post" --include="*.py" .

두 목록을 나란히 놓고 봅니다. 발행 목록에는 있는데 게이트 목록에는 없는 파일이 우회로입니다.

제 경우 이런 것들이 나왔습니다.

우회로 왜 생겼나
재시도 로직 처음 시도에서 검사했으니 재시도는 건너뛰게 만듦
수동 실행 경로 사람이 부르는 것이니 괜찮다고 판단
강제 옵션 한 게이트를 열려다 전부 열림
다른 모듈의 직접 호출 나중에 추가하면서 게이트를 몰랐음

전부 그럴듯한 이유가 있었습니다. 일부러 뚫은 게 아니라 하나씩 편의를 봐준 결과였습니다.

저는 이 네 개를 만들 때마다 나름의 판단이 있었습니다. 재시도는 이미 검사했으니 두 번 할 이유가 없다고 봤고, 수동 실행은 사람이 보고 있으니 괜찮다고 봤습니다.

문제는 그 판단들이 쌓이면서 원래 규칙이 사실상 없어졌다는 점입니다. 하나씩 보면 다 말이 되는데, 전부 모아 놓으면 게이트를 지나는 경로가 오히려 소수였습니다.

재현해서 확인합니다

주의 — 코드를 읽어서 판단하지 마세요. 실제로 돌려 봐야 압니다.

문서 주장을 실제로 재현해 확인하는 절차를 정리한 터미널 화면

주장: 근거 자료가 없으면 발행되지 않는다
확인: 자료를 지우고 실제로 실행한다
결과: 발행됨  ← 문서가 틀렸다

저는 네 군데를 전부 이렇게 확인했습니다. 코드를 읽었을 때 "이건 괜찮아 보이는데" 싶었던 곳이 실제로는 뚫려 있었습니다. 반대로 뚫린 줄 알았던 곳이 다른 데서 막히고 있기도 했습니다.

읽어서 아는 것과 돌려서 아는 것은 다릅니다.

강제 옵션을 어디까지 열 것인가

이게 제일 헷갈리는 부분이었습니다. 급할 때 쓰라고 만든 옵션이 다른 게이트까지 같이 열고 있었습니다.

해결 — 강제 옵션은 그 한 가지만 엽니다.

def can_publish(post, force_score=False, allow_no_source=False):
    if post.gates:                       # 안전 게이트는 무엇으로도 안 열린다
        return False, post.gates
    if not post.source and not allow_no_source:
        return False, "근거 자료 없음"
    if post.score < TARGET and not force_score:
        return False, f"{post.score}점"
    return True, ""

옵션마다 여는 것이 하나씩 대응돼야 합니다. 하나로 전부 여는 옵션은 만들지 않는 편이 낫습니다. 급할 때 그걸 쓰게 되고, 급할 때가 제일 위험합니다.

그리고 어떤 옵션으로도 안 열리는 항목을 따로 둡니다. 저는 법 위반 표현과 개인정보를 그렇게 뒀습니다.

다시 벌어지지 않게 — 구조로 막기

한 번 맞춰도 시간이 지나면 또 벌어집니다.

장치 무엇을 막나
발행 경로를 한 곳으로 모음 새 경로가 게이트를 빠뜨리는 것
게이트를 발행 함수 안에 둠 부르는 쪽이 잊는 것
우회로 점검을 정기적으로 시간이 지나며 다시 벌어지는 것
재현 시험을 회귀 검사로 고친 것이 다시 풀리는 것

두 번째가 제일 확실합니다. 게이트를 발행 함수 밖에 두면 부르는 쪽마다 챙겨야 하는데, 안에 두면 어디서 불러도 지납니다.

def publish(post, *, force_score=False):
    ok, reason = can_publish(post, force_score=force_score)   # 안에서 반드시 지난다
    if not ok:
        raise BlockedError(reason)
    _do_publish(post)

_do_publish 는 밖으로 노출하지 않습니다. 이름 앞에 _ 를 붙여 두면 최소한 실수로 부르는 일은 줄어듭니다.

정기 점검에 넣을 항목도 정해 뒀습니다. 한 달에 한 번쯤 돌립니다.

점검 항목 명령
발행 호출 지점 grep -rn "_do_publish" --include="*.py" .
게이트 우회 여부 위 결과가 publish() 안뿐인지
부정 조건 grep -rn "!= \"차단\"" .
강제 옵션 범위 force_ 로 시작하는 인자 목록

저는 이걸 문서에만 적어 뒀다가 안 돌렸습니다. 점검도 자동으로 돌게 만들어야 실제로 합니다. 지금은 정기 점검 스크립트 안에 넣어 뒀습니다.

확인 못 한 것

우회로를 코드에서 자동으로 찾아내는 방법은 아직 못 만들었습니다. 지금은 검색해서 눈으로 대조합니다.

호출 관계를 그려서 발행에 닿는 모든 경로를 뽑으면 될 것 같은데, 조건 분기까지 따라가야 해서 간단하지 않아 보입니다. 실제로 될지는 확인하지 못했습니다.

판정 — 계속 쓴다

우회로 점검은 반드시 해야 합니다. 게이트를 붙였다고 안전한 게 아니라, 지나야 안전합니다.

게이트를 발행 함수 안에 두는 구조가 가장 확실했습니다. 구조로 막는 쪽이 규율로 막는 것보다 오래 갑니다.

문서를 믿지 않는 습관도 같이 필요합니다. 저는 제가 쓴 문서를 기준으로 판단하다가 네 군데를 놓쳤습니다.

직접 하실 분께

하나. 발행 함수를 부르는 곳을 전부 나열해 보세요. 검색 한 줄이면 됩니다.

둘. 찾은 것은 실제로 재현해서 확인합니다. 읽어서 판단하지 마세요.

셋. 강제 옵션은 하나에 하나씩 대응시킵니다. 전부 여는 옵션은 만들지 않습니다.

넷. 게이트는 발행 함수 안에 둡니다. 밖에 두면 새 경로가 생길 때마다 빠집니다.

다음에는 발행에 닿는 경로를 코드에서 자동으로 뽑아내는 방법이 될지 해보려고 합니다. 되면 이어서 적겠습니다.