평상시에 예외를 사용할 때는 아무 생각 없이 throw new IllegalArgumentException();으로 처리하곤 했다.
우테코에서 예외에 대해 다루는데, 뭔가 아리까리...? 용어부터 헷갈리기 시작했다.
나처럼 그런 분들이 혹시 또 있을 것 같아서 정리해봤다!
잘못된 정보는 언제나 댓글로 달아주세요!
💋 프로그램의 오류란?
- 프로그램이 오작동 또는 비정상적으로 종료되는 원인을 프로그램 오류라고 한다.
💋 에러와 예외의 차이점?
에러는 코드를 수정하는 것으로 수습할 수 없는 심각한 오류이며, 일단 발생하면 복구가 어렵다.
예) OutOfMemoryError, ThreadDeath, StackOverflowError 등
예외란 비교적 덜 심각한 오류로, 코드 수정을 통해 해결할 수 있다.
오늘 포스팅에서 소개할 내용이다!
💋 프로그램의 오류의 분류
발생 시점에 따라 컴파일 에러와 런타임 에러로 분류한다.
컴파일 예외(Compiletime Exception) 컴파일 할 때 발생 = Checked Exception = 검사 예외
런타임 예외(Runtime Exception) 프로그래밍 실행 도중에 발생 = Unchecked Exception
논리적 예외(Logical Exception) 의도와 다른 동작(메인으로 다루지 않음)
나는 예외에 대한 내용을 처음 공부할 때에 용어 때문에 굉장히 헷갈린 경험이 있는데,
컴파일 예외는 컴파일 시점에 Exception에 대한 처리를 하지 않은 경우, 곧바로 체크하여 실행조차 되지 않기 때문에 Checked Exception이라고 한다.
반대로 런타임 예외는 컴파일 시점에는 Exception이 발생할 것인지 판단할 수 없기 때문에, 런타임 에러에 대한 확인을 하지 않고 따라서 Unchecked Exception이라고 한다.
💋 예외 클래스의 상속 구조
모든 클래스의 조상 Object 클래스 아래 Throwable 클래스가 존재한다.
여기서 위에 언급한 Exception(예외)와 Error(에러) 클래스가 Throwable 클래스를 직접 상속한다.
우리과 관심있는 Exception 클래스에 대해 살펴보면, 하위에 RuntimeException 클래스와 그 외의 클래스가 존재한다.
Exception 클래스 하위의 클래스 중 RuntimeException을 제외한 IOException 등은 모두 컴파일 에러와 관련있다.
예외도 특별한 것 없이, 클래스라는 것을 잊어서는 안된다.
커스텀 예외를 만드는 경우에 그러면 어떻게 해야 할까?
예외에 대한 모든 메서드를 담은 클래스를 내가 새로 생성하면 되는 것일까?
그래도 좋지만, 우리는 다른 사람들이 이미 만들어놓은 코드를 사용하기 위해 상속이라는걸 한다.
내가 정의한 클래스 옆에 extends 조상님이름을 적어주면 되는것이다!
public class 내가_만든_RuntimeException extends RuntimeException {
}
public class 내가_만든_CompileException extends Exception {
}
👍 컴파일 예외 (Exception의 후손들)
컴파일 예외 = 검사 예외 = Checked Exception
- 주로 외부의 영향으로 발생할 수 있는 것
- 사용자의 동작 때문에 발생하는 경우가 많다
- 예) FileNotFoundException(존재하지 않는 파일 이름을 입력), DataFormatException(입력한 데이터 형식이 잘못됨) 등
컴파일 예외가 발생할 가능성이 있는 코드는 처리해주지 않으면, 컴파일 조차 되지 않는다.
이 때의 처리에 대해서는 다른 포스팅에서 다룰 예정이다!
따라서 프로그램에서 예외 발생 확률을 없애 안정적이게 해주지만, 코드를 가져다 사용하는 과정이 굉장히 불편하다.
👍 런타임 예외 (RuntimeException의 후손들)
런타임 예외 = Unchecked Exception
- 주로 프로그래머의 실수에 의해서 발생할 수 있는 예외
- 예) ArrayIndexOutOfBoundsException, NullPointerException, ClassCastException, ArithmeticException 등
런타임 예외는 처리를 하지 않아도 성공적으로 컴파일 된다.
다르게 말해서, 예외 처리를 선택적으로 할 수 있다는 말이다.
만약 RuntimeException이 발생할 모든 가능성이 있는 곳을 매번 다 처리하고 있는다고 생각해보면... 참조변수와 배열이 사용되는 모든 곳에서 예외 처리를 해 주어야 할텐데, 매번 안 해도 돼서 왕창 다행이다!
💋 그렇다면, 컴파일 예외와 런타임 예외는 각각 언제 사용해야 할까?
정답은 없다!
추천사항은 있지만, 기준이 모호하다.
어떤 사람은 이런 기준을 갖고 있다고 한다.
호출하는 메소드가 예외을 활용해 무엇인가 의미 있는 작업을 할 수 있다면 컴파일 예외를 사용하라.
만약 호출하는 메소드가 예외를 catch해 예외 상황을 해결하거나 문제를 해결할 수 없다면 Unchecked Exception을 사용하라.
명확하지 않다면 런타임 예외를 사용하라.
나는 여기서 의미 있는 작업이라는 것에 대한 판단이 전혀 없는 상태이다.
이해가 가는건 세 번째 줄 밖에 없으므로, 나는 당분간 extends RuntimeException만 사용할 예정이다!
+++
지토의 설명을 듣고 이 내용에 대해서 어느 정도 받아들일 수 있게 되었다.
예를 들어, 갑자기 DB에 문제가 생겨서 SQLException이 발생했다. 1분 간 프로그램이 죽었다 살아났다고 하면, 우리는 1분 간 아무것도 하지 않고 기다리기만 해도, 코드 수정 없이 정상 상태로 돌아갈 수 있다. 이런 경우는 내부의 상황과 무관하게 제3자가 프로그램을 망쳐버리는 경우다. 이런 경우 Checked 예외가 적절하다.
컴파일 예외에 대한 장점은 빨간색, 단점은 파란색
자바 프로그래머들은 현재 컴파일 예외를 굉장히 싫어해서 어떤 개발 철학으로는 컴파일 예외를 아예 없애버리자는 내용도 있다고 한다. 하지만 어떤 사람들은 컴파일 예외를 좋게 보기도 한다. 억지로라도 처리해야 하니, 불안함을 지루함으로 대체한다는 점에서 약간 TDD(Test Driven Development)와 비슷한 면이 있다는 의견도 있었다.
컴파일 예외를 사용해 항상 예외를 먼저 처리하도록 하면 코드가 지저분해지고 귀찮긴 할 것이다. 남용하게 되면 매번 try/catch 블록이 돌아다니는 복잡한 코드가 될 것이다. 하지만, 무조건 처리해야 컴파일을 해 주니, 프로그램의 안정성은 높아질 것이다. 물론! 컴파일 예외를 썼다고 완벽하게 안전하다고 볼 수는 없다. 호출하는 사람이 예외 처리를 잘 해야 안전하다...
어떤 건 컴파일 할 때 체크하고 어떤 건 컴파일 후에 런타임에 체크한다니...
판단 기준은 개발이 발전하며 계속 바뀌고 있다지만, 어렵긴 하다.