AI 코딩 에이전트에게 운영 서버를 맡겼을 때 실제로 버틴 안전장치
AI 에이전트가 하룻밤에 운영 사이트와 앱 API 8개를 옮겼다. 에이전트가 낸 실수, 그것을 잡아낸 검사, 사람에게 남긴 일을 정리했다.
AI 코딩 에이전트는 이제 실제 운영 작업을 할 수 있는 수준에 왔습니다. 서버를 살펴보고, 이전 스크립트를 쓰고, 실행하는 데까지요. 그래서 질문도 바뀌었습니다. "운영 서버를 만질 수 있느냐"가 아니라 "무엇이 갖춰져 있어야 맡겨도 되느냐"입니다.
2026년 11월 8일, AI 코딩 에이전트(터미널에서 동작하는 Claude Code)가 tlaboratory.net을 WordPress에서 정적 사이트로 옮기고, 모바일 앱 API 폴더 8개를 /api/v1/ 아래로 옮겼습니다. 운영 서버를 바꾸는 단계마다 사람이 승인했습니다. 이 사이트에는 설치된 앱 16개가 연결돼 있고 스토어 등록 24건이 개인정보 페이지를 가리키고 있었지만, 한 번도 멈추지 않았습니다. 이 글은 AI가 똑똑했다는 이야기가 아닙니다. 안전장치에 대한 이야기이고, 그 안전장치가 에이전트의 실수를 다섯 번 잡아낸 이야기입니다.
구성 한눈에 보기
| 안전장치 | 내용 |
|---|---|
| 읽기 전용 탐색부터 | FTP 목록, WordPress REST API, curl, 그리고 앱 20개의 소스에서 서버 URL을 모두 찾아낸 서브 에이전트 |
| 문서화된 계획을 승인받은 뒤 수정 | 단계, 롤백, 건드리면 안 되는 것(광고 설정 API, 스마트링크) |
| 첫 쓰기 전에 전체 백업 | API 폴더·스토어용 페이지·.htaccess 1,067개 파일(19MB) |
| 응답 기준값 | API 엔드포인트 95개의 상태·헤더·본문 해시 |
| 기본값은 dry-run | 배포 스크립트는 할 일을 보여 주기만 하고, 실행하려면 --go가 필요 |
| 허용 목록과 "남의 파일" 정지 | 블로그 밖 경로를 거부하고, 자신이 올리지 않은 파일은 덮어쓰지 않음 |
| 운영 서버 위의 일회용 격리 폴더 | 리라이트 규칙은 임시 하위 폴더에서 시험하고 finally에서 삭제 |
| 단계적 전환 | API 1개 먼저, 나머지 7개는 23분 뒤 |
| 사람만 하는 일 | 문안 승인, 전환 시각, CDN 캐시 비우기, Search Console, 실기기 확인 |
어느 것도 AI 전용은 아닙니다. 신중한 신입 동료에게 기대할 만한 것들입니다. 다른 점은 에이전트가 이 도구들 대부분을 위험한 작업 전에 직접 만들었고, 그 검사를 통과해야만 다음 단계로 갈 수 있었다는 것입니다.
잡아낸 실수 다섯 가지
1. 코드 블록의 공백이 조용히 바뀌었다 (글 31개)
에이전트는 WordPress 글 67개를 Markdown으로 변환했습니다. 변환기는 줄 끝 공백을 지우고 연속된 빈 줄을 합쳤는데, 이것을 코드 블록 안에도 적용하고 있었습니다. 화면에서는 아무 이상이 없어 보였습니다. 코드 블록 479개를 WordPress 원문과 바이트 단위로 비교하는 검증 스크립트가 이렇게 알려 줬습니다.
MISMATCH 1190 16 16
'using System;\nusing System.Collections.Generic;\n\n\nusing System.Linq;...'
'using System;\nusing System.Collections.Generic;\n\nusing System.Linq;...'
...
code blocks 479 posts mismatched 31
정리 작업을 코드 펜스 바깥으로만 제한하고 다시 돌리자 불일치가 0이 됐습니다. 검증 스크립트는 저장소에 남겨 뒀습니다.
2. 리라이트 규칙의 공백 이중 이스케이프
규칙 생성기가 공백을 \가 아니라 \\로 바꾸고 있었습니다. 스토어에 등록된 URL을 모두 생성된 규칙에 대 보는 시뮬레이션에서 드러났고, 시뮬레이터 자체도 그 패턴에서 멈췄습니다. 그대로 나갔다면 앱 하나의 iOS 지원 URL이 매칭되지 않았을 겁니다. 어떤 설정 파서도 다르게 읽을 수 없는 \s로 바꿨습니다.
3. 표기 변형 한쪽에 규칙이 없었다
한 스토어 등록은 페이지를 US Lottery Infomation(대문자·공백)으로 등록했고, WordPress는 us-lottery-infomation을 쓰고 있었습니다. 생성기는 이 둘을 같은 페이지로 보고 한쪽 형태에만 규칙을 만들었습니다. WordPress 옛 URL 196개 전부에 대해 "처음 매칭되는 규칙이 올바른 대상으로 가는가"를 확인하는 검사가 이 빈틈을 찾았습니다. 공백 자리를 [\s-]로 바꿔 해결했습니다.
4. 스스로 모순된 프리뷰 빌드
로컬 프리뷰는 모든 페이지를 noindex로 만들면서, 그 페이지들이 담긴 사이트맵도 함께 출력하고 있었습니다. 콘텐츠 검사기가 "noindex인데 사이트맵에 있음" 38건을 지적했습니다. 로컬에서는 무해하지만, 검색엔진이 싫어하는 신호 그 자체입니다. 이제 프리뷰 빌드는 사이트맵을 만들지 않습니다.
5. 주석을 코드로 착각
PHP 파일 101개의 include를 바꾸던 중, lib/AppConfig.php 언급이 남아 있다며 안전 검사가 멈췄습니다. 실제로는 주석이었습니다. 안전장치가 지나치게 엄격했던 경우인데, 실패하려면 이쪽으로 실패하는 게 맞습니다. 검사 대상을 실제 require/include 문으로 좁혔습니다.
다섯 가지의 공통점은 이것입니다. 실수한 것도 에이전트였고, 그 실수를 서버보다 먼저 잡은 것도 에이전트가 만든 검사였습니다. 검증 도구를 먼저 만들게 하는 이유가 여기에 있습니다.
에이전트의 권한 시스템이 한 번 거절했다
작업 중에 에이전트는 설정을 비교하려고 다른 프로젝트의 .env 파일을 읽으려 했습니다. 그 파일에는 DB 비밀번호가 들어 있었습니다. 도구의 권한 계층이 이를 자격 증명 노출로 보고 거절했고, 에이전트는 방법을 바꿨습니다. DB 설정 파일 10개를 읽는 대신 해시로 비교해서, "이 파일들이 같은가"라는 실제 질문에 비밀을 출력하지 않고 답한 것입니다.
5ae45e5d7d2f3e0c15e99acf1b40d61c Converters/lib/AppConfig.php
5ae45e5d7d2f3e0c15e99acf1b40d61c DreamApp/lib/AppConfig.php
... (10개 모두 동일)
운영 서버를 위험하게 하지 않고 운영 서버에서 시험하기
공유 호스팅에는 스테이징 서버가 없습니다. 그래서 에이전트는 rwtest.py를 만들었습니다. /_rwtest_YYYYMMDD/를 만들고(이미 있으면 중단), 그 폴더 안에서만 동작하는 .htaccess를 올려 요청을 보낸 뒤 finally에서 모두 지웁니다. 네 번 실행했습니다. URL 매칭 동작 확인에서 15/15, 생성한 루트 파일 전체를 격리 폴더 기준으로 바꿔 넣은 확인에서 12/12를 세 번 통과했습니다. 마지막 확인 덕분에, 도메인의 앱 API를 모두 멈추게 할 수 있는 문법 오류가 없다는 것을 운영 루트에 넣기 전에 알 수 있었습니다.
사람이 꼭 필요했던 곳
- 법적 무게가 있는 문안. 개인정보처리방침은 앱 16개를 다룹니다. 에이전트는 각 앱의 실제 구성, 즉
pubspec.yaml의 SDK와Info.plist·AndroidManifest.xml의 권한으로 문안을 만들었습니다. 그래도 업로드 전에는 사람의 승인이 필요했습니다. 업로드하는 순간 스토어 등록 24건이 보여 주는 내용이 바뀌기 때문입니다. - 시점과 영향 범위. 전환 시각, 그리고 API 하나를 먼저 단독으로 옮기기로 한 판단.
- 관리 화면에서만 가능한 일. CDN 캐시 비우기, Search Console 사이트맵 등록, 휴대폰으로 앱 열어 보기.
- 개인 정보. 에이전트가 처음 만든 소개 페이지는 운영자를 국적과 거주지로 소개했습니다. 사람이 빼 달라고 하자, 에이전트는 같은 정보가 저자 박스, 홈, 개인정보처리방침, 구조화 데이터에도 있다는 것을 찾아 함께 지웠습니다.
정리
- 변경보다 먼저 기준값과 검증 스크립트를 에이전트에게 만들게 한다
- 모든 스크립트의 기본값은 dry-run, 위험한 동작은 명시적인 플래그로
- 스테이징이 없다면 운영 환경 안에 격리 공간을 만든다. 디코딩된 URL 매칭이나 CDN의 API 캐시 같은 운영 환경 특유의 버릇은 거기서만 보인다
- 단계적으로 전환하고 롤백은 명령 한 줄로
- 승인·시점·법적 판단은 사람이 쥔다
작업은 몇 시간 만에 끝났습니다. 그 몇 시간을 맡길 수 있었던 건 안전장치 덕분이고, 그 대부분은 다음 변경에도 그대로 쓸 수 있습니다.
자주 묻는 질문
AI 에이전트가 실수를 했나요?
네, 운영까지 갈 수 있었던 실수가 다섯 번 있었습니다. 글 31개의 코드 블록 공백을 바꿨고, 리라이트 규칙의 공백을 이중 이스케이프했고, URL 표기 변형 하나를 빠뜨렸고, 서로 모순되는 사이트맵을 만들었고, 주석을 코드로 오인했습니다. 모두 에이전트가 변경 전에 직접 만든 검사로 잡았습니다.
사람에게 남겨야 할 일은 무엇인가요?
외부에 영향을 주는 결정입니다. 스토어 등록 24건이 가리키는 개인정보처리방침 문안 승인, 전환 시각, CDN 캐시 비우기, Search Console, 실기기 확인입니다. 운영 서버를 바꾸는 단계마다 에이전트가 먼저 승인을 요청했습니다.
가장 쓸모 있었던 안전장치는요?
변경 전에 저장한 응답 기준값입니다. 이후 모든 단계를 그것과 비교했기 때문에 '괜찮아 보인다'가 판단 기준이 된 적이 없습니다.