on
[4장] 예외
[4장] 예외
4장에서는 JdbcTemplate을 대표로 하는 스프링의 데이터 액세스 기능에 담겨 있는 예외처리와 관련된 접근 방법에 대해 알아본다. 이를 통해 예외를 처리하는 베스트 프랙티스도 살펴본다.
1. 사라진 SQLException
3장에서 JdbcTemplate으로 바꾸고 나서 throws SQLException이 사라졌다.
먼저 개발자들의 코드에서 종종 발견되는 초난감 예외처리의 대표선수들을 살펴보자.
예외 블랙홀
try/catch 블록 중 catch로 예외를 잡고 아무것도 하지 않는 경우. 조치를 취할 방법이 없다면 굳이 예외를 잡지 말고 메소드에 throws SQLException을 선언해 자신을 호출한 코드에 예외처리 책임을 전가하자.
무의미하고 무책임한 throws
모든 메소드에 기계적으로 예외를 무조건 던져버리는 선언을 넣는 것
예외의 종류와 특징
자바에서 throw를 통해 발생시킬 수 있는 예외는 크게 세 가지가 있다.
1. Error : java.lang.Error 클래스의 서브클래스들. 시스템에 비정상적인 상황이 발생했을 경우에 사용된다. 주로 자바 VM에서 발생시켜 애플리케이션 코드에서 잡으려고 하면 안 된다. OutOfMemoryError, ThreadDeath 등
2. Exception과 체크 예외 : java.lang.Exception 클래스와 서브클래스들. 개발자가 만든 코드 작업 중 예외상황이 발생했을 경우에 사용된다.
체크 예외 : Exception 클래스의 서브클래스면서 RuntimeException 클래스를 상속하지 않은 것.
언체크 예외 : RuntimeException을 상속한 클래스
체크 예외가 발생할 수 있는 메소드를 사용할 경우 반드시 예외 처리 코드를 작성해야 한다.
3. RuntimeException과 언체크/런타임 예외 : java.lang.RuntimeException 클래스를 상속한 예외들은 명시적인 예외처리를 강제하지 않기 때문에 언체크 예외 or 런타임 예외라고 한다. 주로 프로그램의 오류가 있을 때 발생하도록 의도된 것들이다. NullpointerException, IllegalArgumentException 등
이제 일반적인 예외 처리 방법을 살펴보자.
예외 복구
예외상황을 파악하고 문제를 해결해 정상 상태로 돌려놓는 것
예를 들어 사용자가 요청한 파일이 없어 IOException이 발생한 경우 다른 파일을 이용하도록 안내해서 예외상황을 해결할 수 있다. 예외로 인해 기본 작업 흐름이 불가능하다면 다른 작업 흐름으로 자연스럽게 유도해주는 것이다.
예외처리 회피
예외처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 것
throws 문으로 선언해 예외가 발생하면 알아서 던져지게 하거나 catch문으로 예외를 잡은 후 로그를 남기고 다시 예외를 던지는 것이다.
public void add() throws SQLException { try{ //JDBC API } catch(SQLException e) { //로그 출력 throw e; } }
JdbcContext나 JdbcTemplate이 사용하는 콜백 오브젝트는 SQLException을 자신이 처리하지 않고 템플릿으로 던져버린다. SQLException을 처리하는 일은 콜백의 역할이 아니라고 보기 때문이다.
Q. 만약 DAO가 SQLException을 생각 없이 던진다면?
A. 해당 예외를 서비스, 컨트롤러에서도 다시 던져 그냥 서버로 전달되고 말 것.
이처럼 무분별하게 예외를 회피하는 것은 무책임하므로 자신을 사용하는 쪽에서 예외를 다루는 게 최선의 방법이라는 확신이 있어야 한다.
예외 전환
발생한 예외를 적절한 예외로 전환해서 메소드 밖으로 던지는 것
예외 전환은 다음과 같은 두 가지 목적으로 사용된다.
1. 발생한 예외를 그대로 던지는 것이 그 예외상황에 대한 적절한 의미를 부여해주지 못할 때, 의미를 분명하게 해주는 예외로 바꿔주기 위해서이다.
ex) 새로운 사용자 등록 시 아이디 중복 → DAO가 SQLException을 밖으로 던지면 서비스 계층에서는 SQLException이 발생한 이유를 알기 힘들다.
예외 전환 기능을 가진 DAO 메소드
public void add(User user) throws DuplicateUserIdException, SQLException { try { //JDBC를 이용해 user 정보를 DB에 추가하는 코드 또는 //그런 기능을 가진 다른 SQLException을 던지는 메소드를 호출하는 코드 } catch(SQLException e) { //ErrorCode가 MySQL의 "Duplicate Entry(1062)"면 예외 전환 if (e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY) throw DuplicateUserIdException(e); //중첩 예외 //throw DuplicateUserIdException().initCause(e); else throw e; //그 외의 경우는 SQLException 그대로 } }
2. 예외를 처리하기 쉽고 단순하게 만들기 위해 포장하기 위해서이다.
주로 예외처리를 강제하는 체크 예외를 언체크 예외인 런타임 예외로 바꾸는 경우에 사용한다.
ex) EJBException
예외 포장
try{ OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome(); Order order = orderName.findByPrimaryKey(Integer id); } catch (NamingException ne){ throw new EJBException(ne); } catch (SQLException se){ throw new EJBException(se); } catch (RemoteException re){ throw new EJBException(re); }
from http://spring-com.tistory.com/7 by ccl(A) rewrite - 2021-12-21 20:27:15