📑 목차
Image by Pexels on Pixabay
왜 리팩터링인가? 혼돈 속에서 길을 찾다
혹시 이런 경험 있으신가요? 갓 합류한 프로젝트의 코드를 열어봤을 때, 혹은 몇 달 전 내가 작성했던 코드인데도 불구하고 눈앞이 캄캄해지는 순간 말입니다. 변수 이름은 의미를 알 수 없고, 함수는 수백 줄을 넘나들며, 조건문과 반복문이 겹겹이 쌓여 마치 스파게티처럼 얽혀 있는 코드. 한 줄을 수정하려 해도 어디서 어떤 부작용이 터질지 몰라 두려움에 떨었던 적은 없으신가요?
많은 개발자들이 이러한 레거시 코드의 늪에서 허우적대곤 합니다. 기능 추가는 거북이처럼 느려지고, 버그는 끊임없이 발생하며, 팀원들은 코드 베이스에 대한 이해도가 낮아지면서 의욕마저 상실하게 되죠. 저 또한 이런 문제에 직면하며 매번 좌절감을 느끼곤 했습니다. "과연 이 코드를 깔끔하게 만들 수는 있을까?", "어떻게 하면 안전하게 코드를 개선할 수 있을까?" 하는 질문들이 머릿속을 떠나지 않았습니다.
이러한 고민의 해답을 찾던 중, 개발 커뮤니티에서 끊임없이 회자되던 한 권의 책을 접하게 되었습니다. 바로 마틴 파울러의 '리팩터링: 기존 코드를 개선하고 유지보수성을 높이는 실전 기법' 도서입니다. 이 책은 단순히 깨끗한 코드를 지향하는 것이 아니라, 실질적인 기법과 단계별 가이드를 통해 기존 코드를 점진적으로 개선해 나가는 방법을 제시합니다. 저처럼 엉망진창인 코드 앞에서 막막함을 느끼던 개발자들에게는 한 줄기 빛과 같은 존재일 것입니다.
이 책을 선택한 이유와 기대했던 점
제가 이 책을 집어 든 가장 큰 이유는 바로 '실전 기법'이라는 문구 때문이었습니다. 이론적인 클린 코드 원칙은 많지만, 당장 복잡한 레거시 코드에 어떤 것부터 손을 대야 할지 막막한 경우가 태반이었습니다. 제가 필요했던 것은 추상적인 원칙보다는, 특정 상황에서 '어떻게' 코드를 개선해야 하는지에 대한 구체적인 방법론이었습니다.
또한, 프로젝트의 규모가 커지고 여러 개발자가 협업하면서, 코드의 유지보수성은 단순한 효율성 문제를 넘어섰습니다. 버그가 발생했을 때 빠르게 원인을 파악하고 수정하는 능력, 새로운 기능이 추가될 때 기존 코드에 미치는 영향을 최소화하는 능력, 그리고 신규 팀원이 빠르게 코드를 이해하고 기여할 수 있도록 돕는 능력. 이 모든 것이 결국 코드의 유지보수성에 달려 있다는 것을 절감했습니다. 이 책이 이러한 문제들을 해결해 줄 수 있을 것이라는 기대를 가지고 있었습니다.
저는 이 책을 통해 다음과 같은 질문에 대한 답을 얻고자 했습니다:
- 어떤 코드가 '나쁜 코드'인지 객관적으로 판단하는 기준은 무엇인가?
- 안전하게 리팩터링을 수행하기 위한 가장 기본적인 절차는 무엇인가?
- 자주 사용되는 리팩터링 패턴에는 어떤 것들이 있으며, 언제 어떻게 적용해야 하는가?
- 리팩터링이 개발 생산성과 팀 문화에 어떤 긍정적인 영향을 미칠 수 있는가?
이러한 기대감을 안고 책을 펼쳤을 때, 저는 생각보다 훨씬 더 체계적이고 실용적인 지식들을 만나게 되었습니다.
핵심 내용 요약: 실전 리팩터링 기법 심층 분석
'리팩터링' 책은 단순한 코드 개선을 넘어, 소프트웨어 개발의 본질적인 고민을 해결해 주는 지침서와 같았습니다. 이 책의 핵심 내용을 요약하자면 다음과 같습니다.
리팩터링의 기본 원칙과 코드 냄새
책은 리팩터링을 "외부 동작을 변경하지 않으면서 내부 구조를 개선하는 작업"이라고 정의합니다. 이 정의에서 가장 중요한 부분은 바로 '외부 동작 변경 없음'입니다. 즉, 리팩터링은 기능 변경이 아니며, 반드시 테스트 코드가 수반되어야 함을 강조합니다. 작은 단계로 나누어 변경하고, 매 단계마다 테스트를 통해 안전성을 확보하는 것이 핵심이죠.
또한, 리팩터링의 필요성을 느끼게 하는 '코드 냄새(Code Smells)' 개념을 소개합니다. 이는 특정 코드 패턴이 잠재적인 문제나 개선의 여지를 가지고 있음을 암시하는 일종의 지표입니다. 예를 들어,
- 기묘한 이름(Mysterious Name): 변수나 함수 이름만으로는 그 목적을 알 수 없는 경우
- 긴 함수(Long Function): 한 함수가 너무 많은 일을 하거나 지나치게 긴 경우
- 중복 코드(Duplicated Code): 동일하거나 매우 유사한 코드가 여러 곳에 존재하는 경우
- 긴 매개변수 목록(Long Parameter List): 함수에 너무 많은 인자가 전달되는 경우
- 전역 데이터(Global Data): 전역 변수를 과도하게 사용하여 의존성이 복잡해지는 경우
이러한 코드 냄새들을 식별하고, 각 냄새에 대응하는 리팩터링 기법을 적용하는 것이 이 책의 핵심적인 접근 방식입니다. 마치 의사가 환자의 증상을 보고 적절한 처방을 내리듯, 코드 냄새를 파악하고 그에 맞는 리팩터링 기법을 적용하는 것이죠.
주요 리팩터링 패턴과 적용 사례
책은 수십 가지의 리팩터링 패턴을 구체적인 예시 코드와 함께 설명합니다. 몇 가지 인상 깊었던 패턴들을 소개하자면:
- 함수 추출(Extract Function): 너무 긴 함수나 특정 로직을 하나의 독립적인 함수로 분리하여 가독성과 재사용성을 높이는 기법입니다.
- 변수 인라인(Inline Variable): 임시 변수가 코드를 더 복잡하게 만들 때, 그 변수를 제거하고 값을 직접 사용하는 기법입니다.
- 조건부 로직을 다형성으로 바꾸기(Replace Conditional with Polymorphism): 복잡한 if-else 또는 switch-case 문을 객체 지향의 다형성을 활용하여 간결하게 만드는 기법입니다.
- 반복문을 파이프라인으로 바꾸기(Replace Loop with Pipeline): 반복문을 map, filter, reduce와 같은 컬렉션 파이프라인 연산으로 대체하여 코드를 더 선언적이고 읽기 쉽게 만드는 기법입니다.
- 명령 캡슐화(Encapsulate Command): 특정 작업을 수행하는 일련의 과정을 하나의 객체로 캡슐화하여 유연성과 재사용성을 높이는 기법입니다.
이러한 패턴들은 단순히 코드 형태를 바꾸는 것을 넘어, 코드의 응집도(Cohesion)를 높이고 결합도(Coupling)를 낮추는 데 기여합니다. 실제 코드를 통해 전후를 비교해 보면 그 효과를 명확히 이해할 수 있습니다. 예를 들어, 다음과 같이 복잡한 계산 로직이 있는 함수가 있다고 가정해 봅시다.
function calculateOrderTotal(order) {
let total = 0;
for (let i = 0; i < order.items.length; i++) {
let item = order.items[i];
total += item.price * item.quantity;
}
if (order.customer.isPremium) {
total *= 0.9; // 10% premium discount
}
if (total > 500) {
total -= 50; // Large order discount
}
return total;
}
위 코드는 여러 책임(아이템 가격 계산, 프리미엄 할인 적용, 대량 주문 할인 적용)을 한 함수가 모두 처리하고 있습니다. 이를 함수 추출(Extract Function) 기법을 사용하여 리팩터링하면 다음과 같이 훨씬 읽기 쉽고 유지보수하기 좋은 코드가 됩니다.
function calculateOrderTotal(order) {
const baseTotal = calculateBaseTotal(order);
const totalAfterPremiumDiscount = applyPremiumDiscount(baseTotal, order.customer.isPremium);
const finalTotal = applyLargeOrderDiscount(totalAfterPremiumDiscount);
return finalTotal;
}
function calculateBaseTotal(order) {
let total = 0;
for (let item of order.items) {
total += item.price * item.quantity;
}
return total;
}
function applyPremiumDiscount(total, isPremium) {
return isPremium ? total * 0.9 : total;
}
function applyLargeOrderDiscount(total) {
return total > 500 ? total - 50 : total;
}
각 함수가 하나의 책임만 가지게 되어, 특정 로직을 수정하거나 테스트하기가 훨씬 용이해졌습니다. 이러한 작은 변화들이 모여 코드 베이스 전체의 품질을 크게 향상시킬 수 있다는 것을 이 책은 끊임없이 강조합니다.
Image by fancycrave1 on Pixabay
직접 적용해 본 리팩터링 경험과 변화
이 책을 읽고 나서 가장 먼저 시도한 것은, 제가 담당하던 프로젝트의 핵심 모듈 중 하나를 리팩터링하는 것이었습니다. 특히, 결제 처리 로직과 관련된 모듈은 여러 할인 정책과 외부 연동 로직이 복잡하게 얽혀 있어, 새로운 할인 정책을 추가할 때마다 버그가 발생하거나 예상치 못한 사이드 이펙트가 생기곤 했습니다.
실제 프로젝트에서의 적용 사례
이 모듈의 초기 상태는 약 700줄에 달하는 단일 함수였고, 깊은 중첩 조건문과 임시 변수들이 난무했습니다. 저는 이 책에서 배운 '코드 냄새'를 기준으로 문제점을 파악하기 시작했습니다.
- 긴 함수(Long Function): 단일 함수가 너무 많은 책임을 가지고 있었습니다.
- 긴 매개변수 목록(Long Parameter List): 함수 호출 시 10개가 넘는 매개변수를 넘기고 있었습니다.
- 중복 코드(Duplicated Code): 여러 할인 정책 계산 로직에서 유사한 패턴의 코드가 반복되었습니다.
가장 먼저 테스트 코드를 작성하여 기존 기능이 정상 동작하는지 확인하는 것부터 시작했습니다. 기존에 테스트 코드가 전무했기 때문에, 이 과정 자체가 리팩터링의 첫걸음이 되었습니다. 이후, 책에서 제시하는 '함수 추출(Extract Function)' 기법을 활용하여 각 할인 정책별 계산 로직, 결제 수단별 처리 로직 등을 작은 함수들로 분리했습니다.
예를 들어, 기존에는 하나의 거대한 함수 내에서 '프리미엄 할인', '쿠폰 할인', '대량 주문 할인'이 모두 처리되었지만, 리팩터링 후에는 다음과 같이 구조화되었습니다.
// Before: 하나의 거대한 함수
// function processPayment(order, customer, paymentMethod, coupons, ...) { ... }
// After: 추출된 작은 함수들
class PaymentProcessor {
constructor(order, customer, paymentMethod) { /* ... */ }
process() {
let amount = this.calculateBaseAmount();
amount = this.applyCoupons(amount);
amount = this.applyPremiumDiscount(amount);
amount = this.applyVolumeDiscount(amount);
this.executePayment(amount);
return amount;
}
calculateBaseAmount() { /* ... */ }
applyCoupons(amount) { /* ... */ }
applyPremiumDiscount(amount) { /* ... */ }
applyVolumeDiscount(amount) { /* ... */ }
executePayment(amount) { /* ... */ }
}
이렇게 리팩터링을 진행한 결과, 놀라운 변화를 경험할 수 있었습니다. 기존 700줄이 넘던 함수는 100줄 미만의 오케스트레이션 함수와 여러 개의 작은 책임 함수들로 분리되었고, 총 코드 라인 수는 오히려 20%가량 감소했습니다. 무엇보다 중요한 것은:
- 버그 감소: 새로운 할인 정책 추가 시 발생하던 버그 발생률이 50% 이상 감소했습니다. 각 로직이 독립적으로 테스트 가능해졌기 때문입니다.
- 개발 속도 향상: 새로운 기능 추가에 필요한 시간이 약 30% 단축되었습니다. 특정 로직만 수정하면 되므로, 전체 코드를 이해할 필요가 없어졌기 때문입니다.
- 코드 이해도 증가: 신규 팀원이 해당 모듈을 이해하는 데 걸리는 시간이 절반 이하로 줄었습니다. 명확한 함수 이름과 작은 책임 덕분입니다.
팀 문화와 생산성에 미친 영향
개인적인 코드 개선을 넘어 팀 전체에도 긍정적인 영향을 미쳤습니다. 제가 리팩터링한 모듈을 시작으로, 다른 팀원들도 점차 리팩터링의 중요성을 인식하고 자발적으로 코드 개선에 동참하기 시작했습니다. 코드 리뷰 시에도 "여기는 어떤 리팩터링 기법을 적용하면 좋을 것 같다"는 구체적인 피드백이 오가기 시작했습니다.
이는 단순히 코드 품질 향상을 넘어, 개발 생산성과 팀워크에도 큰 영향을 주었습니다. 복잡한 코드 때문에 망설였던 개선 사항들이 빠르게 반영될 수 있었고, 각자의 코드가 전체 시스템에 미치는 영향을 더욱 명확하게 인지하게 되었습니다. 또한, 테스트 커버리지가 점진적으로 증가하면서, 코드 변경에 대한 팀원들의 자신감이 눈에 띄게 상승했습니다. "이 부분은 테스트가 잘 되어 있으니 안심하고 고쳐도 된다"는 말을 들었을 때, 리팩터링이 단순한 기술적 작업을 넘어 개발자의 심리적 안정감까지 제공한다는 것을 깨달았습니다.
이 책의 장점과 아쉬운 점
이 책을 통해 얻은 것이 너무나 많지만, 객관적인 관점에서 장점과 아쉬운 점을 정리해 보았습니다.
장점
- 압도적인 실용성: 이론적인 설명보다는 실제 코드를 통한 '어떻게'에 초점을 맞춥니다. 각 리팩터링 기법마다 구체적인 적용 전/후 코드를 보여주며, 심지어 '이런 경우는 리팩터링 하지 마라'는 조언까지 줍니다.
- 단계별 가이드: 복잡한 리팩터링도 작은 단계로 쪼개어 설명합니다. 이는 특히 초보 개발자나 레거시 코드에 처음 도전하는 개발자에게 큰 도움이 됩니다. '안전하게' 코드를 변경하는 방법을 체득할 수 있습니다.
- 풍부한 예시: 자바스크립트 예시 코드를 중심으로 설명하지만, 언어의 본질적인 구조 개선에 대한 내용이므로 다른 언어 사용자에게도 충분히 적용 가능합니다. 다양한 상황에 맞는 예시들이 이해를 돕습니다.
- 리팩터링의 본질 강조: 단순히 기술적인 기법 나열에 그치지 않고, 왜 리팩터링을 해야 하는지, 리팩터링이 소프트웨어 개발에 어떤 가치를 제공하는지에 대한 철학적 통찰을 제공합니다. 이는 개발자의 사고방식을 변화시키는 데 큰 영향을 줍니다.
아쉬운 점
- 방대한 분량: 책의 내용이 매우 상세하고 풍부하여, 처음 접하는 독자에게는 다소 압도적인 분량으로 느껴질 수 있습니다. 처음부터 끝까지 정독하기보다는, 필요에 따라 찾아보는 사전식 활용도 좋은 방법입니다.
- 예시 코드의 현대적 감각: 책의 기본적인 내용은 시대를 초월하는 보편적인 원칙이지만, 일부 예시 코드가 작성된 시점에 따라 특정 언어의 최신 문법이나 트렌드를 반영하지 못할 수 있습니다. 하지만 핵심 원리는 변함없으므로 크게 문제되지 않습니다.
이 책은 다른 유명한 개발 서적들과 비교했을 때, 실질적인 문제 해결에 초점을 맞춘다는 점에서 독특한 강점을 가집니다. 아래 표는 이 책과 몇몇 유명 개발 서적들의 특징을 비교한 것입니다.
| 특징 | 리팩터링: 기존 코드를 개선하고 유지보수성을 높이는 실전 기법 | 클린 코드: 애자일 소프트웨어 장인 정신 | 실용주의 프로그래머 |
|---|---|---|---|
| 주요 초점 | 기존 코드 개선 및 유지보수, 안전한 변경 | 좋은 코드 작성 원칙, 설계 철학, 장인 정신 | 개발 프로세스, 생산성, 개발자의 태도 |
| 실용성 | 매우 높음 (단계별 기법과 구체적인 예시 중심) | 높음 (원칙과 패턴을 통한 코드 개선) | 높음 (다양한 개발 상황에 대한 조언) |
| 대상 독자 | 레거시 코드 개선이 필요한 모든 개발자 | 클린 코드를 지향하는 모든 개발자 | 초보부터 숙련자까지, 개발 전반에 관심 있는 개발자 |
| 접근 방식 | 구체적인 '어떻게'에 집중, 도구와 패턴 제공 | '무엇을'과 '왜'에 대한 철학적 접근, 원칙 제시 | 다양한 주제에 대한 팁과 지침, 마인드셋 강조 |
결론적으로 '리팩터링'은 클린 코드의 철학을 실천으로 옮기는 방법을 구체적으로 알려주는 책이며, 이는 다른 어떤 책에서도 얻기 힘든 독보적인 강점이라고 생각합니다.
Image by jamesmarkosborne on Pixabay
누구에게 이 책을 추천하는가?
이 책은 특정 경력의 개발자에게만 국한되지 않고, 코드 품질 향상에 관심 있는 모든 개발자에게 유익합니다. 특히 다음과 같은 분들에게 강력히 추천합니다.
- 레거시 코드에 시달리는 개발자: 기존에 작성된 복잡하고 지저분한 코드를 개선해야 하지만, 어디서부터 손대야 할지 막막한 분들에게 실질적인 가이드라인을 제공합니다.
- 성장하고 싶은 주니어 개발자: 단순히 기능 구현에만 급급하기보다는, 견고하고 유지보수하기 좋은 코드를 작성하는 방법을 배우고 싶은 분들에게 좋은 길잡이가 될 것입니다.
- 코드 리뷰 문화를 개선하고 싶은 팀 리더: 팀원들과 함께 코드 품질을 높이고 일관된 코드 개선 기준을 정립하고 싶은 분들에게 효과적인 도구가 될 수 있습니다.
- 코드 변경에 대한 두려움이 있는 개발자: 기존 코드를 건드렸다가 버그가 생길까 봐 불안해하는 분들에게, 안전하게 리팩터링하는 방법을 알려주어 자신감을 심어줍니다.
- 개발 생산성을 높이고 싶은 모든 개발자: 장기적으로 개발 속도를 높이고, 버그를 줄이며, 팀의 효율성을 극대화하고 싶은 분들에게 필수적인 지식과 기술을 제공합니다.
마무리: 지속적인 코드 개선을 위한 여정
리팩터링은 한 번 하고 끝내는 작업이 아니라, 소프트웨어 개발 과정에서 끊임없이 반복되어야 하는 지속적인 여정입니다. 이 책은 저에게 단순히 코드 개선 기법을 알려주는 것을 넘어, 코드를 대하는 태도와 마인드셋을 변화시키는 계기가 되었습니다.
이제 저는 복잡한 코드를 만났을 때 무조건적인 한숨부터 쉬기보다는, '어떤 코드 냄새가 나는가?', '이 책에서 배운 어떤 기법을 적용할 수 있을까?'를 먼저 고민하게 됩니다. 그리고 작은 단위로 테스트를 작성하고, 조심스럽게 코드를 개선해 나가는 과정에서 큰 성취감과 자신감을 얻게 되었습니다.
만약 여러분도 저처럼 지저분한 코드 앞에서 좌절하거나, 더 나은 코드를 작성하고 싶은 열망을 가지고 있다면, '리팩터링: 기존 코드를 개선하고 유지보수성을 높이는 실전 기법' 도서를 꼭 한번 읽어보시길 강력히 추천합니다. 이 책이 여러분의 개발 여정에 큰 전환점이 될 것이라고 확신합니다.
이 책을 통해 얻은 경험을 나누어 주시거나, 여러분만의 리팩터링 노하우가 있다면 댓글로 공유해 주세요! 함께 성장하는 개발 문화에 기여할 수 있기를 바랍니다.