7 장 오류 처리
생성일
2024년 2월 13일 오후 8:16
태그
비어 있음
뭔가 잘못될 가능성은 늘 존재한다. 뭔가 잘못되면 바로 잡을 책임은 바로 우리 프로그래머에게 있다.
깨끗한 코드와 오류처리는 연관성이 있다.
흩어진 오류 처리 코드 때문에 코드가 하는 일을 파악하기가 어려워진다면 깨끗한 코드라 부르가 어렵다.
✏️ 오류 코드보다 예외를 사용하라
오류 코드를 반환한다면 호출한 직후 오류를 확인해야 하지만 이 단계는 잊어버리기 쉽다.
오류가 발생하면 예외를 던지는 편이 좋다.
Java
복사
public class DeviceController {
...
public void sendShutDown() {
DeviceHandle handle = getHandle(DEV1);
// 디바이스 상태를 점검한댜.
if (handle != DeviceHandle.INVALID) {
// 레코드 필드에 디바이스 상태를 저장한다.
retrieveDeviceRecord(handle);
// 디바이스가 일시정지 상태가 아니라면 종료한다.
if (record.getStatus() != DEVICE_SUSPENDED) { /
pauseDevice(handle);
clearDeviceWorkQueue(handle);
closeDevice(handle);
} else {
logger.log("Device suspended. Unable to shut down");
}
} else { logger.log("Invalid handle for: " + DEV1.toString());
}
}
...
}
→ 오류를 발견하면 예외를 던지게 수정
Java
복사
public class DeviceController {
...
public void sendShutDown() {
try {
tryToShutDown();
} catch (DeviceShutDownError e) {
logger.log(e);
}
}
//디바이스 종료
private void tryToShutDown() throws DeviceShutDownError {
DeviceHandle handle = getHandle(DEV1);
DeviceRecord record = retrieveDeviceRecord(handle);
pauseDevice(handle);
clearDeviceWorkQueue(handle);
closeDevice(handle);
}
//오류를 처리하는 알고리즘
private DeviceHandle getHandle(DeviceID id) {
...
throw new DeviceShutDownError("Invalid handle for: " + id.toString());
...
}
...
}
✏️ Try-Catch-Finally 문부터 작성하라
예외가 발생하는 코드를 짤 때는 try-catch-finally 문으로 시작하는 편이 좋다.
강제로 예외를 일으키는 테스트 케이스를 작성한 후 테스트를 통과하게 코드를 작성하기
try 블록의 트랜잭션 범위부터 구현하게 되므로 범위 내에서 트랜잭션 본질을 유지하기 쉬움
✏️ 미확인 예외를 사용하라
확인된 예외가 반드시 필요하지 않다는 사실이 분명해졌다.
확인된 예외는 OCP를 위반한다.
최하위 함수를 변경해 새로운 오류를 던질 때 확인된 오류르 던진다면, 최하위에서 최상위 단계까지 연쇄적인 수정이 일어난다.
⇒ throws 경로에 위치하는 모든 함수가 최하위 함수에서 던지는 예외를 알아야 하므로 캡슐화가 깨진다.
✏️ 예외에 의미를 제공하라
자바는 모든 예외에 호출 스택을 제공하지만 실패한 코드의 의도를 파악하려면 호출 스택만으로 부족하다.
오류 메시지에 정보를 담아 예외와 함께 던지기
✏️ 호출자를 고려해 예외 클래스를 정의하라
오류를 분류하는 방법은 많지만 중요한 관심사는 오류를 잡아내는 방법이다.
외부 라이브러리 호출
예외를 모두 잡아냄
Java
복사
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 {
...
}
wrapper 클래스로 감쌈
예외 유형과 무관하게 동일하게 대응
Java
복사
LocalPort port = new LocalPort(12);
try {
port.open();
} catch (PortDeviceFailure e) {
reportError(e);
logger.log(e.getMessage(), e);
} finally {
...
}
LocalPort 클래스 : ACMPPort 클래스가 던지는 예외를 잡아 변환하는 Wrapper 클래스
Java
복사
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에 대한 의존성이 줄고 테스트하기 쉬워진다.
프로그램이 깨끗해진다.
✏️ 정상 흐름을 정의하라
외부 API를 감싸 독자적인 예외를 던지는 것이 대개는 멋진 처리 방식이지만, 때로는 중단이 적합하지 않을 때도 있다.
총계를 계산하는 코드 - 예외가 논리를 따라가기 어렵게 만든
Java
복사
try {
MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();
} catch(MealExpencesNotFound e) {
m_total += getMealPerDiem();
}
DAO를 수정해 청구한 식비가 없다면 일일 기본 식비를 반환하게 함
Java
복사
public class PerDiemMealExpenses implements MealExpenses {
public int getTotal() {
// 기본값으로 일일 기본 식비를 반환한다.
// (예외가 아닌)
}
}
Java
복사
MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();
⇒ 이를 특수 사례 패턴이라고 한다.
✏️ null을 반환하지 마라
문제점
일거리를 늘린다.
호출자에게 문제를 떠넘긴다.
null 반환대신 예외를 던지거나 특수 사례 객체를 반환하기
✏️ null을 전달하지 마라
메서드로 null을 전달하는 코드는 최대한 피한다.
인수로 전달된 Null을 체크해서 새로운 예외 유형을 만들어 던지거나 assert문을 사용할 수 있지만 해결법은 아니다.
✏️ 결론
깨끗한 코드는 읽기도 좋아야 하지만 안전성도 높아야 한다. 오류 처리를 프로그램 논리와 분리해 독자적인 사안으로 고려하면 튼튼하고 깨끗한 코드를 작성할 수 있다.
조혜온
2024. 02. 13.
DAO는 리포지터리 같은 것인데 비지니스 로직이 들어가게 되는건 아닌가?
'책 > Clean Code' 카테고리의 다른 글
[Clean Code] 11장 시스템 (0) | 2025.03.17 |
---|---|
[Clean Code] 9장 단위 테스트 (0) | 2025.03.17 |
[Clean Code] 5장 형식 맞추기 (0) | 2025.03.17 |
[Clean Code] 3장 함수 (0) | 2024.01.30 |
[Clean Code] 1장 깨끗한 코드 (2) | 2024.01.30 |