
AI로 개발은 빨라졌는데, 반년 뒤의 코드가 '무거운' 이유 — AI 시대의 기술적 부채를 측정하고, 빌리고, 갚는 실전 가이드
요약
AI 코딩 지원으로 개발 속도는 빨라졌지만, 관리되지 않은 기술적 부채가 급격히 쌓이는 현상을 분석합니다. AI가 생성한 코드가 어떻게 '무자각적 부채'를 만드는지 설명하고, 이를 측정하고 관리하는 실전 가이드를 제공합니다.
핵심 포인트
- AI는 기술적 부채를 빠르고 보이지 않게 축적하는 도구가 될 수 있음
- 부채는 그 자체보다 '이자(유지보수 비용)'가 무서운 복리 개념임
- 목표는 '신중하고 의도적인' 부채 활용과 관리 전략을 세우는 것
- AI 시대에는 부채를 측정하고 상환하는 프로세스가 필수적임
AI 코딩 지원을 사용하기 시작하면서 개발은 확실히 빨라졌습니다. 쓰고 싶은 함수의 이름을 입력하면 내용이 나오고, 테스트의 틀도 순식간에 만들어집니다. 솔직히 처음 몇 주 동안은 조금 감동하기도 했습니다.
하지만 시간이 좀 지나면, 이상한 일이 일어나지 않나요?
기능은 점점 늘어나고 있는데, 코드를 만지는 것이 이전보다 두렵다 는 느낌 말이죠. 비슷한 처리가 여기저기 흩어져 있어서 한 곳을 수정하면 다른 곳이 망가집니다. 리뷰에 시간이 걸립니다. "이거 누가 짠 거지?"라고 생각했더니, 반년 전의 나(와 AI)였던 적이...
이것은 기분 탓이 아닙니다. 그리고 "AI가 나쁘다"는 이야기도 아닙니다.
정체는, 기술적 부채 (Technical Debt)가 AI에 의해 "빠르고·보이지 않게" 쌓이고 있다 는 현상입니다.
이 기사에서는 AI에 익숙하지 않은 사람도 이해할 수 있도록,
- 애초에 기술적 부채란 무엇인가 (빚에 비유하여)
- 왜 AI 시대에 그것이 더 빠르고·보이지 않게 쌓이는가 (2026년 최신 데이터로)
- 부채를 "측정하기", "의도적으로 빌리기", "더 이상 늘리지 않기", "갚기"의 구체적인 방법
- 인간이 무엇을 판단하고, AI에게 무엇을 맡길 것인가
를 그대로 복사해서 시도해 볼 수 있는 코드 예시와 프롬프트 예시와 함께 천천히 정리해 나가겠습니다.
먼저 결론부터 말씀드릴게요. AI는 부채 제조기가 될 수도 있고, 부채 상환의 파트너가 될 수도 있습니다. 차이는 "전달하는 방식"과 "측정하고 있는지 여부"뿐 입니다.
기술적 부채. 왠지 무서운 단어죠. 하지만 정체는 매우 심플하며, 요컨대 "빚" 입니다.
이 비유를 처음 제안한 사람은 Wiki를 발명한 엔지니어인 Ward Cunningham (워드 카닝햄) 씨입니다. 1992년의 일이었습니다. 그가 말하고자 했던 것은 다음과 같습니다.
"빨리 출시해서 배우기 위해, 일단 '엉성하지만 작동하는' 코드로 빚을 지는 것은 건전한 전략이 될 수 있다. 단, 그 빚을 갚지 않은 채(=나중에 제대로 정리하지 않은 채) 계속 달려가면, 이자 때문에 움직일 수 없게 된다."
이 부분은 매우 중요하므로 다시 한번 말씀드리겠습니다. 빚 그 자체는 악이 아닙니다. 마감 기한을 맞추기 위해 지금은 의도적으로 엉성하게 작성한다. 이것은 훌륭한 의사결정입니다. 문제는 빌려놓고 이자만 계속 내는 것입니다.
돈의 빚과 코드의 빚을 나란히 비교해 봅시다.
| 돈의 빚 | 코드의 부채 | 구체적으로 |
|---|---|---|
| 원금 | 엉성하게 작성한 부분 그 자체 | 복사해서 붙여넣은 처리, 너무 긴 함수, 테스트가 없는 부분 |
| ... |
여기서 핵심은 '이자'입니다. 부채는 그냥 두기만 해도 매일 조금씩 당신의 시간을 빼앗아 갑니다. 게다가 복리입니다. 엉성한 코드 위에 또 엉성한 코드가 올라가기 때문입니다.
한 가지만 더 덧붙이자면, Martin Fowler (마틴 파울러) 씨가 정리한 부채의 4분면 (Debt Quadrants) 이라는 개념을 소개하겠습니다. 부채를 "의도적/무자각"과 "무모/신중"이라는 두 축으로 나누면 다음과 같습니다.
| 무모 (Reckless) | 신중 (Prudent) |
|---|---|
| 의도적 | "설계 같은 거 생각할 틈 없어, 일단 써" |
| 무자각 | "어, 이게 부채였어? (지식 부족)" |
우리가 목표로 해야 하는 것은 오른쪽 상단의 "신중·의도적" 입니다. "지금은 의도적으로 빌린다. 하지만 언제·어떻게 갚을지는 결정해 둔다".
그리고 AI가 대량으로 만들어내는 부채의 상당수는 왼쪽 하단의 "무자각·무모" 쪽으로 떨어지기 쉽습니다. 왜 그럴까요? 다음 장에서 숫자를 함께 살펴보겠습니다.
여기서부터는 2026년 시점의 데이터를 조금 보겠습니다. 체감을 "기분 탓"으로 끝내지 않기 위해 출처와 함께 나열하겠습니다.
코드 분석 도구인 GitClear가 2020~2024년 사이의 2억 1,100만 행에 달하는 변경 사항(Google, Microsoft, Meta 등의 리포지토리 포함)을 조사한 『AI Copilot Code Quality 2025』라는 리포트가 있습니다. 거기서 보고된 변화는 다음과 같습니다.
- 리팩터링을 나타내는 「moved lines (이동된 코드 = 재사용·정리의 지표)」가,
2021년 약 25%에서, 2024년에는 10% 미만으로 저하됨 - 복사하여 붙여넣은 「cloned lines (복제 코드)」가,
**8.3%에서 12.3%**로 상승함 - 그리고 사상 처음으로,
복제가 "이동(정리)"을 앞질렀음 - 중복 코드의 덩어리는 대략
4배 증가함
의미를 번역하면 다음과 같습니다. AI는 "새롭게 쓰는 것", "비슷한 것을 복제하는 것"은 굉장히 잘합니다. 하지만, "어질러진 것을 정리하는 것 (리팩터링 (Refactoring))"은 여전히 인간이 하지 않으면 진행되지 않습니다. 그래서 쓰는 양만 늘어나고, 정리하는 양은 뒤처지게 됩니다. 이것이 복제가 정리를 앞질렀던 정체입니다.
또 다른, 더 직접적인 연구가 있습니다. Yue Liu 등이 2026년에 발표한 『Debt Behind the AI Boom』 (arXiv:2603.28592)이라는 대규모 조사입니다.
**30만 건 이상 (302.6k)**의 AI 생성 커밋을, 6,299개의 GitHub 리포지토리, 주요 5개 어시스턴트에 걸쳐 조사 - 각 커밋의 전후에 정적 분석 (Static Analysis)을 수행하여, AI가 "넣은" 문제만을 정확히 특정
- 결과,
48만 4,366건의 문제를 검출. 그중 89.3%가 code smells (코드 스멜) 어떤 AI 어시스턴트라도 15% 초과의 커밋이 최소 1개의 문제를 포함하고 있었음 - 그리고 결정적인 것이 이것입니다.
AI가 넣은 문제의 22.7%가 최신 버전의 코드에도 여전히 남아 있었습니다
여기서 나온 「code smell (코드 스멜)」이라는 용어를 정의해 두겠습니다. 이것은 버그 그 자체가 아니라, **"지금은 동작하지만, 향후 변경을 어렵게 만드는 구조적인 냄새"**를 의미합니다. 너무 긴 함수, 중복, 너무 깊은 중첩 (Nest), 의미를 알 수 없는 변수명... 같은 것들입니다. 냄새가 나는 단계에서는 동작하기 때문에 무심코 방치되기 쉽습니다. 하지만 그것이 바로 기술적 부채입니다.
22.7%가 남아 있다는 것은, **"AI가 넣은 부채는 자연적으로 상환되지 않는다. 방치하면 정체된다"**라는 꽤 명확한 증거입니다.
보충하자면, SonarSource의 『State of Code 2026』 조사에서는 코드의 약 42%가 AI 생성 또는 AI 지원을 받았다고 보고되었습니다. 반면 Stack Overflow의 2025년 개발자 조사에서는, 84%가 AI를 사용하거나 사용할 예정이지만, 출력을 신뢰하는 사람은 약 3할에 불과했습니다. "거의 맞지만 미묘하게 어긋나는" 코드에 대한 불만이 66%라는 수치도 나왔습니다. Stack Overflow 블로그에는 2026년 1월에 "AI는 개발자의 생산성을 10배로 높일 수 있다. 기술적 부채를 만들어내는 속도도 10배로 높일 수 있다"라는 풍자 섞인 제목의 기사까지 올라왔습니다.
세 가지 메커니즘으로 정리합니다.
쓰는 속도가 빨라진 만큼, 이자가 보이지 않게 되었다— 순식간에 대량의 코드가 나오기 때문에, "이걸 나중에 누가 어떻게 유지보수할까"를 생각할 틈이 줄어듭니다. 빚을 지고 있다는 감각 없이 빌려 쓰게 됩니다. -
AI는 "추가·복제"는 잘하지만, "정리"는 인간에게 의존한다— GitClear가 보여주듯, 방치하면 쓰는 양만 늘어나고 정리하는 작업이 따라가지 못합니다. -
동작은 하지만, 구조적인 판단이 없다— 보안 기업 Ox Security는 AI 생성 코드를 "기능적으로는 우수하지만, 아키텍처적인 판단이 체계적으로 결여되어 있다"라고 표현했습니다. AI는 "동작하게 만드는 것"에는 전력을 다하지만, "이 코드베이스 전체로서 여기는 이렇게 배치해야 한다"라는 대국적인 관점은 여전히 인간의 몫입니다.
비난할 대상은 AI도, 과거의 자신도 아닙니다. "측정하지 않고 빌려 쓰고 있었다"라는 방식의 문제입니다. 이제부터는 그 해결 방법입니다.
"왠지 무겁다"를 "이 부분이 이렇게 무겁다"로 바꾸는 것. 이것이 가시화(Visualization)입니다. 건강검진과 마찬가지로, 숫자로 나타내야 비로소 어디부터 손을 댈지 판단할 수 있습니다.
처음에 측정해야 할 것은 이 세 가지만으로도 충분합니다.
| 지표 | 무엇을 보는가 | 무료 대표 도구 |
|---|---|---|
| 중복 (Duplication) | 비슷한 코드가 얼마나 흩어져 있는가 | jscpd, PMD/CPD |
| 복잡도 (Cyclomatic Complexity) | 함수 내에서 분기가 얼마나 얽혀 있는가 | radon (Python), ESLint complexity (JS/TS) |
| Churn (천) | 어떤 파일이 반복해서 다시 작성되는가 | git 로그 |
용어만 간단히 정의하겠습니다.
cyclomatic complexity (순환 복잡도) = 대략적으로 "해당 함수 안에 있는 분기(if / for / and / or 등)의 수 + 1". 숫자가 클수록 머릿속으로 따라가야 할 경로가 늘어나며, 버그가 살기 쉬운 환경이 됩니다. 기준으로 보통 10을 넘으면 주의, 20을 넘으면 경고 신호라고 말합니다. -
churn (천/변동) = 해당 코드가 "썼다 고치고, 또 썼다 고치고"를 반복하는 횟수. 여러 번 다시 작성되고 있는 곳은 설계가 확정되지 않았다 = 부채가 쌓이고 있다는 신호입니다.
도구를 도입하기 전에, git만으로 할 수 있는 가장 간편한 측정부터 시작합니다. 최근에 "자주 수정된 파일"을 찾아냅니다.
#!/usr/bin/env bash
# churn.sh — 최근 90일 동안 변경 횟수가 많은 파일 TOP20 추출
# "자주 다시 작성하는 곳" = 부채가 쌓이기 쉬운 곳의 신호
...
실행하면 "42 src/payment/calc_total.py"와 같이 변경 횟수와 함께 파일 목록이 나타납니다. 변경 횟수가 많은 파일은, **"이곳은 아직 설계가 안정되지 않았습니다"**라고 코드가 알려주는 것과 같습니다.
다음은 중복과 복잡도입니다. 이 부분은 무료 도구에 맡깁니다.
# 중복 코드 검출 (jscpd: JS/TS/Python 등 다국어 대응)
npx jscpd ./src --min-lines 5 --reporters console
# 복잡도 측정 (Python의 경우: radon)
...
포인트는, 처음부터 전부 완벽하게 측정하려고 하지 않는 것입니다. 먼저 churn으로 "자주 건드리는 곳"을 특정하고, 그곳만 중복과 복잡도를 확인합니다. 부채는 균등하게 퍼져 있지 않으므로, "자주 건드림 × 복잡함 × 중복됨"이 겹치는 지점이 가장 많은 이자를 지불하고 있는 "통증 지점(pain point)"입니다. 그곳부터 갚아 나가는 것이 가장 비용 대비 효율이 높습니다.
빚 그 자체는 악이 아니라는 이야기를 기억하시나요? 문제는, 몰래 빌리는 것입니다.
그러므로 빌릴 때는 "차용증"을 써야 합니다. 제가 추천하고 싶은 것은 **Debt Ledger (부채 장부)**라는 개념입니다. 코드베이스 안에 파일 하나를 두고, "현재 어떤 부채를, 왜, 언제 갚는 것을 전제로 안고 있는가"를 기록해 두는 것입니다.
머릿속이나 Slack DM에 흩어진 "나중에 고치기"는 거의 100% 잊혀집니다. 하지만 리포지토리에 커밋된 장부는, 미래의 자신에게도, 팀에게도, AI에게도 읽힐 수 있습니다.
# debt-ledger.yml — 의도적으로 안고 있는 기술적 부채 장부
# 규칙: "일부러 대충 작성하기"로 결정했다면, 반드시 여기에 한 줄 추가할 것
debts:
...
여기서 핵심적인 역할을 하는 것이 repay_when, 즉 상환 조건입니다. "두 번째 국가가 진입하면", "데이터가 1만 건을 넘으면"과 같이, 상환 계기를 "사실(fact)"로 미리 결정해 두는 것입니다. 이것이 없으면 부채는 영원히 "나중에" 상태로 남게 됩니다.
장부와 실제 코드를 느슨하게 연결하면 길을 잃을 확률이 더욱 줄어듭니다. 코드 안의 TODO 주석에 장부의 ID를 적어두는 방식입니다.
# 코드 내 TODO 주석 규약:
# # TODO(DEBT-001): 국가별 세율을 테이블화할 것
# # FIXME(DEBT-002): 페이지네이션 미지원
...
이렇게 하면 "코드에는 DEBT-003이 있는데, 장부에는 없다"와 같은 누락을 잡아낼 수 있습니다. 빌렸다면 장부에 적는다. 이 규칙 하나만으로 부채는 "보이지 않는 불안"에서 "관리 가능한 항목"으로 변합니다.
과거의 부채는 한꺼번에 갚을 수 없습니다. 하지만, "더 이상은 늘리지 않겠다"는 것은 오늘부터 가능합니다.
여기서 사용하는 것이 **라쳇 (ratchet)**이라는 개념입니다. 라쳇이란 한 방향으로만 돌아가는 톱니바퀴를 말하며, 되돌아가지 않는 메커니즘의 비유로 쓰입니다.
방법은 간단합니다. "현재의 부채량"을 베이스라인(기준치)으로 기록해 두고, 새로운 변경 사항이 이를 늘리면 CI에서 차단합니다. 하지만 과거의 분량은 책임을 묻지 않습니다. 줄어드는 방향(상환)은 언제든 OK입니다.
이것은 사소해 보이지만 엄청나게 효과적입니다. 왜냐하면, 과거의 모든 것을 고치지 않더라도, 늘어나는 것을 막는 것만으로 부채는 오늘부터 횡보하게 되기 때문입니다. 그리고 "하는 김에 조금씩 갚기"가 쌓이면, 나머지는 자연스럽게 내려가게 됩니다.
#!/usr/bin/env python3
# ratchet.py — 중복이나 복잡도 등의 「부채 카운트」가 기준치를 초과하면 실패하게 함
# 과거의 부채(baseline)는 허용하고, "새롭게 늘어난 부분"만 막는 일방통행 게이트
...
중요한 것은 두 가지입니다. (1) 과거의 부채는 비난하지 않는다 (baseline으로서 허용한다). (2) 줄어들면 베이스라인을 낮추어, 다시 늘어나는 방향으로는 돌아가지 않도록 한다. 이렇게 하면 "고쳤는데, 또 같은 곳이 늘어나는" 사이노카와라(賽の河原, 끝없는 헛수고)를 방지할 수 있습니다.
이 라쳇(Ratchet)을 GitHub Actions를 통해 각 풀 리퀘스트(Pull Request, PR)에 적용합니다.
# .github/workflows/debt-gate.yml
name: debt-gate
on: [pull_request]
...
이로써, 「동작하니까 OK」라는 이유만으로 머지(Merge)되던 PR이, 「동작하면서도 부채를 늘리지 않음」이라는 조건으로 게이트(Gate)를 통과하게 됩니다. 주의할 점은, 처음부터 임계치(Threshold)를 너무 엄격하게 잡지 않는 것입니다. 팀이 "게이트가 너무 까다롭다"고 느껴서 무효화해 버린다면 본말전도이기 때문입니다. 우선은 「늘리지 않는 것」만으로도 충분합니다.
여기까지 해서, 측정하고, 차용증을 쓰고, 늘어나지 않게 만들었습니다. 마지막은 드디어 상환입니다. 그리고 바로 여기서 AI가 본령을 발휘합니다.
서두에서 말했듯이, AI는 부채 제조기가 될 수도, 상환의 파트너가 될 수도 있습니다. 상환의 파트너로 만들기 위한 요령은 **「무턱대고 리팩터링(Refactoring)시키지 않는다」, 「반드시 현재의 동작을 고정한 뒤에 변경하게 한다」, 「최종 판단은 인간이 쥔다」**라는 세 가지입니다.
당신은 시니어 엔지니어입니다. 다음 메트릭(Metrics) 출력(churn·중복·복잡도)을 읽고,
기술적 부채의 「상환 우선순위」를 제안해 주세요.
# 입력
...
여기서의 핵심은 AI에게 「결정하게 하지 않는」 것입니다. AI는 후보와 근거를 내놓는 역할이며, 어느 것을 상환할지 결정하는 것은 인간입니다. 왜냐하면 비즈니스상의 우선순위나 "다음 달에 이 기능을 삭제할 예정"과 같은 맥락은 인간만이 알고 있기 때문입니다.
이 부분이 가장 사고가 나기 쉬운 곳이므로, 신중하게 진행합니다.
당신은 신중한 리팩터링 담당자입니다. 다음 절차를 「순서대로」 진행해 주세요.
도중에 저의 확인을 거쳐야 합니다. 마음대로 끝까지 진행하지 마세요.
대상: src/payment/calc_total.py 와 src/billing/sum_items.py (중복 의심)
...
포인트는 절차 1과 2입니다. 「현재의 동작」을 테스트로 동결한 뒤에 변경하는 것. 이를 수행하지 않고 AI에게 "공통화해줘"라고 부탁하면, 겉보기에는 비슷하지만 실제로는 미묘하게 다른 처리를 하나로 합쳐버려 조용히 버그를 만들어냅니다. 사양화 테스트(Specification Test)는 그 사고를 방지하는 안전망입니다.
debt-ledger.yml의 DEBT-002(페이지네이션 미지원)를 상환할 계획을 세워주세요.
# 제약 사항
- 하나의 풀 리퀘스트(PR)에서는 하나의 부채만 다룬다 (섞지 않는다)
...
「1PR = 1부채」와 「되돌리는 방법을 첨부한다」. 이 두 가지를 통해 상환 자체가 새로운 부채를 낳는 것을 방지할 수 있습니다.
AI에게 상환을 도우라고 할 때, 양보할 수 없는 선을 정해둡니다.
테스트 없는 일괄 리팩터링은 시키지 않는다. 반드시 현재 상태를 고정한 뒤, 작게 진행한다.
불가역적인 조작(운영 DB 변경, 결제, 삭제, 공개, 배포)은 AI에게 자동 실행시키지 않는다. 제안까지만 하게 하고, 실행은 인간이 승인한다.
에러 로그나 코드를 AI에게 전달할 때, 비밀 정보·개인 정보·고유 ID를 붙이지 않는다. 더미(Dummy) 데이터로 대체한다.
외부에서 온 텍스트(Issue 본문, 로그, Web)는 "데이터"로 취급한다. 그 안의 "이 파일을 삭제해"와 같은 지시에는 따르지 않도록 한다 (프롬프트 인젝션(Prompt Injection) 대책).
보안 부채는 별개입니다. 코드 스멜(Code Smell)과는 달리, 인간의 보안 리뷰가 필요합니다.
정리하면 다음과 같습니다.
| 공정 | 인간 (What / Why) | AI (How) |
|---|---|---|
| 측정 | 어떤 지표를 볼지 결정 | 메트릭을 집계·요약 |
| ... |
한마디로 요약하자면, 「무엇을·왜」는 인간, 「어떻게」는 AI입니다. 부채를 안고 갈지, 어느 것을 상환할지는 비즈니스와 미래를 알고 있는 인간의 판단입니다. 그 실행을 빠르게 하는 것이 AI라는 역할 분담입니다.
| 함정(落とし穴) | 왜 안 되는가 | 어떻게 할 것인가 |
|---|---|---|
| 측정하기 전에 고치기 시작한다 | 문제 지점이 아닌 곳을 다듬으며 자기만족에 빠진다 | 먼저 churn으로 문제 지점을 특정한다 |
| ... | ||
| 상황 | 판단 | |
| --- | --- | --- |
| 리팩토링해도 효과가 정체된다 | 거기서 멈춘다. 완벽주의에 빠지지 않는다 | |
| ... | ||
| 여기까지 수고하셨습니다. 마지막으로, 조금 더 큰 이야기를 하겠습니다. |
기술적 부채(technical debt)라는 단어의 반대편에는 **자본(Capital)**이 있다고 저는 생각합니다. 같은 코드라도 방치하면 이자를 낳는 '부채'가 될 수도 있고, 제대로 측정하고 다듬으면 미래의 자신과 팀을 도와주는 '자산'이 될 수 있습니다. 어느 쪽이 될지는 작성한 양이 아니라, 어떤 태도로 임하느냐에 따라 결정되는 것입니다.
AI는 이 두 가지 모두를 가속합니다. 아무 생각 없이 사용하면 부채가 10배 속도로 쌓입니다. 하지만 측정하고, 차용증을 쓰고, 늘어나지 않게 하는 시스템을 갖추고 상환의 파트너로 삼으면, 자산도 10배 속도로 쌓일 수 있습니다.
그리고, 여기서 가장 전하고 싶은 것이 있습니다.
오늘 코드를 조금 가볍게 만드는 것은 내일의 나에게 주는 작은 선물입니다. 반년 후, 급하게 기능을 추가하고 싶은 미래의 자신이
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기