CS/클린코드
[클린코드] chapter-7 오류처리
yhsim98
2022. 9. 1. 18:03
- 오류 처리 코드로 인해 프로그램 논리를 이해하기 어려워진다면 깨끗한 코드라 부르기 어렵다
오류 코드보다 예외를 사용해라
- 오류 코드를 반환하는 것이 아니라 예외를 사용해라
- 실제 로직과 예외처리 부분이 나뉘어져 깔끔해진다
미확인 예외를 사용하라
- checkedException을 사용하면 OCP 원칙에 어긋난다
- 하위 모듈이 checked 예외를 던지면 상위 모든 모듈이 throw를 선언부에 추가해야 한다
- 아주 중요한 라이브러리에서 예외를 꼭 처리해야 하는 경우를 제외하고는 uncheckedException을 사용해라
예외에 의미를 제공해라
- 예외를 던질 때 전후 상황을 충분히 덧붙인다
- 그러면 오류가 발생한 원인과 위치를 찾기 쉬워진다
- 오류 메시지에 정보를 담아 예외와 함께 던져라
호출자를 고려해 예외 클래스를 정의하라
- 오류 발생 컴포넌트로 분류 등 오류가 발생한 위치로 오류 분류가 가능하다
- 또는 유형으로도 분류가 가능하다
- 디바이스 실패, 네트워크 실패, 프로그래밍 오류 등등
- 하지만 애플리케이션에서 오류를 정의할 때, 프로그래머에게 가장 중요한 관심사는 오류를 잡아내는 방법이 되어야 한다
// Bad
// catch문의 내용이 거의 같다.
ACMEPort port = new ACMEPort(12);
try {
port.open();
} catch (DeviceResponseException e) {
reportPortError(e);
logger.log("Device response exception", e);
} catch (ATM1212UnlockedException e) {
reportPortError(e);
logger.log("Unlock exception", e);
} catch (GMXError e) {
reportPortError(e);
logger.log("Device response exception");
} finally {
...
}
// Good
// ACME 클래스를 LocalPort 클래스로 래핑해 new ACMEPort().open() 메소드에서 던질 수 있는 exception들을 간략화
LocalPort port = new LocalPort(12);
try {
port.open();
} catch (PortDeviceFailure e) {
reportError(e);
logger.log(e.getMessage(), e);
} finally {
...
}
public class LocalPort {
private ACMEPort innerPort;
public LocalPort(int portNumber) {
innerPort = new ACMEPort(portNumber);
}
public void open() {
try {
innerPort.open();
} catch (DeviceResponseException e) {
throw new PortDeviceFailure(e);
} catch (ATM1212UnlockedException e) {
throw new PortDeviceFailure(e);
} catch (GMXError e) {
throw new PortDeviceFailure(e);
}
}
...
}
- 아래 코드는 호출하는 라이브러리 API을 감싸 던지는 예외를 잡아 던지는 wrapper 클래스를 사용하여 중복을 해결했다
- 이같은 wrapper 클래스는 매우 유용하다
- 외부 라이브러리와 프로그램 사이에서 의존성이 크게 줄어듬과 함께 나중에 다른 라이브러리로 갈아타도 비용이 적다
- 또한 wrapper 클래스에서 외부 API를 호출하는 대신 테스트 코드를 넣어주는 방법으로 프로그램을 테스트하기도 쉬워진다
- 마지막으로 특정 업체가 API를 설계한 방식에 발목 잡히지 않는다, 프로그램이 사용하기 편리한 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();
그것보다는 청구했으면 청구한 값을, 아니면 기본값을 반환하는 방식으로 해라
null을 반환하지 마라
null 을 반환하는 코드는 할 일을 늘린다
nullPointerException 발생하기도 쉽다
null 을 감싸서 반환하던가 예외를 날려라
null 전달하지 마라
전달하는 것은 더 나쁘다
null이 들어오면 처리해야 하는 일이 많아진다 넘기지 마라