14일 동안 AI에게 레거시 코드 리팩터링을 맡겨보았다 — 데이터가 보여준 놀라운 결과
요약
작성자는 Rust 기반 API 게이트웨이의 기술 부채를 해결하기 위해 Llama-4-70B와 OpenDevin을 활용한 로컬 자율 에이전트 시스템을 구축하여 2주간 테스트했습니다. 에이전트는 초기에는 단순한 린팅 작업을 수행하다가 점차 복잡한 코드 구조를 개선하는 리팩터링 단계로 진입하며 실전에서의 에이전틱 워크플로우 가능성을 보여주었습니다.
핵심 포인트
- M3 Max MacBook Pro 환경에서 Ollama와 Llama-4-70B를 활용한 완전한 로컬 AI 스택 구축 가능
- 에이전트에게 파일 읽기, 코드 편집, Bash 실행 권한을 부여하여 자율적인 리팩터링 루프 구현
- 단순 린팅(unused imports, Clippy 경고)부터 복잡한 로직 평탄화(arrow code 개선)까지 단계적 수행
- 컨텍스트 윈도우 제한으로 인한 변수 기억 상실 등 로컬 LLM 활용 시의 기술적 한계 존재
고백할 것이 있습니다. 저는 레거시 코드 (Legacy Code) 리팩터링을 싫어합니다. 그것은 지루하고, 위험하며, 솔직히 재미도 없습니다. 2026년 1월, 저는 이를 수동으로 하는 것을 그만두기로 결심했습니다. 저는 2023년부터 운영해 온 Rust 기반 API 게이트웨이 사이드 프로젝트의 기술 부채 (Technical Debt)를 처리하기 위해 최신 로컬 LLM 스택을 사용하여 자율 에이전트 (Autonomous Agent)를 설정했습니다. 목표는 간단했습니다. AI가 저의 직접적인 개입 없이 코드 스멜 (Code Smells)을 식별하고, 수정안을 제안하며, 테스트를 실행하게 하는 것이었습니다. 저는 "에이전틱 워크플로우 (Agentic Workflows)"가 마침내 실전에 투입될 준비가 되었는지, 아니면 그저 또 다른 유행 (Hype Cycle)일 뿐인지 확인하고 싶었습니다. 저는 2주간의 시간을 주었습니다. 실제로 일어난 일은 다음과 같습니다.
설정: 오직 로컬 에이전트만 사용
저는 클라우드 기반의 코딩 어시스턴트를 전혀 사용하지 않았습니다. 개인정보 보호는 중요하며, 2026년에는 독점적인 로직을 외부 API로 보내는 것이 잘못된 일처럼 느껴지기 때문입니다. 저는 모든 것을 M3 Max MacBook Pro에서 로컬로 실행했습니다. 스택은 다음과 같았습니다:
모델 (Model): Ollama를 통한 Llama-4-70B (양자화 버전)
오케스트레이터 (Orchestrator): 커스텀 Rust 플러그인이 포함된 OpenDevin 포크
테스트 러너 (Test Runner): 엄격한 커버리지 요구 사항을 가진 Cargo test
가드레일 (Guardrails): 공개 API 시그니처를 변경하는 수정 사항을 거부하도록 훈련된 별도의 소형 모델
저는 에이전트가 매일 밤 새벽 2시에 src/ 디렉토리를 스캔하도록 설정했습니다. 에이전트에게는 브랜치를 생성하고, 변경 사항을 커밋하며, 풀 리퀘스트 (Pull Request)를 열 수 있는 권한이 있었습니다. 하지만 이를 머지 (Merge)할 권한은 없었습니다. 그 부분은 여전히 저의 몫이었습니다. 제가 에이전트의 핵심 루프에 사용한 구성 스니펫은 다음과 같습니다:
{ "agent_config" : { "model" : "llama4:70b-q4_K_M" , "max_iterations" : 50 , "tools" : [ "file_reader" , "code_editor" , "bash_runner" ], "constraints" : [ "no_changes_to_public_traits" , "maintain_95_percent_test_coverage" , "zero_clippy_warnings" ], "rollback_strategy" : "git_reset_hard_on_failure" } }
이 설정에는 API 비용이 0달러 들었습니다. 대신 초기 설정에 약 4시간 정도의 시간이 소요되었습니다. 저는 그 시간의 대부분을 컨텍스트 윈도우 (Context Window) 제한과 싸우는 데 보냈습니다. 에이전트는 세 번의 턴 동안 건드리지 않은 파일의 변수 이름을 계속 잊어버리곤 했습니다.
1주 차: 허니문 단계
첫 3일은 인상적이었습니다.
에이전트는 사용되지 않는 임포트 (unused imports) 14건을 찾아냈고, 8개의 사소한 Clippy 경고를 수정했습니다. 이는 손쉬운 작업 (low-hanging fruit)입니다. 어떤 린터 (linter)라도 할 수 있는 일이죠. 하지만 에이전트는 이들을 논리적인 커밋 (commits) 단위로 그룹화하고 괜찮은 커밋 메시지를 작성했습니다. 4일 차에 에이전트는 첫 번째 실제 리팩터링 (refactor)을 시도했습니다. 인증 모듈에서 6단계나 중첩된 복잡한 매치 문 (match statement)을 식별해냈습니다. 이것은 전형적인 "화살표 코드 (arrow code)"입니다. 에이전트는 조기 반환 (early returns)과 헬퍼 함수 (helper functions)를 사용하여 이를 평탄화 (flattening)할 것을 제안했습니다. 저는 PR (Pull Request)을 검토했습니다. 로직은 타당했습니다. 테스트도 통과했습니다. 저는 이를 머지 (merge)했습니다. 제가 똑똑하게 느껴졌습니다. 시스템을 해킹한 것 같은 기분이었습니다. 지루한 작업을 떠넘김으로써 몇 시간의 정신적 에너지를 아끼고 있었습니다. 그 주에 약 3시간을 절약했다고 추정했습니다. 그러다 5일 차가 되었습니다.
2주 차: 컨텍스트 붕괴 (The Context Collapse)
에이전트가 자신감을 갖기 시작했습니다. 너무 과한 자신감이었습니다. 에이전트는 여러 모듈에 걸쳐 에러 처리 (error handling) 패턴을 수정하기 시작했습니다. Rust에서 에러 전파 (error propagation)는 매우 특수합니다. 모든 호출자 (caller)를 확인하지 않고는 단순히 Result<T, E> 타입을 바꿀 수 없습니다. 에이전트는 다른 크레이트 (crate)에 있는 두 명의 호출자를 놓쳤습니다. CI 파이프라인이 실패했습니다. 한 번이 아니라 연속으로 열두 번이나 말이죠. 저는 에이전트가 빌드를 "수정"하려고 시도하는 로그를 지켜보았습니다. 에이전트는 에러를 패치하기 위해 더 많은 코드를 추가했습니다. 근본 원인 (root cause)을 이해하지 못한 것입니다. 질병이 아니라 증상을 치료하고 있었습니다. 각각의 수정은 두 개의 새로운 버그를 유발했습니다. 8일 차가 되었을 때, 저는 15개의 열린 PR을 가지고 있었습니다. 12개는 망가져 있었고, 3개는 의심스러웠습니다. 저는 시작할 때보다 더 나빠진 코드를 검토하는 데 6시간을 보냈습니다. 로컬 모델 (local model)은 장기 의존성 (long-range dependencies) 문제로 고전했습니다. 프로젝트 전체 그래프를 컨텍스트 윈도우 (context window)에 담아둘 수 없었습니다. models.rs에서 구조체 (struct)를 변경했을 때, 세 폴더 떨어진 api_handlers.rs에서 해당 구조체가 어떻게 직렬화 (serialized)되는지 잊어버렸습니다. 저는 개입해야 했습니다. 에이전트를 일시 중지시켰습니다. 수동으로 망가진 부분을 수정했습니다. 그러고 나서 제약 사항을 강화했습니다.
숫자는 거짓말을 하지 않습니다
14일이 끝난 후, 저는 데이터를 분석했습니다. 실험 전후의 레포지토리 (repo) 상태를 비교했습니다.
| 지표 | 실험 전 | 실험 후 | 변화 |
|---|---|---|---|
| 총 코드 라인 수 (Total Lines of Code) | 12,450 | 12,890 | +440 |
| Clippy 경고 (Clippy Warnings) | 42 | 3 | -39 |
| 테스트 커버리지 (Test Coverage) | 88% | 87.5% | -0.5% |
| 순환 복잡도 (Cyclomatic Complexity) | 14.2 (평균) | 12.1 (평균) | -2.1 |
| 인간 리뷰 시간 (Human Review Hours) | 0 | 18 | +18 |
| 발생한 버그 (Bugs Introduced) | 0 | 7 | +7 |
순환 복잡도 (Cyclomatic Complexity)의 감소는 서류상으로는 좋아 보입니다. 중첩 (nesting) 측면에서 코드는 기술적으로 더 "깔끔해"졌습니다. 하지만 다른 열들을 보십시오. 라인 수는 늘어났습니다. 에이전트 (agent)는 장황한 변수 이름과 추가적인 헬퍼 함수 (helper functions)를 선호합니다. 자신의 로직을 설명하기 위해 상용구 코드 (boilerplate)를 추가합니다. 테스트 커버리지 (test coverage)는 약간 하락했습니다. 에이전트는 새로운 테스트를 작성했습니다. 💡 추가 읽기 : 저는 AI 자동화와 오픈 소스 도구에 대해 실험합니다. Pi Stack에서 더 많은 가이드를 찾아보세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기