예외
예외를 처리하는 베스트 프랙티스들을 봐보자
초난감 예외처리
대표적인 예외처리 초난감 주자들
예외 블랙홀
try {
} catch(SQLException e){
}
예외를 받고 아무것도 하지 않은 상황. 발생한 예외를 무시해버리는 상황이 발생한다.
오류가 나도 어디서 문제가 생기는지 전혀 알 수 없게 되버린다.
} catch(SQLException e){
System.out.println(e);
또는
e.printStackTrace();
}
이것도 문제가 많다. 콘솔에만 나오기 때문에 콘솔을 보고있지 않으면 메시지를 볼 수 없다.
무의미하고 무책임한 throws
public void method1() throws Exception {
method2();
}
남용하면 throws 에 의미가 없어진다.
예외처리를 할 때 지켜야 할 핵심 원칙
- 예외를 적절하게 복구하던지
- 작업을 중단시키고 개발자에게 통보가 되어야 한다.
예외의 종류와 특징
예외는 크게 3종류가 있다.
Error
java.lang.Error 클래스와 그 서브 클래스에서 발생하는 예외다.
시스템에서 발생하는 비정상적인 상황을 뜻하는데
어플리케이션이 아닌 VM 이나 OS 등에서 발생하는 경우다. (OutOfMemory, ThreadDeath 등) 이런 에러는 어플리케이션에서 잡을 필요가 없다.
Exception
java.lang.Exception 클래스와 그 서브 클래스
Checked Exception
RuntimeException을 상속하지 않는다.
catch 문이 의무적이다.
컴파일단에서 확인이 가능하고, 복구를 시도해보는것이 일차적인 관심사다.
복구될 가능성이 있는 문제를 checked exception이라 한다.
Unchecked Exception
RuntimeException을 상속받아 런타임 예외라고도 한다.
catch/throw 를 하지 않아도 된다.
이 예외들은 코드에서 미리 주의깊게 만든다면 피할 수 있는 문제들이다.
복구하는 것보다는 빨리 확인하고 처리해야 하는 문제들
예외처리 방법
예외 복구
예외상황을 파악하고 문제를 해결해서 정상 상태로 돌린다.
예를들면 사용자가 파일을 읽으려고 했는데 파일이 없다면? 다른 파일을 읽게 한다던지 등등
네트워크가 불안정해서 접속이 안된경우면? 일정시간 대기 후 재시도 한다던지 등의 복구 작업을 해본다.
예외 처리 회피
예외처리를 자신이 하지않고 호출한 쪽으로 던져버리는것
throws 문으로 예외가 발생하면 알아서 던지게 하거나 catch로 예외를 잡고 로그를 남긴 후에 다시 예외를 던지는 방법등이 있다.
예외 전환
발생한 예외를 그대로 던지지 않고 적절한 예외로 전환해서 던진다.
2가지 목적
- 예외를 좀 더 적절하고 분명한 의미를 가진 예외로 바꿔서 던지기 위해
- 예외를 처리하기 쉽고 단순하게 만들기 위해 포장
try {
OrderHome orderHome = EJBHomeFactorY.getlnstance().getOrderHome();
Order order = orderHome.findByPrimaryKey(Integer id);
} catch (NamingException ne) (
throw new EJBException(ne);
} catch (SQLException se) (
throw new EJBException(se);
} catch (RemoteException re ) (
throw new EJBException(re)
}
예외처리 전략
checked 에러도 런타임 예외로 전환해서 던지는게 낫다.
예전엔 복구할 가능성이 조금이라도 있다면 체크 예외로 만든다 생각했지만,
항상 복구 할 수 있는게 아니라면 런타임 예외로 만드는 경향
런타임에서도 catch로 복구 처리를 할 수 있기 때문
좀 더 명확한 런타임 예외로 포장해서 던진다.
public void add(User user) throws DuplicateUserIdException {
try {
// 로직
} catch(SQLException e) {
if (e.QetErrorCode() == MysQIErrorNumbers.ER_DUP_ENTRY)
throw new DuplicateUserIdException(e); // 예외 전환
} else
throw new RuntimeException(e); // 예외 포장
예외 전환
예외 전환은 2가지 목적을 가진다.
- 런타임 예외로 포장해서 불필요한 catch 를 줄여주는것
- 좀 더 명확한 예외로 던져서 바꿔주는것
JDBC의 한계
DB에 관계없이 자유롭게 사용할수없었다.
JDBC는 SQLException하나로 모든 예외가 압축되어서 에러 코드를 확인해야만 정확한 에러를 확인 할 수 있었는데
DB는 서로 다른 에러코드를 사용했다.
이 문제를 해결하기 위해 스프링 JdbcTemplate 에서는 DataAccessException 예외를 계층구조로 두고
DB 에러 코드를 스프링 예외 클래스로 맵핑을 시켜서 같은 예외들이 얼추 같은 예외 클래스로 모이게 했다.
"DataAccessException 계층구조는 '데이터 액세스 기술'의 종류와 상관없이 일관된 에러가 발생하도록 만들어준다."