sgkstudio.
엔지니어링

AI 코드 품질 측정을 점수 목표에서 회귀 문턱으로 바꾼 기록

AI 코드 품질 측정은 「몇 점 이상」을 목표로 걸 때가 아니라 「지난번보다 떨어지지 않게」 막을 때 작동했다. 점수 네 개 중 strict 하나만 판정에 쓰고, 프로젝트마다 기준선을 재서 문턱을 단계별로 올리도록 규칙을 바꿨다.

2026-10-04코드 품질 운영 규칙 문서 — 점수 도구의 점수 4종 정의, 단계별 문턱 3단계(기준선·기준선+5·80), 목표 기준선+5·80·85같은 문서의 기록 형식 한 줄 — strict 75.5 → 76.2 (+0.7), 미해결 이슈 1192 → 1187같은 문서의 주간 보고 형식 — strict 75.5 → 78.2 (+2.7), low_level_elegance 64 → 71, 5 PR·18 issues2026년 9월 2일 정정 — 문턱 미달 시 사람 승인 대기를 폐지하고 수정·분리로 통과, 3회 실패 시에만 사람 호출

AI 코드 품질 측정은 왜 필요했나

AI 코드 품질 측정이 필요해진 이유는 단순하다. 에이전트가 쓰는 코드의 양이 사람이 읽을 수 있는 양을 넘었는데, 「요즘 코드가 지저분해진 것 같다」는 느낌만으로는 언제 어디서 나빠졌는지 짚을 수 없었다. 규칙 문서 첫 줄이 이 문제를 한 문장으로 적어 둔다. AI가 만든 코드의 품질을 객관 점수로 재고, 그 점수를 커밋과 세션 흐름에 자동으로 끼워 넣는다.

우리 작업 흐름은 7단계다. 작업 브랜치를 만들고, 코드를 쓰고, 테스트를 돌리고, 커밋하고, 푸시하고, 합치고, 정리한다. 에이전트는 이 일곱 단계를 사람 승인 없이 끝까지 간다. 사람이 보는 시점은 합친 뒤의 요약 보고다. 이 구조에서는 테스트가 통과하면 코드가 그대로 운영으로 나간다. 테스트는 「돌아가는가」를 묻지만 「읽기 좋은가, 중복이 늘었나, 구조가 무너지는가」는 묻지 않는다.

그래서 테스트 바로 뒤에 점수 검사를 하나 더 넣기로 했다. 문제는 그 점수를 어떻게 읽느냐였다. 점수 도구는 숫자를 네 개나 내놓고, 프로젝트마다 출발점이 다르고, 점수를 목표로 걸면 사람이든 AI든 점수를 쫓기 시작한다. 이 글은 그 세 가지를 어떻게 정리했는지에 관한 기록이다.

규칙 문서는 이 방향을 「바이브 코딩에서 바이브 엔지니어링으로」라는 한 줄로 요약한다. AI에게 말로 시켜서 돌아가는 코드를 얻는 단계가 바이브 코딩이라면, 그 코드가 시간이 지나도 무너지지 않는지 숫자로 지키는 단계가 바이브 엔지니어링이다. 앞 단계는 누구나 할 수 있게 됐고, 차이는 뒤 단계에서 난다고 봤다.

코딩 회사만의 이야기는 아니다. 외주로 AI 자동화를 맡긴 회사도 같은 질문을 받는다. 납품된 코드가 석 달 뒤에도 고칠 만한 상태인지, 무엇으로 확인하느냐는 질문이다. 점수 하나를 계약서에 적는 방식이 왜 잘 안 되는지가 아래 내용의 절반이다.

처음 잰 숫자는 무엇이었고, 어떻게 쟀나

측정 도구는 오픈소스 코드 품질 점수 도구다(이하 점수 도구). 프로젝트 폴더를 훑어 코드 위생 문제를 찾고, 이슈 목록과 점수를 낸다. 명령은 프로젝트 폴더에서 scan 명령 한 줄이고, 결과는 프로젝트 안 점수 도구 전용 폴더에 쌓인다. 이 폴더가 있는 프로젝트에만 규칙을 적용하고, 없는 프로젝트에는 강제하지 않는다.

도구가 내는 점수는 네 개다. 처음에는 이 넷을 모두 보려고 했다. 그런데 넷이 서로 다른 것을 재고 있어서, 둘이 오르고 하나가 내리면 판정을 내릴 수 없었다. 아래 표가 네 점수의 차이다.

점수 도구 4종 점수 비교표 — strict는 고치지 않기로 한 이슈까지 감점, overall은 그것을 무시, objective는 기계 검사만, verified는 검증된 수정만 인정
판정에 쓰는 점수는 strict 하나, 나머지 셋은 진단용

우리 프로젝트의 출발점은 규칙 문서의 기록 형식에 남아 있다. strict 75.5점, 미해결 이슈 1192건이다. 이 숫자를 어떻게 읽어야 할지 감이 없었다. 75.5점이 나쁜 점수인가, 이 정도면 괜찮은가. 1192건을 다 고쳐야 하나, 일부만 고치면 되나. 숫자는 있는데 그 숫자로 할 수 있는 결정이 없었다.

검사 시점은 다섯 곳으로 나눴다. 커밋 직전에는 회귀를 막으려고 자동으로 돈다. 세션이 끝날 때는 변경이 있을 때만 돌려 누적 추이를 남긴다. 매주 월요일에는 검사와 함께 기록을 정리해 한 주 흐름을 본다. 큰 변경을 합친 뒤에는 전체 영향을 확인하려고 자동으로 한 번 더 돈다. AI 리뷰 재실행은 언어 모델 비용 때문에 분기 1회로 따로 떼었다.

세션 종료 때의 순서도 고정했다. 현재 폴더가 점수 도구를 쓰는 프로젝트일 때만 검사를 돌리고, 직전 점수와의 차이를 계산하고, 기록 파일 끝에 한 줄을 붙이고, 진행 상황 문서의 품질 칸을 한 줄 고친다. 네 단계 모두 에이전트가 하고, 사람은 나중에 기록 파일을 읽기만 한다.

  • strict 점수 75.5점 — 고치지 않기로 표시한 이슈까지 감점에 넣은 값
  • 미해결 이슈 1192건 — 도구가 찾아 아직 닫지 않은 문제 수
  • 측정 명령 — 프로젝트 폴더에서 scan 명령 1회
  • 측정 시점 — 커밋 직전, 세션 종료 시, 매주 월요일, 큰 변경을 합친 뒤

처음 세운 가설은 무엇이었나

처음 가설은 지금 돌아봐도 자연스럽다. 좋은 코드의 기준 점수를 하나 정하고, 그 점수 아래면 합치지 않는다. 80점이든 85점이든 숫자 하나를 정해 모든 프로젝트에 같은 선을 긋는다. 공장처럼 같은 품질 기준을 모든 제품에 적용한다는 원칙과도 맞아 보였다.

두 번째 가설은 사람 판단을 다뤘다. 점수가 기준 아래로 떨어지면 에이전트가 멈추고 사람에게 승인을 묻는다. 규칙의 초기 문장이 이 생각을 그대로 담고 있다. 문턱 아래로 떨어지면 되돌릴 수 없는 외부 작업과 같은 등급으로 멈춘다. 결제를 켜거나 고객에게 메일을 보내기 전에 멈추는 것과 같은 무게로 다뤘다.

세 번째 가설은 점수를 자주 잴수록 좋다고 봤다. 도구에는 기계 검사 말고도 AI가 코드를 읽고 주관 평가를 하는 리뷰 기능이 있다. 20개 묶음으로 나눠 병렬로 돌리는 방식이다. 이걸 세션마다 돌리면 품질을 더 촘촘히 볼 수 있다고 생각했다.

기준 점수 하나, 모든 프로젝트 동일, 미달이면 사람이 판단, 리뷰는 자주.

가설은 어디서 틀렸나

가설 넷이 모두 틀렸다. 규칙 문서 끝에 「하지 말 것」으로 남은 다섯 항목이 그 흔적이다. 하나씩 보면 각각 다른 방식으로 측정을 망가뜨렸다.

첫째, 모든 프로젝트에 같은 문턱을 걸자 신규 프로젝트가 늘 떨어졌다. 새로 시작한 프로젝트나 오래 방치한 프로젝트는 출발점부터 낮다. 80점 선을 그으면 첫 커밋부터 막히고, 막힌 상태가 계속되면 문턱은 의미를 잃는다. 결국 누군가 문턱을 낮추고, 한 번 낮춘 문턱은 다시 올라가지 않는다. 규칙은 이걸 「게이트 무력화」라고 부른다.

둘째, 점수를 목표로 걸자 점수 추격이 생겼다. 의미 없는 형식 수정으로 점수만 올리는 방식이다. 코드의 실제 품질은 그대로이고, 도구에 들어 있는 점수 조작 방지 장치에 결국 걸린다. 사람이 하든 에이전트가 하든 결과가 같다. 점수가 목표가 되는 순간 점수는 더 이상 품질을 재지 못한다.

셋째, AI 리뷰를 세션마다 돌리자 비용이 감당되지 않았다. 20개 묶음 리뷰는 언어 모델 호출이 많아서, 매 세션 돌리면 쓰는 요금제의 사용 한도를 압박한다. 리뷰는 분기 1회면 충분하고, 일상 검사는 기계 검사만으로 하도록 바꿨다.

넷째, 이슈를 한꺼번에 몰아 고치자 되돌릴 수 없게 됐다. 이슈 30개를 한 번의 변경으로 합치면 리뷰도 안 되고, 문제가 생겼을 때 어느 수정이 원인인지 가려서 되돌릴 수도 없다. 다섯째, 문턱 미달을 사람 승인으로 넘기는 방식은 2026년 9월 2일에 정정했다. 사람에게 묻는 대신 에이전트가 고치거나 분리해서 통과시키고, 3회 연속 실패할 때만 사람을 부른다.

처음 가설과 실제 결과 비교표 — 동일 문턱은 신규 프로젝트 상시 실패, 점수 목표는 점수 추격, 세션마다 리뷰는 비용 압박, 30개 이슈 한 번에는 되돌리기 불가, 미달 시 사람 승인은 2026년 9월 2일 정정
가설 다섯 개와 그것이 실제로 만든 문제

진짜 원인은 무엇이었나 — 절대값과 회귀를 한 숫자에 섞었다

다섯 실패의 공통 원인은 하나였다. 점수의 절대값과 점수의 변화량을 한 숫자로 판정하려 했다. 「이 코드는 좋은가」와 「이번 변경이 코드를 나쁘게 만들었나」는 다른 질문이다. 앞의 질문에 답하려면 기준이 필요하고, 그 기준은 프로젝트마다 다르다. 뒤의 질문은 직전 점수만 있으면 답할 수 있다.

커밋 직전 검사가 막아야 하는 대상은 뒤쪽이다. 75.5점짜리 프로젝트에서 변경 하나가 점수를 75.5점 아래로 떨어뜨렸다면 그 변경을 막아야 한다. 반대로 75.5점을 76.2점으로 올린 변경을 「아직 80점이 안 된다」는 이유로 막을 이유는 없다. 같은 점수가 어떤 날엔 통과하고 어떤 날엔 막혀야 하는데, 고정 문턱 하나로는 이걸 구분할 수 없었다.

사람 승인 문제도 같은 뿌리였다. 점수가 떨어졌다는 사실은 기술 판단의 재료이지 사업 판단의 재료가 아니다. 원인이 된 변경을 찾고, 고치거나 의도한 변경이면 근거를 남기면 된다. 비개발자인 운영자에게 「점수가 1점 떨어졌는데 합칠까요」를 물으면, 판단할 근거가 없으니 결국 내용과 무관하게 승인하게 된다. 묻는 행위가 안전장치 역할을 하지 못했다.

점수 네 개를 함께 본 것도 같은 혼동에서 나왔다. overall은 고치지 않기로 한 이슈를 빼고 계산해서, 「이건 두자」고 표시만 해도 오른다. objective는 기계 검사만 보고, verified는 검증까지 끝난 수정만 센다. 넷 중 무엇을 기준으로 삼느냐에 따라 같은 변경이 회귀가 되기도 하고 개선이 되기도 했다. 판정 기준이 흔들리면 문턱이 몇 점이든 의미가 없다.

문턱은 회귀만 막고, 절대값을 강제하지 않는다.

어떻게 조치했나 — 기준선에서 시작하는 3단계 문턱

첫 조치로 판정 점수를 하나로 줄였다. 네 점수 중 strict만 의사결정에 쓰고, 나머지 셋은 진단용으로 돌렸다. strict를 고른 이유는 가장 엄격해서다. 고치지 않기로 표시한 이슈까지 감점에 넣기 때문에, 「이건 그냥 두자」로 점수를 올리는 길이 막힌다.

두 번째 조치로 문턱을 프로젝트별, 단계별로 나눴다. 새 프로젝트에 도구를 들이면 먼저 기준선을 잰다. 첫 1개월은 그 기준선 자체가 문턱이다. 지금보다 나빠지지만 않으면 통과한다. 목표는 기준선+5점이다. 목표에 닿으면 다음 단계로 올라간다.

단계별 문턱 표 — 1단계 도입 직후 문턱 현재 기준선 목표 기준선+5 첫 1개월, 2단계 정착 문턱 기준선+5 목표 80 1~3개월, 3단계 성숙 문턱 80 목표 85 3개월 이후
문턱은 단계별로만 올라가고, 내려가지 않는다

2단계(1~3개월)에서는 기준선+5가 문턱이 되고 목표는 80점이다. 3개월이 지나 성숙 단계에 들면 문턱이 80점, 목표가 85점이 된다. 목표에 못 닿아도 문턱만 지키면 괜찮다. 핵심은 방향이다. 문턱은 위로만 움직이고, 일시적으로 점수가 떨어지면 문턱을 낮추는 대신 회복 변경으로 되돌린다.

새 프로젝트에 들이는 절차도 이 원칙에 맞췄다. 점수 도구 전용 폴더를 버전 관리 제외 목록에 넣고, 외부 라이브러리·빌드 산출물·로그처럼 자동 생성되는 폴더를 검사에서 뺀다. 그다음 언어를 지정해 첫 검사를 돌리고, 그 점수를 기준선으로 기록 파일 맨 위에 적는다. 문턱과 목표는 이 기준선에서 계산한다. 이 절차의 요점은 다른 프로젝트의 문턱을 가져오지 않는 데 있다. 신규 프로젝트가 늘 떨어지던 첫 번째 실패를 절차 단계에서 막는다.

세 번째 조치로 고치는 단위를 정했다. 한 번의 변경에 이슈 3~5개만 묶는다. 같은 영역, 같은 모듈의 이슈끼리 묶고, 변경 하나는 커밋 3개 이하, 바뀐 줄 500줄 이하로 유지한다. 큰 구조 개편은 따로 떼서 사람이 명시적으로 승인할 때만 한다.

커밋 직전 검사는 어떻게 동작하나

7단계 흐름의 3단계(테스트)와 4단계(커밋) 사이에 3.5단계를 끼웠다. 점수 도구 전용 폴더가 있으면 에이전트가 자동으로 검사를 돌리고, 새 strict 점수를 문턱과 비교한다. 이 검사는 자동 합치기 흐름의 멈춤 조건 목록에 5번째로 들어갔다.

커밋 직전 품질 검사 흐름도 — 테스트 통과 후 점수 검사, 문턱 이상이면 커밋, 1점 이상 오르면 기록, 문턱 미만이면 원인 진단 후 수정 또는 분리, 3회 실패 시 사람 호출
3.5단계 판정 — 통과·기록·회귀 세 갈래

판정은 세 갈래다. 새 점수가 문턱 이상이면 통과해서 커밋으로 간다. 점수가 1점 이상 올랐으면 기록에 남기고 보고에 넣는다. 문턱 아래로 떨어지면 푸시를 멈추고 원인을 찾는다. 원인 진단은 최근 1시간 안에 열린 이슈를 보는 명령 하나로 시작한다. 어떤 영역의 점수가 떨어졌는지가 여기서 나온다.

  • 경로 a — 변경 안에서 고친다. 회귀 원인을 수정하고 다시 검사해서 문턱을 넘으면 진행한다
  • 경로 b — 변경을 나눈다. 회귀와 무관한 의도된 변경이면 그 이슈를 「고치지 않음」으로 표시하고 근거를 기록한다
  • 3회 연속 실패 — 같은 시도가 세 번 막히면 그때 사람을 부른다

경로 b를 둔 이유는 모든 점수 하락이 회귀는 아니기 때문이다. 새 기능을 넣으면서 일부러 단순한 구조를 택했는데 도구가 그걸 이슈로 잡는 경우가 있다. 이때 억지로 고치면 의도와 다른 코드가 생기고, 그냥 넘기면 문턱이 무너진다. 그래서 「고치지 않음」 표시와 근거를 함께 남기게 했다. strict 점수는 이 표시까지 감점에 넣으므로 표시가 쌓이면 점수에 그대로 드러난다. 표시가 공짜가 아니어야 남용이 생기지 않는다.

3회라는 숫자도 같은 원리에서 나왔다. 한 번 막혔을 때 사람을 부르면 에이전트가 원인을 찾을 기회가 없다. 같은 시도가 세 번 막혔다면 그건 고칠 줄 모르는 상태이고, 그때부터는 사람의 눈이 의미를 갖는다.

우선순위 규칙도 붙였다. 기본은 도구가 권하는 순서를 따르되, 보안 이슈는 점수 영향과 무관하게 1순위다. 한 영역이 점수를 4점 넘게 끌어내리고 있으면 그 영역을 먼저 본다. 파이썬 프로젝트에는 보안 검사기 Bandit을 추가로 붙여 셸 명령 주입, 안전하지 않은 역직렬화, SQL·하위 프로세스 패턴까지 잡게 했다. 같은 판단을 사람 대신 기계 신호로 넘긴 다른 사례는 LLM as a judge 점수가 노이즈 안일 때에 정리했다.

재측정 결과는 어땠나

검사가 돌면 기록 파일 끝에 한 줄이 붙는다. 날짜와 사건, strict 점수의 전후, 어떤 변경이었는지, 미해결 이슈 수의 전후다. 규칙 문서가 형식으로 남긴 한 줄은 이렇다. strict 75.5점에서 76.2점으로 0.7점, 미해결 이슈 1192건에서 1187건으로 5건 줄었다. 변경 내용은 코드 간결성 이슈 3개 수정이었다.

기록 한 줄 수치 — strict 75.5에서 76.2로 0.7점 상승, 미해결 이슈 1192건에서 1187건으로 5건 감소, 간결성 이슈 3개 수정
변경 하나가 남긴 기록 — 큰 숫자가 아니라 방향을 본다

0.7점은 작은 숫자다. 고정 문턱 80점 방식이었다면 이 변경은 여전히 「미달」이었을 것이고, 사람에게 승인을 묻는 줄에 섰을 것이다. 새 방식에서는 문턱(기준선)을 넘었고 점수가 오른 변경이라 그대로 통과한다. 1점 미만이라 별도 보고 대상도 아니다. 작은 개선이 막히지 않고 쌓이는 구조가 이번 조치의 목적이었다.

주간 단위로 묶으면 숫자가 조금 더 커진다. 매주 월요일 정리하는 주간 보고 형식은 strict 75.5점에서 78.2점으로 2.7점, 코드 저수준 간결성 점수 64점에서 71점, 한 주에 변경 5건으로 이슈 18건 해결을 예로 든다. 변경 5건에 이슈 18건이면 변경당 3~5개 묶음 규칙 안에 들어온다.

기록에 남는 숫자 — 한 번과 한 주
항목전후변화
strict 점수 (변경 1건)75.576.2+0.7
미해결 이슈 (변경 1건)1192건1187건−5건
strict 점수 (주간)75.578.2+2.7
저수준 간결성 점수 (주간)6471+7
주간 처리량—변경 5건 · 이슈 18건변경당 3~5개 범위

보고 기준을 1점으로 잡은 이유도 여기에 있다. 1점 미만의 움직임은 기록에만 남기고, 1점 이상 오를 때만 보고에 올린다. 운영자가 읽는 보고에는 방향이 분명한 변화만 남기고, 작은 흔들림은 기록 파일에서 추이로 본다. 한 주가 지나 2.7점처럼 쌓인 변화가 보이면 그때 의미를 읽는다.

이 숫자들을 과장해서 읽지 않으려 한다. 75.5점이 76.2점이 됐다는 건 「이 변경이 코드를 나쁘게 만들지 않았다」는 뜻이지 「코드가 좋아졌다」는 증명이 아니다. 이 방식은 회귀가 없었다는 사실 하나만 증명한다. 우리는 그 하나만 확실히 알고 싶었다.

남는 한계는 무엇인가

첫째, 점수는 여전히 대리 지표다. 도구는 기계적으로 셀 수 있는 위생 문제와, 분기 1회 돌리는 AI 리뷰가 보는 범위까지만 잡는다. 설계가 틀린 코드, 업무 규칙을 잘못 이해한 코드는 점수가 높아도 틀렸다. 이 부분은 여전히 테스트와 사람이 읽는 차이(diff)로 봐야 한다.

같은 이유로 점수 하락이 없다는 사실을 「품질 보증」으로 고객에게 내밀지 않는다. 문턱은 우리 안에서 회귀를 멈추는 장치이고, 납품물의 품질은 테스트 결과와 실제 동작으로 따로 확인한다. 두 가지를 섞어 말하면 처음 가설과 똑같은 혼동을 밖에서 다시 만든다.

둘째, 기준선이 낮은 프로젝트는 오래 낮게 머물 수 있다. 1단계 문턱은 「지금보다 나빠지지 않기」라서, 아무도 개선 작업을 하지 않으면 점수는 제자리다. 목표(기준선+5)는 승격 조건일 뿐 강제가 아니다. 문턱만으로는 개선이 일어나지 않고, 개선은 따로 시간을 들여야 한다.

셋째, AI 리뷰를 분기 1회로 줄인 만큼 주관 평가의 빈도가 떨어졌다. 일상 검사는 기계 검사만 하므로, 구조나 이름 짓기처럼 기계가 못 재는 영역의 회귀는 최대 한 분기 늦게 잡힌다. 비용과 촘촘함 사이에서 비용 쪽을 고른 결과다.

넷째, 3회 실패 후 사람 호출은 아직 충분히 시험하지 못했다. 규칙상 경로는 정해 두었지만, 비개발자 운영자가 그 시점에 무엇을 판단할 수 있는지는 열린 문제다. 같은 이유로 AI 에이전트가 같은 실패를 반복하는 상황을 따로 다룬 글이 AI 에이전트 무한 루프 감지다.

마지막으로, 이 숫자를 밖에 쓸 때 지키는 선이 있다. 우리 점수 추이는 공개 개발 기록 글감으로 쓰지만, 남의 코드에 도구를 돌려 나온 결함을 공개하지는 않는다. 점수는 우리 코드가 나빠지지 않았는지를 확인하는 도구이지, 남과 비교하는 순위표가 아니다. 납품한 코드의 품질을 어떻게 확인할지 고민이라면 상담으로 이 방식을 그대로 설명해 드린다.

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

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

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

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