예외처리 시 하지 말아야 할 행동
모든 예외는 반드시 적절하게 복구하거나 중단시키고 개발자에게 통보되어야 하는데
예외를 잡고 어떠한 처리도 하지 않고 넘어가거나 출력만 하는 행동과
무의미하고 무책임하게 다른 곳으로 예외를 던져버리는 행동은
무슨 일이 있어도 해서는 안되는 행동이다.
예외의 종류
CheckedException
말그대로 예외를 확인하고 처리해야 하는 경우를 말하며
일반적으로 예외라고 하면 이 ChekedException을 말한다.
이때 예외처리는 필수이기 때문에
처리해주지 않을 경우 컴파일을 에러가 발생한다.
대표적으로 IOException, SQLException 등이 있다.
UncheckedException
RuntimeException 클래스를 상속 받은 예외들로
ChekedException처럼 예외처리가 필수는 아니다.
컴파일에는 문제 없지만 런타임 시에 문제가 생기는 경우로
개발자의 부주의로 인한 문제기 때문에 예외처리가 아니더라도
코드의 수정을 통해서 방지할 수 있다.
대표적으로 아무것도 할당되지 않은 빈 참조변수를 사용하는 경우
발생하는 NullPointerException이 있다.
Error
Error 클래스를 상속 받는 서브 클래스들을 뜻한다.
시스템적으로 비정상적인 오류가 발생한 경우로
보통 자바 가상머신에서 발생시키기 때문에 개발자가 코드로 잡을 수 있는 것이 아니다.
대표적으로 OutOfMemoryError가 있는데
개발자가 이러한 현상을 예방하기 위한 코드를 작성하는 것은 가능해도
이러한 에러가 생긴 경우를 처리하는 코드를 작성할 수는 없다.
그렇기 때문에 Error는 개발자가 직접 신경쓸 영역이 아니다.
Exception
Exception 클래스를 상속 받는 서브 클래스들로 에러와는 다르게
개발자가 작성한 코드에 문제가 있는 경우 발생한다.
이 말은 개발자가 직접 예외를 처리할 수 있다는 것인데,
Exception 클래스의 서브 클래스 중에서
RuntimeException 클래스의 상속 여부에 따라
상속 받지 않는 CheckedException과 상속 받는 UncheckedException이 있다.
Runtime Exception
RuntimeException 클래스를 상속 받은 서브 클래스들이 해당된다.
예외처리 방법
예외 복구
말그대로 예외가 어떻게 발생했는지 파악하고 해결하여 정상적으로 만드는 방법이다.
특정 횟수만큼 반복해서 재시도해보거나 다른 방법으로 전환하여 시도하는 등의
방법을 통하여 복구 시도를 하는데 어떤 식으로든 예외를 복구할 가능성이
있는 경우에만 해당 방법을 사용한다.
예외처리 회피
예외를 직접 처리하는 것이 아닌 호출한 쪽으로 떠넘기는(던지는) 방법이다.
예외를 던질 때는 받는 곳에서 해당 예외를 처리할 수 있어야 하는데
예외를 처리하지도 못하는 곳에 무작정 던지고 보는 것은
무책임한 회피 방식이기에 좋지 않다.
객체지향에서 하나의 객체는 관련된 기능만 수행하게 설계되니
만약 DAO에서 발생하는 예외는 꼭 그 안에서 해결해야 하며,
전혀 상관 없는 서비스나 컨트롤러에 예외를 넘기는 것은 하지 말아야 한다.
예외를 회피할 때는 반드시 의도가 분명한 경우에만 해야한다.
예외 전환
예외처리 회피처럼 예외를 떠넘기는 것은 똑같지만
예외를 바꿔서 던져준다는 것이 다르다.
이 방법은 보통 두 가지 상황에서 사용한다.
try {
// 예외가 발생할 가능성이 있는 코드
}
catch (SQLException s) {
// 에러 코드가 특정 에러와 같은 경우
// 특정 에러의 예외로 바꿔서 던져줌
if(s.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY) {
// 기존예외를 담아서 전달하는 중첩예외
throw DuplicateUserIdException(s);
}
// 그렇지 않은 경우 기존 예외를 다시 던짐
else {
throw s;
}
}
첫 번째로는 예외의 의미를 분명하게 해주는 경우에 사용하는데
IOException처럼 의미의 범위가 큰 예외를 던져주면
입출력에서 어떤 예외가 발생했는지 알기가 어렵다.
그래서 IOException 예외보다 좀 더 상세하고 확실한 의미를
가지고 있는 예외로 바꾸어 던져주는 방법이다.
또한 getCause 메서드를 이용하여 기존 예외를 알 수가 있어서
기존 예외를 담아서 중첩 예외로 던지는 것이 좋다.
try {
OrderHome orderHome = EJBHomeFactory.getInstance().getOrderHome();
Order order = orderHome.findByPrimaryKey(Integer id);
} catch (NamingException ne) {
throw new EJBException(ne);
} catch (SQLException se) {
throw new EJBException(ne);
}
두 번째로는 예외를 처리하기 쉽게 포장하는 것이다.
중첩 예외를 만드는 것은 똑같지만
의미를 명확한 예외로 전환하는 것이 아닌
강제로 처리해야하는 체크 예외를 언체크 예외로 바꿀 때 사용한다.
의미 있는 예외이거나 복구 가능한 예외가 아닌 경우
다루기 쉬운 런타임 예외로 바꾸는 것이다.
예외처리 전략
런타임 예외의 보편화
보통 체크 예외는 일반적인 예외, 언체크 예외는 시스템 장애나 프로그램상의 오류를 처리한다.
체크 예외는 복구 가능성이 조금이라도 있는 예외적인 상황이기에
이 예외의 처리를 강제한다.
프로그램의 경우에는 잘못된 요청을 받아서 예외가 생겼다고
종료를 해버릴 수 없기에 예외를 강제로 처리해야했지만,
서버에서는 수많은 요청 중에서 예외가 생긴 요청만 중단시키면 된다.
그래서 서버에서는 체크 예외의 활용도가 낮기 때문에
체크 예외를 직접적으로 다루기보단 런타임 예외로 전환해서
처리하는 것이 좋을 수도 있다.
애플리케이션 예외
의도적으로 발생시키는 예외로, 반드시 조치를 취하도록 요구하는 예외다.
은행 프로그램에서 돈을 꺼내는 경우에 잔고 부족 같은 상황은
언체크 예외로 그냥 넘어가고 돈을 꺼내면 안되는 상황이기에
체크 예외를 만들어서 의도적으로 예외를 발생시키는 경우가 이에 해당한다.
'Back-End > Spring' 카테고리의 다른 글
[JPA] N + 1 문제와 해결 방법 (1) | 2024.03.01 |
---|---|
슬라이스 테스트 (0) | 2023.07.05 |
템플릿 정리 (0) | 2023.06.14 |
JDBC Template (0) | 2023.06.13 |
템플릿과 콜백 (0) | 2023.06.13 |