Exception의 필요성

심지원·2020년 1월 15일
0

Exception이란?

image.png

그림에서 볼 수 있듯이 Error와 Exception은 다르다.

Error는 시스템 내 비정상적인 상황이 생겼을 때 발생한다. 따라서 시스템 레벨에서 발생하며, 개발자가 미리 처리할 수도 예측할 수도 없다.

Exception은 개발자가 구현한 로직 내에서 발생한다. 따라서 Exception은 미리 예측하여 처리할 수 있다.

Exception Class

image.png

Exception 클래스를 상속한 하위 클래스는 RuntimeException을 기준으로 구분된다. Exception의 자식 중 RuntimeException을 제외한 모든 클래스는 Checked Exception이며, RuntimeException과 그의 하위 클래스들은 Unchecked Exception이라 부른다.

Checked Exception / Unchecked Exception

예외를 던져야 할 때 해당 예외에 대한 명확한 복구 방법이 정의돼 있다면 Checked Exception, 그렇지 않은 경우에는 Unchecked Exception 이라고 한다. 즉, Compile 단계에서 Unchecked Exception은 체크되지 않는다는 것이다.

Checked Exception은 컴파일 단계에서 확인할 수 있으며, 반드시 예외 처리를 해야 한다. 예외발생 시 Roll-back 하지 않는다. (e.g. IOException/SQLException/FileNotFoundException...)

Unchecked Exception은 반드시 예외 처리를 할 필요는 없다. Runtime에서 확인할 수 있고 예외 처리 시 Roll-back을 해야 한다.

표로 나타내면 아래와 같다.

CheckedExceptionUncheckedException
처리여부반드시 예외 처리를 해야 한다.명시적인 처리를 강제하지 않는다.
확인시점컴파일 단계실행 단계
예외발생시 트랜잭션 처리Roll-back XRoll-back O
대표 예외IOException, QLException, FileNotFoundException...RuntimeException 하위 클래스(NullPointerException, IllegalArgumentException, IndexOutOfBoundException, SystemException)

예외 처리 방법

1. 예외복구

예외 복구를 이용해 예외가 발생하여도 애플리케이션이 계속해서 정상적인 흐름을 진행한다.

int maxRetryCnt = MAX_RETRY; 

while (maxRetryCnt-- > 0) {
	try {
    	return; 
    }
    catch (NewException e) {
    	...
    }
    finally {
    	...
    }
}
throw new RetryFailedException(); 

위의 코드는 일정 횟수만큼 재시도를 반복하여 예외를 복구하는 코드이다. 불안정적인 네트워크 환경에서의 서버 접속을 원활하게 하는 시스템 내에서 적용할 때 효율적이겠다.

또는 메소드를 나누어 진행하는 방법도 있다.

public static void main(String[] args) {
	try {
    	String test = method("1"); 
        if (test == null) { ... }
    } 
    catch (Exception e) {
    }
}

private static String method(String str) throws Exception {
	if (str.equals("1")) return null;
    else if (str.equals("2")) throw new Exception("Throw exception");
    else return str; 
}

위와 같은 코드에서 예외를 복구하는 방식으로 작성한다면 아래와 같다.

private static String method(String str) {
	try {
    	if (str.equals("1")) return null;
        else if (str.equals("2")) throw new Exception("Throw exception");
    } catch(Exception e) {
    	return null; 
    }
}

2. 예외처리 회피

public void add() throws SQLException { 
	... 
} 

위 코드는 throws를 통해 호출한 쪽으로 예외를 던지고 그 처리를 회피하는 것이다.

무책임하게 던지기만 해서는 안 되는 신중해야 할 로직이다. 호출한 쪽에서 다시 예외를 받아 처리하도록 하거나, 해당 메소드에서 이 예외를 던지는 것이 최선의 방법이라는 확신이 있을 때만 사용해야 한다.

3. 예외 전환

catch(SQLException e) {
	...
    throw DuplicateUserIdException(); 
}

예외를 잡아 다른 예외를 던지는 것이다. 호출한 쪽에서 예외를 받아 처리할 때 좀 더 명확하게 인지할 수 있도록 돕기 위한 방법이다.

예를 들어, Checked Exception 중 복구가 불가능한 예외가 잡혔다면 이를 Unchecked Exception 으로 전환해서 다른 계층에서 일일이 예외를 선언할 필요가 없도록 할 수도 있다.

Exception의 필요성

예외 처리는 실패하지 않는 방법이다. 언어를 공부하고 코드를 짜서 동작하게 만드는 것을 성공하는 법이었다고 하면, 그 과정에서 발생할 수 있는 실패를 방지하거나 덜 실패하게끔 만드는 것이 예외이다.

예외를 잡았을 때 아무런 처리도 하지 않는 것은 정말 위험하다. try/catch문에서 catch를 비워두면 컴파일 오류는 벗어날 수 있지만, 예외 발생 시 그 원인을 파악하기 어려워 유지보수만이 아닌 개발까지도 치명적이다. 따라서 어떤 처리를 해야 하는지 모르더라도 무작정 catch를 비워두거나 throw해버리는 행위는 신중해야 한다.

시스템은 고객의 요구사항을 만족시키는 것도 중요하지만, 작동되어서는 안 될 기능이 작동되는 것을 막는 것도 매우 중요하다. 신뢰 있는 개발자가 되기 위해서는 예외처리가 필수적이다.




참고 자료
Java 예외(Exception) 처리에 대한 작은 생각
생활코딩-자바(JAVA)
Recover from a method throwing an exception
Checked Exception & Unchecked Exception

profile
안녕하세요.

0개의 댓글