2011년 3월 16일 수요일

Spring DAO에서 DataAccessException 사용.

예외 처리는 분명 좀 복잡한 주제이기는 하다.
아래의 소스코드를 보자.

public interface UserService {
    public void addUser(User user) throws Exception;
}

public class UserServiceImpl implements UserService {
    public void addUser(User user) throws Exception {
        userDao.create(user);
    }
}

public interface UserDao {
    public void create(User user) throws Exception;
}

public class UserDaoImpl implements UserDao {
    public void create(User user) throws Exception {
        // 구현로직
    }
}


토비의 스프링3 의 저자는 이런 예외처리를 무의미하고 무책임하다라고 이야기하고 있다.

예를 들어 보자.
사용자 추가 시 보통 데이터베이스 테이블의 사용자 정보에는 중복되어 들어 갈 수 없는 사용자 로그인 아이디 같은 것이 있다.
이러한 컬럼들은 중복해서 테이블에 넣을 수가 없기 때문에 보통 Unique Constraints 에러를 발생시킨다. 하지만 DBMS 마다 어떤 SQLException을 던지는 지, 또 중복에러를 확인하기 위해서 에러코드로 체크해야 하는지, Exception의 타입으로 구분해야 하는지 확인해야 할 것이 너무나 많다.
이러한 상황을 피하기 위해서 테이블에 삽입하기 전에 데이터가 존재하는지 체크해 볼 수도 있다. 운이 아주 나쁘다면 체크하고 삽입하는 순간에 누군가 번개보다 빠르게 데이터를 삽입할 경우 역시나 예외가 발생할 것이고, 이런 상황이 현실화 될 가능성이 아주 희박할테니 이런 것쯤은 무시해도 된다해도 삽입할때마다 매번 데이터를 확인하는 것도 그렇게 우아한 것처럼 보이지는 않는다.
오래전부터 하이버네이트에는 이런 처리를 위해 예외 전환이라는 방식으로 개발자에게 도움을 주었다.
특정 DBMS에서 던지는 SQLException을 똑똑한 하이버네이트는 모두 알고 있기 때문에 이것을 하이버네이트에서 정의한 예외로 변경해서 던진다. 하이버네이트를 이용하여 DAO를 구현하는 개발자는 이제 DBMS에서 중복에러가 발생했을 때 어떤 에러인지를 확인할 필요가 없다. 언제나 org.hibernate.exception.ConstraintViolationException 이 던져질 테니 말이다.
이제 스프링 입장에서 생각해 보자. 하이버네이트가 저런 저급작업을 대신 해주니까 니들도 하이버네이트만 사용해! 라고 할 수는 없다. 각종 JPA,하이버네이트, iBatis 뿐만 아니라 스프링에서 제공하는 JdbcTemplate 까지 DAO를 구현할 만한 것들이 너무 많다.
스프링도 똑똑한 걸로 치자면 하이버네이트에 절대 뒤지지 않을 테니, 각종 DAO 관련한 것들에서 던지는 Exception 쯤은 모두 다 알고 있다. 하이버네이트가 org.hibernate.exception.ConstraintViolationException를 던진다면 스프링은 이걸 받아서 org.springframework.dao.DataIntegrityViolationException 이런 것 쯤은 던져 줄 수 있는 것이다.
결국 스프링은 이용해서 DAO를 개발하는 개발자 입장에서는 사용자 추가로직을 구현할 때 DataIntegrityViolationException 을 try {} catch{} 해서 UserDuplicationException 정도는 던질 수 있는 상황이 된 것이다.

이제 스프링에서 이런 것들을 가능하게 하는 설정을 알아보자.

스프링 설정 파일에 아래와 같이 추가한다.

<bean  class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

PersistenceExceptionTranslationPostProcessor 는 DAO 클래스에 붙히는 스테레오타입인 @Repository 애너테이션이 설정된 클래스의 메서드에서 던지는 Exception을 스프링에서 정의한 Exception으로 전환하여 던지는 역할을 한다. 생각했던 것보다 간단히지 않은가?
위의 소스 코드들은 그렇다면 이렇게 수정해도 되지 않을까?

public interface UserService {
    public void addUser(User user) throws UserDuplicationException;
}

public class UserServiceImpl implements UserDuplicationException {
    public void addUser(User user) throws Exception {
        try {
            userDao.create(user);
        } catch (DataIntegrityViolationException ex) {
            throw new UserDuplicationException();
        }
    }
}

public interface UserDao {
    public void create(User user);
}

public class UserDaoImpl implements UserDao {
    public void create(User user) {
        // 구현로직
    }
}


조금 무의미하고 무책임한 에러 처리 로직을 개선한 것 같은지 모르겠다.

댓글 없음:

댓글 쓰기