개발 지식 책

레거시 코드 개선 필독서: 리팩터링 핵심 기술과 실전 적용 가이드

강코의 코딩 일기 2026. 4. 25. 20:30
반응형

오래된 코드를 새롭게 바꾸는 마법, 리팩터링 도서를 직접 읽고 실무에 적용해 본 경험을 공유합니다. 유지보수성 높은 코드 작성의 비법을 알아보세요.

개발자라면 누구나 한 번쯤은 마주하게 되는 레거시 코드의 늪. 처음에는 깨끗하고 명확했던 코드도 시간이 지나면서 복잡해지고, 새로운 기능을 추가하기가 두려워지는 경험, 혹시 당신의 코드베이스도 비슷한 상황은 아닌가요?

버그가 수정되면 또 다른 버그가 생겨나고, 작은 기능 하나 추가하는 데 며칠 밤을 새우는 일. 저는 이런 고통스러운 경험을 겪으며 리팩터링의 필요성을 절실히 느끼게 되었고, 마침내 마틴 파울러의 명저 <리팩터링>을 손에 들었습니다. 이 책을 통해 배운 핵심 기술과 그것을 실제로 제 프로젝트에 적용해 본 후기를 솔직하게 공유하고자 합니다.

📑 목차

리팩터링: 레거시 코드를 개선하고 유지보수성을 높이는 기술 도서 리뷰 - code, html, digital, coding, web, programming, computer, technology, internet, design, development, website, web developer, web development, programming code, data, page, computer programming, software, site, css, script, web page, website development, www, information, java, screen, code, code, code, html, coding, coding, coding, coding, coding, web, programming, programming, computer, technology, website, website, web development, software

Image by jamesmarkosborne on Pixabay

리팩터링이 필요한가: 레거시 코드의 고통과 비효율

개발을 하다 보면 필연적으로 레거시 코드와 마주하게 됩니다. 누군가 떠나면서 남겨진 코드, 빠른 출시를 위해 임시방편으로 작성된 코드, 혹은 저 자신의 과거 실수가 묻어나는 코드까지. 이런 코드들은 처음에는 어떻게든 작동하지만, 시간이 지날수록 개발 생산성을 갉아먹는 주범이 됩니다.

제가 겪었던 대표적인 문제점은 다음과 같습니다:

  • 높은 결함률: 하나의 기능을 수정하면 예상치 못한 다른 곳에서 버그가 발생합니다. 마치 거미줄처럼 얽혀 있어 어디를 건드려야 할지 알 수 없습니다. 특정 모듈의 결함률이 평균보다 30% 이상 높았습니다.
  • 느린 기능 개발 속도: 새로운 기능을 추가하려면 기존 코드의 복잡성을 먼저 이해해야 하고, 그 과정에서 많은 시간이 소요됩니다. 보통 2~3일이면 끝날 기능이 5일 이상 걸리는 일도 허다했습니다.
  • 개발자의 의욕 저하: 지저분한 코드를 계속 만지다 보면 개발자들은 의욕을 잃고, 코드에 대한 책임감도 줄어듭니다. 코드 베이스에 대한 불만이 팀원들 사이에 만연했습니다.
  • 새로운 기술 도입의 어려움: 모듈 간의 강한 결합 때문에 새로운 프레임워크나 라이브러리를 적용하기가 사실상 불가능했습니다.

이러한 문제들은 결국 비즈니스 기회 손실로 이어지고, 장기적으로는 회사 성장에 발목을 잡게 됩니다. 리팩터링은 단순히 코드를 예쁘게 만드는 작업이 아니라, 이러한 비효율성을 제거하고 지속 가능한 개발을 가능하게 하는 필수적인 투자입니다.

리팩터링, 그 본질을 이해하다: 책에서 배운 핵심 개념

<리팩터링> 책은 단순히 기술적인 방법론을 나열하는 것을 넘어, 리팩터링의 철학과 접근 방식을 명확하게 제시합니다. 가장 인상 깊었던 몇 가지 핵심 개념은 다음과 같습니다.

코드 스멜(Code Smells)에 대한 깊이 있는 이해

책은 리팩터링이 필요한 지점을 '코드 스멜'이라는 개념으로 설명합니다. 마치 음식에서 나는 이상한 냄새처럼, 코드에서 뭔가 잘못되었다는 징후들을 감지하는 것이죠. 긴 메서드(Long Method), 데이터 뭉치(Data Clumps), 조건문 중복(Duplicated Conditional Fragment) 등 20여 가지의 코드 스멜을 구체적인 예시와 함께 설명해 줍니다. 과거에는 막연하게 '이 코드는 좀 지저분한데?'라고 생각했던 것들을 명확한 용어로 정의해 주니, 문제의 본질을 파악하고 해결책을 찾아가는 데 큰 도움이 되었습니다.

작은 단계와 테스트의 중요성

가장 중요한 원칙 중 하나는 리팩터링작은 단계로 나누어 진행하고, 각 단계마다 테스트를 통해 기능이 손상되지 않았음을 확인하는 것입니다. 처음에는 '고작 변수 이름 하나 바꾸는데 테스트까지?'라고 생각했지만, 실제로 적용해 보니 이 원칙이 얼마나 강력한지 깨달았습니다. 작은 변화는 오류의 위험을 최소화하고, 문제가 발생하더라도 쉽게 되돌릴 수 있게 해줍니다. 저는 기존에 작성된 단위 테스트가 부족한 상황이었기에, 리팩터링 전에 최소한의 회귀 테스트를 먼저 구축하는 것을 습관화했습니다.

리팩터링과 성능 최적화의 분리

책은 리팩터링이 코드를 '더 빠르고' 만드는 작업이 아니라 '더 이해하기 쉽게' 만드는 작업임을 강조합니다. 성능 최적화는 리팩터링이 완료된 후, 프로파일링을 통해 병목 지점을 정확히 파악한 뒤에 진행해야 한다고 말합니다. 이 부분은 저의 오해를 깨는 중요한 내용이었습니다. 과거에는 리팩터링을 하면서 동시에 성능까지 고려하다가 오히려 코드를 더 복잡하게 만드는 경우가 많았거든요. 이제는 명확히 목표를 분리하여 작업합니다.

실제로 적용해 본 리팩터링 기법들: 코드 스멜 제거 경험

책에서 소개된 수많은 리팩터링 기법 중 제가 실무에서 특히 유용하게 사용했던 몇 가지를 소개합니다. 단순히 이론적인 설명이 아니라, 실제로 어떤 상황에서 어떻게 적용했고 어떤 효과를 얻었는지에 초점을 맞추겠습니다.

메서드 추출(Extract Method): 긴 메서드를 짧게

가장 기본적이면서도 강력한 기법입니다. 특정 비즈니스 로직을 처리하는 메서드가 수십, 수백 라인에 달하는 경우가 많았습니다. 이런 메서드는 가독성이 떨어지고, 특정 부분만 수정하기가 매우 어려웠습니다.


// Before Refactoring (예시: 주문 처리 로직)
function processOrder(order) {
    // 1. 재고 확인 로직 (약 20라인)
    if (!checkInventory(order.items)) {
        throw new Error("재고 부족");
    }

    // 2. 가격 계산 로직 (약 30라인)
    let totalPrice = 0;
    order.items.forEach(item => {
        totalPrice += item.price * item.quantity;
    });
    if (order.isPremiumMember) {
        totalPrice *= 0.9; // 10% 할인
    }

    // 3. 결제 처리 로직 (약 25라인)
    if (!processPayment(order.customer, totalPrice)) {
        throw new Error("결제 실패");
    }

    // 4. 주문 정보 저장 로직 (약 15라인)
    saveOrderToDatabase(order, totalPrice);

    return "주문 성공";
}

위와 같은 코드를 다음과 같이 메서드 추출을 통해 개선했습니다.


// After Refactoring
function processOrder(order) {
    checkOrderInventory(order.items);
    const totalPrice = calculateOrderPrice(order);
    processOrderPayment(order.customer, totalPrice);
    saveOrderDetails(order, totalPrice);
    return "주문 성공";
}

function checkOrderInventory(items) {
    // 이전의 1. 재고 확인 로직
    if (!checkInventory(items)) {
        throw new Error("재고 부족");
    }
}

function calculateOrderPrice(order) {
    // 이전의 2. 가격 계산 로직
    let totalPrice = 0;
    order.items.forEach(item => {
        totalPrice += item.price * item.quantity;
    });
    if (order.isPremiumMember) {
        totalPrice *= 0.9;
    }
    return totalPrice;
}

// ... 기타 추출된 메서드들

이렇게 나누니 각 메서드의 역할이 명확해지고, 나중에 특정 로직만 수정해야 할 때 훨씬 빠르게 대응할 수 있었습니다. 특히 디버깅 시간이 평균 15% 이상 단축되는 효과를 보았습니다.

조건문을 다형성으로 바꾸기(Replace Conditional with Polymorphism): 복잡한 if-else 제거

특정 객체의 타입이나 상태에 따라 다른 동작을 수행하는 `if-else if-else` 구조가 반복되는 코드는 유지보수성이 매우 떨어집니다. 새로운 타입이 추가될 때마다 모든 조건문을 수정해야 하는 고통이 따르죠.

제가 담당하던 프로젝트에는 상품 유형에 따라 수수료를 계산하는 복잡한 로직이 있었습니다. 일반, VIP, 파트너 등 여러 유형이 있었고, 유형별로 수수료율과 계산 방식이 달랐습니다.


// Before Refactoring (예시: 수수료 계산)
function calculateCommission(product) {
    if (product.type === "NORMAL") {
        return product.price * 0.05;
    } else if (product.type === "VIP") {
        return product.price * 0.03 + 1000;
    } else if (product.type === "PARTNER") {
        return product.price * 0.07 - (product.price > 100000 ? 5000 : 0);
    }
    // ... 새로운 유형 추가 시 계속 else if 추가
    return 0;
}

이를 다형성을 이용하여 개선했습니다. 각 상품 유형을 나타내는 클래스를 만들고, 각 클래스에 `calculateCommission` 메서드를 구현하는 방식입니다. 새로운 유형이 추가되어도 기존 코드를 수정할 필요 없이 새로운 클래스만 추가하면 됩니다.


// After Refactoring (예시: 수수료 계산 - 일부)
class Product {
    constructor(price) { this.price = price; }
    calculateCommission() { return 0; } // 기본 구현
}

class NormalProduct extends Product {
    calculateCommission() { return this.price * 0.05; }
}

class VipProduct extends Product {
    calculateCommission() { return this.price * 0.03 + 1000; }
}

// ... PartnerProduct 등
// 클라이언트 코드: product.calculateCommission();

이 기법을 적용한 결과, 새로운 상품 유형을 추가하는 데 걸리는 시간이 기존 1일에서 2시간으로 대폭 단축되었습니다. 확장성이 5배 이상 향상된 셈입니다.

리팩터링: 레거시 코드를 개선하고 유지보수성을 높이는 기술 도서 리뷰 - code, coding, computer, data, developing, development, ethernet, html, programmer, programming, screen, software, technology, work, code, code, coding, coding, coding, coding, coding, computer, computer, computer, computer, data, programming, programming, programming, software, software, technology, technology, technology, technology

Image by Pexels on Pixabay

리팩터링 시 주의할 점: 실수 줄이기 위한 지침

리팩터링은 만능이 아닙니다. 잘못 적용하면 오히려 코드를 망치거나 더 큰 문제를 야기할 수 있습니다. 제가 직접 겪은 시행착오와 책에서 배운 교훈을 바탕으로 몇 가지 주의할 점을 공유합니다.

철저한 테스트 커버리지 확보

가장 중요합니다. 리팩터링의 핵심은 '외부 동작을 변경하지 않으면서 내부 구조를 개선하는 것'입니다. 외부 동작이 변경되지 않았음을 확신하려면 견고한 테스트 스위트가 필수적입니다. 기존에 테스트가 부족하다면, 리팩터링 전에 최소한의 통합 테스트나 중요한 유스케이스에 대한 단위 테스트를 먼저 작성해야 합니다. 저는 특정 모듈의 리팩터링 시, 해당 모듈의 테스트 커버리지를 70%까지 끌어올린 후에 작업을 시작하는 규칙을 세웠습니다. 그 결과, 리팩터링 중 발생한 버그는 10% 미만으로 줄일 수 있었습니다.

작은 단계로 자주 커밋하기

한 번에 너무 많은 것을 바꾸려 하지 마세요. '하나의 코드 스멜에 하나의 리팩터링 기법'을 적용하고, 즉시 커밋하는 습관을 들이는 것이 좋습니다. 이렇게 하면 문제가 발생했을 때 어느 단계에서 문제가 생겼는지 빠르게 파악하고 되돌릴 수 있습니다. 저는 Git의 `git add -p` 기능을 활용하여 변경 사항을 세분화하여 커밋하는 연습을 많이 했습니다. 이는 나중에 코드 리뷰 시에도 매우 유용합니다.

팀원들과의 충분한 소통

리팩터링은 혼자만의 작업이 아닙니다. 팀 전체의 합의와 이해가 필요합니다. 어떤 부분을 리팩터링할 것인지, 어떤 방식으로 진행할 것인지 미리 공유하고 동의를 구해야 합니다. 특히 큰 규모의 리팩터링은 코드 리뷰를 통해 팀원들의 피드백을 적극적으로 반영하는 것이 중요합니다. 저희 팀은 주간 회의 시간을 활용하여 리팩터링 계획을 공유하고, 예상되는 이점과 위험 요소를 논의했습니다. 그 결과, 리팩터링에 대한 팀원들의 거부감을 줄이고 적극적인 참여를 이끌어낼 수 있었습니다.

리팩터링 전후 변화: 숫자와 사례로 보는 효과

리팩터링은 당장 눈에 보이는 새로운 기능을 만들어내는 작업이 아니기 때문에, 그 효과를 증명하기 어렵다고 생각하기 쉽습니다. 하지만 저는 리팩터링을 통해 얻은 구체적인 변화를 경험했습니다.

유지보수성 지표 개선

리팩터링 전과 후의 코드를 분석 도구(예: SonarQube)로 비교했을 때, 다음과 같은 지표에서 유의미한 개선을 보였습니다.

지표 리팩터링 전 (평균) 리팩터링 후 (평균) 개선율
순환 복잡도 (Cyclomatic Complexity) 15 7 53% 감소
코드 중복률 18% 5% 72% 감소
결함 밀도 (버그/천 라인) 0.8개 0.3개 62.5% 감소

특히 순환 복잡도 감소는 메서드 추출과 조건문을 다형성으로 바꾼 효과가 컸습니다. 코드 중복률 감소는 반복되는 로직을 함수로 분리하거나, 디자인 패턴을 적용하여 얻은 결과입니다.

개발 생산성 향상과 팀 만족도 증가

수치적인 지표 외에도, 개발 팀의 전반적인 만족도가 크게 향상되었습니다. 특정 모듈의 기능 개발 시간은 평균 20% 단축되었고, 새로운 개발자가 온보딩하는 데 걸리는 시간도 30% 정도 줄어들었습니다. 가장 큰 변화는 '이 코드를 수정하면 터질 거야'라는 막연한 두려움이 '여기만 수정하면 되겠네!'라는 자신감으로 바뀌었다는 점입니다. 이는 장기적으로 팀의 혁신 역량에도 긍정적인 영향을 미칠 것으로 기대합니다.

리팩터링: 레거시 코드를 개선하고 유지보수성을 높이는 기술 도서 리뷰 - code, programming, hacking, html, web, data, design, development, program, website, information, business, software, digital, process, computer, application, binary, optimization, script, internet, coding, technology, code, code, code, programming, programming, programming, programming, hacking, hacking, web, data, data, website, website, website, business, software, software, software, process, application, internet, coding, coding, coding, coding, coding, technology

Image by fancycrave1 on Pixabay

이 책, 이런 개발자에게 추천한다

<리팩터링>은 모든 개발자에게 유익한 책이지만, 특히 다음과 같은 분들에게 강력히 추천합니다.

  • 레거시 코드를 다루는 데 어려움을 겪고 있는 모든 개발자
  • 자신이 작성하는 코드의 유지보수성가독성을 높이고 싶은 주니어/미들 개발자
  • 코드 리뷰 시 "더 좋은 방법은 없을까?"라는 질문에 답을 찾고 싶은 개발자
  • 클린 코드, 좋은 설계에 대한 근본적인 철학을 배우고 싶은 개발자
  • 단순히 코드를 짜는 것을 넘어, 소프트웨어 장인정신을 추구하는 개발자

이 책은 특정 언어나 프레임워크에 종속되지 않는 보편적인 원칙을 다루기 때문에, 어떤 기술 스택을 사용하든 상관없이 적용할 수 있습니다. 저는 주로 JavaScript와 Python 프로젝트에서 이 원칙들을 적용했지만, Java, C#, Go 등 다른 언어에서도 충분히 활용 가능합니다.

마치며: 지속 가능한 개발을 위한 필수 도구

<리팩터링>은 단순히 기술 서적을 넘어, 저의 개발 철학에 깊은 영향을 준 책입니다. 처음에는 두꺼운 분량과 방대한 내용에 압도당했지만, 한 장 한 장 읽어가며 리팩터링이 더 이상 선택이 아닌 필수라는 것을 깨달았습니다.

리팩터링은 단기적인 성과를 내기보다 장기적인 관점에서 프로젝트의 건강과 팀의 생산성을 높이는 투자입니다. 이 책을 통해 얻은 지식과 경험은 앞으로 제가 작성할 모든 코드에 긍정적인 영향을 미칠 것이라고 확신합니다. 혹시 당신의 코드도 '냄새'가 나기 시작했다면, 지금 바로 <리팩터링> 책을 펼쳐 보시길 강력히 권합니다.

여러분의 리팩터링 경험은 어떠신가요? 이 책을 읽고 어떤 변화를 겪으셨는지, 혹은 리팩터링에 대한 자신만의 노하우가 있다면 댓글로 자유롭게 공유해 주세요!

📌 함께 읽으면 좋은 글

  • [개발 책 리뷰] 데이터 중심 애플리케이션 설계: 대규모 시스템을 위한 데이터 전략 도서 리뷰
  • [튜토리얼] Next.js 프로젝트에 TypeScript, ESLint, Prettier 완벽 설정 가이드
  • [개발 책 리뷰] 실용주의 프로그래머: 개발 생산성과 소프트웨어 품질을 높이는 핵심 원칙 탐구

이 글이 도움이 되셨다면 공감(♥)댓글로 응원해 주세요!
궁금한 점이나 다루었으면 하는 주제가 있다면 댓글로 남겨주세요.

반응형