본문 바로가기
야미스터디/Java

[Java] Checked Exception vs Unchecked Exception 📌

by 의정부핵꿀밤 2022. 10. 14.
728x90

예외 계층

1) Error

  • 메모리 부족이나 시스템 오류처럼 애플리케이션에서 복구 불가능한 시스템 예외를 말한다
  • 시스템에 무엇인가 비정상적인 상황이 발생한 경우에 사용된다
  • 주로 자바 가상 머신(JVM)에서 발생시키는 것이며, 예외와 반대로 이를 애플리케이션 코드에서 잡을 수 없다
  • 이는 개발자가 미리 예측하여 처리할 수 없기 때문에 예외 처리에 신경 쓰지 않아도 된다
  • ex) OutOfMemoryError, ThreadDeath, StackOverflowError 등...

 

2) Exception

  • 체크 예외
  • 입력 값에 대한 처리가 불가능하거나 프로그램 실행 중에 참조된 값이 잘못된 경우 등 정상적인 프로그램의 흐름을 어긋나는 것을 의미한다
  • 애플리케이션 로직에서 사용할 수 있는 실질적인 최상위 예외이다
  • Exception과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다
  • Java에서 예외는 개발자가 직접 처리할 수 있기 때문에 예외 상황을 미리 예측하여 핸들링할 수 있다

 

3) RuntimeException

  • 언체크 예외 또는 런타임 예외라고 한다
  • 컴파일러가 체크하지 않는 언체크 예외이다

 

 

 

 

예외 기본 규칙

예외는 잡아서 처리하거나, 처리할 수 없다면 밖으로 던져야 한다

예외를 잡거나 던질 때, 지정한 예외뿐만 아니라 그 예외의 자식들도 함께 처리된다

 

  • 5번에서 예외를 처리하면 이후 애플리케이션 로직은 정상 흐름으로 작동한다

 

  • 만약 예외를 처리하지 못한다면 호출한 곳으로 예외를 계속 던지게 된다

 

💡 예외를 처리하지 못하고 계속 던지는 경우
- 자바 main() 스레드의 경우, 예외 로그를 출력하면서 시스템이 종료된다
- 웹 애플리케이션의 경우, WAS가 해당 예외를 받아서 처리한다
- 주로 개발자가 지정한 오류 페이지를 사용자에게 보여준다

 

 

 


예외 처리

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

 

1) 예외 복구

  • 예외 상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 방법
  • 예외를 잡아서 일정 시간 및 조건만큼 대기하고 다시 재시도를 반복한다
  • 최대 재시도 횟수를 넘기게 되는 경우 예외를 발생시킨다
final int MAX_RETRY = 100;
public Object someMethod() {
    int maxRetry = MAX_RETRY;
    while(maxRetry > 0) {
        try {
            ...
        } catch(SomeException e) {
            // 로그 출력. 정해진 시간만큼 대기한다.
        } finally {
            // 리소스 반납 및 정리 작업
        }
    }
    // 최대 재시도 횟수를 넘기면 직접 예외를 발생시킨다.
    throw new RetryFailedException();
}

 

 

2) 예외처리 회피

  • 예외 처리를 직접 담당하지 않고 호출한 쪽으로 던져 회피하는 방법
  • 그래도 예외 처리의 필요성이 있다면 어느 정도는 처리하고 던지는 것이 좋다
  • 긴밀하게 역할을 분담하고 있는 관계가 아니라면 예외를 그냥 던지는 것은 무책임하다
// 예시 1
public void add() throws SQLException {
    // ...생략
}

// 예시 2 
public void add() throws SQLException {
    try {
        // ... 생략
    } catch(SQLException e) {
        // 로그를 출력하고 다시 날린다!
        throw e;
    }
}

 

 

 

3) 예외 전환

  • 예외 회피와 비슷하게 메서드 밖으로 예외를 던지지만, 그냥 던지지 않고 적절한 예외로 전환해서 넘기는 방법
  • 조금 더 명확한 의미로 전달되기 위해 적합한 의미를 가진 예외로 변경한다
  • 예외 처리를 단순하게 만들기 위해 포장(wrap)할 수도 있다
// 조금 더 명확한 예외로 던진다.
public void add(User user) throws DuplicateUserIdException, SQLException {
    try {
        // ...생략
    } catch(SQLException e) {
        if(e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY) {
            throw DuplicateUserIdException();
        }
        else throw e;
    }
}

// 예외를 단순하게 포장한다.
public void someMethod() {
    try {
        // ...생략
    }
    catch(NamingException ne) {
        throw new EJBException(ne);
        }
    catch(SQLException se) {
        throw new EJBException(se);
        }
    catch(RemoteException re) {
        throw new EJBException(re);
        }
}

 

 


체크 예외 기본 이해

  • Exception과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다
  • (단, RuntimeExcetion은 예외로 한다)
  • 체크 예외는 잡아서 처리(try-cathc) 하거나 또는 밖으로 던지는(throw) 등의 에러 처리를 해야한다
  • 그렇지 않으면 컴파일 오류가 발생한다

 

 

Exception을 상속 받은 예외는 체크 예외가 된다

static class MyCheckedException extends Exception {
	public MyCheckedException(String message) {
		super(message);
	}
}

 

 

체크 예외를 잡아서 처리하는 코드

public void callCatch() {
	try {
		repository.call();
	} catch (Exception e) {
			//예외 처리 로직
	}
}

 

 

체크 예외를 밖으로 던지는 코드

public void callThrow() throws MyCheckedException {
	repository.call();
}

 

 

체크 예외 장점

  • 개발자가 실수로 예외를 누락하지 않도록 컴파일러를 통해 문제를 잡아준다

 

체크 예외 단점

  • 실제로는 개발자가 모든 체크 예외를 반드시 잡거나 던지도록 처리해야 하기 때문에 번거롭다

 


언체크 예외 기본 이해

  • RuntimeException의 하위 클래스들을 의미한다
  • 언체크 예외는 컴파일러가 예외를 체크하지 않는다
  • 체크 예외가 기본적으로 동일하지만 차이가 있다면, 예외를 던지는 throws를 선언하지 않고 생략할 수 있다는 점이다
  • 즉, 체크 예외와 달리 에러 처리를 강제하지 않는다
  • 이 경우 자동으로 예외를 던진다

 

언체크 예외를 잡아서 치리하는 코드

try {
	repository.call();
} catch (MyUncheckedException e) {
	//예외 처리 로직
}
  • 언체크 예외도 필요한 경우 이렇게 잡아서 처리가 가능하다

 

언체크 예외를 밖으로 던지는 경우

public void callThrow() {
	repository.call();
}
  • 언체크 예외는 체크 예외와 다르게 throws 예외를 선언하지 않아도 된다
  • 참고로 언체크 예외도 throws 예외를 선언해도 된다
  • 언체크 예외는 주로 생략하지만, 중요한 예외의 경우 선언해두면 해당 코드를 호출하는 개발자가 이런 예외가 발생한다는 점을 IDE를 통해 좀 더 편리하게 인지할 수 있다

 

언체크 예외 장점

  • 신경쓰고 싶지 않은 언체크 예외를 무시할 수 있다

 

언체크 예외 단점

  • 언체크 예외는 개발자가 실수로 예외를 누락할 가능성이 있다

 


체크 예외 활용

체크 예외의 기본 원칙은 기억하자!

  • 기본적으로 언체크 예외를 사용하자
  • 체크 예외는 비즈니스 로직 상 의도적으로 던지는 예외에만 사용하자 (ex. 계좌 이체 실패, 로그인 ID/PW 불일치 등)

 

 

체크 예외의 발생 흐름

  1. Service는 Repository와 NetworkClient를 둘 다 호출한다
  2. 따라서 두 곳에서 올라오는 체크 예외인 SQLException과 ConnectException을 처리해야 한다
  3. 하지만 서비스는 이 둘을 처리할 방법을 모르기 때문에 밖으로 던진다
  4. 컨트롤러도 두 예외를 처리할 방법이 없기 때문에 예외를 밖으로 던진다
  5. 웹 애플리케이션이라면 서블릿의 오류 페이지나 스프링 MVC가 제공하는 ControllerAdvice에서 이런 예외를 공통으로 처리해준다
  6. 이렇게 해결 불가능한 공통 예외는 별도의 오류 로그를 남기고, 개발자가 오류를 빨리 인지할 수 있도록 전달받아야 한다

 

 

체크 예외의 문제점

위의 예시를 통해 크게 2가지 문제가 있다는 것을 알 수 있다

 

1) 복구 불가능한 문제

  • 대부분의 예외는 복구가 불가능하다
  • 대부분의 서비스나 컨트롤러는 SQLException과 같은 문제를 해결할 수 없으므로, 오류 로그를 남기고 개발자가 해당 오류를 빠르게 인지하는 것이 필요하다
  • 서블릿 필터, 스프링 인터셉터, 스프링의 ControllerAdvice를 사용하면 이런 부분을 깔끔하게 해결할 수 있다

 

2) 의존 관계에 대한 문제

  • throws SQLException, ConnectException 처럼 예외를 던지는 부분을 코드에 선언하면 서비스와 컨트롤에서 java.sql.SQLException을 의존하기 때문에 문제가 된다
  • 만약 JDBC를 JPA 같은 기술로 변경한다면 예외도 함께 변경해야 하며, 해당 예외를 던지는 모든 부분도 함께 변경해야 한다

 

 

 

 

언체크 예외 활용

 

  • 런타임 예외이기 떄문에 서비스, 컨트롤러는 해당 예외들을 처리할 수 없다면 별도의 선언 없이 그냥 두면 된다
  • 런타임 예외를 사용하면 서비스나 컨트롤러가 복구 불가능한 예외를 신경쓰지 않아도 된다
  • 런타임 예외는 해당 객체가 처리할 수 없는 예외는 무시하면 되므로, 체크 예외처럼 예외를 강제로 의존하지 않아도 된다

 

 

런타임 예외 사용 변환 코드

static class NetworkClient {
	public void call() {
		throw new RuntimeConnectException("연결 실패");
	}
}

static class Repository {
	public void call() {
		try {
				runSQL();
		} catch (SQLException e) {
				throw new RuntimeSQLException(e);
		}
	}

	private void runSQL() throws SQLException {
		throw new SQLException("ex");
	}
}
  • Repository에서 체크 예외인 SQLException이 발생하면, 런타임 예외인 RuntimeException으로 전환해서 예외를 던진다
  • NetworkClient는 단순히 기존 체크 예외를 RuntimeConnectException이라는 런타임 예외가 발생하도록 하였다

 

💡 참고
- throw new RuntimeSQLException(e); 에서 기존 예외 e를 포함하지 않는다면, 기존에 발생한 java.sql.SQLException과 Stack Trace를 확인할 수 없다
- 변환한 RuntimeSQLException부터 예외를 확인할 수 있으므로 꼭 기존 예외를 포함해야 한다

 

 

런타임 예외 throws 생략

class Controller {
	public void request() {
		service.logic();
	}
}

class Service {
	public void logic() {
		repository.call();
		networkClient.call();
	}
}
  • 런타임 예외이기 때문에 컨트롤러나 서비스가 해당 예외를 처리할 수 없다면 다음 부분을 생략할 수 있다
  • method() throws RuntimeSQLException, RuntimeConnectException
  • 런타임 예외를 사용하면 중간에 기술이 변경되어도 해당 예외를 사용하지 않는 컨트롤러, 서비스에서는 코드를 변경하지 않아도 된다
  • 공통 처리하는 한 곳만 변경되기 때문에 변경의 영향 범위는 최소화된다

 

 


체크 예외와 언체크 예외의 Rollback 여부

  • 체크 예외 : Rollback이 되지 않고 트랜잭션이 commit까지 완료된다
  • 언체크 예외 : Rollback이 된다

 

 

 

💡 Checked Exception vs Unchecked Exception

  • 예외 복구 전략이 명확하고 복구가 가능하면 Checked Exception을 try-catch로 잡아서 예외 복구를 하거나, 코드의 흐름으로 제어하는 것이 좋다
  • 그러나 이러한 경우는 흔하지 않기 때문에 Checked Exception이 발생하면 더 구체적인 Unchecked Exception을 발생시키고 예외에 대한 메시지를 명확하게 전달하는 것이 효과적이다
  • 무첵임하게 상위 메서드에 throw로 예외를 던지면 상위 메서드들의 책임이 그만큼 증가하기 떄문에 되도록 상위 메서드에 던지지 않는 것이 좋다
  • Checked Exception은 기본 트랜잭션 속성에서 Rollback을 진행하지 않는다

🔍예상 면접 질문 정리

Error(에러)란?

  • 시스템에 무엇인가 비정상적인 상황이 발생한 경우에 사용된다
  • 주로 JVM에서 발생시키며 이는 애플리케이션 코드에서 잡을 수 없다

 

Exception(예외)란?

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

 

Checked Excetpion 이란?

  • RuntimeException을 상속하지 않은 클래스로, 명시적인 에외 처리를 해야 한다
  • 컴파일 시점에 확인할 수 있고, 트랜잭션 안에서 동작할 때 Checked Exception이 발생하면 롤백되지 않는다

 

Unchecked Exception 이란?

  • RuntimeException을 상속한 클래스로, 명시적인 예외 처리를 하지 않는다
  • 런타임 시점에 확인할 수 있고, 트랜잭션 안에서 동작할 때 Unchecked Exception이 발생하면 롤백된다

 

Checked Exception을 처리하는 방식은?

  • 예외 복구 : 예외 상황을 파악하고 문제를 해결해서 정상 상태로 복구한다
  • 예외 처리 회피 : 예외 처리를 직접 담당하지 않고 호출한 쪽으로 넘긴다
  • 예외 전환 : 적절한 예외로 전환해서 넘긴다

 

올바른 예외 처리 방식

  • 예외 복구 전략이 명확하고 복구가 가능하다면 Checked Exception을 try-catch로 잡아서 예외를 복구하는 것이 좋다
  • 복구가 불가능한 Checked Exception이 발생하면 더 구체적인 Unchecked Exception을 발생시키고 예외에 대하 ㄴ메시지를 명확하게 전달하는 것이 좋다
  • 무책임하게 상위 메서드에 throw로 예외를 던지는 것은 상위 메서드의 책임이 증가하기 때문에 좋지 않다

 

 

 

 


참고)

https://madplay.github.io/post/java-checked-unchecked-exceptions

 

자바 예외 구분: Checked Exception, Unchecked Exception

자바에서 예외는 어떻게 구분할까? Checked Exception과 Unchecked Exception의 차이는 무엇일까?

madplay.github.io

https://unequaled-peach-7e5.notion.site/Checked-Exception-vs-Unchecked-Exception-52aa45c479a04fc68c0897781dd9876f

 

Checked Exception vs Unchecked Exception

🌹 정리 by 장미(https://velog.io/@newbiekim/)

unequaled-peach-7e5.notion.site

https://devlog-wjdrbs96.tistory.com/351

 

[Java] Checked Exception vs Unchecked Exception 정리

체크 예외와 언체크 예외(Checked, Unchecked Exception) 자바의 예외는 크게 3가지로 나눌 수 있습니다. 체크 예외(Checked Exception) 에러(Error) 언체크 예외(Unchecked Exception) 자바에서 에러 , 예외 관련..

devlog-wjdrbs96.tistory.com

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

 

[Java] Checked Exception과 Unchecked Exception

java-study에서 스터디를 진행하고 있습니다. 에러(Error)와 예외(Exception) 프로그래밍에서 예외란 입력 값에 대한 처리가 불가능하거나, 프로그램 실행 중에 참조된 값이 잘못된 경우 등 정상적인 프

steady-coding.tistory.com

 

728x90

댓글