코드를 밖으로 안 내보내고, 토큰 비용 0으로 LLM을 개발 워크플로우에 끼워넣기
보안 회사라 코드를 외부로 못 내보내고 토큰 비용도 부담인 제약에서, 사내에 상주하는 로컬 LLM을 설계·구현·리뷰·문서화에 단계별로 들인 과정. GitLab MR 리뷰봇에서 시작한 같은 골격을 경력 DB·이력서 자동화까지 그대로 옮겼다.
보안 제품 코드는 사내 경계 밖으로 한 줄도 못 내보낸다. 이 전제 하나면 시중의 LLM 자동화 도구는 대부분 탈락한다 — diff를 외부 API로 보내는 순간 규정 위반이기 때문이다. 여기에 호출당 토큰 비용이 누적되는 부담이 겹친다. 이 두 제약이 출발점이었다.
모델을 사내에 두면 두 제약이 같이 풀린다
답은 의외로 단순했다. 이미 사내에 상주하는 로컬 LLM을 쓰면 된다. 모델을 로컬에서 돌리니 코드는 경계 밖으로 나가지 않고, 호출당 비용은 0이 된다.
비용 0은 절약 문구가 아니라 설계 전제다. 호출 한 번에 돈이 들면 “이 MR은 리뷰할 가치가 있나”를 매번 따져야 하지만, 0이면 그 판단 자체가 사라진다. 모든 MR에 무차별로 붙일 수 있게 된다.
코어는 이미 있었다. 사내 코드와 문서를 의미 기반으로 검색하는 로컬 RAG 서버(TextRAG)다. diff를 받아 관련 코드를 찾고 리뷰를 생성하는 코드 리뷰 API까지 들어 있었다. 그러니 새로 만들 건 모델이 아니라, 그 API를 개발 워크플로우의 어느 단계에서 언제 부를지 정하는 조립 계층이었다.
패턴 하나로 수렴했다
조립 계층을 만들다 보니 골격이 하나로 모였다. 입력이 무엇이든 흐름은 같다.
입력(diff·경력 데이터) → 사내 RAG로 관련 컨텍스트 검색
→ 로컬 LLM이 정해진 형식으로 생성
→ 정형 출력(MR 코멘트·문서)
가운데 “사내 RAG → 로컬 LLM”은 고정이고, 양 끝의 입력·출력만 용도에 따라 바뀐다. 이 세 단계가 아래 나오는 모든 도구의 공통 골격이 된다.
점진 적용, 그리고 MR 리뷰봇
한 번에 전 단계를 자동화하진 않았다. 설계 → 구현 → 리뷰 → 문서화 순으로 LLM을 들였고, 단계마다 무엇을 넘기고 무엇을 쥘지는 직접 갈랐다. 설계 땐 사내 코드를 검색해 컨텍스트를 모으는 데 썼고, 형식이 반복되는 명세 작성은 LLM에 넘겼다. 도메인 판단은 사람이 그대로 쥐었다.
가장 손에 잡히는 산출물은 GitLab MR 리뷰봇이다. iOS 팀 리뷰 가이드는 “24시간 이내 리뷰”를 원칙으로 두는데, 릴리즈 시즌과 고객 이슈가 겹치면 리뷰 대기가 밀렸다. 봇이 그 빈자리를 메운다.
동작은 단순하다. MR 이벤트를 받아 TextRAG의 코드 리뷰 API를 호출하고, 결과를 MR 코멘트로 단다. 리뷰는 버그·보안·성능·품질·정합성 5축으로 나누되, 의견을 달기 전에 관련 소스 근거부터 제시한다. 근거 없는 지적을 막으려는 순서다.
막힌 건 품질이 아니라 가용성이었다
정작 발목을 잡은 건 리뷰 품질이 아니라 가용성이었다. 임베더가 과부하에 걸리거나 로컬 LLM이 재시작되는 일시 장애가 운영 중에 그대로 노출됐다. 외부 API였다면 그쪽 SLA 뒤에 숨었겠지만, 모델을 밖으로 못 옮기니 가용성까지 내가 책임져야 했다. 로컬로 가져오기로 한 순간 같이 따라온 청구서였다.
두 가지로 막았다. 일시 장애는 재시도로 흡수해 다음 호출에서 회복하게 했고, 코퍼스가 없는 프로젝트는 자동으로 건너뛰게 해 봇이 빈 컨텍스트로 헛리뷰를 다는 걸 막았다. 지금은 iOS 팀 7개 제품 MR에 자동 리뷰가 붙어 있다.
봇은 의견을 달지, 승인을 하지 않는다
선은 분명히 그었다. 봇이 다는 건 의견이고, 머지 판단은 사람이 쥔다. 24시간 원칙을 대체한 게 아니라 보완한다. 보안 코드의 머지를 자동 리뷰에 넘기는 건 위험하니까. 자동화한 건 리뷰 의견을 빠짐없이 즉시 다는 일이고, 사람이 남긴 건 그 의견을 읽고 머지할지 정하는 결정이다.
같은 골격을 다른 데도 붙였다
리뷰봇을 만들고 나니 이 골격이 코드 리뷰 전용이 아니란 게 보였다. 입력을 코드 diff에서 경력 데이터로, 출력을 MR 코멘트에서 문서로 바꾸니 그대로 경력 DB·이력서·자소서 작성에 옮겨졌다. 경계 밖 금지와 토큰 0이라는 두 전제는 어느 쪽이든 같다. 흩어져 있던 개인 OSS — 도서 원고를 docx로 바꾸는 book-forge, 로컬 모델·메모리 제어 레이어 Glimi — 도 “RAG 컨텍스트 + 로컬 LLM + 정형 출력”이라는 한 패턴으로 묶이니 사내 도구와 같은 골격을 공유했다.
남는 것
처음엔 토큰 비용 때문에 로컬 모델을 골랐는데, 계속 쓰게 만든 건 비용이 아니라 경계였다. 코드를 밖으로 못 내보낸다는 제약이 오히려 사내에서 도는 구조를 기본값으로 박았고, 그래서 같은 골격이 리뷰·문서화·개인 도구로 계속 굴러갔다. 막상 만들어 운영해 보기 전엔, 어디까지 자동화하고 어디서 사람이 멈춰야 하는지가 손에 안 잡혔다.