[개발 도서] Clean Code :: 7장 - 오류 처리

Jihyoung·2021년 7월 26일
0

Clean Code

목록 보기
2/11
post-thumbnail
post-custom-banner

📕 오류코드보다 예외를 사용하라

기존 방식

  • 오류 플래그를 설정
  • 호출자에게 오류 코드를 반환

이와 같은 방법을 사용 시 코드의 복잡성 증가 -> 함수 호출 즉시 오류 확인해야 되기 때문

💻 7-1
...
	} else {
  	logger.log("Device suspended");
	}
} else {
  logger.log("Invalid handle");
}
...

대안

오류가 발생하면 예외를 던져라

이렇게 할 경우 알고리즘과 오류처리 부분이 구분되어 코드의 품질이 향상된다

💻 7-2
...
try {
	tryToShutDown();
} catch (DeviceShutDownError e){
	logger.log(e);
}
...
throw new DeviceShutDownError("Invalid handle");
...
  • throw : 오류 발생 / 예외를 알림
  • catch : 예외 처리 / 예외 회복을 위한 행위
try { /** 
* 블록의 시작부터 끝까지 실행
* 경우에 따라 예외가 발생 가능 
* throw 문에 의해 직접적으로 발생하거나
* 예외를 발생시키는 메서드의 호출에 의해 발생 가능 
*/ } 
catch (e) { /** 
* try 블록에서 예외가 발생할 경우에만 실행
* 지역 변수 e를 사용하여 Error 객체 또는 앞에서 던진 다른 값을 참조 가능 
* 발생한 예외 처리 가능 
* 예외 무시 가능
* throw 를 사용해서 예외를 다시 발생시키는것도 가능
*/ } 
finally { /** 
* try 블록에서 일어난 일에 관계없이 무조건 실행
* try 블록이 어떻게든 종료되면 실행
* try 블록이 종료되는 상황은 다음과 같다.
* 1) 정상적으로 블록의 끝에 도달했을 때 
* 2) break, continue 또는 return 문에 의해서 
* 3) 예외가 발생했지만 catch 절에서 처리했을 때 
* 4) 예외가 발생했고 그것이 잡히지 않은 채 퍼져나갈 때 
*/ }

📗 Try-Catch-Finally 문부터 작성하라

try에서 문제가 생기면 catch 블록에서 프로그램 상태를 일관성 있게 유지해야 한다. 따라서 예외가 발생할 수 있는 코드는 try-catch-finaly 문으로 시작하는 것이 좋다. 그러면 try 블록에서 무슨 일이 생기든지 호출자가 기대하는 상태를 정의하기가 쉬우며, 구현한 코드가 예외를 던지게 되면 해당 시점에서 리팩터링이 가능하다.

강제로 예외를 일으키는 테스트 케이스를 먼저 작성하고, 테스트를 통과하게 코드를 작성하는 방법을 권장한다. 자연스럽게 try 블록의 트랜잭션 범위부터 구현하게 되어서 트랜잭션 본질을 유지하기 쉬워진다.

* 리팩터링 : 결과의 변경 없이 코드의 구조를 재조정함

##### * 트랜잭션 : 한번에 모두 수행되어야하는 일련의 행위?? SQL에서 많이 쓰이는거 같은데 실제 코드에서는 어떤걸 가리키는지 정확하게 모르겠당..


📙 미확인 예외를 사용하라

의미

  • 확인된 예외가 안정적인 SW 제작 요소에 필수적이지 않기 때문에 확인된 오류가 비용에 상응하는 이익을 제공하는지 판단해야 한다.
  • OCP 위반 : 하위 단계에서 코드를 변경 시 상위 단계 메서드 선언부를 수정해야 한다. (해당 메서드에서 예외를 던졌는데 catch 블록이 상위에 있다면 그 사이 메서드 모두 해당 예외를 정의 -> 연쇄적 수정 필요)
  • 확인된 예외는 모든 함수가 최하위 함수의 예외를 알아야 하기 때문에 캡슐화가 깨지게 된다.
* OCP (Open Closed Principle) : 기존의 코드를 변경하지 않으면서 기능을 추가할 수 있도록 설계가 되어야 한다.

확인된 예외 vs 미확인 예외

확인된 예외미확인 예외
잘못된 코드가 아닌 잘못된 상황에서 발생하는 예외런타임 시 잘못 구현된 코드로 발생하는 예외
파일 열기와 같이 정확한 코드로 구현했음에도,
외부 환경(파일이 없는 상황 등)에 따라 발생 가능
컴파일 에러가 나지 않지만 적절한 예외처리가 없을 경우 프로그램이 강제 종료
예외처리를 구현하지 않으면 컴파일 에러 발생 (컴파일 시 확인된 예외)컴파일 시 확인하지 않기 때문에 미확인 예외


📘 예외에 의미를 제공하라

예외를 던질 때에는 오류 발생 원인과 위치를 찾기 위해 전후 상황을 덧붙인다.
이를 위해 오류 메세지에 정보를 함께 담아 예외와 함께 던진다.


📒 호출자를 고려해 예외 클래스를 정의하라

외부 라이브러리가 던질 예외를 모두 잡아내는 방법은 비효율적이다. 따라서 외부 라이브러리가 던질 예외를 잡아 변환하는 wrapper 클래스를 만드는 방법을 이용함으로써 프로그램과의 의존성을 줄인다. 이렇게 되면 다른 라이브러리로 변환하기에 용이하며, 테스트하기도 쉽다.

* 호출자 : main 함수에서 btn.getItem(); 함수를 호출한다고 했을 때, getItem 함수를 호출하는 main 함수가 '호출자(Caller)'이고, 호출을 받아 실행되는 getItem 함수는 '피호출자(Callee)이다.

📕 정상 흐름을 정의하라

때로는 외부 API를 감싸 독자적인 예외를 던지고, 코드 위에 처리기를 정의해 중단된 계산을 처리하는 방법이 좋을 수 있지만, 이런 예외 처리 방법이 논리를 해치는 경우가 존재한다.

이때는 '특수 사례 패턴' 을 이용하여 클래스나 객체를 별도로 생성하여 이들이 예외적인 상황을 캡슐화해서 처리할 수 있도록 한다. 그렇게 되면 클라이언트 코드는 예외적인 상황을 별도로 처리할 필요가 없어지게 된다.


📗 Null을 반환하지 말아라

null을 확인하는 코드가 너무 많아지게 되면 가독성이 떨어지고, NullPointException을 반환하면 수정하는데 어려움이 생긴다.

또한 null을 반환하게 되면 문제가 생기는 발생할 수 있는데 이는 예외를 던지거나, 특수 사례 객체를 반환하는 방식으로 수정한다.

List의 경우 Java에는 Collections.emptyList()를 이용하여 null 대신에 미리 정의된 읽기 전용 리스트를 반환할 수 있다.
이러한 방법을 사용한다면 NullPointException이 발생할 가능성이 낮아진다.


📙 Null을 전달하지 말아라

인수로 null을 전달하는 경우가 없도록 애초에 null을 넘기지 못하도록 금지하는 정책이 가장 합리적이다.


📚 Reference

profile
로그를 생활화
post-custom-banner

0개의 댓글