[토비의 스프링] 4장 예외

초난감 예외처리

// 무책임한 회피 throws
public void method1() throws Exception {
    throw new Exception("This is a test");
}

public void method2() throws Exception {
    method1();
}

public void method3() throws Exception {
    method2();
}

// catch 하지만 아무것도 하지 않음
public void method4() throws Exception {
    try {
        method1();
    } catch (Exception e) {
        // Do nothing
    }
}

예외처리 방법

복구

예외를 잘 파악하고 정상흐름으로 돌리는 조치를 취하는것 (ex. 다른 대안을 제시, 적절한 재시도)

public void retry() {
    int retryCount = 0;
    while (retryCount-- < 3) {
        try {
            errorMethod();
            ...
            return;
        } catch (Exception e) {
            // logging
        } finally {
            // cleanup
        }
    }
}

회피

예외를 처리하지않고 처리책임을 클라이언트에게 넘길수있다. 하지만 주의해야할점은 분명한 의도를 가지고 회피해야하는 것이다. 단순히 적절한 조취를 취하기 귀찮아서 throws 하면 위에서 봤듯이 초난감 예외처리가 될 뿐이다.

전환

회피와 비슷하지만 예외를 그대로 던지는게아니라 적절한 예외로 바꿔서 던져준다. 예외를 전환하는데는 두가지 이유가 있다.

 

1. 해당 예외상황에 더 명확한 의미를 가지는 예외로 바꿔주기 위해서

SqlException 이름만 봤을땐 너무 범위가 넓고 어떤 상황인지 파악하기 힘들다. 

fun pay(command: PayOrderCommand): PayOrderResult {
    try {
        val res = pgClient.pay()
        return PayOrderResult()
    } catch (e: FeignClientException) {
    	when (e.cause.code) {
            PgClientExceptionCode.InsufficientBalance.value -> throw InsufficientBalanceException(e)
            else -> throw PayFailedException(e)
        }
    }
}

위의 예제는 회사코드에서 비슷하게 작성한 코드인데 FeignClient를 사용해서 결제api 호출했을때 예외가 발생하면 예외안에 code를 읽어 의미가 명확한 에러로 바꿔서 전달해주는 것이다.  pay 메서드를 사용하는 클라이언트 코드는 추상화된 예외만 처리함으로서 FeignClient 같은 특정 기술의 예외를 몰라도 되는 장점도 있다.

 

2. 처리하기 쉽게 단순히 포장하기

처리를 계속 강제하는 check 예외를 그대로 전파하지 않고 uncheck 예외로 전환해서 위와같은 무지성 throws를 방지하는 것

어차피 복구할수 없고 비즈니스적으로 중요한 예외가 아닐때 throws를 반복하느니 시스템에 맡기는 방법이다. @Transactional 같은 애노테이션을 사용했다면 자동롤백의 효과도 가져갈수있다. ( check 는 기본설정으론 롤백하지 않음 ) 

try {
} catch(Exception e) {
	RuntimeException(e)
}

런타임 예외 보편화

요즘은 저런 피로한 check 예외 처리를 피하기 위해서 보통 RuntimeException 을 사용하고 꼭 필요하면 catch 해서 처리하는 식으로 흐름이 많이 바뀌었다 한다.

 Kotlin은 이런 Java 예외처리 방법을 통계내서 봤을때 단순히 check 예외를 회피하는 의미없는 코드가 대다수라 checked 예외 자체를 없애버려서 어떤 예외든 처리를 강제하지 않게 했다.

DataAccessException

Spring에서 제공하는 데이터 접근기술에 독립적인 추상적인 예외구조다. JDBC, Hibernate 등 기술마다 SqlException이 내용이 다르고 아예 본인들만의 예외를 내기도 하기 때문에 DataAccessException 구조안의 예외로 변경해서 던지는것인데, 완벽하게 신용할수는 없다.

예를들어 키값 중복상황에서 JDBC는 SqlException의 db error code를 사용해서 매핑하지만 다른 기술은 독자적인 예외를 가지고 매핑하기 때문에 각 기술에서 주는 예외가 충분히 어떤 상황인지 알수 없다면 뭉뜽그려서 예외를 매핑해줄수 밖에 없는 한계가 있는 것이다.