Programming Language/Java

[JAVA] 자바 예외(Exception) 구분 - Checked Exception, Unchecked Exception

장쫄깃 2022. 4. 14. 12:58
728x90


예외(Exception) 란?


예외(Exception)란 입력 값에 대한 처리가 불가능하거나, 프로그램 실행 중에 참조된 값이 잘못된 경우 등 정상적인 프로그램의 흐름을 어긋나는 것을 말한다. 그리고 자바에서 예외는 개발자가 직접 처리할 수 있기 때문에 예외 상황을 미리 예측하여 핸들링할 수 있다. 이러한 예로는 RuntimeException, SQLException, IOException 등이 있다.

 

한편, 에러(Error)는 시스템에 무엇인가 비정상적인 상황이 발생한 경우에 사용된다. 주로 자바 가상머신에서 발생시키는 것이며 예외와 반대로 이를 애플리케이션 코드에서 잡으려고 하면 안 된다. (사실 잡아도 방법이 없다.) 에러의 예로는 OutOfMemoryError, ThreadDeath, StackOverflowError 등이 있다.

 

try {
	// ...
} catch(StackOverflowError e) {
	// ... Error에 대한 처리는 개발자가 미리 예측할 수 없기 때문에 대부분 사용 X
} catch(RuntimeException e) {
	// ...
} catch(Exception e) {
	// ...
}

 

 

예외 구분


Exception은 Checked Exception과 Unchecked Exception으로 구분할 수 있는데, 간단하게 RuntimeException을 상속하지 않는 클래스는 Checked Exception, 반대로 상속한 클래스는 Unchecked Exception으로 분류할 수 있다.

또한, Checked Exception은 프로그래머의 실수에 의해서 발생할 수 있는 예외, Unchecked Exception은 외부의 영향으로 발생할 수 있는 예외라고 할 수 있다.

 

 

예외 처리


예외를 처리하는 방법에는 예외 복구, 예외 처리 회피, 예외 전환 방법이 있다.

 

예외 복구


예외 상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 방법으로, 정해진 시간/횟수만큼 재시도를 반복한다.

최대 재시도 횟수를 초과하는 경우 예외를 발생시킨다.

final int RETRY_CNT = 10;

public void someMethod() {
	int retryCnt = 0;
    while(retryCnt > RETRY_CNT) {
    	try {
        	// ...
        } catch(Exception e) {
        	// 로그 출력, 재시도 진행
        } finally {
        	// 리소스 반납 및 정리 작업
        }
    }
    
    // 재시도 횟수 초과 시 예외처리 직접 발생
    throw new RuntimeException("재시도 처리 실패...");
}

 

예외처리 회피


예외 처리를 직접 담당하지 않고 호출한 쪽으로 던져 회피하는 방법이다. 필요 시 내부에서 어느 정도 예외 처리를 하고 던지는 것이 좋다. 긴밀하게 역할을 분담하고 있는 관계가 아니라면 예외를 그냥 던지는 것은 무책임하다.

// 예시 1
public void someMethod1() throws SQLException {
	// ...
}

// 예시 2
public void someMethod2() throws SQLException, IOException {
	try {
    	// ...
    } catch(SQLException e) {
    	// ...
        throw e;
    } catch(IOException e) {
    	// ...
        throw e;
    }
}

 

예외 전환


예외를 밖으로 던지지만, 예외처리 회피와는 달리 조금 더 명확한 의미로 전달하기 위해 적합한 의미를 가진 예외로 전환해 넘기는 방법이다. 예외 처리를 단순하게 만들기 위해 포장(wrap) 할 수도 있다.

// 예시 1 (예외 전환)
public void someMethod1() throws SQLException, IOException {
	try {
    	// ...
	} catch(SQLException e) {
        // ErrorCode가 MySQL의 "Duplicate Entry(1062)"이면 예외 전환
		if (e.getERrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY)
			throw DuplicateUserException();
		else
			throw e;
    }
}

// 예시 2 (예외 포장)
public void someMethod2() throws SQLException, IOException {
	try {
    	// ...
	} catch (NamingException ne) {
		throw new EJBException(ne);
	} catch (SQLException se) {
		throw new EJBException(se);
	} catch (RemoteException re) {
		throw new EJBException(re);
	}
}

 

예외 전달을 할 때, 예외를 중첩 예외(nested exception)로 만들면 좋다. 중첩 예외는 getCause() 메소드를 이용하여 처음 발생한 예외가 무엇인지 확인할 수 있게 해준다.

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

initCause()를 이용해 근본 원인이 되는 예외를 넣어주어도 된다.

 

 

정리하면


자바에서 예외는 RuntimeException을 상속하지 않고 꼭 처리해야 하는 Checked Exception과 반대로 명시적으로 처리하지 않아도 되는 Unchecked Exception로 기본적으로 구분할 수 있다.

 

Checked Exception은 프로그래머의 실수으로 발생할 수 있는 예외이기 때문에 반드시 예외 처리를 해야한다.

Unchecked Exception은 외부의 영향에 의해서 발생할 수 있는 예외로 필요 시 예외 처리를 해야한다.

728x90