본문 바로가기
Java

[Java] Throwable vs Error vs Exception 그리고 예외 처리 전략

by 태크민 2025. 1. 14.

1. 프로그램 오류

프로그램은 어떤 원인에 의해 오작동 또는 비정상적 종료가 일어나는 경우가 있다.

이러한 결과를 초래하는 원인을 프로그램 오류 또는 에러라고하며 발생 시점에 따라 3가지로 나뉜다.

  • 컴파일 에러 : 컴파일 시에 발생하는 오류
  • 런타임 에러 : 실행 시 발생하는 오류
  • 논리적 에러 : 실행은 되지만, 의도와 다르게 동작하는 것

컴파일러는 소스코드(.java) 오타, 잘못된 구문, 자료형 체크 등 기본적인 검사를 수행하여 오류가 있는지 알려준다. 이때 발생하는 것이 컴파일 에러이다.

그리고 이 컴파일 과정이 끝나면 클래스 파일(.class)이 생성되고 생성된 클래스 파일을 실행할 수 있게 된다.
하지만, 컴파일이 잘되었어도 실행 도중 발생할 수 있는 잠재적 오류까지 검사할 수 없기 때문에 실행 도중 오류가 발생할 수 있다.

이때 발생하는 것이 런타임 에러이며 자바에서는 이를 에러(Error)와 예외(Exception)로 구분한다.

 

2. 에러 vs 예외

  • 에러(Error) : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
  • 예외(Exception) : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류

에러는 메모리 부족(OutOfMemoryError)나 스택오버플로우(StackOverflowError)와 같이 일단 발생하면 복구할 수 없는 심각한 오류이고,

예외는 NullPointerException, illegalArgumentException과 같이, 발생하더라도 적절한 코드(try-catch)를 미리 작성해 놓음으로써 프로그램의 비정상적 종료를 막을 수 있는 비교적 덜 심각한 오류이다.

 

3. 에러와 예외 클래스 계층 구조

오류(Error)와 예외(Exception) 모두 Throwable를 상속받는다.

모든 Error와 Exception 클래스는 Throwable 클래스를 상속받고있으며, getMessage()와 printStackTrace() 메서드를 통해 현재 Error와 Exception 여부를 확인할 수 있다.

사용자는 Error의 상황을 미리 미연에 방지하기 위해서 Exception 상황을 만들 수 있으며, java에서는 try-catch문으로 Exception handling을 할 수 있다.


Checked Exception, Unchecked Exception

Exception클래스는 두 그룹으로 나눌 수 있다.

  1. Exception클래스와 자손들 (RuntimeException클래스와 그 자손들 제외) ➡️ Checked Exception
  2. RuntimeException클래스와 자손들 ➡️ Unchecked Exception

쉽게 말하면, RuntimeException을 상속하지 않은 클래스는 Checked Exception,

반대로 상속한 클래스는 Unchecked Exception으로 분류할 수 있다.

 

Checked Exception

  • 컴파일러가 예외처리를 강제(확인)하는 예외
  • 사용자(외부)의 동작에 의해서 발생될 수 있는 예외
  • ex) 존재하지 않는 파일의 입력(FileNotFoundException), 클래스 이름을 잘못 입력(ClassNotFoundException) 등
  • 트랜잭션 도중 예외가 발생하면 롤백(rollback)하지 않음

명시적인 예외 처리를 강제하기 때문에 Checked Exception이라 한다. 반드시 try ~ catch로 예외를 잡거나 throw로 호출한 메소드에게 예외를 던져야 한다.

 만일 컴파일 시점에 예외에 대해 처리(try/catch) 하지 않는다면 컴파일 에러가 발생하게 된다. 또한 트랜잭션 Rollback이 안된다는 특징도 있다.


Checked Exception 예시는 아래와 같다.

public sendFile(String fileName)() {
        File file;
        try {
                file = FileFindService.find(fileName);
        } catch (FileNotFoundException e){ 
                // 기본 파일을 찾아서 전송한다.
                file = FileFindService.find("default.png");
        }

        send(file);
        }
}

 

Unchecked Exception

  • 컴파일러가 예외처리를 강제(확인)하지 않는 예외
  • ex) null인 참조변수의 멤버호출(NullPointException), 배열의 범위를 벗어난 동작(ArrayIndexOutOfBoundException) 등
  • 트랜잭션 도중 예외가 발생하면 자동 롤백(rollback)

💡 중요 : 가장 큰 차이점은 예외에 대한 처리를 컴파일 시점에 강제하냐 안하냐의 차이입니다. (예외를 언제 던지냐의 차이가 아닙니다.) 코드에 Checked Exception에 대한 예외 처리가 안되어 있을 때 발생하는 건 컴파일 에러입니다.

 

Uncheckced Exception은 왜 예외처리를 강제하지 않을까?

RuntimeException클래스와 자손들은 예외처리 강제하지 않는다.
만일 RuntimeException클래스들에 속하는 예외에도 예외처리를 필수로해야한다면? 아래와 같이 참조변수와 배열이 사용되는 모든 곳에 예외처리를 해주어야할 것이다.

try {
	int[] arr = new int[10];
    System.out.println(arr[0]);
} catch(ArrayIndexOutOfBoundException ae) {
	...
} catch(NullPointerException ne) {
	...
}

이외에도 예외처리가 불필요한 경우 try-catch문을 무조건 넣어야 하므로 코드가 복잡해지게 된다.

 

왜 Checked Exception은 롤백해주지 않는걸까?

CheckedException은 예외처리가 컴파일러에 의해 강제되어있기 때문에 롤백 혹은 다른 처리를 개발자가 진행할 수 있는 기회가 있다. 하지만 Unchecked Exception은 예외 처리가 되어있지 않은 경우가 훨씬 많다. 이 경우 commit은 치명적일 수 있기때문에 롤백을 수행해준다라고 한다.

 

Exception Handling

Java에서 모든 예외가 발생하면 (XXX)Exception 객체를 생성한다.
예외를 처리하는 방법에는 크게 2가지가 있다.

 

1. try-catch

직접 try-catch를 이용해서 예외에 대한 최종적인 책임을 지고 처리하는 방식

try {
	// 예외가 발생할 가능성이 있는 코드
} catch(Exception1 e1) {
	// 예외1이 발생했을 경우, 처리하기 위한 코드
} catch(Exception2 e1) {
	// 예외2이 발생했을 경우, 처리하기 위한 코드
} catch(Exception3 e1) {
	// 예외3이 발생했을 경우, 처리하기 위한 코드
} finally {
	// 예외 발생여부에 관계없이 항상 수행되어야하는 문장을 넣는다. (선택 사항)
}

 

2. 예외 처리 회피

throws Exception을 이용해서 발생한 예외의 책임을 호출하는 쪽이 책임지도록 하는 방식 (주로 호출하는 쪽에 예외를 보고할 때 사용함) 
하지만, 무책임하게 상위 메서드에 throw로 예외를 던지는 것은 상위 메서드의 책임이 증가하기 때문에 좋지 않은 방법이다.

void method() throws Exception1, Exception2, Exception3 {
	...
}

 

3. 예외 전환
예외 처리 회피와 비슷하게 메소드 밖으로 예외를 던지지만, 그냥 던지지 않고 적절한 예외로 전환해서 넘기는 방법이다. 명확한 의미로 전달되기 위해 적합한 의미를 가진 예외로 변경해야 한다.

public Object method() {
        try {
                ...
        } catch (IOException e) {
                throw new CustomException ("IOException 발생");
        }
}

public class CustomException extends RuntimeException{
    public CustomException(String message) {
        super(message);
    }
}

 


참고자료

https://steady-coding.tistory.com/583

https://toneyparky.tistory.com/40

https://velog.io/@gjwjdghk123/Checked-Exception-Unchecked-Exception-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC

https://seungjjun.tistory.com/250

'Java' 카테고리의 다른 글

[Java] 제네릭(Generic) 이란?  (0) 2025.01.31
[Java] 스레드 생명주기와 스케줄링  (0) 2025.01.28
[Java] Weak Refrence란?  (0) 2024.12.17
[Java] static 블록과 생성자  (1) 2024.12.14
[Java] 기본타입 vs 참조타입  (1) 2024.06.10