Post

예외

예외

예외(Exception)란 입력 값에 대한 처리가 불가능하거나, 프로그램 실행 중에 참조된 값이 잘못된 경우 등 정상적인 프로그램의 흐름을 어긋나는 것을 말한다. 에러(Error)는 시스템에 비정상적인 상황이 발생한 경우에 사용된다.

예외 구분

예외 구분

자바에는 크게 세종류의 예외가 존재하며 각각의 특징은 다음과 같다.

Error

  • 응용 프로그램 외부에 있으며 일반적으로 응용 프로그램이 예상하거나 복구할 수 없는 예외적인 조건
  • 주로 VM에서 발생시키는 것이고 발생 시 프로세스에 영향을 주므로 에러를 잡아도 대응 방법이 없다.
  • OutOfMemoryError, ThreadDeath, StackOverflowError 등이 있다.

Exception (Checked)

  • 컴파일러가 강제로 메서드를 호출한 쪽에서 예외를 반드시 처리하도록 한다.
  • 즉 catch문으로 잡거나 메소드 시그니처에 throws를 정의해야한다.
  • IOException, SQLException 등이 있다.

RuntimeException (Unchecked)

  • 컴파일 시 체크를 하지 않는다. 즉 메소드를 호출한 쪽에 명시적 예외처리가 강제되지 않는다.
  • 즉 catch문으로 잡거나 throws를 정의하는 것이 강제가 아니다.
  • NullPointException, IllegalArgumentException 등이 있다.

예외 처리 방법

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

예외 복구

  • 예외 상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 방법이다.
  • 예외로 기본 작업 흐름이 불가능하면 다른 작업 흐름으로 우회하는 것이다.
  • 예시
    • 연결 예외가 발생한 경우 일정 간격으로 재시도하다가 최종적으로 실패를 알리는 시도를 할 수 있다.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
      final int MAX_RETRY = 100;
      public Object someMethod() {
        int maxRetry = MAX_RETRY;
        while(maxRetry > 0) {
          try {
            ...
          } catch(SomeException e) {
            // 로그 출력. 정해진 시간만큼 대기한다.
          } finally {
            // 리소스 반납 및 정리 작업
          }
        }
        // 최대 재시도 횟수를 넘기면 직접 예외를 발생시킨다.
        throw new RetryFailedException();
      }
      

예외처리 회피

  • 예외 처리를 직접 당담하지 않고 자신을 호출한 쪽으로 던지는 것이다.
  • throws 문으로 선언해서 예외가 발생하면 (예외 처리의 필요성이 있을 경우 어느정도 처리 후) 다시 예외를 던진다.(rethrow)
  • 그러나 긴밀하게 역할을 분담하고 있는 관계가 아니라면 자신의 코드에서 발생하는 예외를 그냥 던지는 것은 무책임한 책임회피이다.
  • 예시
    1
    2
    3
    4
    5
    6
    7
    8
    
    public void add() throws SQLException {
      try {
        // ... 생략
      } catch(SQLException e) {
        // 다시 날린다
        throw e;
      }
    }
    

예외 전환

  • 예외처리 회피와 비슷하게 메소드 밖으로 예외를 던지지만 그냥 던지는 것이 아닌 적절한 예외로 전환해서 넘기는 방법이다.
  • 예외 전환의 목적
    • 조금 더 명확한 의미로 전달되기 위해 적합한 의미를 가진 예외로 변경한다.
      • 기술적인 의미를 가지는 로우레벨 예외를 사용자 상황에 적합한 의미를 가진 예외로 변경한다.
      • 보통 전환하는 예외에 원래 발생한 예외를 담아서 중첨 예외(nested exception)으로 만드는 것이 좋다.
  • 예시
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    // 조금 더 명확한 예외로 던진다.
    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);
      }
    }
    
This post is licensed under CC BY 4.0 by the author.