800===Dev Docs and License/Clean Code

파이썬 코드 리팩토링 마스터 가이드 - Part 1: 코드 구조 개선 🎯

블로글러 2025. 1. 7. 21:25

들어가며 🌟

이번 시리즈에서는 파이썬 코드 리팩토링의 핵심 기법들을 자세히 살펴보겠습니다. Part 1에서는 코드 구조 개선에 초점을 맞춰보겠습니다.

1. 함수 추출 기법 (Extract Method) 🔍

문제 상황

def process_order(order):
    # 100줄 이상의 복잡한 코드
    # 주문 유효성 검사
    if not order.items:
        raise ValueError("주문 항목이 비어있습니다")
    if order.total_amount <= 0:
        raise ValueError("주문 금액이 유효하지 않습니다")

    # 재고 확인
    for item in order.items:
        if item.quantity > item.product.stock:
            raise ValueError(f"{item.product.name}의 재고가 부족합니다")

    # 결제 처리
    payment_result = payment_gateway.process_payment(order.total_amount)
    if not payment_result.success:
        raise PaymentError("결제 실패")

    # 주문 저장
    order.status = "PAID"
    order.payment_id = payment_result.id
    db.save(order)

개선된 코드

def process_order(order):
    """주문을 처리하고 결제를 진행합니다."""
    validate_order(order)
    check_inventory(order)
    process_payment(order)
    save_order(order)

def validate_order(order):
    """주문의 유효성을 검사합니다."""
    if not order.items:
        raise ValueError("주문 항목이 비어있습니다")
    if order.total_amount <= 0:
        raise ValueError("주문 금액이 유효하지 않습니다")

def check_inventory(order):
    """주문 항목의 재고를 확인합니다."""
    for item in order.items:
        if item.quantity > item.product.stock:
            raise ValueError(f"{item.product.name}의 재고가 부족합니다")

def process_payment(order):
    """주문에 대한 결제를 처리합니다."""
    payment_result = payment_gateway.process_payment(order.total_amount)
    if not payment_result.success:
        raise PaymentError("결제 실패")
    order.payment_id = payment_result.id

def save_order(order):
    """처리된 주문을 저장합니다."""
    order.status = "PAID"
    db.save(order)

2. 매개변수 객체화 (Introduce Parameter Object) 📦

문제 상황

def calculate_shipping(weight, height, width, length, destination, is_express):
    # 복잡한 배송비 계산 로직
    pass

# 사용
shipping_cost = calculate_shipping(2.5, 10, 20, 15, "Seoul", True)

개선된 코드

from dataclasses import dataclass
from typing import Optional

@dataclass
class ShippingParameters:
    """배송 계산에 필요한 매개변수를 포함하는 클래스입니다."""
    weight: float
    height: float
    width: float
    length: float
    destination: str
    is_express: bool = False

    def volume(self) -> float:
        """화물의 부피를 계산합니다."""
        return self.height * self.width * self.length

@dataclass
class ShippingCalculator:
    """배송비 계산을 처리하는 클래스입니다."""
    base_rate: float = 1000
    express_multiplier: float = 1.5

    def calculate_cost(self, params: ShippingParameters) -> float:
        """배송비를 계산합니다."""
        volume_cost = params.volume() * 0.1
        weight_cost = params.weight * 100
        base_cost = self.base_rate + volume_cost + weight_cost

        return base_cost * self.express_multiplier if params.is_express else base_cost

# 사용
params = ShippingParameters(
    weight=2.5,
    height=10,
    width=20,
    length=15,
    destination="Seoul",
    is_express=True
)
calculator = ShippingCalculator()
shipping_cost = calculator.calculate_cost(params)

3. 조건문 간소화 (Simplify Conditional) 🔀

문제 상황

def get_user_discount(user):
    discount = 0
    if user.is_registered:
        if user.purchase_count > 100:
            if user.total_amount > 1000000:
                discount = 0.2
            else:
                discount = 0.1
        else:
            if user.total_amount > 500000:
                discount = 0.05
            else:
                discount = 0.02
    return discount

개선된 코드

from enum import Enum
from dataclasses import dataclass

class CustomerTier(Enum):
    """고객 등급을 정의합니다."""
    BRONZE = "BRONZE"
    SILVER = "SILVER"
    GOLD = "GOLD"
    PLATINUM = "PLATINUM"

@dataclass
class DiscountRule:
    """할인 규칙을 정의하는 클래스입니다."""
    min_purchase_count: int
    min_total_amount: int
    discount_rate: float

class DiscountCalculator:
    """할인율을 계산하는 클래스입니다."""

    def __init__(self):
        self.discount_rules = {
            CustomerTier.PLATINUM: DiscountRule(100, 1000000, 0.2),
            CustomerTier.GOLD: DiscountRule(100, 500000, 0.1),
            CustomerTier.SILVER: DiscountRule(50, 500000, 0.05),
            CustomerTier.BRONZE: DiscountRule(0, 0, 0.02)
        }

    def get_customer_tier(self, user) -> CustomerTier:
        """사용자의 등급을 결정합니다."""
        if not user.is_registered:
            return 0

        if user.purchase_count > 100 and user.total_amount > 1000000:
            return CustomerTier.PLATINUM
        elif user.purchase_count > 100:
            return CustomerTier.GOLD
        elif user.total_amount > 500000:
            return CustomerTier.SILVER
        return CustomerTier.BRONZE

    def calculate_discount(self, user) -> float:
        """사용자의 할인율을 계산합니다."""
        tier = self.get_customer_tier(user)
        rule = self.discount_rules.get(tier)
        return rule.discount_rate if rule else 0

# 사용
calculator = DiscountCalculator()
discount = calculator.calculate_discount(user)

정리 🎁

Part 1에서는 코드 구조를 개선하는 세 가지 핵심 기법을 살펴보았습니다:

  1. 함수 추출로 복잡한 로직 분리
  2. 매개변수 객체화로 데이터 구조화
  3. 조건문 간소화로 로직 명확화

다음 Part 2에서는 성능 최적화와 관련된 리팩토링 기법들을 다루겠습니다.


References:

  1. Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin) - Chapter 3, 6
  2. Python Clean Code Documentation - PEP 8
  3. Design Patterns: Elements of Reusable Object-Oriented Software (Gang of Four) - Chapter 1
  4. Python Design Patterns Guide (https://python-patterns.guide/)
728x90