Effective Java : 아이템 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라.

    아이템 70. 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라.

    • 체크 예외 : 복구할 수 있는 상황일 때 던져라.
    • 언체크 예외 : 복구할 수 없는 상황일 때 던져라.
    • 언체크 예외를 구현할 때는 반드시 RuntimeException 클래스를 상속해서 만들어라. 
    • 복구 가능한 상황인지 잘 모르겠으면, 언체크 예외를 던져라
    • 필요하다면 예외 클래스에 도우미 메서드를 추가해서 복구를 손쉽게 할 수 있도록 하는게 좋을 수 있음.
      • 몇 초 후에 재시도 해야한다는 메세지가 있다면, 그 메세지를 예외에 넣는다. 그리고 사용자에서 이 값을 간단히 get() 메서드를 이용해서 사용해 볼 수도 있다. 

     


    체크 예외  vs 언체크 예외

    자바에서는 크게 두 가지 타입의 예외가 존재한다.

    • 체크 예외 : 예외를 처리하도록 강제함.
    • 언체크 예외 : 예외를 처리하도록 강제하지 않음. 

    체크 예외는 언젠가는 try ~ catch로 예외를 처리하도록 강제하며, 덕분에 Stream에서는 사용하기 어렵다. 즉, 체크 예외를 던지는 API는 사용하기 어려운 API가 된다. 그렇다면 각각의 예외는 언제 쓰는 것이 좋을까?

    • 체크 예외는 복구 가능한 상황일 때 사용한다.
    • 언체크 예외는 복구 불가능한 상황일 때 사용한다. → 주로 RuntimeException. 

    체크 예외를 사용할만한 경우는? 

    체크 예외는 복구 가능한 상황일 때 사용하는 예외라고 했다. 그러면 어떨 때 복구가 가능한 예외일까? 한 가지 예시로는 SlackAPI의 rateLimitedException를 들 수 있겠다. 

    public static void main(String[] args) throws InterruptedException {
        SlackClient slackClient = new SlackClient();
        int afterTime = 0;
        while (true) {
            Thread.sleep(afterTime * 1000);
            try {
                slackClient.get();
                break;
            } catch (SlackApiRateLimitedException e) {
                afterTime = e.getAfterTime();
            }    
        }
    }

    위의 예시에서 복구 가능한 상황에 체크 예외를 던지고, 예외 상황을 복구하는 상황 예시를 들었다.

    • slackClient에 너무 빈번히 요청을 하면 RateLimitException()을 던진다. RateLimitException()에는 언제 이후에 다시 시도해보라는 AfterTime이 반환됨. 
    • 예외가 발생할 경우, 예외를 Catch하고 afterTime을 받은 후 이후에 다시 재시도한다.

    체크 예외라면 복구 가능함을 내포한다. 따라서 복구 가능한 것과 관련된 정보를 예외 객체 안에 넣어주고, 사용자가 쉽게 가져다 쓸 수 있도록 해주는 것이 좋을 것이다. 이것을 위해서 getAfterTime()이라는 메서드를 하나 추가했다. 

     


    사용자가 언체크 예외를 생성해야 한다면? 

    이미 자바가 제공하는 언체크 예외가 많기 때문에 존재하는 것만 쓰더라도 충분히 모든 경우에 대응할 수 있을 것이다. 그럼에도 불구하고 언체크 예외를 직접 생성해야하는 경우가 있다면, 이 때는 반드시 RuntimeException을 상속 받아서 구현해야한다. 

    사용자 정의 비검사 예외는 RuntimeException을 구현해서 사용하는 것이 컨벤션이다. 


    예외도 완벽한 객체다. 예외 복구에 필요한 정보 제공하자.

    일반적으로 예외 클래스에서 그냥 예외를 던지는 것만으로 만족한다면 예외 클래스를 100% 활용하지는 못하는 상태다. 특히나 체크 예외 클래스를 하나 만든다면, 복구에 필요한 유용한 정보를 멤버 메서드로 제공해 주는 것이 더 유용한 예외가 될 것이다.

    static class SlackApiRateLimitedException extends Exception {
        private final int afterTime;
        public SlackApiRateLimitedException(int afterTime) {
            this.afterTime = afterTime;
        }
    
        public int getAfterTime() {
            return afterTime;
        }
    }

    앞서 예시로 든 SlackApiRateLimitedException은 얼마 후에 다시 시도하라는 값을 Slack 요청을 보냈을 때, 응답의 헤더에 넣어서 반환해준다. 사용자가 이런 예외를 받았을 때 마다 예외의 값을 파싱해서 복구를 시도하려고 한다면, 비즈니스 로직 코드가 얼마나 더러워질까? 상상조차 할 수 없다.

    이런 경우라면 예외를 생성할 때, 이 값을 파싱해서 생성하고 사용자에서 손쉽게 가져다 쓸 수 있도록 getAfterTime() 같은 도우미 메서드를 추가하는 것이 더 좋을 것이다. 

    댓글

    Designed by JB FACTORY