본문 바로가기
IT적응기

레거시 코드 리팩토링: AI와 함께 코드를 살린 실전 경험

by IT적응기 2026. 5. 24.

레거시 코드 리팩토링: AI와 함께 10년 된 코드를 살린 실전 경험 참조 이미지
레거시 코드 리팩토링

솔직히 저는 그 코드를 처음 열었을 때 무슨 일이 일어나고 있는지 전혀 몰랐습니다. 7년 된 PHP 5.6 쇼핑몰 백엔드, 변수명은 $a와 $b, 함수 하나가 300줄. 인수인계 문서는 없었고, 짠 사람은 이미 퇴사한 상태였습니다. 그 코드와 씨름하면서 AI를 쓰고, 실패하고, 다시 쪼개 붙이는 3개월을 겪고 나서야 레거시 코드 리팩토링이 무엇인지 몸으로 이해하게 됐습니다.

코드 분석: AI에게 물어보는 것부터 시작했습니다

처음엔 정말 막막했습니다. 어디서부터 손을 대야 할지 감조차 오지 않았습니다. 그래서 제가 직접 써봤는데, 가장 먼저 한 일이 Claude에게 함수 전체를 붙여넣고 "이 코드가 뭘 하는지 설명해줘"라고 묻는 것이었습니다. 300줄짜리 함수를 그대로 넣었는데, 생각보다 정확하게 로직의 흐름을 짚어줬습니다. 주문 처리 단계가 어디서 시작되고, 재고 차감이 어느 시점에 일어나는지를 순서대로 정리해줬을 때는 솔직히 좀 놀랐습니다.

여기서 정적 분석(Static Analysis)이란 코드를 실제로 실행하지 않고 구조와 흐름을 파악하는 방식을 의미합니다. AI가 코드를 읽어 설명해주는 것도 일종의 정적 분석에 해당하는데, 사람이 직접 읽는 것보다 빠르게 전체 구조를 잡아준다는 장점이 있습니다.

다만 제 경험상 이건 좀 다릅니다. AI가 코드의 동작은 꽤 잘 파악하지만, "왜 이 로직이 이렇게 짜였는가"는 끝내 설명하지 못했습니다. 그 쇼핑몰에는 특정 업체 코드를 우선 처리하는 조건문이 있었는데, AI는 단순히 "조건 분기입니다"라고 했습니다. 나중에 옛 슬랙 기록을 뒤져보니 해당 업체와 특별 계약이 있었기 때문이었습니다. 비즈니스 컨텍스트는 결국 사람이 파악해야 합니다.

테스트 커버리지: 건드리기 전에 반드시 깔아야 하는 안전망

코드 분석이 어느 정도 됐다 싶을 때, 저는 바로 리팩토링에 들어가려다 멈췄습니다. 이전에 한 번 전체를 뜯으려다 배포 직전에 롤백한 경험이 있었기 때문입니다. 그때 느낀 건, 테스트 없이 건드리는 건 눈 감고 수술하는 것과 다를 바 없다는 것이었습니다.

테스트 커버리지(Test Coverage)란 전체 코드 중 테스트가 실제로 실행되는 비율을 의미합니다. 예를 들어 커버리지가 80%라면 코드의 80%가 테스트를 통해 검증됐다는 뜻입니다. 마이클 페더스는 그의 저서 "레거시 코드 활용 전략"에서 테스트 코드가 없는 코드 자체를 레거시 코드로 정의했습니다. 문서가 없거나 작성자가 퇴사했다는 기준이 아니라, 테스트 유무가 핵심이라는 시각입니다(출처: Martin Fowler의 리팩토링 블로그).

Claude에게 단위 테스트 초안을 만들어달라고 했을 때는 꽤 쓸 만한 결과가 나왔습니다. 그런데 제가 직접 써봤는데, AI가 제안한 테스트 코드가 기존 동작을 미묘하게 다르게 가정하는 경우가 있었습니다. 특히 예외 처리 흐름에서 그런 일이 생겼고, 그 테스트를 그대로 믿고 넘어갔다면 나중에 큰 문제가 됐을 것입니다. AI 제안을 참고하되 반드시 사람이 검토해야 하는 이유가 여기 있습니다.

리팩토링에 들어가기 전 테스트 커버리지를 확보할 때 저는 다음 순서를 지켰습니다.

  • 가장 자주 호출되는 핵심 함수부터 테스트 작성
  • 엣지 케이스(빈 값, 음수, 최대값 등) 별도로 케이스 분리
  • AI가 생성한 테스트 초안을 사람이 직접 리뷰 후 확정
  • 커밋 전 전체 테스트 자동 실행 파이프라인 연결

이 네 단계를 지키지 않았을 때 어떻게 됐는지는 이미 롤백 경험으로 충분히 배웠습니다.

점진적 마이그레이션: 한 번에 뜯으면 반드시 터집니다

배포 직전 롤백했던 그날을 저는 아직도 기억합니다. 전체 주문 처리 모듈을 한꺼번에 교체하려다 실패했습니다. QA 환경에서는 문제없었는데 운영 환경에서 결제 연동 부분이 틀어졌고, 결국 서비스를 잠깐 내려야 했습니다. 그 이후로 저는 절대 큰 단위로 한 번에 교체하지 않습니다.

그때부터 적용한 것이 스트랭글러 피그 패턴(Strangler Fig Pattern)입니다. 스트랭글러 피그 패턴이란 기존 시스템을 한 번에 교체하지 않고, 새로운 코드가 기존 코드를 서서히 감싸 대체하는 방식을 의미합니다. 마치 덩굴 식물이 나무를 천천히 감아 올라가 결국 대체하는 모습에서 이름을 따왔습니다. 마틴 파울러가 정리한 이 패턴은 레거시 시스템 전환에서 가장 현실적인 접근법으로 꼽힙니다(출처: Martin Fowler - Strangler Fig Application).

제 경우는 3개월에 걸쳐 기능 단위로 쪼개 마이그레이션했습니다. 주문 조회 → 재고 연동 → 결제 처리 순으로 하나씩 교체했고, 각 단계마다 기존 코드와 새 코드를 병렬로 운영하며 결과를 비교했습니다. 느리고 답답하게 느껴질 수 있지만, 이 방식이 결과적으로 훨씬 안전했습니다.

리팩토링의 핵심은 외부 동작을 바꾸지 않으면서 내부 구조만 개선하는 것입니다. 이 원칙을 지키려면 속도보다 범위 관리가 더 중요합니다. 욕심을 줄이고, 작게 끊고, 자주 배포하는 것이 제가 그 3개월 동안 배운 가장 중요한 교훈이었습니다.

네이밍과 구조 개선: AI가 의외로 잘하는 일

테스트를 깔고, 점진적으로 교체하는 방식을 정착시킨 뒤에는 코드 품질 자체를 올리는 작업을 병행했습니다. 여기서 AI가 의외로 큰 도움이 됐습니다. $a, $b 같은 변수명을 보고 "이 변수의 역할에 맞는 이름을 제안해줘"라고 물으면, 컨텍스트를 읽고 $orderQuantity나 $discountRate 같은 이름을 제안해줬습니다. 제가 직접 써봤는데, 네이밍 작업에서는 체감 속도가 확실히 빨라졌습니다.

순환 복잡도(Cyclomatic Complexity)란 하나의 함수 안에 분기 경로가 얼마나 많은지 수치로 나타낸 것입니다. 이 수치가 높을수록 함수가 복잡하고 테스트하기 어려우며 유지보수 비용도 올라갑니다. 300줄짜리 함수는 대부분 이 수치가 매우 높은데, AI는 이런 함수를 역할 단위로 쪼개는 초안을 제시하는 데 꽤 쓸 만합니다.

일반적으로 AI 리팩토링 제안을 그대로 쓰면 된다고 생각하는 분들도 있는데, 저는 그렇게 보지 않습니다. AI가 제안한 구조가 기존 동작을 미묘하게 바꾸는 경우를 실제로 경험했기 때문입니다. 특히 사이드 이펙트(Side Effect)가 있는 함수, 즉 함수가 반환값 외에 외부 상태를 변경하는 함수를 분리할 때 AI는 그 부수 효과를 놓치는 경우가 있었습니다. 이 부분은 테스트 커버리지가 있어야만 잡아낼 수 있었습니다.

결국 AI는 반복적이고 패턴 기반의 작업에서 강하고, 비즈니스 로직의 맥락을 이해하는 데는 한계가 있습니다. 두 가지를 구분해서 쓰는 것이 핵심입니다.

3개월간의 마이그레이션이 끝났을 때, 코드베이스의 평균 함수 길이가 300줄에서 30줄 수준으로 줄었습니다. 테스트 커버리지는 0에서 약 72%까지 올라갔습니다. 그 숫자보다 더 체감됐던 건, 새 기능을 붙일 때 겁이 없어졌다는 것입니다. 레거시 코드를 다루는 분들이 있다면, 전체를 한 번에 바꾸려는 욕심을 내려놓고 테스트 하나, 함수 하나씩 쪼개 나가는 것부터 시작해보시길 권합니다. 그게 가장 느린 것 같지만 실제로는 가장 빠른 길이었습니다.


참고: https://www.youtube.com/watch?v=예시_레거시리팩토링


소개 및 문의 · 개인정보처리방침 · 면책조항

© 2026 깜짝,황금이 아빠 IT적응기

서치어드바이저