4장 예외

Soonwoo Kwon·2022년 3월 7일
0

토비의 스프링

목록 보기
4/11

사라진 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 종류와 데이터 액세스 기술에 관계없이 동일한 상황에서 동일한 예외를 발생시키는 것이 완전히 보장되지는 않는다.
  • 따라서

0개의 댓글