대규모 리팩토링은 모든 시니어 개발자가 한 번쯤은 맞닥뜨리는 도전입니다. 수십만 줄의 레거시 코드, 사라진 문서, 떠나간 원작자, 그리고 "건드리면 안 된다"는 팀 내부의 구전 설화가 뒤얽힌 코드베이스. 이런 환경에서 새로운 기능을 추가하기 위해 내부를 재구성해야 할 때, 대부분의 프로젝트는 일정 초과와 회귀 버그로 고통을 겪습니다. Claude Code는 이 게임의 규칙을 바꿉니다. 1M 토큰 컨텍스트로 전체 코드를 한 번에 읽고, 숨겨진 의존성을 찾아주며, 자동화된 검증까지 한 세션에서 수행할 수 있기 때문입니다. 이 글에서는 AI 시대의 대규모 리팩토링 방법론을 9단계로 정리합니다.
1. 코드 아카이올로지: 과거를 발굴하기
모든 리팩토링은 현재 상태에 대한 깊은 이해에서 출발해야 합니다. Claude Code에 "이 프로젝트의 src 디렉터리를 읽고, 모듈별 책임과 의존 관계를 정리해 줘"라고 요청하는 것만으로도 수 시간 분량의 탐색을 몇 분으로 줄일 수 있습니다. 여기서 중요한 것은 Claude가 생성한 지도를 "살아있는 문서"로 만드는 것입니다. 즉, docs/ARCHITECTURE.md에 저장하고, 리팩토링 중 구조가 바뀔 때마다 Claude에게 업데이트를 요청하세요.
claude "src 디렉터리를 분석해 다음 형식으로 ARCHITECTURE.md를 작성해줘.
1. 핵심 도메인 모듈 (역할, 주요 클래스)
2. 인프라 계층 (DB, 외부 API, 큐)
3. 순환 의존성 혹은 의심 구간
4. 테스트 커버리지가 낮은 영역"
2. 리팩토링 목표 정의
리팩토링이 실패하는 가장 큰 이유는 "무엇을 성공으로 볼 것인가"가 모호하기 때문입니다. 목표는 측정 가능해야 합니다. 예를 들어 "결제 모듈을 클래스 기반에서 함수형으로 전환하고, 단위 테스트 커버리지를 40%에서 80%로 높이며, 결제 API 응답시간의 p95를 120ms 이하로 유지한다"처럼 구체적으로 정의해야 합니다. Claude에 "이 리팩토링의 성공 기준을 OKR 형식으로 작성해 줘"라고 요청하면 팀과 정렬하기 쉬운 초안을 얻을 수 있습니다.
3. 안전망 구축: 테스트가 먼저다
레거시 코드의 테스트 커버리지가 낮다면, 리팩토링보다 먼저 해야 할 일이 있습니다. 바로 "현재 동작을 고정하는 특성 테스트(characterization test)"를 작성하는 것입니다. Claude는 이 작업에서 압도적으로 유용합니다. 함수의 현재 동작을 관찰해 입력-출력 쌍을 기반으로 테스트를 자동 생성할 수 있기 때문입니다.
claude "src/billing/invoice.js 의 generateInvoice 함수에 대해
현재 동작을 고정하는 특성 테스트를 Jest로 작성해줘.
다양한 입력 케이스(정상, 할인, 세금 예외, null 입력)를 포함하고,
현재 반환값을 스냅샷으로 캡처해줘."
이렇게 생성된 테스트는 "이상적 동작"이 아니라 "현재 동작"을 고정합니다. 리팩토링 중 실수로 동작이 바뀌면 즉시 알아차릴 수 있게 해주는 안전망입니다.
4. 점진적 변경: 스트랭글러 패턴
대규모 리팩토링을 빅뱅 방식으로 하면 거의 반드시 실패합니다. Martin Fowler가 제안한 스트랭글러 무화과 패턴을 기본 전략으로 삼으세요. 기존 코드와 새 코드를 공존시키고, 트래픽을 점진적으로 새 코드로 이동시키며, 최종적으로 구 코드를 제거하는 방식입니다. Claude에 "현재 모듈을 새 아키텍처로 옮기기 위한 스트랭글러 계획을 단계별 마이그레이션으로 작성해 줘"라고 요청하면, 실행 가능한 계획을 얻을 수 있습니다.
| 단계 | 변경 내용 | 롤백 가능성 |
|---|---|---|
| 1주차 | 새 모듈 추가(미사용) | 파일 삭제로 즉시 롤백 |
| 2주차 | 피쳐 플래그로 5% 트래픽 전환 | 플래그 토글 |
| 3주차 | 50% 전환, 메트릭 비교 | 플래그 토글 |
| 4주차 | 100% 전환, 구 모듈 유지 | 플래그 토글 |
| 5주차 | 구 모듈 제거 | git revert |
5. 자동화된 코드 변환
이름 변경, 시그니처 변경, 모듈 이동 같은 기계적 작업은 Claude에게 일괄 처리시키는 편이 안전합니다. 다만 변경 범위를 명시하고, 변경 후 반드시 테스트를 돌리게 하세요. 변경 자체가 커밋 단위로 작은 것이 핵심입니다. "하나의 커밋은 하나의 의미"를 지키면 나중에 문제가 생겼을 때 정확히 어느 커밋이 원인인지 추적하기 쉽습니다.
claude "사용자 ID를 받는 모든 함수에서 'userId: string' 인자를
'userId: UserId' 브랜드 타입으로 변경해줘.
- 영향받는 파일 먼저 나열
- 각 파일별로 커밋을 분리
- 테스트가 통과하는지 확인 후 다음 파일로 이동"
6. 의존성 역전과 경계 도입
레거시 모듈이 깊게 얽혀 있을 때는 의존성 역전 원칙(DIP)을 활용해 경계를 먼저 만드는 것이 효과적입니다. Claude에 "이 모듈의 외부 의존성을 포트-어댑터 패턴으로 추상화해 줘"라고 요청하면, 인터페이스를 추출하고 구현을 분리한 모범적인 구조를 제안받을 수 있습니다. 경계가 생기면 구현을 바꾸는 일이 훨씬 안전해지며, 테스트 대역 주입도 용이해집니다.
7. 실시간 회귀 감지
리팩토링 중에는 회귀가 발생하기 쉽습니다. 이를 조기에 잡으려면 다음 세 가지 자동화를 갖추는 것이 좋습니다.
- PR 단위 테스트 실행: 모든 커밋에서 관련 테스트가 자동으로 실행되도록 CI를 구성.
- Claude 자동 리뷰: PR이 열릴 때 Claude GitHub Action이 변경사항을 요약하고, 의심 구간을 표시.
- 성능 스냅샷: 주요 엔드포인트의 응답 시간을 CI에서 기록하고, 이전 커밋 대비 유의미한 변화를 감지.
특히 Claude GitHub Action은 리팩토링 PR에서 인간 리뷰어가 놓치기 쉬운 미묘한 동작 변화를 잡아내는 데 탁월합니다. 자세한 설정은 GitHub Actions 가이드를 참고하세요.
8. 병렬 리팩토링 워크트리 활용
Claude Code의 워크트리 기능은 대규모 리팩토링에서 진가를 발휘합니다. 서로 독립적인 모듈을 여러 워크트리에서 병렬로 리팩토링하고, 각 워크트리의 브랜치를 개별적으로 머지할 수 있습니다. 한 워크트리에서의 실험이 다른 워크트리의 진행을 방해하지 않으므로, 여러 가설을 동시에 시도하기에도 이상적입니다.
# 워크트리로 실험적 리팩토링 분리
claude --worktree feature/strangle-billing
# 메인 트리에서는 일상적인 버그 수정을 계속 수행
9. 문서화와 지식 이전
리팩토링이 끝나면 반드시 "왜 이렇게 바꿨는가"를 문서로 남겨야 합니다. 이때 Claude에 "지금까지의 대화와 커밋을 토대로 ADR(Architecture Decision Record)을 작성해 줘"라고 요청하세요. 형식은 Michael Nygard 스타일이 무난합니다: 제목, 상태, 배경, 결정, 결과. ADR은 "왜 우리가 이렇게 했는지"를 미래의 팀원에게 설명해 주며, 같은 논의를 반복하지 않게 해 줍니다.
흔한 함정과 극복법
기능 추가를 함께 하지 마라
리팩토링 PR에 새 기능을 섞으면 리뷰 난도가 기하급수적으로 올라갑니다. Claude에 "이 변경이 순수 리팩토링인지, 기능 변경이 섞였는지 분류해 줘"라고 주기적으로 확인하세요. 순수 리팩토링만 담긴 PR은 거의 자동 승인이 가능하지만, 기능이 섞이면 훨씬 엄격한 리뷰가 필요합니다.
완벽주의의 덫
리팩토링은 "완성"되는 것이 아닙니다. "충분히 좋은" 상태에 도달했다면 멈추고 배포하세요. Claude에 "추가 리팩토링으로 얻는 가치 대비 위험이 더 높은 지점을 알려 줘"라고 물으면, 객관적인 판단 근거를 얻을 수 있습니다.
팁: 주당 "리팩토링 예산"을 정하고 그 안에서만 개선 작업을 하세요. 예산이 끝나면 다음 주 몫으로 넘깁니다. 이렇게 하면 "끝없는 개선"이라는 블랙홀에 빠지지 않습니다.
테스트 없는 리팩토링
테스트 없이 리팩토링하는 것은 맨몸으로 절벽을 오르는 것과 같습니다. 반드시 특성 테스트를 먼저 만들고, 그 위에서 변경을 쌓으세요. Claude는 이 과정 전체를 동반해 줄 수 있습니다.
사례 연구: 모놀리식 Rails 앱의 모듈화
한 SaaS 기업은 8년 된 Rails 모놀리스를 6개월에 걸쳐 모듈 단위로 분해했습니다. 팀은 Claude Code를 네 가지 용도로 사용했습니다. 첫째, 초기 분석 단계에서 모델/컨트롤러/서비스 수백 개의 관계를 지도화. 둘째, 각 경계 내부의 순환 의존을 Claude에 지시해 자동으로 제거. 셋째, 월 단위 스프린트 전에 Claude가 다음 달 목표 모듈의 특성 테스트를 생성. 넷째, 매 PR마다 Claude가 자동 리뷰를 수행. 그 결과 6개월 후, 배포 시간은 15분에서 3분으로, 신규 기능 개발 속도는 2배 이상 향상되었고, 프로덕션 인시던트는 40% 감소했습니다.
PR 단위 설계의 예술
대규모 리팩토링을 하나의 거대한 PR에 담는 것은 조직 차원의 실패로 이어집니다. 리뷰어가 10분 안에 파악할 수 있어야 리뷰가 제때 이루어집니다. Claude에 "이 변경을 리뷰 친화적인 PR 시리즈로 쪼개 줘"라고 요청할 때는 다음 기준을 함께 전달하세요.
- 각 PR은 독립적으로 머지 가능해야 한다(빌드, 테스트 통과).
- 각 PR은 하나의 목적만 가진다(이동, 변경, 제거를 섞지 않는다).
- 순수 이동(move) PR은 반드시 diff가 없거나 최소여야 한다.
- 각 PR의 설명에는 "이 PR이 하지 않는 것"도 명시한다.
이렇게 쪼갠 PR 시리즈는 리뷰어가 "읽기 지루한 기계적 변경"과 "신중한 판단이 필요한 의미 변경"을 분리해서 볼 수 있게 해 주며, 리뷰 속도와 정확도를 동시에 높여 줍니다.
의미 보존의 증명
리팩토링의 정의 자체가 "외부 동작을 유지한 채 내부 구조를 개선하는 것"이므로, 변경 전후의 의미 동등성을 증명하는 습관이 중요합니다. Claude에 "이 변경이 의미상 동등함을 보여 주는 근거를 나열해 줘"라고 요청하면, 단위 테스트 결과, 계약 테스트, 스냅샷 비교, 타입 수준의 동등성 같은 증거들을 모아 줍니다. 이 증거들이 PR 설명에 포함되면 리뷰어의 신뢰가 크게 높아지고, 머지 후의 사고 가능성도 줄어듭니다.
리팩토링 체크리스트
- 아키텍처 문서를 최신화했는가?
- 성공 기준이 측정 가능한가?
- 특성 테스트 안전망이 있는가?
- 빅뱅이 아닌 점진적 방식인가?
- 각 커밋이 단일 의미인가?
- CI에서 회귀 감지가 자동인가?
- 기능 추가와 분리되어 있는가?
- ADR을 남겼는가?
- 추가 개선의 ROI를 평가했는가?
마무리: 리팩토링은 조직 활동이다
리팩토링은 본질적으로 개인의 기술적 작업이 아니라 조직의 지속가능성 문제입니다. 코드를 읽기 쉽게 만들고, 변경하기 안전하게 만들고, 배우기 쉽게 만드는 모든 활동이 곧 팀의 생산성과 직결됩니다. Claude Code는 이 활동을 사람이 혼자 짊어지지 않도록 돕는 강력한 파트너입니다. 1M 컨텍스트와 자동 리뷰, 테스트 생성, 계획 수립을 결합하면, 과거에는 수개월이 걸리던 작업을 수 주 만에 안전하게 수행할 수 있습니다.
하지만 가장 중요한 것은 여전히 사람입니다. 기준을 정하고, 균형을 잡고, 이해관계자를 설득하고, 언제 멈출지 판단하는 일은 여러분의 몫입니다. Claude를 도구가 아니라 동료로 여기고, 오늘부터 작은 영역에서 스트랭글러 패턴을 시도해 보세요. 한 달 뒤에는 놀라운 결과를 보게 될 것입니다.
리팩토링의 목표는 깨끗한 코드가 아니라, 내일도 변경할 수 있는 코드입니다.