엔지니어링

AI 에이전트 재발 방지: 기각한 상담 예약 버튼은 왜 두 번 돌아왔나

AI 에이전트 재발 방지에서 저희가 놓친 곳은 기억이 아니라 경로였습니다. 상담은 글로만 받는다고 2026년 7월 13일에 정해 두었는데도, 사흘 뒤 홈페이지 문의 화면에 “15분 화상 상담 예약” 버튼이 라이브로 올라갔고, 8월 19일에는 외부 리서치가 같은 축의 처방을 또 써 왔습니다. 두 번 모두 결정은 기록에 있었고, 에이전트가 외부 사례를 옮기는 길에서 그 기록을 열지 않았을 뿐이었습니다.

2026-11-06검색하는 사람: Claude Code 같은 AI 코딩 에이전트에게 운영 업무를 맡기면서 같은 실수가 세션마다 되풀이되는 문제를 겪는 개발자·1인 사업자 (수요 검색어 목록에 맞는 말이 없어 직접 지음)기각 목록 문서 docs/REJECTED.md의 활성 기각 2건(2026-08-19 미팅·통화 유도 버튼, 2026-08-13 main 브랜치 push 차단 훅)재발 실측: 2026-07-16 문의 화면 화상 상담 예약 버튼 라이브 후 PR #60 제거, 2026-08-19 외부 리서치 처방 재등장, 2026-08-08 한 세션 안 기각안 2회 재등장적용 사례: 2026-10-07 홈페이지 재견적에서 검토 패널 제안 15건 중 고객 요구가 아닌 항목을 기각 목록에 등재남는 한계: 장치 도입 뒤의 재발 횟수는 이 글의 자료에 측정값이 없음

증상: 비대면이라고 적힌 화면에 화상 상담 버튼이 왜 붙었나?

AI 에이전트 재발 방지 문제는 저희 홈페이지 문의 화면에서 처음 드러났습니다. 2026년 7월 16일, 문의 화면에 “15분 화상 상담 예약” 버튼이 올라갔습니다. 누르면 외부 일정 예약 서비스(Calendly류)로 넘어가는 버튼이었고, 라이브 배포까지 갔습니다. 같은 화면 바로 위에는 “모든 상담은 이메일 기반 비대면”이라는 문구가 그대로 있었습니다. 한 화면이 두 가지를 동시에 말하고 있었습니다.

이 버튼은 실수로 끼어든 한 줄이 아니었습니다. 전환율을 높이려는 개선 계획 문서가 다른 회사들의 상담 퍼널을 옮기면서 “15분 화상 상담 예약 버튼을 달라”고 권고했고, 에이전트가 그 권고를 그대로 구현했습니다. 코드는 깨끗했고 빌드도 통과했습니다. 검사 장치 어디에도 걸리지 않았습니다. 틀린 것은 코드가 아니라 버튼의 존재 자체였습니다.

저희는 7월 13일에 이미 상담 방식을 정해 두었습니다. 대표의 원문은 이렇습니다. “영상 통화, 상담 전화 이런거 하지 않아. 영상 매뉴얼을 녹화해서 파일로 전달을 할지언정 실시간으로 일정잡고 대면, 비대면 미팅, 전화 상담, 실시간 채팅 상담 이런거 안해.” 근거도 짧았습니다. 혼자서 전화를 언제 다 하나. 비대면은 취향이 아니라 1인 운영의 물리 제약이었습니다.

버튼은 PR #60으로 걷어 냈습니다. 그런데 한 달 뒤인 8월 19일, 같은 축의 제안이 다른 얼굴로 돌아왔습니다. 이번에는 외부 창업 미디어의 세일즈 사례를 옮겨 정리한 리서치가 “콜드메일에서 미팅 요청을 빼라”는 처방을 썼고, 그 처방을 다른 작업 세션에 포인터로 보내기까지 했습니다. 저희 콜드메일에는 미팅 요청이 애초에 한 줄도 없었습니다.

표. 재발 이력. 2026년 7월 13일 비대면 상담 결정, 7월 16일 화상 상담 15분 예약 버튼 라이브 후 PR #60 제거, 8월 8일 한 세션 안 기각안 2회 재등장으로 기각 목록 문서 신설, 8월 13일 push 차단 훅이 문서 커밋 4개를 막아 기각, 8월 19일 외부 리서치가 미팅 처방을 다시 써서 대표 지적 후 정정 발송
같은 축이 경로만 바꿔 돌아왔다

처음 의심한 것: 결정이 어디에도 안 적혀 있었다

처음 세운 가설은 단순했습니다. 에이전트는 세션이 끝나면 대화를 잊습니다. 그러니 대표가 “미팅 안 한다”고 말한 대화가 다음 세션까지 살아남지 못했고, 새 세션의 에이전트는 그 결정을 모른 채 버튼을 달았다는 가설이었습니다. 이 가설대로라면 고칠 곳은 저장소입니다. 결정을 파일에 적어 두면 끝납니다.

이 가설은 그럴듯한 근거가 하나 있었습니다. 8월 8일에 비슷한 일을 실제로 겪었기 때문입니다. 그날 한 세션 안에서만 이미 기각한 제안이 2회 다시 올라왔고, 대표가 “그거 아니라고 했잖아”라고 지적했습니다. 그 사건 직후 저희는 기각한 축을 따로 모으는 문서를 만들었습니다. 같은 병이라면 같은 약이 들어야 했습니다.

무엇으로 확인했나: 결정 기록, 화면, 템플릿을 한 줄씩 대조

확인 수단은 로그나 프로파일러가 아니라 파일 대조였습니다. 문제가 코드 버그가 아니라 판단의 출처였기 때문입니다. 세 가지를 열었습니다. 첫째, 결정 기록 파일에서 상담 방식 결정을 찾았습니다. “미팅 안 한다” 원문은 결정 기록 8587줄에 있었고, 날짜는 7월 13일이었습니다. 버튼이 올라간 7월 16일보다 사흘 앞섭니다.

둘째, 버튼이 올라간 문의 화면 코드를 열었습니다. 같은 파일 안에 “모든 상담은 이메일 기반 비대면” 문구와 화상 상담 버튼이 함께 있었습니다. 에이전트가 그 화면을 고치면서 바로 위 문구를 읽을 수 있는 위치였습니다. 셋째, 8월 19일 리서치가 고치라고 한 콜드메일 템플릿을 열었습니다. 작성 규칙 문서 13줄과 발송 미리보기 46줄은 오히려 반대를 말하고 있었습니다. “사전 영업 미팅 없이 비대면으로 진행하니 결정 빠르신 팀에 합당합니다”.

마지막으로 두 사건을 나란히 놓고 경로를 비교했습니다. 버튼을 단 작업과 처방을 쓴 작업이 어디서 출발해 어디로 갔는지, 그 사이에 저희 결정을 조회하는 단계가 있었는지를 봤습니다.

알리바이: 결정은 이미 기록에 있었다

첫 가설은 여기서 깨졌습니다. 결정은 사라지지 않았습니다. 7월 13일 결정은 결정 기록에 원문과 함께 남아 있었고, 7월 16일에도 8월 19일에도 그 자리에 있었습니다. 저장소가 비어서 생긴 일이라면, 기록이 있는 상태에서는 재발하지 않아야 합니다. 그런데 두 번 다 기록이 있는 상태에서 일어났습니다. “기록이 없어서”는 원인이 아니었습니다.

두 번째로 의심할 수 있는 답도 있었습니다. 외부 처방이 우리 상황에 맞아서 받아들인 게 아니냐는 해석입니다. 이것도 아니었습니다. 8월 19일 처방은 “콜드메일에서 미팅 요청을 빼라”였는데, 뺄 미팅 요청이 없었습니다. 처방을 쓴 쪽이 저희 템플릿을 한 번도 열지 않았다는 뜻입니다. 외부 사례는 미팅을 파는 사람의 전제를 달고 왔고, 저희는 이미 그 지점을 지나 비대면 자체를 판매 포인트로 쓰고 있었습니다.

표. 가설과 반증. 가설 1 결정이 기록에 없었다는 결정 기록 8587줄 대조로 반증, 7월 13일 기록이 두 사건보다 앞섬. 가설 2 외부 처방이 우리 상황에 맞았다는 콜드메일 템플릿 대조로 반증, 뺄 미팅 요청이 원래 없음. 가설 3 push 같은 동작을 기계로 막으면 안전하다는 8월 13일 실측으로 반증, 문서 커밋 4개가 막힘
세운 가설 셋이 모두 깨졌다

고친다면서 하나 더 틀렸다: push 차단 훅

같은 시기에 저희는 반대 방향으로도 한 번 틀렸습니다. 8월 13일, 대표가 “왜 커밋·푸시를 계속 승인받았냐”고 물었습니다. 승인 절차가 마찰이니 줄이라는 요구였습니다. 에이전트는 이 질문을 “그럼 정작 막아야 할 main 브랜치 push는 왜 안 막고 있지”로 넓혔고, 명령 실행 직전에 끼어드는 훅에 main 브랜치 직접 push를 차단하는 규칙을 새로 넣었습니다.

결과는 그날 밤에 나왔습니다. 문서만 바꾼 커밋 4개가 이 차단에 걸려 배포되지 못했습니다. 저희 홈페이지는 콘텐츠를 main 브랜치에 바로 올려 매일 발행하도록 설계돼 있었고, 새 차단은 그 목표 자체를 막았습니다. 대표의 판정은 짧았습니다. push는 승인 게이트 대상이 아니라는 요지였습니다. 강제 push가 아닌 push는 되돌릴 수 있고, 되돌릴 수 없는 강제 push와 reset은 이미 막혀 있었습니다.

이 사건이 진짜 원인을 보는 데 도움이 됐습니다. 마찰을 없애라는 요구를 새 차단으로 갚은 것도, 외부 사례를 우리 결정과 대조 없이 옮긴 것도, 같은 모양이었습니다. 에이전트가 요청의 겉모양에서 출발해 그럴듯한 다음 수를 두는데, 그 사이에 “이 축은 이미 판정이 났나”를 묻는 단계가 없었습니다.

진짜 원인은 무엇이었나: “하지 않기로 한 것”을 담는 자리가 없었다

저희 작업 기록은 세 가지만 저장했습니다. 열린 일을 적는 진행 문서, 내린 결정을 적는 결정 기록, 행동 습관을 적는 메모리입니다. 셋 다 하기로 한 것만 남깁니다. 대표가 죽인 축, 곧 “이건 다시 꺼내지 마라”는 판정은 어디에도 독립된 자리가 없었습니다. 되돌린 사실은 git에 남지만, 왜 되돌렸는지와 다시 꺼내지 말라는 판정은 git에도 안 남습니다.

7월 13일 결정은 결정 기록 안에 다른 결정들과 함께 한 항목으로 있었습니다. 외부 벤치마크를 옮기는 작업은 그 기록이 아니라 외부 사례에서 출발합니다. 결정 기록은 무엇을 하기로 했는지는 말해 주지만, 어떤 제안을 다시 꺼내지 말아야 하는지와 언제 다시 꺼내도 되는지는 따로 말해 주지 않았습니다. 에이전트가 새 제안 앞에서 그 기록을 일부러 열지 않는 한, 이미 기각된 축이라는 사실을 알아챌 길이 없었습니다.

그래서 재발 경로는 두 번 모두 같았습니다. 외부 벤치마크에서 출발해, 처방을 쓰고, 우리 화면이나 템플릿에 옮기는 길. 그 길 어디에도 저희 확정 결정을 조회하는 단계가 없었습니다. 원인은 기억력이 아니라 이 경로의 빈칸이었습니다.

흐름도. 수정 전 경로는 외부 벤치마크에서 처방 작성, 우리 화면과 템플릿 반영으로 바로 이어지고 결정 조회 단계가 없다. 수정 후에는 세션 시작 훅이 활성 기각 목록을 주입하고, 처방이 기각 축에 걸리면 착수하지 않고 재개 조건을 대표 판정으로 넘긴다
빈칸은 기억이 아니라 경로에 있었다

어떻게 고쳤나: 기각 사유와 재개 조건을 한 항목에

당장 할 일은 두 가지였습니다. 문의 화면의 버튼은 PR #60으로 제거했고, 8월 19일 처방을 받아 간 다른 세션에는 정정을 다시 보냈습니다. 그다음 기각 목록 문서에 이 축을 활성 기각으로 올렸습니다. 항목 하나에 다섯 가지를 담았습니다. 죽인 것, 대표 원문, 왜 죽었나, 어떻게 재발했나, 재개 조건입니다.

가장 신경 쓴 부분은 금지 범위입니다. “미팅·통화·화상·실시간 채팅·상담 예약 링크를 콜드메일·랜딩·상담봇·제안서·퍼널 어디에도 추가하는 모든 형태”로 적었고, “옵션으로만”, “원하는 고객에게만”, “킥오프 1회만”도 포함한다고 명시했습니다. 에이전트는 금지를 피해 가는 변형을 잘 만들어 냅니다. 변형까지 이름을 붙여 두지 않으면 같은 축이 “선택 사항”이라는 옷을 입고 다시 들어옵니다.

재개 조건도 좁혔습니다. 대표가 명시적으로 “미팅 하겠다”고 말할 때만입니다. 전환율 저조, 경쟁사 벤치마크, 외부 전문가 처방은 전부 재개 사유가 아니라고 적었습니다. 두 번의 재발이 모두 바로 그 논리로 들어왔기 때문입니다. push 차단 훅도 같은 형식으로 기각 목록에 올렸고, 재개 조건은 대표가 직접 “main push 막아줘”라고 요구할 때만으로 정했습니다.

코드 비교. 수정 전에는 작업 기록이 진행 문서·결정 기록·메모리 세 가지로 하기로 한 것만 저장하고 기각 판정과 재개 조건을 담을 자리가 없다. 수정 후 기각 항목 형식은 축, 기각 날짜, 사유인 대표 원문 요지, 재개조건, 근거 포인터를 한 줄에 담는다
하기로 한 것만 적으면 하지 않기로 한 것이 사라진다

같은 실수를 어떻게 막나: 세션이 열릴 때마다 주입

문서를 만드는 것만으로는 첫 가설과 같은 함정에 빠집니다. 기록은 있었는데 안 열어서 재발했으니까요. 그래서 열기를 사람이나 에이전트의 성실성에 맡기지 않았습니다. 세션이 열리는 순간 실행되는 훅이 기각 목록의 활성 기각 항목을 매 세션 창에 강제로 주입합니다. 에이전트가 무엇을 하든 첫 화면에 “다시 제안 금지” 목록이 이미 들어와 있습니다.

적는 시점도 규칙으로 묶었습니다. 대표가 “그거 아니라고 했잖아”, “되돌려”, “기각”이라고 말한 그 턴에 바로 추가합니다. 세션을 닫을 때 정리하는 절차까지 미루지 않습니다. 세션이 중간에 죽으면 그 판정이 통째로 사라지는 일이 이 문서가 생긴 이유였기 때문입니다. 재개 조건이 충족돼 기각을 풀 때도 항목을 지우지 않고 해제 이력으로 옮깁니다.

이 장치는 개발 작업 밖에서도 쓰입니다. 2026년 10월 7일 한 홈페이지 재견적에서, 적대적 관점으로 검토한 패널이 개선 제안 15건을 냈습니다. 그중 알림톡, 지사별 알림, 2단계 인증, 접근성 항목은 고객이 요구한 것이 아니어서 범위에서 뺐고, 같은 기각 목록에 사유와 함께 올렸습니다. 다음 세션이 이 고객 작업을 열 때 “이것도 넣으면 좋겠다”는 제안이 다시 올라오지 않게 하려는 장치입니다. 그 견적이 어떻게 정해졌는지는 견적 할인 요청 23%, 범위를 줄이는 답이 틀렸던 이유에 따로 정리했습니다.

아직 못 푼 것은 무엇인가?

이 장치가 재발을 얼마나 줄였는지는 아직 숫자로 말할 수 없습니다. 이 글의 자료에는 장치 도입 뒤의 재발 횟수가 측정돼 있지 않습니다. 주입은 에이전트가 목록을 보게 만들 뿐, 새 제안이 목록의 어느 항목과 같은 축인지 판단하는 일은 여전히 에이전트 몫입니다. “상담 예약”과 “무료 진단 콜”처럼 말이 다른 같은 축을 놓칠 여지는 남아 있습니다.

목록이 길어지는 문제도 있습니다. 매 세션 주입하는 항목이 늘면 정작 중요한 항목이 묻힐 수 있습니다. 그래서 항목마다 재개 조건을 반드시 적게 했습니다. 조건이 충족되면 활성 기각에서 내려 해제 이력으로 옮기고, 주입 목록은 살아 있는 금지만 남깁니다.

정리하면 이렇습니다. AI 에이전트가 같은 실수를 되풀이할 때 첫 반응은 대개 “기억을 더 남기자”입니다. 저희 경우 기억은 이미 있었고, 빠진 것은 하지 않기로 한 판정을 담는 자리와 그 판정을 새 제안 앞에 들이미는 경로였습니다. 맥락을 세션 너머로 남기는 전체 구조는 Claude Code 맥락 유지 구조에 따로 정리했습니다. 저희는 상담도 지금처럼 글로 받습니다. 비슷한 자동화 운영 문제가 있다면 상담 화면에 상황을 적어 주시면 서면으로 답을 드립니다.

같은 방식으로 우리 업무를 재보고 싶다면

이 글에 쓴 계측·판정·자동화는 저희가 실제로 매일 돌리는 방식 그대로입니다. 어떤 작업을 자동화할 수 있고 어디를 사람이 잡아야 하는지, 현재 업무 흐름을 놓고 같이 짚어 드립니다.

새 글이 올라오면 알려 드립니다

이런 계측 기록을 새로 쓰면 메일로 한 통 보냅니다. 광고나 판촉은 보내지 않습니다.