[클린코드] 7장. 오류처리
클린코드(CleanCode)를 읽고 간략하게 정리한 글입니다.
7장. 오류처리
1. 오류 코드보다 예외를 사용하라
- 오류 코드를 반환하는 방식보다 예외를 사용하는 것이 더 바람직하다.
- 오류 코드는 코드의 흐름을 복잡하게 만들고, 정상 코드와 오류 처리 코드를 섞어 놓는다.
- 반면에 예외는 오류 처리 코드와 정상 코드를 명확히 구분해준다.
// Bad Example - 오류 코드를 사용하는 경우
public int divide(int a, int b) {
if (b == 0) {
return -1; // 오류 코드
}
return a / b;
}
// Good Example - 예외를 사용하는 경우
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Divisor cannot be zero");
}
return a / b;
}
2. Try-Catch-Finally 문부터 작성하라
- 오류 처리는 프로그램의 논리에 중요한 부분이다.
- Try-Catch-Finally 문을 먼저 작성하면 오류 처리와 관련된 부분을 명확하게 만들 수 있다.
public void processFile(String fileName) {
try {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
// 파일 처리 로직
} catch (IOException e) {
System.err.println("File processing failed: " + e.getMessage());
} finally {
// 리소스 정리 로직
}
}
3. 미확인(Unchecked) 예외를 사용하라
- Checked 예외는 오류 처리 코드를 강제하지만, 코드가 지저분해질 수 있다.
- Unchecked 예외를 사용하면 메소드 시그니처가 깔끔해지고, 진짜 오류 처리가 필요한 곳에서만 예외 처리를 할 수 있다.
public void doSomething() {
if (someCondition) {
throw new RuntimeException("Unchecked exception");
}
// 정상 로직
}
4. 예외에 의미를 제공하라
- 예외 메시지는 문제의 본질을 명확히 전달해야 한다.
- 구체적인 예외 클래스를 만들어서 오류 상황을 구체적으로 기술하는 것이 좋다.
public class InsufficientFundsException extends RuntimeException {
public InsufficientFundsException(String message) {
super(message);
}
}
5. 호출자를 고려해 예외 클래스를 정의하라
- 예외는 오류가 발생했음을 알리기 위한 수단이다.
- 예외가 발생할 수 있는 상황을 호출자가 명확히 이해할 수 있도록 예외 클래스를 정의해야 한다.
public void withdraw(double amount) {
if (amount > balance) {
throw new InsufficientFundsException("Balance is not enough to withdraw " + amount);
}
balance -= amount;
}
6. 정상 흐름을 정의하라
- 예외는 예외적인 상황에서만 사용해야 한다.
- 정상적인 흐름에서 예외를 사용하는 것은 코드의 가독성을 떨어뜨린다.
- 정상 흐름에서 처리해야 할 상황은 예외를 사용하지 말고, 명확한 조건문 등을 사용해야 한다.
// Bad Example
try {
int result = divide(a, b);
// 정상 흐름 처리
} catch (IllegalArgumentException e) {
// 예외 흐름 처리
}
// Good Example
if (b != 0) {
int result = divide(a, b);
// 정상 흐름 처리
} else {
// 예외 흐름 처리
}
7. null을 반환하지 마라
- null을 반환하면 null 검사 코드가 많이 생겨난다.
- null 대신 예외를 던지거나, 빈 컬렉션, 옵셔널 등을 반환해야 한다.
// Bad Example
public String getCustomerName(int id) {
if (customer == null) {
return null;
}
return customer.getName();
}
// Good Example
public Optional<String> getCustomerName(int id) {
return Optional.ofNullable(customer).map(Customer::getName);
}
8. null을 전달하지 마라
- 메서드 인수로 null을 전달하는 것은 위험하다.
- null 대신 의미 있는 객체를 전달해야 한다.
// Bad Example
public void printCustomerName(Customer customer) {
if (customer != null) {
System.out.println(customer.getName());
}
}
// Good Example
public void printCustomerName(Customer customer) {
Objects.requireNonNull(customer, "Customer cannot be null");
System.out.println(customer.getName());
}
9. 결론
- 오류 처리는 프로그램의 중요한 부분이다.
- 예외를 잘 활용하면 코드의 가독성을 높이고 유지보수성을 향상시킬 수 있다.
- 오류 처리 코드는 명확하게 작성하고, 호출자에게 의미 있는 정보를 제공하는 것이 중요하다.
- null을 반환하거나 전달하는 것을 피하고, 가능한 한 예외 상황을 명확히 구분하는 코드를 작성해야 한다.