Study/CleanCode

[클린코드] 14장. 점진적인 개선

Wonol 2024. 7. 4. 08:25
반응형

클린코드(CleanCode)를 읽고 간략하게 정리한 글입니다.

 


14장. 점진적인 개선

1. 점진적인 개선의 중요성

  • 지속적 개선
    - 소프트웨어는 한 번에 완벽해질 수 없다.
    - 꾸준한 점진적 개선이 필요하다.
    - 작은 변경을 자주 적용하는 방식이 대규모 변경을 한 번에 적용하는 것보다 효과적이다.
  • 유지보수성 향상
    - 코드는 시간이 지남에 따라 변경되고 개선되어야 유지보수성이 높아진다.
    - 점진적인 개선은 코드를 최신 상태로 유지하는 데 중요하다.

2. 개선 방법

2-1. 리팩토링

  • 정의
    - 리팩토링은 소프트웨어의 기능을 변경하지 않으면서 코드를 재구성하는 것을 의미한다.
  • 목표
    - 코드를 더 읽기 쉽고 이해하기 쉽게 만들며, 중복을 제거하고, 구조를 개선한다.
  • 방법
    - 작은 단위로 점진적으로 리팩토링한다.
    - 하나의 작업을 끝내고 테스트를 통과한 후에 다음 작업으로 넘어간다.

2-2. 테스트 주도 개발(TDD)

  • 정의
    - 테스트를 먼저 작성하고, 그 테스트를 통과하는 코드를 작성하는 개발 방식
  • 목표
    - 코드가 예상대로 동작하는지 확인하고, 리팩토링을 안전하게 수행
  • 방법
    - 새로운 기능을 추가할 때마다 먼저 테스트를 작성하고, 테스트가 실패하면 코드를 작성하여 통과시킴

2-3. 작은 단위의 변경

  • 정의
    - 작은 단위로 자주 변경하는 것이 큰 단위로 드물게 변경하는 것보다 낫다.
  • 목표
    - 변경 사항을 쉽게 이해하고, 버그 발생 가능성을 줄이며, 문제 발생 시 원인을 쉽게 찾을 수 있게 한다.
  • 방법
    - 변경할 부분을 작게 나누어, 각 변경 후에 테스트를 수행하여 코드의 안정성을 유지한다.

3. 코드 리뷰와 피드백

  • 코드 리뷰
    - 코드 리뷰는 다른 개발자의 피드백을 받아 코드를 개선하는 중요한 과정이다.
    - 코드 리뷰를 통해 코드 품질을 높이고, 잠재적인 문제를 사전에 발견할 수 있다.
  • 피드백 루프
    - 짧은 피드백 루프를 유지하여 빠르게 개선점을 반영한다.
    - 이는 개발 과정에서 지속적으로 학습하고 성장할 수 있게 한다.

4. 개선 문화

  • 지속적 개선 문화
    - 팀 전체가 점진적 개선의 중요성을 이해하고, 이를 실천하는 문화를 조성한다.
    - 코드의 품질을 높이기 위해 누구나 개선 제안을 할 수 있는 환경을 만든다.
  • 자동화
    - 빌드, 테스트, 배포 과정을 자동화하여 개발 주기를 단축하고, 반복 작업을 줄여 점진적인 개선을 촉진한다.

5. 리팩토링 예제

- 간단한 은행 계좌 클래스를 리팩토링하고 점진적으로 개선하는 과정

5-1. 초기 코드

- 먼저 기본적인 은행 계좌 클래스와 관련 메소드를 작성한다.

public class BankAccount {
    private double balance;

    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
        }
    }

    public double getBalance() {
        return balance;
    }
}

5-2. 테스트 코드

- TDD를 위해 은행 계좌 클래스의 동작을 검증하는 테스트 코드를 작성한다.

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class BankAccountTest {

    @Test
    public void testDeposit() {
        BankAccount account = new BankAccount(100);
        account.deposit(50);
        assertEquals(150, account.getBalance());
    }

    @Test
    public void testWithdraw() {
        BankAccount account = new BankAccount(100);
        account.withdraw(50);
        assertEquals(50, account.getBalance());
    }

    @Test
    public void testWithdrawMoreThanBalance() {
        BankAccount account = new BankAccount(100);
        account.withdraw(150);
        assertEquals(100, account.getBalance());
    }
}

5-3. 리팩토링 1 : 중복 코드 제거

- 입금과 출금 메소드의 검증 로직을 별도의 메소드로 추출하여 중복 코드를 제거한다.

public class BankAccount {
    private double balance;

    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    public void deposit(double amount) {
        if (isValidAmount(amount)) {
            balance += amount;
        }
    }

    public void withdraw(double amount) {
        if (isValidAmount(amount) && amount <= balance) {
            balance -= amount;
        }
    }

    public double getBalance() {
        return balance;
    }

    // 중복 코드 메소드로 추출
    private boolean isValidAmount(double amount) {
        return amount > 0;
    }
}

5-4. 리팩토링 2: 예외 처리 추가

- 입금 또는 출금 시 잘못된 금액에 대한 예외 처리를 추가한다.

public class BankAccount {
    private double balance;

    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }

    public void deposit(double amount) {
        if (!isValidAmount(amount)) {
            // 예외 처리 추가
            throw new IllegalArgumentException("Amount must be greater than zero");
        }
        balance += amount;
    }

    public void withdraw(double amount) {
        if (!isValidAmount(amount)) {
            throw new IllegalArgumentException("Amount must be greater than zero");
        }
        if (amount > balance) {
            throw new IllegalArgumentException("Insufficient balance");
        }
        balance -= amount;
    }

    public double getBalance() {
        return balance;
    }

    private boolean isValidAmount(double amount) {
        return amount > 0;
    }
}

5-5. 예외 처리 테스트 추가

- 새로운 예외 처리 로직에 대한 테스트를 추가하여 TDD 를 유지한다.

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class BankAccountTest {

    @Test
    public void testDeposit() {
        BankAccount account = new BankAccount(100);
        account.deposit(50);
        assertEquals(150, account.getBalance());
    }

    @Test
    public void testWithdraw() {
        BankAccount account = new BankAccount(100);
        account.withdraw(50);
        assertEquals(50, account.getBalance());
    }

    @Test
    public void testWithdrawMoreThanBalance() {
        BankAccount account = new BankAccount(100);
        account.withdraw(150);
        assertEquals(100, account.getBalance());
    }

    @Test
    public void testDepositNegativeAmount() {
        BankAccount account = new BankAccount(100);
        assertThrows(IllegalArgumentException.class, () -> {
            account.deposit(-50);
        });
    }

    @Test
    public void testWithdrawNegativeAmount() {
        BankAccount account = new BankAccount(100);
        assertThrows(IllegalArgumentException.class, () -> {
            account.withdraw(-50);
        });
    }

    @Test
    public void testWithdrawInsufficientBalance() {
        BankAccount account = new BankAccount(100);
        assertThrows(IllegalArgumentException.class, () -> {
            account.withdraw(150);
        });
    }
}

6. 결론

  • 지속적인 리팩토링
    - 점진적인 개선을 위해 지속적으로 코드를 리팩토링하고, 테스트를 통해 코드의 안정성을 확인한다.
  • 작은 단위로 자주 변경
    - 큰 단위의 변경보다 작은 단위로 자주 변경하는 것이 좋다.
  • 팀 협업과 피드백
    - 코드 리뷰와 피드백을 통해 코드 품질을 높이고, 지속적인 개선 문화를 조성한다.
반응형