9회차. 예외 처리

KIMA·2023년 2월 1일
0
post-thumbnail

목표

  • 자바의 예외 처리에 대해 학습하기

학습할 것

강제로 예외를 던지는 방법 - throw 키워드

throw [예외 객체];
  • 예제

    public class ThrowPractice {
      public static void main(String[] args) {
        ThrowPractice practice = new ThrowPractice();
        practice.throwException(13);
      } 
    
      private void throwException(int number) {
        try {
          if(number > 12) {
            throw new Exception("Number is over than 12");
          }
          System.out.println("Number is " + number);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
    java.lang.Exception: Number is over than 12
      (package 생략).ThrowPractice.throwException(ThrowPractice.java: 13)
      (package 생략).ThrowPractice.main(ThrowPractice.java: 8)

예외 처리 방법

방법1. try, catch, finally로 예외 처리하기

: 예외가 발생했을 때, 발생한 메소드에서 예외를 처리하는 방법

try {
  // 예외가 발생할 수 있는 코드
} catch (NoSuchElementException e1) {
  // try 블록에서 해당 예외가 발생할 경우에 실행될 코드
  // e1 변수에는 발생된 예외가 대입된다.
} catch (RuntimeException e2) {
  // try 블록에서 e2 예외가 발생할 경우에 실행될 코드
  // e2 변수에는 발생된 예외가 대입된다.
} finally {
  // try 블록내의 예외 발생 여부와 상관없이 무조건 실행될 코드
}

💡 예외 처리시, 사용한 리소스를 자동으로 닫아주는 방법 - try-with-resources

  • try-catch-finally 예제
    FileOutputStream out = null;
    try {
      out = new FileOutputStream("temp.txt");
    } catch(FileNotFoundException e) {
      e.printStackTrace();
    } finally {
      if(out !- null) { // 예외가 발생되지 않았다면, 오픈한 파일을 다시 닫아준다.
        try {
          out.close();
        } catch(IOException e) {
          e.printStackTrace();
        }
      }
    }
    • 단점 : 오픈한 리소스를 다시 닫아주는 과정이 번거롭다.
      • 해결 방안 : try-with-resources로 사용한 리소스를 자동으로 닫아준다.
  • try-with-resources 예제
    try(FileOutputStream out = new FileOutputStream("temp.txt")) {
    } catch(IOException e) {
      e.printStackTrace();
    }
    • 자동으로 닫아줄 리소스를 try 괄호안에 작성한다.
    • 해당 리소스는 반드시 AutoCloseable인터페이스를 구현해야 한다.
    • 자바 7버전부터 추가된 기능이다.

💡 하나의 catch문에서 여러 예외를 동시에 처리하기 - Multi Catch
: 많은 예외를 각 catch문에서 처리하면 중복된 코드가 반복될 수 있다. 따라서 중복된 코드를 줄이기 위해 하나의 catch문에서 여러 예외를 동시에 처리하는 기능이 추가되었다.

try {
} catch(NoSuchElementException | IllegalArgumentException e) {
}
  • Multi Catch문에 사용된 예외들은 예외의 상속관계에서 부모와 자식관계에 있으면 안된다.
  • 자바 7버전부터 추가된 기능이다.

방법2. throws로 예외 처리 위임하기

: 예외가 발생했을 때, 발생한 메소드에서 예외를 처리하지 않고 해당 메소드를 호출한 메소드로 예외 처리를 위임하는 방법

메소드 throws [예외 클래스] {
  // 예외가 발생하는 코드
}
  • 위임받은 메소드에서는 try-catch문으로 예외처리를 해주거나 자신을 호출한 메소드로 예외를 다시 throws 한다.

Exception VS Error

프로그램이 실행 중 오작동을 하거나 비정상적으로 종료되는 경우를 오류라 하고 오류에는 Error(오류), Excpetion(예외) 두 종류가 있다.

Error(오류)란?

: 컴퓨터 하드웨어의 오동작 또는 고장으로 인해 응용프로그램에 이상이 생겼거나 JVM 실행에 문제가 생겼을 경우 발생한다.

  • 시스템 레벨(자바 프로그램 외)에서 발생한다.
  • 오류가 발생하면 프로그램이 종료된다.
  • 복구할 수 없다.
  • 컴퓨터 하드웨어의 오동작이므로 프로세스에 영향을 준다.
  • 종류
    • OutOfMemoryError : JVM이 할당된 메모리의 부족으로 더 이상 객체를 할당할 수 없을 때 던져지는 오류
      • 즉, GC에 의해 추가적인 메모리가 확보되지 못하는 상황
      • 해결 방안
        • 새는 메모리를 파악해 차단
        • heap 크기 증가
    • StackOverflowError : 호출의 깊이가 깊어지거나 재귀가 지속되어 stack overflow 발생 시 던져지는 오류
      • 해결 방안
        • 재귀 사용 조심, 대신 가시적인 반복문 사용

Exception(예외)이란?

: 컴퓨터의 에러가 아닌 사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류이다.

  • 개발자가 구현한 로직에서 발생한다.
  • 예외가 발생하면 프로그램이 종료된다.
    • 예외처리(자바의 try-catch문)를 통해 프로그램이 종료되지 않고 정상적으로 작동되게 만들어줄 수 있다.
  • 수습이라도 할 수 있다.
  • 자바 프로그램 로직의 문제이므로 쓰레드에 영향을 준다.
  • 종류
    • Checked Exception(= Compile Exception)
      : 주로 치명적인 예외 상황을 발생시키므로, 자바 컴파일러가 반드시 예외처리를 강제한다.
      • 종류(RunimeException을 제외한 모든 예외)
        • ClassNotFoundException
        • IOException
    • Unchecked Exception(= Runtime Exception)
      : 치명적인 예외 상황을 발생시키지는 않으므로, 예외처리 유무는 옵션이다.
      • try-catch문을 사용하기 보다는 코드를 작성하면서 예외가 발생하지 않도록 주의하는 것이 좋다.
      • 종류(RunimeException을 확장한 예외)
        • NullPointerException : 객체가 필요한 경우인데 응용프로그램이 null을 사용하려고 시도할 경우, 던져지는/개발자가 던질 수 있는 예외
        • IllegalArgumentException : 메소드가 허가되지 않거나 부적절한 argument를 받았을 경우, 던져지는/개발자가 던질 수 있는 예외
        • IllegalStateException : 일을 수행하기에 적합하지 않은 상태의 객체인 경우, 던져지는/개발자가 던질 수 있는 예외
        • UnsupportedOperationException : 요청받은 작업을 지원하지 않는 경우, 던져지는/개발자가 던질 수 있는 예외

          🤔 개발자가 던질 수 있는 에외?
          : 말 그대로 응용 프로그램 로직이 진행되다가 개발자가 임의로 예외를 던질 수 있다는 의미이다.

💡 반복문 내에서 예외처리를 하면 성능이 떨어진다.

for(String item : items) {
  try {
    insert(item);
  }catch (SQLException e) {
    e.printStackTrace();
  }
}
  • insert에서 예외 발생 시, RuntimeException으로 한번 Wrapping하여 Exception이 발생 되도록 하고 반복문 내에서는 최대한 예외처리에 대한 코드를 제거하는 것이 성능상 유리하다.

예외 계층구조

예외 계층구조

출처 : Checked Exception과 Uncecked Exception 정리

  • Throwable 클래스
    : 모든 예외의 부모 클래스는 java.lang.Throwable 클래스이다.
    • 기능
      • getMessage() : 예외 메시지를 출력한다.
      • toString() : 예외 메시지와 예외 클래스 이름을 반환한다.
      • printStackTrace() : 예외 메시지, 예외 클래스 이름, 예외가 발생하게 된 메소드들의 호출 관계(Stack Trace)를 출력한다.
        • 서비스 운용시 사용하면, 엄청난 양의 로그가 쌓이게되므로 반드시 필요한 곳에서만 사용한다.

커스텀 예외 만들기

: 자바가 기본적으로 제공하는 예외(= 표준 예외)들 이외에도 개발자가 직접 커스텀 예외를 만들 수 있다.

  • Error와 관련된 클래스는 건들 수 없으며, Exception을 상속받는 클래스만 만들 수 있다.

  • 예제

    class MyCustomException extends Exception() {
      MyCustomException() {
        super();
      }
    
      MyCustomException(String message) {
        super(message);
      }
    }

🤔 표준 예외 VS 커스텀 예외

표준 예외가 있는데 커스텀 예외를 따로 만들어서 사용하는 이유는 무엇일까?
이것에 대한 의견이 분분하므로 각각의 장단점을 살펴보도록 하자.

"표준 예외를 사용하자!" 쪽 의견

첫째, 커스텀 예외는 표준 예외로 충분히 커버할 수 있다.

  • 표준 예외에도 대부분의 상황을 커버할 수 있는 예외들이 존재한다.
    그럼에도 불구하고 커스텀 예외를 사용하는 이유는 표준 예외의 네이밍이 추상적인 의미를 지니고 있어 좀 더 구체적인 의미를 가지는 네이밍의 예외를 던져주기위함이다.
    예를 들자면 다음의 경우이다.
    • 표준 예외 : IllegalArgumentException
    • 커스텀 예외 : UserNameEmptyException
  • 하지만 단지 구체적이라는 이유만으로 커스텀 예외를 만드는 것은 지나친 구현이다.
    왜냐하면 일일이 커스텀 예외 클래스들을 만들다보면 수가 지나치게 많아져 메모리 문제도 발생할 수 있고, 클래스 로딩에도 시간이 더 소요되기 때문이다.
  • 표준 예외를 그대로 사용하고 예외 메시지만 상황에 맞게 재정의해준다면 예외가 발생한 구체적인 이유(위의 예시에서는 UserName이 Empty인 사실)를 충분히 나타낼 수 있다.

둘쨰, 표준 예외는 이미 통용된 예외인데 반해 커스텀 예외는 처음 보는 예외이므로 구체적인 쓰임을 몰라 해당 예외를 파악하는 비용이 추가된다.

"그래도 커스텀 예외가 필요하지!" 쪽 의견

첫째, 예외명으로도 정보 전달이 가능하다.

  • 표준 예외 : IllegalArgumentException
  • 커스텀 예외 : UserNameEmptyException
  • 커스텀 예외는 예외명만을 읽고도 빈 사용자명으로 인해 예외가 발생했다는 사실을 알 수 있다.
    하지만, 표준 예외도 메시지를 정의하면 예외가 일어난 상황을 파악할 수 있다고 위에서 언급했다.
    하지만, 같은 상황의 에외가 발생하는 곳이 많아진다면? 매번 같은 상황이 반복될 때마다 예외 메시지를 새로 적어줄 것인가?
    결론적으로, 표준 예외의 메시지를 활용해 예외가 발생한 상황을 전달해주는 것은 같은 상황이 발생하는 횟수가 잦다고하면 유지보수 측면에서 좋지않다.
    이럴 때에는 커스텀 예외를 사용하는 것이 더 효율적이며, 이 사실이 커스텀 예외의 두번쨰 장점이 된다.

둘쨰, 같은 예외를 발생시키는 상황에서 동일한 양식의 예외 메시지를 사용할 수 있다.

셋째, 예외가 광범위한 표준 예외로 인해 의도하지 않은 예외까지 잡아내는 혼란을 방지할 수 있다.

Reference

profile
안녕하세요.

0개의 댓글