Stay Hungry Stay Foolish

자바/자바 중급

[자바 중급] 09. 예외 처리 1

dev스카이 2024. 10. 21. 00:02

예외 처리가 필요한 이유

외부 서버와 통신할 때 다음과 같은 다양한 문제들이 발생할 수 있다.

  • 네트워크 오류 등으로 인해 외부 서버와 연결에 실패
  • 데이터 전송에 문제가 발생

 

자바의 예외 처리 메커니즘 제공

자바는 프로그램 실행 중에 발생할 수 있는 예상치 못한 상황, 즉 예외(Exception)를 처리하기 위한 메커니즘을 제공한다. 이는 프로그램의 안정성과 신뢰성을 높이는 데 중요한 역할을 한다.

 

자바의 예외 처리는 다음 키워드를 사용한다. 그리고 예외를 다루기 위한 예외 처리용 객체들을 제공한다.

  • try
  • catch
  • finally
  • throw
  • throws

 

예외 계층 구조

Exception Layer

 

 

  • Exception : 체크 예외
    • 애플리케이션 로직에서 사용할 수 있는 실질적인 최상위 예외이다.
    • Exception과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다. 단 RuntimeException은 예외로 한다.
  • RuntimeException : 언체크 예외, 런타임 예외
    • 컴파일러가 체크 하지 않는 언체크 예외이다.
    • RuntimeException 과 그 자식 예외는 모두 언체크 예외이다.
    • RuntimeException 의 이름을 따라서 RuntimeException 과 그 하위 언체크 예외를 런타임 예외라고 많이 부른다.

 

📌 주의

상속 관계에서 부모 타입은 자식을 담을 수 있다. 예외 처리에서도 상속 예외를 catch로 잡으면 그 하위 예외까지 함께 잡을 수 있다는 특징이 있다. 따라서 애플리케이션 로직에서는 Throwable 예외를 잡으면, 잡으면 안 되는 Error 예외도 함께 잡을 수 있기 때문에 Throwable 예외는 잡으면 안 된다. 애플리케이션 로직은 이런 이유로 Exception 부터 필요한 예외로 생각하고 잡으면 된다.

 

 

📌 예외 2가지 기본 규칙

  1. 예외는 잡아서 처리하거나 밖으로 던져야 한다.
  2. 예외를 잡거나 던질 때 지정한 예외뿐만 아니라 그 예외의 자식들도 함께 처리될 수 있다.

 

체크 예외

 

1. Exception 상속

public class MyCheckedException extends Exception {
    public MyCheckedException(String message) {
        super(message);
    }
}
  • 예외 클래스를 만들려면 예외를 상속 받으면 된다.
  • Exception을 상속받은 예외는 체크 예외가 된다.

 

2. throws, throw 키워드

public class Client {
    public void call() throws MyCheckedException {
        throw new MyCheckedException("ex");
    }
}
  • throw 예외라고 하면 새로운 예외를 발생시킬 수 있다. 예외도 객체이기 때문에 객체를 먼저 new로 생성하고 예외를 발생시켜야 한다.
  • throws 예외는 발생시킨 예외를 메서드 밖으로 던질 때 사용하는 키워드이다.

 

3. try, catch 키워드

public class Service {
    Client client = new Client();
    public void callCatch() {
        try {
            client.call();
        } catch (MyCheckedException e) {
            //예외 처리 로직
            System.out.println("예외 처리, message = " + e.getMessage());
        }
        System.out.println("정상 흐름");
    }
    public void callThrow() throws MyCheckedException {
        client.call();
    }
}
  • callCatch() 는 예외를 잡아서 처리하는 코드이다.
    • client.call() 을 호출하면 Client 클래스에서 예외가 발생한다.
    • Client 클래스에서 예외가 발생하면 throws 로 인해 메서드 밖으로 던져진다. 
    • 던져진 걸 catch 문에서 잡을 수 있다.
  • callThrow() 는 체크 예외를 밖으로 던지는 코드이다.
    • 체크 예외는 예외를 잡지 않고 밖으로 던지려면 throws 예외를 메서드에 필수로 선언해야 한다.

 

 

💡정리

체크 예외는 예외를 잡아서 처리할 수 없을 때, 예외를 밖으로 던지는 throws 예외 를 필수로 선언해야 한다. 그렇지 않으면 컴파일 오류가 발생한다. 개발자가 예외를 직접 명시하여 컴파일러를 통해 어떤 체크 예외가 발생하는지 쉽게 파악할 수 있다는 장점이 있지만, 실제로 개발자가 모든 체크 예외를 반드시 잡거나 던지도록 처리해야 하기 때문에 번거롭다는 단점이 있다.

 

 

언체크 예외

 

1. RuntimeException 상속

public class MyUncheckedException extends RuntimeException {
    public MyUncheckedException(String message) {
        super(message);
    }
}
  • RuntimeException을 상속받은 예외는 언체크 예외가 된다.

 

2. throw 키워드

public class Client {
    public void call() {
        throw new MyUncheckedException("ex");
    }
}
  • 체크 예외에서는 throws 예외를 명시해주었는데, 언체크 예외에서는 생략해도 된다.

 

3. try, catch 키워드

public class Service {
    Client client = new Client();
    /**
     * 필요한 경우 예외를 잡아서 처리하면 된다.
     */
    public void callCatch(){
        try {
            client.call();
        } catch (MyUncheckedException e) {
            //예외 처리 로직
            System.out.println("예외 처리, message = " + e.getMessage());
        }
        System.out.println("정상 로직");
    }
    public void callThrow() {
        client.call();
    }
}
  • callCatch() 는 예외를 잡아서 처리하는 코드이다.
    • 필요한 경우에 예외를 잡아서 처리할 수 있다.
  • callThrow() 는 예외를 밖으로 던지는 코드이다.
    • 언체크 예외는 체크 예외와 다르게 throws 예외 를 선언하지 않아도 된다.
    • 말 그대로 컴파일러가 이런 부분을 체크하지 않기 때문에 언체크 예외이다.

 

💡정리

언체크 예외는 예외를 잡아서 처리할 수 없을 때, 예외를 밖으로 던지는 throws 예외 를 생략할 수 있다. 신경쓰고 싶지 않은 언체크 예외를 무시할 수 있다는 장점이 있지만, 개발자가 실수로 예외를 누락할 수 있다는 단점이 있다.

 

 

체크 예외와 언체크 예외의 차이

  • 체크 예외 : 예외를 잡아서 처리하지 않으면 항상 throws 키워드를 사용해서 던지는 예외를 선언해야 한다.
  • 언체크 예외 : 예외를 잡아서 처리하지 않아도 throws 키워드를 생략할 수 있다.