우리 팀은 사내 서비스를 개발하고 인프라/보안 솔루션을 운영한다. 그러다 보니 들어오는 요청의 종류가 다양하다. 패스워드를 잊어버렸으니 초기화해달라는 접근권한 건도 있고 이 화면이 불편하니 이렇게 바꿔달라는 서비스 개선 요구사항도 온다.
문제는 그게 들어오는 방식이었다. 전화로 오고 채팅으로 오고 회의 끝나고 나가면서 말로도 온다. 시스템 담당자가 아닌 사람에게 가는 경우도 많아서 받은 사람이 담당자를 찾아 다시 넘기는 일이 생긴다.
처리하고 나면 아무것도 남지 않는다
건당 몇 분이면 끝나는 일이지만 그게 하루에 몇 번씩 쌓이면 시간은 만만치 않게 들어간다. 시간도 시간이지만 너무 쌓이면 요청을 받고도 잊어버리는 일이 생긴다.
전화나 구두 요청으로 받아서 처리하고 끊으면 그걸로 끝이라 어디에도 기록이 없고 실적을 정리할 때가되면 내 시간은 분명히 들어갔는데 성과로 내보일 만한 지표에는 잡히지 않는다.
요청한 쪽도 답답하기는 마찬가지다. 어제 부탁한 게 처리가 됐는지, 어떤 담당자가 해결하고 있는지 알 방법이 없으니 결국 다시 연락이 온다.
그중에 매달 똑같이 반복되는 요청이 있다. 그때마다 사람이 붙어서 답하다 보니 한 번 정리해두면 될 것을 매번 새로 설명하는 셈이다.
요구사항은 내가 가장 잘 알고 있다
일단 만들어보기로 했다. 거창한 계획은 없었고 내 불편을 없애고 싶은 마음과 마침 바이브코딩을 제대로 해보고 싶던 참이 겹쳤을 뿐이다.
이렇게 시작한 것이 흔히 ITSM이라 부르는 사내 IT 요청을 티켓으로 관리하는 도구다. 사용자가 요청을 접수한 뒤 담당자와 처리 상태를 확인할 수 있고 운영자에게는 접수량과 처리 이력이 남는다.
평소 회사에서 진행하는 프로젝트라면 먼저 하는 일은 현업에 요구사항을 받으러 다니는 것인데 이번에는 갈 필요가 없었다. 그 요청을 매일 받는 사람이 나였으니 어떤 건이 얼마나 들어오는지도 알고 있었다.
그래서 필요한 게 처음부터 세 가지로 정리돼 있었다. 처리한 건이 숫자로 남아야 했고 요청한 사람이 자기 건이 지금 어디까지 갔고 누가 들고 있는지 볼 수 있어야 했다. 반복되는 요청은 지식베이스로 쌓여 접수 전에 스스로 해결되게 하고 싶었다. 이 세 가지를 직접 구현해보려고 바이브코딩을 시작했다.
이걸 정하고 개발에 착수하기까지는 오랜 시간이 걸리지 않았다. Node.js와 Express로 API를 만들고 PostgreSQL에 티켓과 처리 이력을 남기는 구조로 잡았다. 화면과 서버, 데이터베이스를 Docker Compose로 묶은 뒤 요청 접수부터 담당자 배정과 상태 전이, 권한과 감사 로그까지 차례로 붙였다.
바이브코딩은 한 번에 완성된 코드를 받는 일이 아니었다.
기능 하나를 자연어로 AI에게 설명해 구현시킨 다음 요청자와 담당자 계정으로 직접 써봤다. 흐름이 어색하거나 빠진 조건이 보이면 요구사항부터 고쳐 다시 구현했고 이 과정을 짧게 반복하면서 화면과 API를 맞췄다.
만드는 데 오래 걸리지 않았고 완성도도 생각보다 나왔다. 처음에는 AI가 코드를 잘 뽑아서 그런 줄 알았는데 지금 돌아보면 요구사항을 두고 헤맬 일이 없었던 게 컸다. 무엇을 만들지가 처음부터 분명하면 AI에게 시킬 말도 정확해지고 나온 결과가 맞는지 바로 판단할 수 있다.
무엇을 만들지가 흐릿한 상태에서는 그럴듯한 화면이 빨리 나오는 만큼 방향도 빨리 어긋난다.
사이드 프로젝트에서 공식 배포로
“이거 전 IT부서들이 현업 요구사항 받는 창구로 확장해보는 건 어때?”
팀장님의 한마디로 인프라/보안 파트에서 쓰려던 도구가 전 직원이 IT 요청을 넣는 창구로 성격이 바뀌었다.

동작하는 화면은 금방 만들었지만 실제 업무에 넣으려니 다른 질문이 생겼다. 사용자가 잘못 접수한 요청을 지워도 되는지, 다른 담당자에게 넘긴 티켓을 언제 기존 담당자의 화면에서 빼야 하는지, 무엇을 긴급한 요청으로 볼지까지 정해야 했다.
잘못 올린 요청은 삭제 대신 취소 상태로 남겼는데 요청과 처리 이력을 지워버리면 운영 지표도 감사 기록도 함께 사라지기 때문이다.
담당자 이관은 상대가 수락한 뒤에 완료되도록 만들었다. 넘기는 즉시 기존 담당자의 목록에서 사라지게 하면 새 담당자가 자리를 비웠거나 이관 사실을 확인하지 못한 동안 티켓을 맡은 사람이 아무도 없는 상태가 생길 수 있었다.
사용자가 긴급도를 직접 고르는 방식도 업무 영향 범위를 묻는 방식으로 바꿨다. 모두가 자기 요청을 긴급으로 표시하면 우선순위는 다시 사람이 정해야 해서 몇 명의 업무가 멈추는지, 대체 수단이 있는지를 판단 근거로 삼았다.
이런 조건은 코드를 들여다본다고 나오지 않는다. 요청이 어떻게 들어와서 누구를 거쳐 어디서 끝나는지가 머릿속에 그려져 있어야 나온다. 나는 그 업무를 매일 하던 사람이라 어디서 병목이 생기는지 알았고 그래서 AI에게 넘길 조건도 수월하게 만들 수 있었다.
반대로 이 로직이 없는 상태에서 구현부터 들어갔다면 요청자와 담당자, 관리자 계정으로 접수부터 종결까지 통과시켜봐도 무엇이 잘못된 건지 알아채지 못했을 것이다. 단위 기능만 확인할 때 보이지 않던 알림과 권한 문제를 이 과정에서 잡을 수 있었던 것도 원래 흐름을 알고 있었기 때문이다.
화면이 작동하는 것과 현업에 배포해 프로덕션 환경에서 운영하는 것은 별개였다. 여기서 현업과 IT 부서가 하는 바이브코딩의 차이가 드러난다.
화면은 정상 경로만 통과해도 완성된 것처럼 착각하게 만들지만 실제 업무에서는 사용자가 잘못 접수했을 때 무엇이 남는지, 담당자가 자리를 비웠을 때 그 건의 주인이 누구인지, 중간에 사람이 바뀌면 어디까지 넘어가는지까지 정해져야 굴러간다. 어떤 서비스를 만들든 여기서 갈린다.
업무 흐름을 아는 사람이 결과를 검수해야 하는 이유도 여기에 있었다. 그래서 지금은 MVP가 완성되었다고 해도 어긋나는 경로부터 밟아보고 그 과정이 매끄럽지 않으면 아직 배포할 단계가 아니라고 판단한다.
기능이 돌아가도 실제 운영 배포는 신중해야 한다
요구사항은 알고 있었지만 코드의 안전성까지 판단할 수 있는 것은 아니었다.
바이브코딩으로 만든 것이라 코드를 한 줄씩 읽어서 취약한 부분을 잡아낼 수준이 나는 아니다. 사내망에서 나 혼자 쓸 때는 그래도 됐는데 쓰는 사람이 전사로 늘어나면 얘기가 달라진다.
나는 정보보호 담당자이기 때문에 보안성 검토를 가장 먼저 진행했고 1차 코드 검토를 AI에게 맡겼다. 다만 그냥 봐달라고 하면 인증이 걸려 있는지 입력값 검증이 있는지를 훑은 목록이 나오고 그 목록은 대체로 통과로 채워진다. 당시 코드는 눈에 띄는 문법 오류가 없었고 기능도 제대로 돌아갔기 때문이다.
그래서 기준을 밖에서 가져왔다. 내가 이미 알고 있는 것과 널리 알려진 진단법을 활용해 AI에게 시큐어코딩 가이드를 줬다. 항목마다 이 코드가 걸리는지를 보게 해서 1차로 훑었다. 여기서 걸린 것들을 고치고 나면 남는 게 생기는데 그때부터는 시키는 방식을 바꿨다. 이 설정이면 공격자가 무엇을 할 수 있는지를 물었다. 취약점마다 문제 코드와 피해 범위까지 한 세트로 내놓게 했다.
시크릿 설정이 눈에 걸렸다. JWT_SECRET || 'default_secret' 형태로 환경변수가 없으면 기본값을 쓰게 되어 있었다. 그 기본값을 아는 사람은 관리자 토큰을 위조할 수 있고 그러면 계정을 가져가는 데다 감사 로그까지 우회한다. 누가 무엇을 했는지 남기려고 만든 시스템에서 그 기록을 못 믿게 되는 셈이다.
app.use(cors())로 모든 출처의 교차 출처 요청을 허용해둔 것도 있었다. 사내 서비스에서 그렇게 열어둘 이유가 없어서 실제 서비스 출처만 남겼다. 나머지는 파일 업로드에 타입 검증이 없었고 개발 중에 만들었다가 안 쓰게 된 로그인 라우트가 인증 없이 남아 있었다.
기본 시크릿은 없애고 환경변수가 비어 있으면 서버가 시작되지 않도록 바꿨다. 파일 업로드에는 MIME 타입과 확장자 검증을 함께 넣었고 인증 없이 남아 있던 로그인 라우트는 제거했다. 수정 후에는 요청자와 담당자, 관리자 권한으로 접수부터 종결까지 다시 통과시키며 기존 기능이 유지되는지도 확인했다.
모양은 달랐지만 공통점이 있었다. 전부 기능 확인 단계에서는 아무 신호를 주지 않고 운영 환경에 들어가면서 공격면이 된다. CORS를 전부 열어두면 개발 중에 막힐 일이 없고 시크릿에 기본값을 두면 환경변수를 깜빡해도 서버가 뜬다. 버그가 아니라서 테스트를 통과하고 요구사항을 안다고 잡히지도 않는다.
코드만 볼 일도 아니었다. 사내망에서 혼자 쓸 때는 화면과 서버와 데이터베이스가 한 묶음으로 떠 있어도 됐는데 전사로 나가려니 그대로 둘 수 없었다. Web과 WAS와 DB를 계층별로 분리하고 외부 요청은 리버스 프록시가 받은 뒤 WAS로 전달하도록 바꿨다. 애플리케이션 서버와 데이터베이스를 외부에 직접 노출하지 않는 구조다.
이쪽은 사내 인프라 기준을 따라야 하는 영역이라 코드 고치는 것보다 오히려 손이 더 갔다.
실제로 쓰이는 프로덕트를 만들어낸 소감
사내에 배포한 지 약 한 분기가 지났다.
이제 요청의 대부분은 ITSM의 티켓으로 들어온다. 몇 건이 접수됐고 얼마나 처리됐는지가 숫자로 남고 요청한 사람이 자기 건의 상태를 직접 확인한다.
결과적으로는 전화로 업무요청을 받는 일이 줄어들었다.
지금은 AI가 지식베이스를 자동으로 만들어주게 하는 걸 붙이고 있다. 매달 어떤 요청이 얼마나 들어왔는지를 보고 반복되는 것부터 AI가 매뉴얼화한 문서로 만들어서 접수 전에 사용자가 스스로 해결하게 하는 게 목표다. 여기까지 가야 반복되는 업무를 크게 줄일 수 있다고 생각한다.
만들어보고 남은 생각은 이렇다. 바이브코딩이 바꾼 건 만드는 속도지 무엇을 만들지를 정하는 일이 아니다. 이번에 빨랐던 건 그 앞단이 이미 내 안에 있었기 때문이고 그게 없는 상태였다면 AI가 아무리 빨리 코드를 뽑아도 같은 자리를 맴돌았을 것이다.

실무에 대입해보면 이제는 현업이 원하는 걸 얼마나 빨리 돌아가는 형태로 만들어 보여주느냐가 중요해진 것 같다.
예전에는 요구사항을 문서로 넘기고 몇 달 뒤에 결과를 받았으니 중간에 몇 번은 어긋났다. 지금은 며칠 만에 MVP를 세워놓고 써보면서 고치니 업무에 서비스가 녹아드는 속도가 빨라졌다고 느낀다.


