infitry 2023. 7. 23. 14:45
반응형

이번에는 함수에 대해 깨끗한 코드를 작성하는 방법에 대해 몇 가지 예시와 함께 알아보겠습니다.

 

작게 만들어라

나쁜 코드

아래와 같은 뭔가 결제하고 주문 상태를 변경하고 주문확인 이메일을 전송하는 로직을 가지고 있는 함수가 있습니다.

public class PaymentProcessor {
	public void processPayment(Order order) {
    	// 추상화 되지않은 소스코드 들..!
        
        // 결제 처리
        // ... 긴 로직
        
        // 주문 상태 업데이트
       	// ... 긴 로직
       	
        // 주문 확인 이메일 전송
        // ... 긴 로직
    }
}

실무를 접하다 보면 많이 보게 되는 코드인데요.

무엇인가 굉장히 길고 늘어뜨려 놓아 개발 툴 한 화면에서도 전부 확인할 수 없는 코드가 되게 됩니다.

읽는 입장에서 해당 코드를 본다면? 쭈욱 길게 늘어진 코드 중 마지막 부분을 보다 다시 처음을 확인해야 한다면?

굉장히 읽기 어렵고 한 눈에 무엇을 하는지 알 수가 없습니다.

책에서는 작게, 더 작게 만들라고 되어있는데 저는 이 부분에 대해서는 어느 정도만 작게 만들어도 읽기 편하다고 생각됩니다.

하나의 메소드는 15줄 내외로 추상화하여 표현하는 것이 좋은 것 같습니다.

 

좋은 코드

public class PaymentProcessor {
	public void processPayment(Order order) {
    	// 한 눈에 알아보기 쉽다! 세부 내용은 각 추상화 된 함수의 상세 로직을 확인한다.
        chargePayment(order);
        updateOrderStatus(order);
        sendConfirmationEmail(order);
    }
    
    private void chargePayment(Order order) {
    	// 결제를 처리하는 로직
    }
    
    private void updateOrderStatus(Order order) {
    	// 주문 상태를 업데이트하는 로직
    }
    
    private void sendConfirmationEmail(Order order) {
    	// 주문 확인 이메일을 보내는 로직
    }
}

위의 코드와 비교해보면 processPayment를 확인해 봤을 때 한눈에 무엇을 하는 함수인지 알기 쉽습니다.

결제를 처리하고, 주문 상태를 업데이트하고, 주문 확인 이메일을 보낸다.

세부 내용을 확인하고 싶다면? 각 추상화 된 함수의 상세 로직을 확인하면 됩니다.

한 번에 좋은 코드를 작성하기 보다 일단은 나쁜 코드의 케이스처럼 비즈니스 로직을 쭈욱 늘어뜨려 작성한 후

리팩토링 하는 방법으로 연습 하는 것이 필요합니다.

 

블록과 들여쓰기

if 문 else 문 while 문 등에 들어가는 블록은 한 줄 이어야 합니다.

함수에서 들여 쓰기 수준이 1단이나 2단을 넘어가면 안 됩니다.  (복잡도가 올라가고 가독성이 떨어집니다.)

 

나쁜 코드

public void processOrder(Order order) {
	if (order.getStatus() == OrderStatus.PENDING) {
    	if (order.getPaymentMethod() == PaymentMethod.CARD) {
        	// 긴 로직 1
        }
        // 긴 로직 2
        // 긴 로직 3
        // ...
    }
}

 

좋은 코드

public void processOrder(Order order) {
	if (ordere.getStatus() == OrderStatus.PENDING) {
    	processPendingOrder(order);
    }
}

private void processPendingOrder(Order order) {
	processForPaymentMethodCard(order);
    // 긴 로직2
    // 긴 로직3
    // ...
}

private void processForPaymentMethodCard(Order order) {
	if (order.getPaymentMethod() == PaymentMethod.CARD) {
    	// 긴 로직 1
    }	
}

다음과 같이 if 문 들여쓰기 수준을 줄일 수 있습니다.

if 문 자체를 함수로 추출하게 되면 들여쓰기 수준이 줄어듭니다.

 

한 가지만 해라.

하나의 함수에서 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 한 가지 일만 하는 게 아닙니다.

하나의 함수에서 추상화 수준은 일관되게 유지합니다.

 

나쁜 코드

public class CalcuationService {
	public double calculateTotalPrice(double unitPrice, int quantity, double discountRate) {
    	// 총 가격을 계산한다.
        double totlaPrice = unitPrice * quantity;
        // 할인율을 1 이상이면 할인을 적용한다.
        if (discountRate > 0) {
        	double discountAmount = totalPrice * discountRate;
            totalPrice -= discountAmount;
        }
        // 세금을 적용한다.
        totlaPrice = applyTax(totalPrice);
        return totalPrice;
    }
    
    private double applyTax(Double amount) {
    	// 세금 적용 로직
	}
}

다음 코드는 추상화 수준이 일관되지 못합니다.

또한 총 가격 계산, 할인율 적용 로직을 추상화하여 다른 함수로 분리할 수 있습니다.

 

좋은 코드

public class CacluationService { 
 	public double calculateTotalPrice(double unitPrice, int quantity, double discountRate) {
        // 총 가격을 계산한다.
        double totalPrice = calculateBasePrice(unitPrice, quantity);
        // 할인을 적용한다.
        totalPrice = applyDiscount(totalPrice, discountRate);
        // 세금을 적용한다.
        totalPrice = applyTax(totalPrice);
        return totalPrice;
    }
	
    private double calcuateBasePrice(double unitPrice, int quantity) {
    	return unitPrice * quantity;
    }
    
    private double applyDiscount(double price, double discountRate) {
    	if (discountRate > 0) {
       		double discountAmount = price * discountRate;
            price -= discountAmount;
        }
        return price;
    }
    
    private double applytax(double amount) {
    	// 세금 적용로직
    }
}

다음과 같이 calculateTotlaPrice 함수에서 추상화 수준을 일관되게 유지하고, 더 이상 새로운 함수를 추출할 수 없게 되었습니다.

 

정리

사실 함수 부분이 가장 애매한 부분도 많고 어려웠던 것 같습니다.

코에 걸면 코걸이 귀에 걸면 귀걸이 인 부분들이 꽤나 있었던 것 같고 꾸준한 연습이 필요한 부분 같습니다.

반응형