예외 (토비의 스프링 vol.1 4장)
예외처리 : 프로그램 실행 중 발생할 수 있는 문제에 대비하는 방법
예외의 종류와 특징
- Error
- java.lang.Error 클래스의 서브클래스
- 시스템에 비정상적인 상황이 발생할 때 사용
- 주로 자바 VM에서 발생시키며 애플리케이션 코드에서 잡지 않아야 함
- 예:
OutOfMemoryError,ThreadDeath
- Exception
- java.lang.Exception 클래스와 그 서브클래스들
- 애플리케이션 코드에서 발생할 수 있는 예외 상황에 사용
- 체크 예외와 언체크 예외(
RuntimeException)로 구분됨 - 체크 예외:
Exception클래스의 서브클래스이지만RuntimeException을 상속하지 않음 - 체크 예외는 반드시 예외처리 코드가 필요함
- 예:
IOException,SQLException
- RuntimeException
- java.lang.RuntimeException 클래스를 상속한 예외들
- 명시적인 예외처리를 강제하지 않음
- 프로그램 오류가 있을 때 주로 발생
- 예:
NullPointerException,IllegalArgumentException
잘못된 예외처리
- try/catch
try { ... } catch(SQLException e){ } // try { ... } catch(SQLException e){ System.out.println(e); } // try { ... } catch(SQLException e){ e.printStackTrace(); } -
모든 예외는 적절하게 복구되든지 아니면 작업을 중단시키고 개발자나 운영자에게 확실하게 통보돼야함
- throws
public void method1() throws Exception { method2(); ... } public void method2() throws Exception { method3(); ... } public void method3() throws Exception { ... }
예외처리 방법
- 예외 복구
- 예외상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 것
- 예외처리 회피
- 예외처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 것
- 예외 전환
- 발생한 예외를 그대로 넘기는 게 아니라 적절한 예외로 전환해서 던지는 것
- 내부에서 발생한 예외를 그대로 던지는 것이 그 예외상황에 대한 적절한 의미를 부여해주지 못하는 경우
- 의미를 가지는 새로운 예외를 만들고 원래 발생한 예외를 담아서 중첩 예외로 던져주는 것
- 예외 처리를 강제하는 체크예외를 언체크예외로 바꾸기 위해
예외처리 전략
자바의 환경이 서버로 이동하며 체크예외의 가치가 떨어지고 있음
- 최근에 등장하는 오픈소스 프레임워크에서는 API가 발생시키는 예외를 언체크예외로 정의하는것이 일반화
-
대부분 복구 불가능하고 따로 포장해서 던져야하기때문에 애초부터 런타임 에러를 던지도록 만든다
- 런타임 예외 중심 전략
- 어떻게든 처리 되겠지 라는 낙관적인 태도를 기반으로 함
- 애플리케이션 예외
- 시스템 또는 외부의 예외상황이 원인이 아니라 애플리케이션 자체의 로직에 의해 의도적으로 발생시키고, 반드시 catch 해서 무엇인가 조치를 취하도록 요구 (ex 계좌 출금, 송금 실패 안내)
- 설계 방법
- 상황에 따라 다른 리턴 타입으로 구분
- 명확하지 않은 리턴값으로 인해 문제 발생 가능성
- 해당 의미를 지닌 예외를 생성하여 던짐
- 확실한 처리를 위한 체크 예외로 생성
- 상황에 따라 다른 리턴 타입으로 구분
JDBC의 한계
Connection, Statement, ResultSet 등의 표준 인터페이스를 통해 그 기능을 제공해주기 때문에 개발자들은 표준화된 JDBC의 API에만 익숙해지면 DB의 종류에 상관없이 일관된 방법으로 프로그램을 개발할 수 있다
DB를 자유롭게 변경하는 유연한 코드를 보장해주지는 않음
- 비표준 SQL
- 해당 DB의 특별한 기능, 최적화된 SQL 생성에 유용
- 호환성 없는 DB에러 정보
- DB마다 에러의 종류와 원인이 다름
- SQL 상태정보를 참조하는 코드가 다름
- getSQLState()을 통해 표준화된 상태코드를 가져와야함(그런데 제대로 만들어주지 않는다는 점)
스프링은 DB별 에러코드를 분류해서 스프링이 정의한 예외 클래스와 매핑해놓은 에러코드 매핑정보 테이블을 만들어두고 이용한다
- 오라클 에러 코드 매핑 파일
<bean id="Oracle" class="org.springframework.jdbc.support.SQLErrorCodes"> <property name="badSqlGrammarCodes"〉 <value>900,903,904,917,936,942,17006</value> </property> <property name="invalidResultSetAccessCodes"> <value>17003</val니e> </property> <property name="duplicateKeyCodes"> <value>1</value> </property> <property name="dataIntegrityViolationCodes"> <value>1400,1722,2291,2292</value> </property> <property name="dataAccessResourceFailureCodes"〉 <value>17002,17447</value> </property>
DataAccessException
스프링은 데이터 엑세스 기술을 사용할 떄 발생하는 예외들을 추상화해서 DataAccessException 계층구조 안에 정리해놓음 (SQLException을 전환할 때 사용)
예외 전환 + 예외 추상화 = 데이터 엑세스 기술과 구현 방법에 독립적인 DAO
어느정도 추상화된 공통 예외로 변환해주지만 전부는 아님
댓글남기기