리팩토링이란? 코드 품질을 높이는 리팩토링 실전 가이드와 예제
리팩토링의 정의와 중요성
리팩토링은 소프트웨어 개발에서 코드의 외부 동작을 변경하지 않으면서 내부 구조를 개선하는 과정입니다.
코드개선의 핵심은 가독성, 유지보수성, 확장성을 높이는 것입니다.
왜 리팩토링이 필요한가?
개발 프로젝트가 진행되면서 코드는 점점 복잡해지고 중복이 생기기 마련입니다.
이때 리팩토링을 통해 코드스멜을 제거하고 클린코드를 만들어야 합니다.
초보개발자들이 흔히 범하는 실수는 기능 구현에만 집중하고 코드 품질을 간과하는 것입니다.
하지만 장기적으로 보면 리팩토링은 개발 생산성을 크게 향상시킵니다.
리팩토링의 주요 효과
- 코드 가독성 향상
- 버그 발생 가능성 감소
- 새로운 기능 추가 용이성
- 개발팀 간 협업 효율성 증대
리팩토링 프로세스 다이어그램:
[기존 코드] → [문제점 분석] → [리팩토링 계획] → [점진적 개선] → [개선된 코드]
↓ ↓ ↓ ↓ ↓
복잡하고 코드스멜 테스트 코드 작은 단위 깔끔하고
읽기 어려움 식별 작성 변경 적용 유지보수 쉬움
리팩토링 방법과 기본 원칙
리팩토링 원칙
효과적인 리팩토링을 위해서는 다음 원칙을 따라야 합니다.
1. 테스트 코드 작성 우선
리팩토링 전에 반드시 테스트코드를 작성해야 합니다.
이는 리팩토링 후에도 기존 기능이 정상적으로 동작함을 보장합니다.
# 리팩토링 전 테스트 코드 예시
def test_calculate_total_price():
items = [{'price': 100, 'quantity': 2}, {'price': 200, 'quantity': 1}]
expected = 400
assert calculate_total_price(items) == expected
2. 작은 단위로 점진적 개선
한 번에 대규모 변경을 시도하기보다는 작은 단위로 나누어 진행합니다.
각 단계마다 테스트를 실행하여 기능이 정상 작동하는지 확인합니다.
3. 명확한 목적과 범위 설정
리팩토링의 목적을 명확히 하고 범위를 제한합니다.
무작정 모든 코드를 개선하려 하면 오히려 버그가 발생할 수 있습니다.
리팩토링 안전장치 시스템:
테스트 코드 작성
↓
작은 단위 변경
↓
테스트 실행 확인
↓
문제 없으면 다음 단계
↓
문제 발생시 롤백
리팩토링 프로세스
단계 | 설명 | 주요 활동 |
---|---|---|
1단계 | 현재 상태 파악 | 코드스멜 식별, 문제점 분석 |
2단계 | 테스트 코드 작성 | 기존 기능 보장을 위한 테스트 |
3단계 | 리팩토링 실행 | 작은 단위로 점진적 개선 |
4단계 | 테스트 실행 | 기능 정상 동작 확인 |
5단계 | 코드리뷰 | 팀원과 개선사항 검토 |
대표적인 리팩토링 패턴과 기법
1. 메서드 추출 (Extract Method)
가장 기본적인 리팩토링기법 중 하나입니다.
긴 메서드를 여러 개의 작은 메서드로 나누어 가독성을 높입니다.
리팩토링 전
// 리팩토링 자바 예시
public void printOwing() {
// 배너 출력
System.out.println("**************************");
System.out.println("***** Customer Owes ******");
System.out.println("**************************");
// 상세 정보 출력
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
리팩토링 후
public void printOwing() {
printBanner();
printDetails();
}
private void printBanner() {
System.out.println("**************************");
System.out.println("***** Customer Owes ******");
System.out.println("**************************");
}
private void printDetails() {
System.out.println("name: " + name);
System.out.println("amount: " + getOutstanding());
}
2. 변수 인라인 (Inline Variable)
불필요한 변수를 제거하여 코드를 단순화합니다.
리팩토링 파이썬 예시
# 리팩토링 전
def calculate_discount(order):
base_price = order.quantity * order.item_price
return base_price > 1000
# 리팩토링 후
def calculate_discount(order):
return order.quantity * order.item_price > 1000
3. 조건부 로직 분해 (Decompose Conditional)
복잡한 조건문을 메서드로 추출하여 의미를 명확히 합니다.
# 리팩토링 전
if date.before(SUMMER_START) or date.after(SUMMER_END):
charge = quantity * winter_rate + winter_service_charge
else:
charge = quantity * summer_rate
# 리팩토링 후
if is_winter(date):
charge = winter_charge(quantity)
else:
charge = summer_charge(quantity)
코드 스멜 식별과 해결 방법
주요 코드 스멜 유형
코드스멜은 리팩토링이 필요한 부분을 나타내는 신호입니다.
1. 코드중복 (Duplicate Code)
동일하거나 비슷한 코드가 여러 곳에 나타나는 현상입니다.
# 문제가 있는 코드
def calculate_employee_salary(employee):
base_salary = employee.base_salary
bonus = base_salary * 0.1
tax = (base_salary + bonus) * 0.22
return base_salary + bonus - tax
def calculate_manager_salary(manager):
base_salary = manager.base_salary
bonus = base_salary * 0.15 # 관리자는 보너스율이 다름
tax = (base_salary + bonus) * 0.22
return base_salary + bonus - tax
개선된 코드
def calculate_salary(employee, bonus_rate):
base_salary = employee.base_salary
bonus = base_salary * bonus_rate
tax = (base_salary + bonus) * 0.22
return base_salary + bonus - tax
def calculate_employee_salary(employee):
return calculate_salary(employee, 0.1)
def calculate_manager_salary(manager):
return calculate_salary(manager, 0.15)
2. 긴 메서드 (Long Method)
메서드가 너무 길어서 이해하기 어려운 경우입니다.
일반적으로 20-30줄을 넘어가면 분할을 고려해야 합니다.
3. 거대한 클래스 (Large Class)
하나의 클래스가 너무 많은 책임을 가지고 있는 경우입니다.
단일 책임 원칙(SRP)을 위반하는 대표적인 코드스멜입니다.
코드스멜 해결 전략
각 코드스멜에 대응하는 리팩토링 전략을 수립해야 합니다.
객체지향설계 원칙 적용
- 단일 책임 원칙 (Single Responsibility Principle)
- 개방-폐쇄 원칙 (Open-Closed Principle)
- 리스코프 치환 원칙 (Liskov Substitution Principle)
이러한 원칙들을 지키면 자연스럽게 코드 품질이 향상됩니다.
코드스멜 해결 플로우:
[코드스멜 감지] → [원인 분석] → [적절한 패턴 선택] → [리팩토링 적용] → [품질 검증]
↓ ↓ ↓ ↓ ↓
- 중복 코드 - 설계 문제 - Extract Method - 점진적 개선 - 테스트 통과
- 긴 메서드 - 책임 분산 - Move Class - 동료 리뷰 - 성능 확인
- 거대한 클래스 - 결합도 높음 - Decompose - 문서화 업데이트 - 유지보수성 향상
리팩토링 도구와 자동화
주요 리팩토링툴
현대 개발 환경에서는 다양한 도구가 리팩토링을 지원합니다.
Java 개발 환경
- IntelliJ IDEA: 강력한 자동 리팩토링 기능
- Eclipse: 기본적인 리팩토링 지원
- NetBeans: 코드 분석과 리팩토링 제안
Python 개발 환경
- PyCharm: 파이썬 전용 리팩토링 도구
- Visual Studio Code: 확장 프로그램 활용
- Rope: 파이썬 전용 리팩토링 라이브러리
리팩토링자동화 활용법
자동화 도구를 사용하면 실수를 줄이고 효율성을 높일 수 있습니다.
자동화 가능한 리팩토링
- 변수명 변경 (Rename Variable)
- 메서드명 변경 (Rename Method)
- 메서드 이동 (Move Method)
- 인터페이스 추출 (Extract Interface)
수동으로 진행해야 할 리팩토링
- 알고리즘 개선
- 디자인 패턴 적용
- 아키텍처 수준의 변경
리팩토링 자동화 수준:
자동화 가능 (높음) ←→ 수동 작업 필요 (높음)
↓ ↓
[변수명 변경] [알고리즘 최적화]
[메서드 이동] [디자인 패턴 적용]
[클래스 추출] [아키텍처 변경]
↓ ↓
IDE 도구 활용 개발자 판단 필요
리팩토링 실전 예제
예제 1: 전자상거래 주문 처리 시스템
복잡한 주문 처리 로직을 리팩토링하는 과정을 살펴보겠습니다.
문제 상황
class OrderProcessor:
def process_order(self, order):
# 재고 확인
if order.quantity > self.inventory.get_stock(order.product_id):
return "재고 부족"
# 가격 계산
base_price = order.quantity * order.unit_price
if order.customer.is_premium:
discount = base_price * 0.1
else:
discount = 0
if base_price > 100000:
discount += base_price * 0.05
total_price = base_price - discount
# 결제 처리
if not self.payment_gateway.charge(order.customer.card, total_price):
return "결제 실패"
# 배송 처리
if order.customer.address.is_remote:
shipping_cost = 5000
else:
shipping_cost = 3000
self.shipping_service.schedule_delivery(order, shipping_cost)
return "주문 완료"
리팩토링 코드
class OrderProcessor:
def process_order(self, order):
if not self._validate_inventory(order):
return "재고 부족"
total_price = self._calculate_total_price(order)
if not self._process_payment(order.customer, total_price):
return "결제 실패"
self._schedule_shipping(order)
return "주문 완료"
def _validate_inventory(self, order):
return order.quantity <= self.inventory.get_stock(order.product_id)
def _calculate_total_price(self, order):
base_price = order.quantity * order.unit_price
discount = self._calculate_discount(order, base_price)
return base_price - discount
def _calculate_discount(self, order, base_price):
discount = 0
if order.customer.is_premium:
discount += base_price * 0.1
if base_price > 100000:
discount += base_price * 0.05
return discount
def _process_payment(self, customer, amount):
return self.payment_gateway.charge(customer.card, amount)
def _schedule_shipping(self, order):
shipping_cost = self._calculate_shipping_cost(order.customer.address)
self.shipping_service.schedule_delivery(order, shipping_cost)
def _calculate_shipping_cost(self, address):
return 5000 if address.is_remote else 3000
예제 2: 데이터 분석 코드 리팩토링
데이터 처리 로직의 리팩토링 예시입니다.
# 리팩토링 전
def analyze_sales_data(data):
total = 0
count = 0
for item in data:
if item['category'] == 'electronics' and item['price'] > 100:
total += item['price']
count += 1
average = total / count if count > 0 else 0
# 월별 분석
monthly_sales = {}
for item in data:
month = item['date'].strftime('%Y-%m')
if month not in monthly_sales:
monthly_sales[month] = 0
monthly_sales[month] += item['price']
return {
'electronics_average': average,
'monthly_sales': monthly_sales
}
# 리팩토링 후
class SalesAnalyzer:
def __init__(self, data):
self.data = data
def analyze(self):
return {
'electronics_average': self._calculate_electronics_average(),
'monthly_sales': self._calculate_monthly_sales()
}
def _calculate_electronics_average(self):
electronics_items = self._filter_electronics_items()
if not electronics_items:
return 0
total_price = sum(item['price'] for item in electronics_items)
return total_price / len(electronics_items)
def _filter_electronics_items(self):
return [item for item in self.data
if item['category'] == 'electronics' and item['price'] > 100]
def _calculate_monthly_sales(self):
monthly_sales = {}
for item in self.data:
month = item['date'].strftime('%Y-%m')
monthly_sales[month] = monthly_sales.get(month, 0) + item['price']
return monthly_sales
팀 단위 리팩토링 전략
코드리뷰와 리팩토링
효과적인 리팩토링을 위해서는 팀 차원의 접근이 필요합니다.
코드리뷰 프로세스:
- 리팩토링 계획 공유
- 변경사항 설명
- 팀원 피드백 수렴
- 합의된 방향으로 진행
리팩토링 가이드라인 수립:
- 팀 내 코딩 컨벤션 통일
- 리팩토링 우선순위 설정
- 정기적인 코드 품질 점검
점진적 개선 전략
대규모 프로젝트에서는 한 번에 모든 코드를 리팩토링할 수 없습니다.
단계적 접근법
- 가장 문제가 되는 부분부터 시작
- 새로운 기능 개발과 병행
- 정기적인 리팩토링 시간 확보
- 성과 측정 및 피드백
팀 단위 리팩토링 전략:
개발 스프린트 계획
↓
┌─────────────────────────────────────┐
│ 기능 개발 70% + 리팩토링 30% │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 코드 리뷰 → 품질 검증 → 문서화 │
└─────────────────────────────────────┘
↓
성과 측정 및 다음 스프린트 계획
리팩토링 시 주의사항과 베스트 프랙티스
흔한 실수들
1. 과도한 리팩토링
모든 코드를 완벽하게 만들려는 욕심은 오히려 독이 될 수 있습니다.
적절한 수준에서 멈추는 것이 중요합니다.
2. 테스트 없는 리팩토링
테스트 코드 없이 리팩토링을 진행하면 예상치 못한 버그가 발생할 수 있습니다.
3. 성능 무시
코드 개선에만 집중하다 보면 성능을 간과할 수 있습니다.
코드최적화와 가독성의 균형을 맞춰야 합니다.
개발자면접에서의 리팩토링
리팩토링 역량은 개발자면접에서 중요한 평가 요소입니다.
면접에서 자주 나오는 질문
- "이 코드를 어떻게 개선하겠습니까?"
- "코드 리뷰에서 어떤 점을 중점적으로 보나요?"
- "리팩토링과 재작성의 차이점은 무엇인가요?"
답변 포인트
- 구체적인 개선 방안 제시
- 비즈니스 가치와 연결
- 팀 협업 경험 강조
리팩토링 역량 평가 구조:
기술적 역량 (40%)
↓
┌─────────────────────────────────────┐
│ • 코드스멜 식별 능력 │
│ • 적절한 패턴 선택 │
│ • 도구 활용 능력 │
└─────────────────────────────────────┘
↓
협업 역량 (35%)
↓
┌─────────────────────────────────────┐
│ • 코드 리뷰 참여 │
│ • 팀 표준 준수 │
│ • 지식 공유 │
└─────────────────────────────────────┘
↓
비즈니스 이해 (25%)
↓
┌─────────────────────────────────────┐
│ • 유지보수 비용 절감 │
│ • 개발 속도 향상 │
│ • 품질 개선 효과 │
└─────────────────────────────────────┘
마무리
리팩토링은 단순한 코드 정리가 아닌 소프트웨어 품질 향상을 위한 필수 과정입니다.
꾸준한 리팩토링을 통해 유지보수가 쉽고 확장 가능한 코드를 만들 수 있습니다.
특히 초보개발자부터 경험이 많은 개발자까지 모두가 체계적으로 접근해야 할 중요한 기술입니다.
성공적인 리팩토링을 위해서는 적절한 도구 활용, 팀 차원의 접근, 그리고 지속적인 학습이 필요합니다.
오늘 배운 리팩토링 기법들을 실제 프로젝트에 적용해보시기 바랍니다.
참고 자료
리팩토링 관련 강의 추천
인프런 - 백기선강사님의 "코딩으로 학습하는 리팩토링" 추천드립니다.
코딩으로 학습하는 리팩토링 강의 | 백기선 - 인프런
백기선 | 리팩토링은 소프트웨어 엔지니어가 갖춰야 할 기본적인 소양 중 하나입니다. 이 강의는 인텔리J와 자바를 사용하여 보다 실용적인 방법으로 다양한 코드의 냄새와 리팩토링 기술을 설
www.inflearn.com
저도 백기선님 강의를 본 다음 "마틴 파울러 리팩토링"책을 읽는 구조로 학습했었습니다.