리팩토링 패턴
Extract Method, Replace Conditional with Polymorphism 등 실무 리팩토링 패턴을 AI와 함께 안전하게 실행하는 방법을 배웁니다.
프리미엄 강좌 콘텐츠
이 레슨은 프리미엄 강좌의 일부예요. Pro로 업그레이드하면 모든 프리미엄 강좌와 콘텐츠를 이용할 수 있어요.
- 모든 프리미엄 강좌 이용
- 1000개 이상의 AI 스킬 템플릿 포함
- 매주 새로운 콘텐츠 추가
🔄 이전 레슨에서 코드 스멜을 찾는 방법을 배웠습니다. Long Method, Duplicate Code, Feature Envy 등이 어디에 있는지 압니다. 이제 진짜 일을 해야 합니다 — 고치는 것.
리팩토링의 핵심: 외부 동작을 바꾸지 않으면서 내부 구조를 개선하는 것. 이게 버그 수정이나 기능 추가와 다른 점입니다. 리팩토링 전후로 모든 테스트가 동일하게 통과해야 합니다.
배울 내용
이 레슨을 마치면 실무에서 가장 자주 쓰는 리팩토링 패턴을 알고, AI를 활용해 안전하게 실행하는 프로세스를 갖추게 됩니다.
안전한 리팩토링의 3단계
어떤 리팩토링이든 이 순서를 따르세요:
1. 테스트 확인 → 기존 테스트가 통과하는지 확인
(테스트가 없으면 먼저 추가)
2. 리팩토링 → 구조만 변경, 동작 미변경
3. 테스트 실행 → 모든 테스트가 여전히 통과하는지 확인
절대 하지 말 것: 리팩토링 + 기능 변경을 한 커밋에서 동시에 하기. 동작이 바뀌면 리팩토링 때문인지, 기능 변경 때문인지 알 수 없습니다.
실무 리팩토링 패턴 TOP 5
1. Extract Method (메서드 추출)
가장 자주 쓰는 패턴. 긴 함수에서 의미 있는 블록을 별도 함수로 분리합니다.
// Before: 결제 처리가 모두 한 함수에
async function checkout(cart) {
// 가격 계산
let total = 0;
for (const item of cart.items) {
total += item.price * item.quantity;
if (item.discount) total -= item.discount;
}
if (cart.coupon) total *= (1 - cart.coupon.rate);
// 재고 확인
for (const item of cart.items) {
const stock = await getStock(item.id);
if (stock < item.quantity) throw new Error(`재고 부족: ${item.name}`);
}
// 결제
const payment = await chargeCard(cart.user, total);
return payment;
}
// After: 각 블록이 이름을 가진 함수
async function checkout(cart) {
const total = calculateTotal(cart);
await validateStock(cart.items);
return chargeCard(cart.user, total);
}
함수 이름이 주석을 대체합니다. calculateTotal이 “가격 계산” 주석보다 낫습니다.
2. Replace Conditional with Polymorphism
if-else 체인을 클래스 구조로 변환합니다.
# Before: 배송비 계산이 if-else 체인
def calculate_shipping(order):
if order.type == "standard":
cost = order.weight * 500
if order.total > 50000: cost = 0
elif order.type == "express":
cost = order.weight * 1200 + 3000
elif order.type == "overnight":
cost = order.weight * 2500 + 8000
elif order.type == "international":
cost = order.weight * 5000 + 15000
cost += customs_fee(order.destination)
return cost
# After: 각 배송 타입이 자체 계산 로직을 가짐
class ShippingCalculator:
def calculate(self, order): raise NotImplementedError
class StandardShipping(ShippingCalculator):
def calculate(self, order):
cost = order.weight * 500
return 0 if order.total > 50000 else cost
class ExpressShipping(ShippingCalculator):
def calculate(self, order):
return order.weight * 1200 + 3000
새 배송 타입 추가 = 새 클래스 추가. 기존 코드를 수정할 필요 없음.
3. Introduce Parameter Object
같이 전달되는 파라미터 그룹을 객체로 묶습니다.
# Before: 관련된 파라미터가 흩어져 있음
def search_products(category, min_price, max_price,
sort_by, sort_order, page, page_size):
...
# After: 관련 파라미터를 객체로 그룹화
@dataclass
class SearchFilter:
category: str
min_price: int = 0
max_price: int = 999999
@dataclass
class Pagination:
page: int = 1
page_size: int = 20
sort_by: str = "relevance"
sort_order: str = "desc"
def search_products(filter: SearchFilter, pagination: Pagination):
...
✅ Quick Check: 파라미터가 4개인 함수가 있습니다. 이 중 3개는 항상 같이 전달됩니다. Introduce Parameter Object를 적용해야 할까요? (네, 3개가 항상 같이 전달되면 논리적 그룹입니다. 지금은 4개지만, 기능이 추가되면 5개, 6개가 됩니다. 지금 그룹화하면 미래의 Long Parameter List를 예방합니다.)
AI에게 리팩토링 요청하기
다음 코드를 리팩토링해주세요.
규칙:
1. 외부 동작(observable behavior)을 절대 변경하지 마세요
2. 리팩토링 전후의 차이점을 설명해주세요
3. 영향받을 수 있는 테스트를 알려주세요
4. 한 번에 하나의 리팩토링만 적용해주세요
적용할 패턴: Extract Method
대상: checkout 함수의 가격 계산 블록
코드:
{여기에 코드}
핵심은 “한 번에 하나의 리팩토링"입니다. 여러 패턴을 동시에 적용하면 어디서 문제가 생겼는지 추적할 수 없습니다.
✅ Quick Check: AI가 리팩토링한 코드를 받았습니다. 바로 머지해도 될까요? (안 됩니다. 1) 테스트를 돌려서 동작이 변하지 않았는지 확인하세요. 2) AI가 “동작을 바꾸지 않았다"고 주장하더라도 실제로 미묘한 변경이 있을 수 있습니다. 3) diff를 직접 검토하고, 이해되지 않는 변경이 있으면 AI에게 이유를 물어보세요.)
핵심 정리
- 리팩토링 = 외부 동작을 유지하면서 내부 구조 개선. 기능 변경과 절대 섞지 마세요
- Extract Method가 가장 자주 쓰는 패턴 — 주석으로 구분되는 블록이 있으면 적용 기회
- Replace Conditional with Polymorphism — if-else가 5개 이상이면 클래스 구조로 전환 검토
- AI에게 리팩토링 요청 시: 동작 미변경 보장, 차이점 설명, 영향 테스트 목록을 반드시 요청
- 한 번에 하나의 리팩토링만 적용하세요 — 여러 패턴 동시 적용은 디버깅을 불가능하게 만듭니다
다음 레슨
개별 코드 단위의 스멜 탐지와 리팩토링을 배웠습니다. 다음 레슨에서는 이 모든 것을 실무 워크플로우에 통합합니다 — PR 리뷰에 AI를 연결하고, CI/CD 파이프라인에 자동 리뷰를 넣는 방법을 배웁니다.
이해도 체크
먼저 위의 퀴즈를 완료하세요
레슨 완료!