사라진 SQL Exception
초난감 예외처리
- 모든 예외는 적절하게 복구되거나 작업을 중단시켜 개발자에게 통보되어야한다.
- 예외처리 코드에서 아무것도 안하고 넘어가거나 로그를 출력하는 방식은 예외상황이 개발자에게 알려지지 않을 가능성이 높다.
- 예외를 잡아서 조치를 취할 방법이 없는 경우 예외를 잡지 않는것이 더 좋다.
예외의 종류와 특징
Error
- java.lang.Error 클래스의 서브클래스들이다.
- 주로 자바 VM에서 발생시키고 애플리케이션 코드에서 다루면 안된다.
Exception과 체크 예외
- java.lang.Exception 클래스와 그 서브클래스이다.
- 개발자들이 만든 애플리케이션 코드의 작업 중 예외상황이 발생했을 때 사용된다.
- 체크 예외는 Exception의 서브클래스이며 RuntimeException을 상속하지 않는 예외이다.
- 체크 예외가 발생하면 반드시 예외를 처리하는 코드를 작성해 주어야 하며 그렇지 않으면 컴파일 에러가 발생하게 된다.
RuntimeException과 언체크/런타임 예외
- java.lang.RuntimeException 클래스를 상속한다.
- RuntimeException을 상속하기 때문에 런타임 예외라고도 부르며 catch문으로 잡거나 throws로 선언하지 않아도 무방한다.
예외처리 방법
예외 복구
- 예외상황을 파악하고 문제를 해결하여 정상 상태로 돌려놓는 방법이다.
- 예를 들어 DB 커넥션이 네트워크 불안정으로 인해 얻어지지 않는다면, 일정 횟수만큼 커넥션 요청을 반복하거나 일정 시간 이후에 요청을 보낼 수 있다.
예외처리 회피
- 예외를 자신이 처리하지 않고 자신을 호출한 쪽으로 던져준다.
- 예를 들어 메소드 선언에서
throws SQLException
을 두어 오브젝트의 메소드 내에서 예외를 처리하지 않고 템플릿 레벨에서 처리하도록 던져준다.
- 콜백/템플릿 처럼 긴밀한 관계에 있는 다른 오브젝트에게 예외처리 책임을 지게 하거나 자신을 사용하는 쪽에서 예외를 다루는 것이 최선의 방법이라는 확신이 있어야한다.
예외 전환
- 예외처리 회피와 마찬가지로 정상적인 상태로 예외를 복구할 수 없어 메소드 밖으로 예외를 던지지만, 적적한 형태로 예외를 전환하여 전달한다.
- 예외를 그대로 던저주는 경우 그 예외상황에 대한 의미를 제대로 전달할 수 없어 더 적절한 의미를 가진 예외로 전환한다.
- 예외를 처리하기 쉽고 단순하게 만들기 위해 포장하는 것이다. 보통 체크예외를 언체크 예외로 바꾸는 경우 사용한다.
예외처리 전략
- 일반적으로 체크 예외가 일반적인 예외를 다루고 언체크 예외는 시스템 장애나 프로그램상의 오류를 다룬다.
- 서버에서의 자바 환경에서 체크 예외의 활용도와 가치가 떨어진다.
- 과거에는 복구할 가능성이 조금이라도 있는 예외는 체크 예외로 다루었지만, 현재는 항상 복구할 수 없는 예외가 아니라면 언체크 예외로 만든은 경향이 있다.
add() 메소드의 예외처리
- add() 메소드에서 DuplicateUserIdException과 SQLException의 두 가지 체크 예외를 던진다.
- 체크 예외인 DuplicateUserIdException을 RuntimeException을 상속하는 런타임 예외로 만든다.
- add() 메소드는 DuplicateUserIdException
애플리케이션 예외
- 런타임 예외 중심 전략은 낙관적인 예외처리 기법이라고 할 수 있다. 예외가 생겨도 런타임 예외이므로 시스템 예외이서 처리해 줄 것이고, 필요한 경우는 런타임 예외라도 잡아서 복구하거나 대응할 수 있으니 문제 될 것이 없다는 낙관적인 태도를 기반하고 있다.
- 애플리케이션 예외는 런타임 예외 중심 전략과는 대비되는 전략으로 직접 처리할 수 없는 예외가 많더라도 일단 모두 잡도록 강제하는 방법이다.
SQLException은 어떻게 됐나?
- 대부분의 SQLException은 복구가 불가능하다.
- Spring의 JdbcTemplate은 SQLException을 런타임 예외인 DataAccessException으로 포장하여 던져준다.
예외 전환
JDBC의 한계
- JDBC API를 통해 DB의 종류에 관계없이 일관된 방법으로 프로그램을 개발할 수 있는 장점이 있다.
- 하지만 DB를 자유롭게 변경해서 사용할 수 있는 유연한 코드를 보장해주지 못한다.
비표준 SQL
- DB마다 표준을 따르지 않는 비표준 SQL 문법과 기능을 제공한다.
- 비표준 SQL을 통해 DB를 최적화 하거나 특별한 기능을 사용할 수 있기 때문에 자주 사용된다.
- 표준 SQL만을 사용하여 개발하는 것을 현실적으로 불가능하다.
호환성 없는 SQLException의 DB 에러정보
- DB 마다 SQL 문법도 조금씩 다르지만 에러의 종류와 원인도 다르다.
- 따라서 getErrorCode() 메소드를 통해 얻어온 에러코드는 DB마다 다르다.
- getSQLState()로 통상적인 규칙을 따르는 상태코드를 얻어올 수 있지만, DB의 JDBC 드라이버가 SQLException이 담을 상태 코드를 정확하게 만들어 주지 못하기 때문에 상태 코드를 맹신할 수 없다.
DB 에러 코드 매핑을 통한 전환
- DB마다 다른 에러코드를 매핑을 통해 전환하여 DB 종류와 관계없이 일관된 예외를 전달 받을 수 있다.
- JdbcTemplate은 SQLException을 DataAccessException으로 포장하며 DataAccessException의 계층구조의 클래스 중 하나로 매핑해 준다.
- 이렇게 매핑된 예외는 일관된 정보를 전달하지만, 여전히 체크 예외이고 그 예외를 세분화하는 기준이 SQL 상태정보를 이용한다는 점에서 한계가 있다.
DAO 인터페이스와 DataAccessException 계층구조
DAO 인터페이스와 구현의 분리
public interface UserDao{
public void add(User user);
...
}
- UserDao의 인터페이스를 분리해서 기술에 독립적인 인터페이스로 바꿀 수 있다. 하지만 메소드 선언에서 문제가 생긴다. 사용하는 기술에 따라 다른 예외를 던지게 되고 throws 해야하는 예외가 기술에 따라 달라져 다른 메소드 선언이 필요하게 된다.
- 하지만 JDBC 보다 늦게 등장하나 JDO, Hibernate, JPA등의 기술은 체크 예외 대신 런타임 예외를 사용하기 때문에 throws 해주지 않아도 되고 throws를 사용하지 않는 공통적인 메소드 선언으로 사용할 수 있다.
데이터 액세스 예외 추상화와 DataAccessException 계층구조
- DAO에서 사용하는 기술에 독립적인 인터페이스 선언이 가능하지만 모든 예외를 다 무시할 수 없고, 기술에 따라 같은 상황에서도 다른 예외를 던지는 상황이 발생한다. 따라서 체크 예외를 런타임 예외로 전환하는 것으로는 충분하지 않다.
- 스프링에서는 데이터 액세스 기술에 따라 달라지는 예외를 추상화해서 DataAccessException 계층구조 안에 정리해놓았다.
기술에 독립적인 UserDao 만들기
인터페이스 적용
- 인터페이스의 이름을 정할 때 'I' 접두어를 붙이거나 인터페이스의 이름을 최대한 간단히 하고 구현 클래스 각각의 특징을 따서 만든다.
- UserDao를 인터페이스로 바꿀 때 setDataSource() 메소드 처럼 구현 방법에 따라 달라지는 메소드는 추가하면 안된다.
테스트 보완
- 테스트 코드에서 UserDao의 변수 선언을 UserDaoJdbc로 변경하지 않아도 된다.
- 스프링의 컨텍스트 내에서 정의된 빈 중 인스턴스의 주입 가능한 타입의 빈을 찾아준다.
- @Test(expected=' ')를 통해 예외를 검증할 수 있다.
DataAccessException 활용 시 주의사항
- 스프링을 활용한다고 해서 DB 종류와 데이터 액세스 기술에 관계없이 동일한 상황에서 동일한 예외를 발생시키는 것이 완전히 보장되지는 않는다.
- 따라서