티스토리 뷰

목차

1. 예외 처리 방식

2. Business Exception

 

 

 

 

1. 예외 처리 방식

오류 코드보다 예외를 사용하자.

오류 코드를 사용하는 것 보다 예외를 던지는 방식이 더 명확하고 메소드를 호출하는 코드와 처리 흐름이 깔끔해진다.

오류가 발생한 부분에서 Checked Exception을 발생시키고

상위 레벨에서는 예외를 catch 하여 처리하거나 메소드 선언부에 throws를 명시한다.

 

Checked Exception은 예외에 대한 처리가 반드시 필요하기 떄문에

하위 레벨 메소드에서 Checked Exception을 발생시키면 catch 블록이 나오기 까지 모든 상위 레벨 메소드에서

throws 처리를 해주어야 한다.

결국 상위 레벨 메소드에서 하위 레벨 메소드의 디테일에 대해 알아야 하기 때문에 OCP 원칙에 위배된다.

 

Unchecked Exception을 사용하라.

출처 https://www.nextree.co.kr/p3239/

  • Exception을 상속하면 Checked Exception 명시적인 예외 처리가 필요하다.
    (예)IOException,SQLException
  • RuntimeException을 상속하면 Unchekced Exception 명시적인 예외 처리가 필요하지 않다.
    (예)NullPointerException, IllegalArgumentException, IndexOutOfBoundException

C#, C++, 파이썬, 루비 등의 언어는

Checked Exception을 지원하지 않지만 안정적인 소프트웨어를 구현하기에는 무리가 없다.

명시적인 예외 처리가 필요하지 않은 Unchekced Exception을 사용하자.

 

예외에 메세지를 담아라.

private DeviceHandle getHandle(DeviceID id) {
    ...
    throw new DeviceShutDownError("Invalid handle for: " + id.toString());
    ...
}

오류가 발생한 원인과 위치를 찾기 쉽도록 예외를 던질 떄는 전후 상황을 충분히 덧붙인다.

실패한 연산 이름과 유형 등 오류 메세지에 정보를 담아 예외와 함께 던진다.

 

Exception Wrapper를 사용하라.

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 {
    …
}

외부 라이브러리를 호출하며 해당 라이브러리가 던질 예외를 모두 잡아내는 방식이다.

호출하는 라이브러리 API를 감싸면서 porte.open()이 던지는 Checked 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를 감싸면 외부 라이브러리와 프로그램 사이의 의존성도 크게 줄어든다.

나중에 다른 라이브러리로 변경할 떄의 비용도 적다.

또한 실제 API를 호출하는 대신 테스트 코드를 넣어주는 방식으로 프로그램을 테스트 하기도 쉬워진다.

 

 

null을 반환하지 마라.

반환 데이터가 없는 경우 null을 리턴하면 이 후 모든 코드에서 null 체크가 있어야 하기 때문에

가독성을 떨어트리며 null 체크를 누락할 수 있는 위험성이 있다.

 

(1) 기본값을 리턴한다.

복수형의 데이터를 가져올 때는 데이터의 없음을 의미하는 컬렉션을 리턴하면 된다.
null 보다 size가 0인 컬렉션이 훨씬 안전하다.

 

(2) 도메인에 맞는 기본값을 리턴한다. (getOrElse)

UserLevel userLevel = userService.getUserLevelOrDefault(userId);

시스템이 중단되지 않고 정상적인 흐름을 가져갈 수 있도록 도메인에 맞는 기본값인 basic level을 리턴한다.

데이터를 제공하는 쪽에서 예외를 처리하기 떄문에 호출부 코드의 논리적인 흐름도 끊기지 않는다.

 

(3) 예외를 던진다.

기본값을 반환할 수 없는 경우에는 Unchecked Exception을 던질수 있다.

마찬가지로 호출부 코드의 가독성도 높일 수 있다.

 

null을 전달하지 마라.

호출자가 실수로 넘기는 null을 적절히 처리하는 별도의 방법이 없기 때문에

애초에 null을 넘기지 못하도록 금지하는 정책이 합리적이다.

 


 

2. Business Exception

자바에서 제공하는 Exception이 아닌 커스텀 Exception을 만들어 사용할 수 있다.

RuntimeException을 상속하는 Business Exception을 만들고 enum으로 ErrorCode를 정의한다.

커스텀 Exception을 사용하면 IllegalArgumentException 처럼 외부 라이브러리에서 발생할 수 있는 예외와 섞이지 않고

에러 로그에서 우리 시스템에서 발생한 예외를 파악하기에 용이하다.

자세한 내용 참고 : Exception 전략

 

 

 

 

 

 

 

 

 

출처

Clean Code(클린 코드) 애자일 소프트웨어 장인 정신 - 로버트 C. 마틴 지음

https://zero-base.co.kr/category_dev_camp/cleancode_1book

 

728x90

'책 내용 정리 > 클린코드(clean code)' 카테고리의 다른 글

[클린코드] 10. 클래스  (0) 2022.03.28
[클린코드] 8. 경계  (0) 2022.03.28
[클린코드] 6. 객체와 자료 구조  (0) 2022.03.07
[클린코드] 5. 형식 맞추기  (0) 2022.03.03
[클린코드] 4. 주석  (0) 2022.01.17