- Published on
7장. 오류 처리
7장. 오류 처리
상당수 코드 기반은 전적으로 오류 처리 코드에 좌우됩니다.
여기서 좌우된다는 이미는 여기저기 흩어진 오류 처리 떄문에 실제 코드가 하는 일을 파악하기가 거의 불가능하다는 의미입니다.
오류처리는 중요하지만, 이로 인해 프로그램 논리를 이해하기 어려워 진다면 깨끗한 코드라 부르기 어렵습니다.
오류 코드보다 예외를 사용하라
예전에는 예외를 지원하지 않는 언어가 많아 오류 코드를 반환했었습니다
- -1: 실패
- 0: 성공
하지만, 이런 방식은 호출자 코드가 복잡해지며, 함수를 호출한 즉지 오류를 확인해야하기 때문에 좋지 않습니다.
이런 방식 보다는 try ~ catch
를 통해 예외를 던지면 논리가 오류 처리 코드와 뒤섞이지 않으므로 좋습니다
Try-Catch-Finally 문부터 작성하라
먼저 강제로 예외를 일으키는 테스트 케이스를 작성한 후 테스트를 통과하게 코드를 작성하는 방법을 권장합니다.
그러면 자연스럽게 try 블록
의 트랜잭션 범위부터 구현하게 되므로 범위 내에서 트랜잭션 본질을 유지하기 쉬워집니다.
미확인(unchecked) 예외를 사용하라
확인된 예외는 OCP(Open Close Principle)
를 위반합니다.
하위 함수에서 예외가 추가되면 해당 함수를 호출하는 모든 곳에서 해당 예외를 처리하거나 해야하기 때문입니다.
때로는 아주 중요한 라이브러리를 작성한다면 모든 예외를 잡아야 합니다.
하지만 일반적인 애플리케이션은 의존성이라는 비용이 이익보다 큽니다.
예외에 의미를 제공하라
오류 메시지에 정보를 담아 예외와 함께 던집니다.
실패한 연산 이름과 실패 유형도 언급하고, 로깅을 사용한다면 catch
블록에서 오류를 기록하도록 충분한 정보를 넘겨줍니다.
그러면 오류가 발생한 원인과 위치를 찾기가 쉬워집니다.
호출자를 고려해 예외 클래스를 정의하라
애플리케이션에서 오류를 정의할 때 프로그래머에게 가장 중요한 관심사는 오류를 잡아내는 방법
이 되어야 합니다.
외부 라이브러리가 던질 예외를 모두 잡는 try ~ catch
구문을 짜는 경우는 많습니다.
이런 경우 매번 잡기보다 예외를 잡아 변환하는 Wrapper 클래스
를 만드는 것이 좋습니다.
외부 라이브러리가 던지는 예외를 처리하는 방식으로는 Wrapper 클래스
를 만드는 기법이 최선입니다.
이렇게 만들면 특정 업체가 API를 설게한 방식에 의존되지 않는 다는 장점이 있습니다.
정상 흐름을 정의하라
try {
MealExpenses expenses = expenseReportDAO.getMeals(employee.getId());
m_total += expenses.getTotal();
} catch (MealExpensesNotFound e) {
m_total += getMealperDiem();
}
위 코드는 비용 청구 애플리케이션에서 총계를 계산하는 로직입니다.
예외가 발생하면 기본 식비를 총계에 더하고 있는데, 예외가 있기 때문에 논리를 따라가기 어렵게 만들고 있습니다.
MealExpenses expenses = expenseReportDAO.getMeals(employee.getId());
m_total += expenses.getTotal();
위 처럼 특수 상황을 처리할 필요가 없도록 구현하는 것이 더 좋습니다.
public class PerDiemMealExpenses implements MealExpenses {
public int getTotal() {
// 기본값으로 일일 기본 식비를 반환
}
}
DAO
를 고쳐 청구한식비가 없다면 일일 기본 식비를 반환하도록 수정하면 이처럼 하는 것이 가능합니다.
클래스를 만들거나 객체를 조작해 특수 사례를 처리하는 방식을 특수 사례 패턴(Special Case Pattern)
이라 부릅니다.
이 처럼 구현하면 클래스나 객체가 예외적인 상황을 캡슐화해서 처리하므로 클라이언트 코드
가 예외적인 상황을 처리할 필요가 없어집니다.
null을 반환하지 마라
null
을 반환하면 모든 값이 null
이 아닌지 체크해야 합니다.
이런 체크는 누락하기 쉬우며 NullPointerException
을 발생시키기 좋습니다.
null
을 반환하기 보다는 예외를 던지거나 특수 사례 패턴을 사용
하는 것이 좋습니다.
null을 전달하지 마라
메서드가 null을 반환하는 것
도 나쁘지만 메서드로 null을 전달하는 방식
은 더 나쁩니다.
null
을 기대하는 API가 아니라면 메서드로 null
을 전달하는 것은 피하는게 좋습니다.
null
을 실수로 넘기는 것을 막을 방법은 없습니다. 애초에 null
을 넘기지 않도록 금지하는 정책이 합리적입니다.
이런 정책을 따르면 그만큼 부주의한 실수를 저지를 확률도 작아집니다.
결론
깨끗한 코드는 가독성도 좋아야하지만 안정성도 높아야 합니다.
오류 처리를 플그램 논리와 분리하면 독립적인 추론이 가능해지며 코드 유지보수성도 크게 높아집니다.