JAVA
[JAVA] 예외처리와 올바른 예외처리 방법
날아
2023. 1. 29. 22:51
1. 에러와 예외
- 에러 : 프로그램 코드에 의해서 수습될 수 없는 심각한 오류
- 예외 : 프로그램 코드에 의해서 수습될 수 있는 다소 미약한 오류
- 체크 예외
- 언체크 예외
체크예외
- 체크예외는 복구가능성이 있는 예외이므로 반드시 예외를 처리하는 코드를 함께 작성해야 한다.
- 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외
- ex) IOException, SQLException
- 예외를 처리하지 않으면 컴파일 에러가 발생한다.
- 실제 프로그래밍에서 발생하는 예외들은 복구 불가능한 경우가 많다. 예를 들어 SQLException과 같은 DB예외는 체크 예외를 catch해도, 쿼리를 수정하여 재배포하지 않는 이상 복구되지 않는다. 따라서 실제 개발에서는 대부분 언체크 예외를 사용한다.
언체크예외
- 복구가능성이 없는 예외들이므로 컴파일러가 예외처리를 강제하지 않는다.
- 프로그래머의 실수로 발생하는 예외
- 언체크 예외는 Error와 마찬가지로 예외를 처리하지 않아도 컴파일에러가 발생하지 않는다.
- ex) NullPointException, IllegalArgumentException
2. 예외 처리 방법
- 예외 복구
- 예외 상황을 파악하고 문제를 해결해서 정상상태로 돌려놓는 것이다. 만약 예외가 발생하여 어떤 작업의 처리가 불가능하다면 다르게 작업을 처리하도록 유도함으로써 예외를 처리하는 방법이다.
- ex) 다른 API 호출에 실패하였을 경우, 3회 정도 retry하여 복구되도록 하는 것
- 예외 처리 회피
- 예외를 직접 처리하지 않고, 자신을 호출한 곳으로 던져버린다.
- 예외 전환
- 예외 회피와 마찬가지로 예외를 복구할 수 없는 상황에 사용 한다. 다만, 적절한 예외로 변환하여 던진다는 특징이 있다.
- 의미있고 추상화된 예외로 바꾸는 경우
- 런타임 예외로 포장하여 불필요한 처리를 줄여주는 경우
- 예외 회피와 마찬가지로 예외를 복구할 수 없는 상황에 사용 한다. 다만, 적절한 예외로 변환하여 던진다는 특징이 있다.
예를 들어 새로운 사용자를 등록하고자 할 때 동일한 아이디가 존재하면 SQLException이 발생하게 된다. 하지만 이 에러를 그대로 던지면 이를 이용하는 서비스 계층에서는 왜 예외가 발생한건지 파악이 힘들다. 그래서 DuplicatedUserIdException과 같은 예외로 바꿔서 던지면 보다 확실히 의미를 전달할 수 있으며 상황에 따라 복구작업을 시도할 수도 있을 것이다.
또한 체크 예외에 의해 불필요하게 해주는 에러 처리가 많아진다면 이를 해결하기 위해 런타임 예외로 포장하여(언체크 예외로 변경하여) 불필요한 처리를 줄여줄 수 있다. 만약 복구하지 못할 예외라면 불필요하게 체크를 할 필요가 없기 때문이다. 그래서 이러한 경우라면 애플리케이션 로직 상에서 런타임 예외로 포장하여 던지고, 자세한 로그를 남기거나 알림을 주는 등의 방식으로 처리할 수 있다.
2. 올바른 예외 처리 방법
조치가 없는 try-catch -> 상황에 맞는 조치 진행
- try-catch로 예외를 잡고 아무 조치도 하지 않는 것은 위험하다.
- 따라서 예외를 처리할 때에는 빈 값을 반환하는 등의 조치를 통해 상황을 적절하게 복구하거나 작업을 중단시키고 관리자에게 이를 전달해야 한다.
무분별한 throws Exception -> 언체크 예외로 전환
- 예외를 던지는 것은 해당 문구를 보고 여기서 어떠한 문제가 발생했는지와 같은 정보를 얻을 수 없다.
- 또한, 해당 메소드를 다른 곳에서 사용한다면 예외가 전파되므로 좋지 않다.
- 만약 SQLException과 같이 복구가 불가능한 예외들이라면 언체크/런타임 예외로 전환해주는 것이 좋다.
2-1. 스프링에서의 예외 처리
Spring에서 데이터베이스에 접근하기 위한 @Repository 빈들에는 대표적으로 예외 전환 기법이 사용되고 있다. 훌륭한 오픈소스인 스프링 프레임워크를 통해 올바른 예외처리를 확인하면 좋을 것이다.
추상화된 예외로 전환
- Spring 애플리케이션을 이용할 때 어떠한 데이터베이스가 사용될지 모른다. 만약 개발을 하다가 데이터베이스를 MySQL에서 PostgreSQL로 전환하는 경우에 에러 추상화가 없다면 모든 MySQL 에러를 PostgreSQL로 전환해야 할 것이다.
- 하지만 스프링의 @Repository에는 각기 다른 데이터베이스에 의한 에러를 Spring의 데이터베이스 에러인 DataAccessException로 전환해주는 기능이 있다. 이를 통해 개발자는 데이터베이스에 종속되지 않는 개발을 할 수 있다.
언체크 예외로 전환
- 데이터베이스에 의한 에러는 일반적으로 체크 예외이다. 하지만 데이터베이스에 의한 체크 예외를 throws 받아도 우리가 특별히 할 작업은 따로 없고 에러 코드를 내려주는 것 뿐이다.
- 대표적으로 중복된 ID로 가입을 하는 경우를 생각해보도록 하자. 만약 중복된 ID로 가입을 시도하여 Constraint 에러가 발생하였다면 우리가 해야할 것은 올바른 에러 코드를 내려주는 것일뿐, 이는 체크 예외로 처리할 필요가 없는 것이다. 만약 체크 예외를 그대로 가져간다면 불필요하게 throw해주어야 하는 코드만 많아지는 것이다.
- Spring은 이러한 문제를 해결하기 위해 예외를 언체크 예외로 정의하고 있으며, 이를 통해 우리는 무분별한 throw를 하지 않아도 되도록 도와준다.
- 예외를 전환할 때는 꼭! 기존 예외를 포함해야 한다.
public void call() {
try {
runSQL();
} catch (SQLException e) {
throw new RuntimeSQLException(e); //기존 예외(e) 포함 }
}