≣ 목차
서론
애플리케이션을 개발하다 보면 데이터베이스 오류에 따라 추가 작업을 수행해야 할 때가 있습니다.
예를 들어, SQLException의 getErrorCode 메서드를 사용하면 특정 데이터베이스 오류가 발생했는지 확인할 수 있고, 그에 따라 추가 작업을 수행할 수 있습니다. 하지만 데이터베이스 관련 오류 코드는 수백 가지가 있으며, 각 데이터베이스 기술마다 오류 코드가 다르기 때문에, 데이터베이스 기술을 변경할 때마다 오류 코드를 일일이 수정해야 하는 번거로움이 생깁니다.
이런 문제를 해결하기 위해 ExceptionTranslator를 활용하면, 데이터베이스 오류를 보다 효율적으로 관리하고, 코드의 유지보수성을 높일 수 있습니다.
스프링 데이터베이스 예외 계층
스프링은 데이터베이스 오류를 보다 효율적으로 관리하고 코드의 유지보수성을 높이기 위해 ExceptionTranslator기술을 제공합니다. 이를 통해 스프링은 데이터베이스 계층에 대한 일관된 예외 계층을 제공하며, 수십 가지의 예외를 정리하고 있습니다.
이 예외들은 특정 기술에 종속적이지 않게 설계되어 있어, 서비스 계층에서도 스프링이 제공하는 예외를 사용할 수 있습니다. 예를 들어, JDBC나 JPA와 같은 다양한 데이터 접근 기술을 사용할 때 발생하는 예외는 스프링이 제공하는 예외로 변환됩니다. 이를 통해 개발자는 특정 기술에 의존하지 않고, 스프링의 예외 처리 메커니즘을 활용하여 일관된 방식으로 오류를 관리할 수 있습니다.
- 최고 상위 예외는 DataAccessException입니다. 이 예외는 RuntimeException을 상속받고 있기 때문에, 그 하위에 있는 모든 예외 계층도 언체크 예외입니다.
- Transient 관련 계층 예외는 일시적인 오류를 의미합니다. 이러한 예외는 동일한 SQL 쿼리를 다시 시도했을 때 성공할 가능성이 있습니다. 예를 들어, 쿼리 타임아웃이나 데이터베이스 락과 관련된 오류가 이에 해당합니다.
- 반면, NonTransient 예외는 일시적이지 않은 오류를 의미합니다. 대표적인 예로는 SQL 문법 오류나 데이터베이스 제약조건 위배가 있습니다. - 당연히 동일한 sql쿼리를 다시 시도해도 실패팜
ExceptionTranslator
ExceptionTranslator 소개
앞에 설명했지만, ExceptionTranslator는 데이터베이스 관련 예외를 추상화하여, 다양한 데이터베이스에서 발생할 수 있는 예외를 일관된 형태로 변환하는 역할을 합니다. 데이터베이스에 따라 발생하는 예외는 각기 다르지만, ExceptionTranslator를 사용하면 이러한 예외를 스프링의 공통 예외 계층으로 변환할 수 있습니다. 이를 통해 데이터베이스에 종속되지 않고, 보다 일관된 방식으로 예외를 처리할 수 있습니다.
ExceptionTranslator의 기능
- 의존성 감소: ExceptionTranslator는 다양한 데이터베이스에서 발생하는 예외를 스프링의 DataAccessException으로 변환합니다. 이를 통해 개발자는 특정 데이터베이스에 대한 예외 처리 로직을 작성할 필요가 없어집니다.
- 추상화: ExceptionTranslator는 데이터베이스 예외를 추상화하여, 개발자가 데이터베이스의 종류에 관계없이 동일한 예외 처리 로직을 사용할 수 있도록 합니다. 예를 들어, MySQL의 SQLException과 Oracle의 SQLException이 발생하더라도, 이를 스프링의 DataAccessException으로 변환하여 처리할 수 있습니다.
- 유연한 예외 처리: ExceptionTranslator를 사용하면, 특정 예외에 대해 커스텀 예외 처리 로직을 구현할 수 있습니다. 예를 들어, 특정 데이터베이스 오류에 대해 사용자 정의 예외를 던지거나, 로깅 및 알림 기능을 추가할 수 있습니다.
ExceptionTranslator 사용 예제 - CustomExceptionTranslator
Repository
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
public UserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private RowMapper<User> userRowMapper() {
return (rs, rowNum) -> {
User user = new User();
user.setId(rs.getLong("id"));
return user;
};
}
public User findUserById(Long id) {
try {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, userRowMapper(), id);
} catch (DataAccessException e) {
throw new CustomDatabaseException("사용자를 찾는 중 오류 발생", e);
}
}
}
JdbcTemplate 관련에서는 나중에 포스팅해보겠습니다!!
CustomExceptionTranslator
public class CustomExceptionTranslator implements SQLExceptionTranslator {
private final SQLExceptionTranslator defaultTranslator = new SQLErrorCodeSQLExceptionTranslator("MySQL");
@Override
public DataAccessException translate(String task, String sql, SQLException sqlEx) {
if (sqlEx.getErrorCode() == 1045) { // 연결 거부
return new CustomAccessDeniedException("접근이 거부되었습니다.", sqlEx);
} else if (sqlEx.getErrorCode() == 1064) { // 문법 오류
return new BadSqlGrammarException(task, sql, sqlEx);
}
return defaultTranslator.translate(task, sql, sqlEx);
}
}
CustomExceptionTranslator는 MySQL의 1045 접근 거부나 1064 SQL 문법 오류에 대해 커스텀한 예외를 던지도록 구현
Config
@Configuration
public class AppConfig {
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setExceptionTranslator(new CustomExceptionTranslator());
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
CustomExceptionTranslator를 JdbcTemplate에 적용하기 위해, AppConfig 클래스에서 설정
Exception
public class CustomAccessDeniedException extends DataAccessException {
public CustomAccessDeniedException(String msg) {
super(msg);
}
public CustomAccessDeniedException(String msg, Throwable cause) {
super(msg, cause);
}
}
public class CustomDatabaseException extends DataAccessException {
public CustomDatabaseException(String msg, Throwable cause) {
super(msg, cause);
}
public CustomDatabaseException(String msg) {
super(msg);
}
}
해당 코드는 간단하게 구현한거라서 ExceptionTranslator를 이런 식으로 커스텀 하는구라 라고 봐주시면 좋을 거 같습니다!!
TMI 간단하게 ErrorCode 확인하는 법
ctrl+N -> sql-error-codes.xml 검색(파일 카테고리)
스프링에서 위에 방법대로 검색하면 다양한 데이터베이스에 애러코드를 확인할 수 있습니다.👍👍
'Spring' 카테고리의 다른 글
[Spring] 트랜잭션 전파 속성 정리 - propagation (0) | 2024.09.02 |
---|---|
[Spring] @Transactional 정리 (1) | 2024.08.28 |
[Spring+DB] 트랜잭션 추상화 및 동기화- TransactionManager (0) | 2024.08.10 |
[Spring]PathPattern 공식 문서 내용에 대한 예시 (0) | 2024.08.01 |
[Spring] Filter(필터) VS Interceptor(인터셉터) 정리 및 차이 (1) | 2024.08.01 |