***
Throwable 클래스는 예외 처리를 하기 위한 최상위 클래스로, 직접 사용되는 경우는 없지만 Throwable 타입과 이 클래스를 상속받은 서브 타입만이 JVM이나 throw 키워드에 의해 던져질 수 있습니다.
'에러(Error) vs 예외(Exception)'
'에러'는 시스템이 비정상적인 상황에서 발생합니다. 컴퓨터 하드웨어의 오동작 또는 고장으로 인해 응용프로그램에 이상이 생겼거나 자바 가상 머신(JVM) 실행에 문제가 생겼을 때 발생하는 것이며, 시스템 레벨에서 발생하는 심각한 수준의 오류이기 때문에 예외와 다르게 개발자가 미리 예측할 수도 없고, 애플리케이션 코드에서 잡아서 처리할 수도 없습니다.
(에러의 예로는 OutOfMemoryError, ThreadDeath, StackOverflowError 등이 있습니다.)
'예외'는 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해서 발생하는 프로그램 오류입니다.
(입력 값에 대한 처리가 불가능하거나, 프로그램 실행 중에 참조된 값이 잘못된 경우 등 정상적인 프로그램의 흐름을 벗어하는 것을 이야기합니다.)
자바에서 예외는 개발자가 예외 처리(Exception Handling)를 통해 직접 처리할 수 있기 때문에 예외 상황을 미리 예측하여 핸들링할 수 있습니다.
(NullPointerException, IllegalArgumentException)
'CheckedException vs UncheckedException'
'CheckedException'은 CompileException이라고도 하며 Exception을 바로 상속받습니다.
컴파일 시점에 예외를 catch 하는지 정적으로 확인하며, 만약 컴파일 시점에서 예외에 대한 처리(try/catch or throw)를 하지 않는다면 컴파일 에러가 발생합니다.
예외가 발생하는 메서드에서 throws 예약어를 사용하여 예외를 호출한 메서드에 전달하는 방법으로도 처리가 가능하며, Transaction Rollback이 안된다는 중요한 특징도 알아두어야 합니다.
(예시로는 FileNotFoundException, ClassNotFoundException, DataFormatException)
'UncheckedException'은 RuntimeException을 상속받습니다.
(RuntimeException은 말 그대로 실행 중에 발생할 수 있는 예외를 의미합니다.)
CheckedException과 반대로 컴파일 시점에서 예외를 catch 하는지 여부를 확인하지 않습니다. 그렇기 때문에 컴파일 시점에서 예외가 발생했는지 여부를 판단할 수 없습니다. Transaction Rollback이 된다는 특징이 있습니다.
(예시로는 IndexOutOfBoundsException, NullPointerException, ClassCastException)
UncheckedException은 RuntimeException을 상속받고, CheckedException은 RuntimeException을 상속받지 않는 것으로 구분할 수 있고, 또 '예외 처리를 반드시 해야 하는가'의 기준으로도 구분할 수 있습니다.
* rollback: 롤백은 데이터베이스에서 업데이트에 오류가 발생할 때, 이전의 상태로 되돌리는 것을 말합니다.
***
RuntimeException은 주로 개발자가 짠 코드 로직에 의해 발생할 수 있는 예외들로 RuntimeException은 예외가 발생할 가능성이 있는 코드들을 try-catch 문을 사용하여 처리하기 보다는 개발자가 예외가 발생하지 않도록 코드를 짜는 것이 중요합니다.
(정수를 0으로 나누려고 했을 때 발생하는 ArithmeticException의 경우 0으로 나누지 않도록 프로그램을 변경하는 것이 올바른 처리방법입니다.)
***
예외를 사용하는 이유는 예외가 발생할 여지가 있는 메서드를 호출하는 메서드가 예외를 활용해 무엇인가 의미있는 작업을 할 수 있을 때 CheckedException을 활용할 수 있습니다. (예외에 대한 책임을 확실하게 하기 위해)
하지만 호출하는 메서드가 예외 상황이나 문제를 해결할 수 없다면 UncheckedException을 활용하여 호출된 메서드가 예외를 터트려 개발자가 예외를 처리할 수 있도록 할 수도 있습니다.
'예외를 처리하는 방법 (예외 복구, 예외 처리 회피, 예외 전환)'
public void exceptionTest() {
try {
// 예외가 발생할 가능성이 있는 시도
...
} catch (SomeException e) {
// 다른 흐름으로 유도하여 정상적으로 작업이 진행되도록
...
}
}
'예외 복구'
예외가 발생했을 때 다른 작업 흐름으로 유도하는 예외 복구입니다. (try - catch - finally)
예외 복구의 핵심은 예외가 발행하여도 어플리케이션은 정상적인 흐름으로 진행된다는 것입니다. 예외 발생 시 재시도를 통해 정상적인 흐름을 타게 한다거나, 예외가 발생하면 이를 미리 예측하여 다른 흐름으로 유도시키도록 구현하면 비록 예외가 발생하더라도 정상적으로 작업을 종료할 수 있을 것입니다.
// 일반적으로 예외를 던지는 경우
public void exceptionTest() throws SomeException {
}
// 어느정도 처리하고 예외를 던지는 경우
public void exceptionTest() throws SomeException {
try {
...
} catch (SomeException e) {
// 예외 처리의 필요성이 있을 때 어느정도 처리하고 던지는 경우
throw e;
}
}
'예외 처리 회피'
간단해보이지만 신중해야 하는 로직입니다. 예외 처리를 직접 담당하지 않고 호출한 쪽으로 던져 회피하는 방법입니다.
예외 처리의 필요성이 있다면 두 번째 예시처럼 어느 정도 처리하고 던질 수도 있습니다. 예외 처리 회피에서는 무책임하게 상위 메서드로 throw를 던지는 행위는 상위 메서드의 책임이 그만큼 증가하기 때문에 적절한 경우에만 사용하는 것이 좋습니다.
public void exceptionTest() {
try {
...
} catch (SQLException e) {
// 더 명확하게 인지할 수 있도록 다른 예외로 전환해서 던지는 방법
throw new DuplicationUserIdException(message);
}
}
'예외 전환'
예외를 잡아서 다른 적절한 예외로 전환하여 자신을 호출한 메서드로 던져버리는 방법입니다.
호출한 쪽에서 예외를 받아서 처리할 때 좀 더 명확하게 인지할 수 있도록 돕기 위한 방법입니다.
< 참고 자료 >
'Programming > Java' 카테고리의 다른 글
자바 추상 클래스와 인터페이스의 차이점 이해하기 (2) | 2022.01.06 |
---|---|
메소드 오버로딩(Overloading)과 오버라이딩(Overriding)의 차이 이해하기 (0) | 2022.01.03 |
Java Generic 제네릭 기본적인 개념 이해하기 (0) | 2021.12.24 |
역할 분리를 위한 Entity, DTO 개념과 차이점 (2) | 2021.12.21 |
Java AES-256 양방향 암호화 제대로 알고 사용하기 (0) | 2021.12.15 |