추가 비용 없이 작은 수정 요청을 코드 반영까지 자동화해보니, 병목은 AI보다 운영 흐름에 있었다

AI Inspector를 만들며 배운 운영 자동화 회고

처음 출발점은 거창하지 않았습니다.

오히려 꽤 현실적인 문제에서 시작했습니다.

작은 수정 요청은 자주 들어오는데, 그걸 처리하기 위해 매번 사람의 손이 너무 많이 들어갔습니다.

“여기 문구만 바꿔주세요.”

“이 버튼 텍스트만 조금 수정해주세요.”

“이 설명을 더 짧게 바꿔주세요.”

하나씩 보면 정말 작은 요청입니다.

그런데 실제 처리 과정은 전혀 작지 않았습니다.

어떤 화면인지 다시 찾고,

어떤 요소를 말하는지 확인하고,

요청 의도를 코드 문맥으로 다시 해석하고,

브랜치를 만들고,

PR을 올리고,

프리뷰 링크를 전달해야 했습니다.

결국 작은 수정일수록 코드 수정 자체보다 그 앞뒤의 커뮤니케이션과 운영 비용이 더 컸습니다.

그래서 질문을 바꿨습니다.

“요청을 저장하는 데서 끝내지 말고, 실제 코드 반영까지 한 흐름으로 묶을 수 없을까?”

그리고 이걸 가능한 한 추가 비용 없이 구현해보고 싶었습니다.

그렇게 만든 것이 AI Inspector였습니다.


진짜 줄이고 싶었던 건 요청 작성 비용이 아니라 요청 이후의 반복 작업이었습니다

처음에는 어드민 화면에서 요소를 선택하고 자연어로 수정 요청을 남길 수 있게 하면 충분할 거라고 생각했습니다.

하지만 금방 한계가 보였습니다.

입력창을 만든다고 운영 비용이 줄어드는 건 아니었습니다.

요청이 저장되어도 결국 누군가는 다시 그 요청을 읽고, 해석하고, 코드에 반영해야 합니다.

즉 요청 입력 UI는 편해질 수 있어도, 실제 운영 병목은 그대로 남아 있었습니다.

그래서 필요한 것은 메모장 같은 기능이 아니었습니다.

필요한 것은 요청을 실제 작업 단위로 바꾸는 파이프라인이었습니다.

제가 원했던 흐름은 명확했습니다.

어드민이 화면에서 요소를 선택하고,

자연어로 수정 요청을 남기면,

요청이 큐에 안전하게 저장되고,

워커가 이를 가져가 실제 코드 수정을 시도하고,

변경이 생기면 PR을 만들고 프리뷰 링크를 수집한 뒤,

최종 결과를 다시 운영자에게 알려주는 흐름이었습니다.

이 시점부터 AI Inspector의 핵심은 “AI가 글을 잘 쓰는가”가 아니라,

요청을 코드 변경까지 이어지는 운영 단위로 바꾸는 구조가 되었습니다.


작은 요청이 어려운 이유는 문장이 짧아서가 아니라 맥락이 사라지기 때문이었습니다

운영 요청이 까다로운 이유는 길어서가 아닙니다.

오히려 짧아서 더 어렵습니다.

“여기 문구 바꿔주세요”라는 요청은 사람에게는 자연스럽지만, 시스템 입장에서는 너무 많은 맥락이 빠져 있습니다.

어떤 페이지인지,

어떤 요소인지,

비슷한 문구가 여러 곳에 있는 건 아닌지,

정말 텍스트만 바꾸면 되는지

전부 다시 해석해야 합니다.

그래서 AI Inspector에서는 자연어만 받지 않았습니다.

요소 선택과 함께 selector, tagName, className, textSnippet, rect 같은 DOM 맥락도 같이 저장했습니다.

이게 꽤 중요했습니다.

이 정보가 있어야 워커가 요청을 처리할 때도 단서를 얻을 수 있고, 사람이 다시 task를 보더라도 “이 요청은 정확히 이 요소를 보고 남긴 거구나”를 바로 이해할 수 있었습니다.

겉으로 보면 단순한 선택 UX처럼 보이지만, 실제로는

요청이 중간에서 맥락을 잃지 않게 만드는 장치였습니다.


이번에도 답은 실시간 처리보다 큐였습니다

이런 자동화를 HTTP 요청 하나 안에서 끝내려 하면 구조가 쉽게 흔들립니다.

권한 검증이 필요하고,

입력 검증이 필요하고,

AI 수정이 들어가고,

Git 작업, PR 생성, 프리뷰 확인, 알림까지 붙습니다.

이걸 전부 한 번의 요청에서 처리하려는 순간 실패 지점이 너무 많아지고, 어디서 끊겼는지도 알기 어려워집니다.

그래서 이번에도 큐가 자연스러운 답이 됐습니다.

서버는 요청을 검증해 queued 상태로 저장하고,

워커는 이를 가져가 processing으로 바꾸고,

성공하면 completed, 실패하면 failed로 기록하는 방식입니다.

이 구조의 장점은 분명했습니다.

누가 어떤 요청을 넣었는지 남고,

어디까지 처리됐는지 상태를 볼 수 있고,

실패해도 다시 시도할 수 있고,

무엇보다 요청과 실행을 분리할 수 있었습니다.

결국 다시 확인한 것은 이것이었습니다.

운영 자동화에서 중요한 건 AI 호출 자체보다, 실패 가능한 작업을 상태 있는 흐름으로 바꾸는 일이었습니다.


자동화에서 가장 위험한 건 가짜 성공이었습니다

이런 시스템에서 가장 위험한 상황은 자동화가 돌아간 것처럼 보이는데 실제로는 아무 코드도 바뀌지 않은 경우입니다.

예를 들어 task 메타 파일만 생성되고 실제 소스 코드는 바뀌지 않았는데, PR만 열려버리는 상황입니다.

이런 경험이 몇 번만 반복되면 도구는 바로 신뢰를 잃습니다.

그래서 저는 성공 기준을 꽤 강하게 잡았습니다.

변경 파일이 없으면 실패,

task 기록 파일만 바뀐 경우도 실패,

즉 실제 코드 변경이 없는 PR은 만들지 않도록 했습니다.

자동화 시스템은 성공을 화려하게 보이게 만드는 것보다, 실패를 정직하게 다루는 편이 훨씬 중요했습니다.

차라리 명확하게 실패를 남기는 편이, 성공처럼 보이는 빈 결과를 만드는 것보다 낫다고 판단했습니다.


PR보다 더 중요했던 건 “어디서 확인하면 되는가”였습니다

처음에는 PR만 열리면 끝이라고 생각했습니다.

그런데 실제 운영에서는 그렇지 않았습니다.

요청자나 QA 입장에서는 “처리됐다”는 말보다 **“어디서 확인하면 되는가”**가 더 중요했습니다.

그래서 PR 생성으로 끝내지 않고, 프리뷰 URL까지 함께 수집하고 저장하도록 만들었습니다.

이 과정도 단순하지 않았습니다.

어떤 경우에는 Vercel API가 더 빠르고, 어떤 경우에는 GitHub status가 더 정확했고, 또 어떤 경우에는 PR comment에 남은 링크가 가장 현실적이었습니다.

그래서 한 경로만 믿지 않고 여러 경로를 순서대로 확인하게 했습니다.

구현할 때는 자잘해 보여도 이런 부분이 실제 운영 경험을 크게 바꿨습니다.

요청자는 “처리 완료”보다 “여기서 확인해보세요”라는 링크 하나를 더 원했기 때문입니다.


알림을 붙이면서 본질과 부가 기능을 더 분리하게 됐습니다

작업이 끝나면 Discord로 알려주는 흐름도 붙였습니다.

처음에는 단순한 웹훅 전송 정도로 생각했는데, 여기서도 중요한 기준이 드러났습니다.

코드 반영과 PR 생성은 성공했는데 Discord 전송만 실패하면, 이 task는 실패일까요?

제 기준은 아니었습니다.

핵심은 코드가 반영되고 확인 가능한 상태가 되었는지였습니다.

알림은 중요하지만 부가 기능이었습니다.

그래서 먼저 task를 completed로 기록하고, Discord 전송은 별도 예외 처리로 분리했습니다.

알림이 실패해도 task 전체는 성공으로 유지하고 notificationError만 남기도록 설계했습니다.

이런 분리가 쌓일수록 시스템은 더 단단해졌습니다.

무엇이 본질적인 성공인지 경계를 명확히 정하는 일이 운영 자동화에서는 특히 중요했습니다.


로컬 워커로 시작한 것도 꽤 좋은 선택이었습니다

모든 자동화를 처음부터 거대한 클라우드 구조로 만들 필요는 없었습니다.

이번에도 로컬 워커를 공식 흐름의 일부로 두었습니다.

이유는 단순했습니다.

요청을 넣고 바로 처리 결과를 확인하기 쉬웠고,

디버깅과 재시도가 빨랐고,

구조를 검증하기에 비용이 가장 낮았습니다.

처음부터 GitHub Actions나 서버 환경만 전제로 잡으면 구조 실험 속도가 오히려 늦어질 수 있습니다.

반대로 로컬 워커로 시작하면 먼저 작동하는 가장 작은 루프를 만든 뒤, 필요할 때 점진적으로 운영 환경으로 옮길 수 있습니다.

이 과정을 거치며 다시 확신한 것은,

운영 구조는 처음부터 완벽한 인프라로 시작하는 것보다 작동하는 최소 루프를 먼저 만드는 편이 훨씬 낫다는 점이었습니다.


결국 남은 것은 AI 기능보다 운영 구조였습니다

겉으로 보면 AI Inspector는 “화면에서 선택하고 자연어로 수정 요청을 남기는 AI 도구”처럼 보일 수 있습니다.

그런데 만들고 나서 제게 더 크게 남은 것은 AI 기능 자체보다 운영 구조에 대한 감각이었습니다.

자연어 요청은 그대로 두면 운영 단위가 되지 않는다는 것,

요청에는 반드시 맥락이 함께 붙어야 한다는 것,

요청 처리와 실행은 분리되어야 한다는 것,

상태 없는 자동화는 운영에서 쉽게 흔들린다는 것,

성공보다 실패 모델을 먼저 정해야 한다는 것,

그리고 PR, 프리뷰, 알림 같은 주변 요소가 실제 만족도를 크게 좌우한다는 것을 다시 확인했습니다.

즉 AI를 붙인다는 건 모델 호출 한 번 추가하는 문제가 아니었습니다.

실제로는 기존 운영 흐름을 다시 설계하는 일에 더 가까웠습니다.


마무리

이번 작업을 하며 다시 느낀 것은, 운영에서 정말 비싼 것은 큰 기능 개발만이 아니라는 점이었습니다.

오히려 작고 애매하고 반복되는 요청들이 생각보다 많은 시간을 잡아먹고 있었습니다.

AI Inspector는 그런 요청을 완전히 없애는 도구는 아닙니다.

하지만 적어도 요청을 잃어버리지 않고, 맥락을 함께 저장하고, 실제 코드 변경으로 이어지게 만들고, PR과 프리뷰까지 한 흐름으로 묶을 수 있는 구조는 만들 수 있었습니다.

처음에는 비용을 거의 늘리지 않으면서 자동화를 붙여보자는 생각에서 시작했지만, 끝나고 보니 더 크게 남은 것은 하나의 기능이 아니라 재사용 가능한 운영 구조였습니다.

이제 비슷한 문제를 보면 단순히 “이걸 더 빨리 처리할 수 없을까?”보다

“이 요청을 큐에 올릴 수 있나?”,

“맥락을 구조화할 수 있나?”,

“실행과 알림을 분리할 수 있나?”

같은 질문을 먼저 하게 됩니다.

이번에도 결국 얻은 건 기능 하나보다 흐름이었습니다.

그리고 운영 자동화에서 더 중요한 것은 AI 자체보다, 그 AI가 실패해도 흔들리지 않도록 만드는 구조라는 점을 더 분명하게 배웠습니다.

원하시면 이걸 바로 브런치/벨로그 발행용 톤으로 더 다듬은 버전으로 바꿔드릴게요.