지난 1편에서는 스파게티처럼 얽혀있던 코드를 작은 함수들로 추출하여 가독성을 높이는 작업을 진행했습니다. 하지만 여전히 하나의 함수(statement) 안에 데이터 계산 로직과 텍스트 출력 로직이 공존하고 있었습니다.
이러한 구조는 다음과 같은 한계를 가집니다:
switch 문을 매번 수정해야 합니다.마틴 파울러가 제안하는 리팩터링의 핵심 전략 중 하나는 "단계 쪼개기(Split Phase)"입니다.
StatementData)를 만듭니다.여기에 더해, 장르별로 상이한 요금 계산 로직은 "조건부 로직을 다형성으로 바꾸기(Replace Conditional with Polymorphism)" 기법을 적용하여 클래스 구조로 재편합니다.
리팩터링된 시스템은 데이터가 여러 층을 거치며 정제되는 파이프라인 구조를 가집니다.
먼저 계산 로직을 분리하기 위해 enrichPerformance 함수를 통해 기존 데이터에 play, amount, volumeCredits 정보를 추가합니다.
export interface EnrichPerformance extends Performance { play: Play; amount: number; volumeCredits: number; } const createStatementData = (invoice: Invocie, plays: Plays): StatementData => { const result: StatementData = { customer: invoice.customer, performances: invoice.performances.map(enrichPerformance), }; result.totalAmount = totalAmount(result); result.totalVolumeCredits = totalVolumeCredit(result); return result; function enrichPerformance(perf: Performance): EnrichPerformance { // 계산 로직이 여기서 실행됨 const calculator = createPerformanceCalculator(perf, playFor(perf)); return { ...perf, play: calculator.play, amount: calculator.amount, volumeCredits: calculator.volumeCredits, }; } };
장르별로 다른 요금 체계를 switch 문 대신 클래스의 상속 구조로 풀어냅니다.
class PerformanceCalculator { constructor(protected perf: Performance, public play: Play) {} get volumeCredits() { return Math.max(this.perf.audience - 30, 0); } } class TragedyCalculator extends PerformanceCalculator { get amount() { let result = 40000; if (this.perf.audience > 30) { result += 1000 * (this.perf.audience - 30); } return result; } } class ComedyCalculator extends PerformanceCalculator { get amount() { let result = 30000; if (this.perf.audience > 20) { result += 10000 + 500 * (this.perf.audience - 20); } result += 300 * this.perf.audience; return result; } get volumeCredits() { return super.volumeCredits + Math.floor(this.perf.audience / 5); } }
팩토리 함수를 통해 적절한 계산기 객체를 생성함으로써, 호출하는 쪽에서는 구체적인 클래스 타입을 알 필요 없이 공통된 인터페이스(amount, volumeCredits)를 사용할 수 있게 됩니다.
statement 함수는 단 한 줄의 코드로 로직의 흐름을 보여줍니다.renderHtml 함수만 새로 만들면 됩니다.리팩터링은 단순히 코드를 예쁘게 정리하는 것이 아닙니다. "어떻게 하면 다음 변경 사항이 왔을 때 가장 적은 비용으로 안전하게 수정할 수 있을까?"라는 질문에 답하는 과정입니다.
단계 쪼개기와 다형성은 이러한 질문에 대한 강력한 해결책을 제공합니다. 이제 여러분의 프로젝트에서도 거대한 switch 문이나 거대한 단일 함수가 보인다면, 오늘 다룬 기법들을 적용해보시길 바랍니다.
참고 자료: