예외
예외
예외(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.