이번에는 함수에 대해 깨끗한 코드를 작성하는 방법에 대해 몇 가지 예시와 함께 알아보겠습니다.
작게 만들어라
나쁜 코드
아래와 같은 뭔가 결제하고 주문 상태를 변경하고 주문확인 이메일을 전송하는 로직을 가지고 있는 함수가 있습니다.
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 함수에서 추상화 수준을 일관되게 유지하고, 더 이상 새로운 함수를 추출할 수 없게 되었습니다.
정리
사실 함수 부분이 가장 애매한 부분도 많고 어려웠던 것 같습니다.
코에 걸면 코걸이 귀에 걸면 귀걸이 인 부분들이 꽤나 있었던 것 같고 꾸준한 연습이 필요한 부분 같습니다.
'클린코드와 리팩토링 > 클린코드' 카테고리의 다른 글
클린코드 - 객체와 자료구조 (0) | 2024.04.14 |
---|---|
클린코드 - 형식 맞추기 (3) | 2024.03.30 |
클린코드 - 주석 (0) | 2023.07.23 |
클린코드 - 의미있는 이름 (0) | 2023.07.23 |