Java - Exception

  • 예외처리는 우리가 개발할 때 정말 중요하게 작용한다.
  • 예외처리를 올바르게 하지 않는다면 스레드가 멈출 수도 있고, 원하는 기능이 동작을 하지 않을 수도 있다.
  • Java를 이용하는 개발자에게 Exception 처리란, 실력을 판가름 할 수 있는 좋은 지표가 되기도 한다.

1. 예외 ( Exceptoin ) 이란?

  • 비정상적인 상황에서 프로그램이나 스레드가 멈추지 않고 정상적으로 돌아갈 수 있게 해주는 처리 방법이다.
  • 예를들어 우리가 배달 음식을 주문하고, 완료 알림을 보내주는 로직을 만든다고 가정하자.
  • 이 때 배달 음식 주문은 완료되었지만 완료 알림에서 갑작스럽게 오류가 발생했다. 근데 Exception 처리가 되지 않았다면, @Transactional 에 의해 주문 완료된게 롤백될 수도 있고, 롤백된 걸 클라이언트가 모를 수도 있으며, 어쩌면 주문은 완료되었지만 알림은 가지 않는채로 유지되어 클라이언트가 알 수 없게 될 수도 있다.
  • 그리고 우리가 흔히 겪는 NullPointException 도 있는데, 서비스 도중 미처 이부분을 예외처리 하지 못하였고, 해당 오류가 발생했다고 하여 다음 서비스가 동작하지 않는다면 큰 문제로 이어질 수도 있다.



2. 예외의 종류

  • 우선 Java에서 모든 객체의 조상은 “Object” 이다. 그리고 그 “Object”에서 Error와 Exception을 포함한 자식 클래스가 생기는데 그게 바로 “Throwble”이다.
  • Throwble은 Error와 Exception을 포함한 종류의 조상님이고, 그 아래에 이제 Error 와 Exception으로 나뉘게 된다.
  • Exception 안에는 RunTime Exception과 그냥 Exception들이 있다.


2.1. 에러 ( Error )

  • Error는 프로그램이 종료될 수도 있을만큼의 심각한 오류를 Error 라고 한다. 이는 예외를 둘 수 없으며, 치명적이므로 반드시 생기지 않게 해주어야 한다.
  • 예시로는 StackOverflowError 나 OutOfMemoryError 가 있다.
    • StackOverflowError 는 JVM 공간 중 Stack 메모리가 한계를 초과하면 발생한다. 예를들면 끝나지 않는 재귀함수 호출로 인해 무한히 자기를 호출할 경우 발생한다.
    • OutOfMemoryError는 JVM의 Heap 메모리가 한계를 초과하면 발생하는데, 예를들면 1,000,000 개의 공간을 갖고 있는 배열을 while과 같은 반복문으로 무한히 생성하는 경우가 있을 수 있다.


2.2. 예외 ( Exception )

  • 예외는 우선 발생하더라도 프로그램이 종료되거나 하는 심각한 오류를 발생하지는 않는다. 그런 오류가 발생할 수 있는 예외문은 Checked Exception으로 분류되어 컴파일 단계에서 조치를 취해준다. 그러나 예외처리를 하지 않을 경우에는 위에서 이야기하였듯 해당 스레드에서 다음 동작이 정상적으로 수행되지 않는다. Exception에는 두 종류가 있는데, Checked Exception과 Unchecked Exception이 있다.


컴파일 예외 ( Checked Exception )

  • 이는 다른 말로 컴파일 예외라고도 한다.
  • IOException이나 NoSuchMethodException 같은 경우가 이에 속하는데, try catch 문이나 throws 를 통해 예외처리를 하지 않으면 컴파일 단계에서 에러를 발생시킨다.
  • 예를 들면 아래와 같은 코드가 있다. 아래 코드에서 throws IOExceptoin을 빼면 컴파일 에러가 발생한다.
      public class FileReadExample {
          public static void main(String[] args) {
              try {
                  readFromFile("example.txt");
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
    
          private static void readFromFile(String fileName) throws IOException {
    		
              try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
                  String line;
                  while ((line = reader.readLine()) != null) {
                      System.out.println(line);
                  }
              }
    			
          }
      }
    


런타임 예외(UnChecked Exception / Runtime Exception)

  • 런타임 예외는 Exception의 하위 클래스 중 RuntimeException 이라는 클래스에 속한 예외클래스들을 이야기한다.
  • 해당 예외 클래스들은 try catch 등으로 예외처리를 해주지 않아도 컴파일 하는데에 문제가 없다.
  • 그러나, 예외 처리를 해주지 않으면 런타임 도중 에러가 발생할 때 다음 동작이 이루어지지 않는다.
  • 예시로는 NullpointException이나 IndexOutOfBoundsException 이 있다.



3. 나만의 예외 ( Exception ) 만드는 방법

  • 기본적으로 예외는 아래 코드처럼 만들어주면 된다.
      class MyException extends Exception {
          public MyException() {
              super("에러 메시지");
          }
      }
    
  • 단, 이 때 유의해야 할 점은 내가 만드는 예외문이 컴파일 단계에서 체크가 되어야 할 예외문인지, 아니면 런타임 도중에 체크되어야 할 예외문인지를 잘 판단해서 extends를 해줘야 한다.
  • 굳이 try catch나 throw가 필요 없으면 extends RuntimeException 을 상속해주면 되고, 그게 아니라 컴파일 단계에서 체크해야 되면 위 코드처럼 extends Exception을 해주는게 좋다.


  • 아래는 필자가 예시로 만든 예외코드이다. ( 1월 ~ 12월 관련 예외문 )
      class CustomMonthException extends Exception {
          public CustomMonthException() {
              super("월은 1부터 12월까지의 값이어야 합니다.");
          }
      }
      public class Main {
    
          public static void monthCheck(int month) throws CustomMonthException {
              try {
                  if ( month < 1 || month > 12 ) {
                      throw new CustomMonthException();
                  }
              } catch ( CustomMonthException e ) {
                  e.printStackTrace();
              }
          }
    		
    		
          public static void main(String[] args) {
              try {
                  monthCheck(13); // Exception 발생
              } catch ( CustomMonthException e ) {
                  e.printStackTrace();
              }
          }
      }
    



4. 예외 ( Exception )를 현업에서 사용하는 좋은 방법

  • 다시 서론에서의 문제로 돌아와서, 배달 주문은 완료가 되었는데 알림에 오류가 발생한 경우를 알아보자. 이 알림 오류가 항상 일어나는거라면 코드를 고치는게 맞지만, 산발적으로 가끔 발생하는 오류라면 예외처리를 해주는 것이 좋다.
      try {
          // 주문 서비스 로직
          // 알림 발생 로직
      } catch ( 예외 e ) {
          // 알림에서 문제가 발생하였을 경우를 대비하는 코드
      }
    
  • 이런 코드를 만들어서, 알림에서 문제가 발생하였을 경우 해당 알림에 대한 정보를 DB에 저장하고, 배치 등을 통하여 1분에 한 번씩 오류가 생겨 DB에 모인 그 알림들을 보내준다거나
  • 아니면 알림 발생 로직을 한 번 더 Retry 해주는 코드를 catch문 안에 넣을 수 있겠다. ( 물론 log 처리는 해줘야 한다. )


  • 또 다른 예시로는, Toss Pay를 결제 방식으로 사용하는 쇼핑몰이 있다고 가정하자. 근데 Toss Pay에서 문제가 발생하였다.
  • 그럴 경우 Toss 측에서 수정을 해주는게 맞지만, 그 사이 매출액에 타격이 생길 수 있다. 그럴 경우 catch문 안에 다른 Pay로 연결하는 로직을 넣어줄 수도 있는 것이다.


이 처럼 예외처리는 굉장히 중요한 영역이라 할 수 있다.






results matching ""

    No results matching ""