Chapter 7 - Error Handling
in Booknotes / Clean Code
오류처리를 프로그램 논리와 분리하면 독립적인 추론이 가능해지며 유지보수성도 높아진다
- 오류 코드보다 예외를 사용하라
- Try-Catch-Finally 문부터 작성하라
- 미확인 예외를 사용하라
- 예외에 의미를 제공하라
- 호출자를 고려해 예외 클래스를 정의하라
- 정상 흐름을 정의하라
- null을 반환하지 마라
- null을 전달하지 마라
- 여기저기 흩어진 오류 처리 코드 때문에 실제 코드가 하는 일을 파악하기가 어려워질 때가 많음
오류 코드보다 예외를 사용하라
if
-else
문 남발대신try
-catch
문으로 깔끔하게 처리public void sendShutDown(){ try { tryToShutDown(); } catch (DeviceShutDownError e) { logger.log(e) } } private void tryToShutDown() throws DeviceShutDownError{ .... }
각 개념을 독립적으로 분리해서 사용
Try-Catch-Finally 문부터 작성하라
- TDD를 사용해 필요한 나머지 논리를 추가
미확인 예외를 사용하라
C#
,C++
,Python
,Ruby
등은 확인된 예외 지원 X- 확인된 오류는 OCL (Open Closed Principle) 위반
- 확인된 예외가 던져지지만
catch
블록이 세 단계 위에 있다면 그 사이 메서드에서 선언부에 해당 예뢰를 정의해야 함 - \(\Rightarrow\) 하위단계에서 코드를 변경하면 상위 단계 메서드 선언부를 전부 고쳐야 함!
- 확인된 예외가 던져지지만
함수 내 다른 함수 호출이 많음. 즉, 하위 함수에서 확인된 오류를 던진다면 최상위 단계까지 연쇄적인 수정이 일어남 \(\Rightarrow\) Encapsulation 깨짐
- 확인된 예외도 유용하지만, 일반적인 개발에서는 의존성이라는 비용이 이익보다 크다
예외에 의미를 제공하라
- 예외를 던질 때 전후 상황을 충분히 덧붙이기 \(\rightarrow\) 오류 발생 원인과 위치 찾기가 쉬워짐
- 오류 메시지에 정보를 담아 예외와 함께 던지기 (실패한 연산 이름, 유형, 로깅 기능 사용 등)
호출자를 고려해 예외 클래스를 정의하라
- 오류 분류법 무궁무진
- 오류 발생 위치로 분류 가능
(ex: 발생한 컴포넌트 등) - 오류 유형
(ex: 디바이스 실패, 네트워크 실패, 프로그래밍 오류 ...etc)
- 오류 발생 위치로 분류 가능
주 관심사: 오류를 잡아내는 방법
정상 흐름을 정의하라
예제: 비용 청구 시 총계에 추가, 청구 안했다면 일일 기본 식비 추가
try { MealExpenses expenses = expenseReportDAO.getMeals(employee.getID()); m_total += expenses.getToal(); } catch(MealExpesnseNotFound e) { m_total += getMealPerDiem(); }
- 더욱 간결하게:
MealExpenses expenses = expenseReportDAO.getMeals(employee.getID()); m_total += expenses.getTotal()
- 위 코드가 가능한 방법:
expenseReportDAO
를 고쳐 언제나MealExpense
객체를 반환하게 함 (청구한 식비가 없다면 일일 기본 식비를 반환하게)public class PerDiemMealExpenses implements MealExpenses { public int getTotal() { // 기본값으로 일일 기본 식비 반환 } }
- 위 코드가 가능한 방법:
- \(\Rightarrow\) 특수 사례 패턴 (SPECIAL CASE PATTERN)
- 클래스를 만들거나 객체를 조작해 특수 사례 처리하는 방식
- \(\Rightarrow\) 클라 코드에서 예외처리 필요 X (클래스/객체에서 encapsulation해줌)
null을 반환하지 마라
public void registerItem(Item item) {
if (item != null) {
ItemRegistry registry = persistentStore.getItemRegistry();
if (registry != null) {
Item existing = registry.getItem(item.getId());
if (existing.getBillingPeriod().hasRetailOnwer()) {
existing.register(item);
}
}
}
}
null
인지 확인하는 코드가 너무 많다.null
대신 빈 리스트나 객체를 반환한다면 더 나아질 것 (깔-끔)- (참고) 2행에
persistentStore
null check가 빠져있다.
null을 전달하지 마라
null
반환도 나쁘지만 **null
전달은 더 나쁘다 **- 대다수 프로그래밍 언어는 호출자가 실수로 넘기는
null
을 적절히 처리 못함 - 애초에
null
을 넘기지 못하도록 금지하는 정책이 나음 - 즉,
null
이 인수로 넘어오는 코드면 문제 많음!!