자바 예외란?
프로그램 실행 중 발생할 수 있는 오류 및 예외 상황을 말하며, 크게 Error, checked exception, unchecked exception 이렇게 3가지로 나누고 있다.
자바에서 오류와 관련된 클래스들의 계층구조를 보면, Throwable 클래스를 기준으로 Error와 Exception으로 나눠지며, Exception은 RuntimeException을 상속 받느냐 안받느냐로 나눠진다.

에러는 코드에서 수습될 수 없는 심각한 오류를 말하며, 시스템에 비정상적인 상황이 발생했을 경우를 말합니다. 예를 들어 메모리 부족(OutOfMemoryError)이나 스택오버플로우(StackOverFlowError) 같은 복구할 수 없는 오류를 나타낸다.
checked exception의 가장 큰 특징은 컴파일 시점에서 확인이 가능하는 것이고, 따라서 반드시 예외 처리를 해주어야 하는 특징이 있다.
주로 외부 환경이나 자원 문제로 인해 발생될 수 있는 예외를 나타낸다.
ex)
FIleNotFoundException: 존재하지 않는 파일의 이름을 입력
ClassNotFoundException: 클래스의 이름을 잘못 입력
IOException: 파일 또는 네트워크 연결에서 읽기 또는 쓰기와 같은 입력/출력 작업과 관련된 오류
SQLException: 데이터베이스 액세스 및 쿼리 관련된 오류
InterruptedException: 스레드 중단 및 동기화 관련된 오류의 경우
unchecked exception은 RuntimeExcepton을 상속받은 클래스로 런타임 시점에서 확인이 가능하며, 따로 예외 처리를 강제하지 않는다는 특징이 있다. 보통 프로그래머의 실수로 인해 발생될 수 있는 예외를 나타낸다.
ex)
NullPointException: 값이 null인 참조변수를 참조
ArrayIndexOutOfBoundsEception: 배열의 범위를 벗어남
RuntimeException은 왜 예외 처리를 강제하지 않을까?
런타임 예외와 그 자손들은 프로그래머의 실수로 발생하는 것들이기 때문에 강제하지 않는다.
만일 런타임 예외에 속하는 모든 예외를 필수로 처리해야 한다면, 단순히 배열을 만들어서 참조하는 코드도 예외 처리가 필요할 것이다. 따라서 코드가 너무 목잡해진다.
checked Exception (extends Exception)
1. 예외가 발생했을 때 복구할 가능성이 있고, 호출자가 해당 예외를 처리하는 것이 합리적일 경우
2. 호출자가 반드시 예외를 처리해야하는 경우 (ex. 파일 IO작업이나 네트워크 통신에서 발생하는 예외 등)
unchecked Exception (extends RuntimeException)
1. 프로그래머의 실수로 발생하는 예외일 경우
2. 예외 발생 시 복구가 불가하고 프로그램 실행을 중단해야하는 경우
3. 예외 처리를 강제하지 않아도 되는 상황에서 불필요한 예외 처리 코드를 줄이고자 하는 경우
try 블록은 예외가 발생할 가능성이 있는 코드를 포함한다.
catch는 예외가 발생했을 때 이를 처리하는 코드를 포함한다.
finally는 예외 발생 여부 상관없이 항상 실행되는 코드를 포함하며, 주로 자원해제를 위해 사용된다.
throw는 명시적으로 예외를 발생시킬 때 사용한다.
throws는 메서드가 호촐된 메서드로 특정 예외를 던질 때 사용된다.
🌟 추가 질문
📍 checkedException은 roll-back하지 않지만, unchecked Exception은 roll-back을 하는 이유
Unchecked Exception은 주로 프로그래밍 오류나 로직 오류를 나타냅니다. 이러한 예외는 보통 프로그래머의 실수로 인해 발생하며, 프로그램 실행 중에 이러한 오류를 발견하면 복구하기 어렵다. 따라서 이러한 예외가 발생하면 트랜잭션을 롤백하는 것이 데이터의 일관성을 유지하는데 중요하다.
Checked Exception은 주로 외부 환경이나 자원 문제로 인해 발생한다. 보통 예상 가능한 예외 상황이며, 호출자가 이를 처리하고 복구할 수 있는 기회를 제공한다. 따라서 기본적으로 Checked Exception이 발생했을 때 트랜잭션을 롤백하지 않도록 설계되어 있다. 예를 들어, 네트워크 장애가 발생하면 재시도하거나 대체 경로를 선택하는 등의 복구 작업을 수행할 수 있습니다.
📍 try/catch와 throws 중 예외 처리 선택 방법
보통 예외가 발생한 지점에 예외와 관련된 정보가 가장 많이 있다. 그렇기 때문에 해당 로직을 작성하는 개발자가 그 곳에서 try/catch를 사용해서 예외를 처리해주는 것이 좋다.
그럼 throws를 이용해서 상위로 던지는 경우는 어떤 경우가 있을까?
예를 들어 모든 예외를 간단하게 로그만 찍어주고 끝내는 경우, 상위로 던져주면 그곳에서 다른 곳에서 발생한 예외들까지 한번에 처리가 가능해진다.
비슷한 이유로 사용자 정의 예외를 만들어서 사용할 경우, 공통으로 처리가 가능하다면 굳이 세분화해서 예외를 만들 필요는 없다.
예를 들어, user를 등록하는 로직에서 2가지 오류가 발생할 수 있다고 가정을 하자.
이때 예외를 처리하는 방법은 각각의 상황에 맞게 UserUnderageException, UserAlreadyRegisteredException을 예외를 정의해서 사용할 수 있고,
세분화하지 않고 하나의 예외로 UserRegistrationException라고 정의해서 사용할 수도 이다.
만약 간단하게 로그만 찍고 넘어가는 경우라면 굳이 세분화하지 않고 하나의 예외를 만들어서 사용하는 것이 깔끔할 것이다.
하지만 각각의 상황에 맞게 처리 방식이 달라져야 한다면 세분화하는 것이 좋다.
참고 자료
Checked Exception, Unchecked Exception 그리고 예외 처리
[Java] Checked Exception vs Unchecked Exception 정리
[Java] Checked Exception과 Unchecked Exception