Java 예외 처리(Handling Exception)에 대해[Nesoy Blog - Que Sera Sera]
Java 예외(Exception) 처리에 대한 작은 생각(NEXTREE.IO)
최근 Exception을 Handling 하면서 세부적인 Handling 기법들에 대해 고심하지 않고,
Handling 하다보니 명확한 기준을 잡으며 처리하기 힘들었다.
이번 gillog때 Exception Handling에 대해 조금 더 알아보려 한다.
먼저 Error, Exception, Checked, UnChecked에 대한 선행 정리 내용을 숙지한 상태로 시작해보려 한다.
아래 Exception Handling 관련 내용은,
UnCheckedException
, RuntimeException
관련 Exception Hadnling 기법들이다.
예외 복구의 핵심은 Exception이 발생하여도 Application은 정상적으로 동작한 다는 것이다.
Exception 발생 시 이를 예측하여 다른 비즈니스 로직 흐름으로 유도 시키거나,
예외가 발생하지 않는 상황으로 복구를 시도하는 로직을 추가하는 방식이다.
아래 코드를 살펴보자.
private void mayThrowExceptionLogic() {
int maxTry = 20;
while(maxTry --> 0) {
try {
// ???Exception 이 Throw 될 수 가능성이 있는 로직
// 성공 시 return, 해당 메소드 종료
return ;
} catch(???Exception e) {
// Error 로그 출력
// 실패 로직 존재 시 원상 복구
// 일정 시간 동안 대기
} finally {
// 작업에 사용한 Resource 반환 및 정리
}
}
// 최대 횟수 실패시 예외 Throw
throw new MaxTryFailedException();
}
만약 서버 상태에 따라 접속 불안정 등으로 실패 가능성이 있는 로직에,
위 maxTry
만큼 정상 로직을 재시도하여 maxTry
가 초과될 경우 또 다른 Exception으로 전환하여 Throw 한다.
maxTry
의 시도로 비즈니스 로직이 정상 수행 될 수 있고,
throw new MaxTryFailedException();
와 같이 새로운 Exception으로 전환하여 Throw 하지 않더라도,
해당 로직 실패시 다른 수행 로직을 첨가하여 Application의 흐름을 정상적으로 마무리할 수도 있다.
private void mayThrowExceptionLogic() throws ???Exception {
// 비즈니스 로직
}
위와 같은 코드를 많이 사용하고 있지만 아주 신중히 생각해야 한다.
Exception 발생 시 throws
를 통해 호출 된 부분으로 해당 Exception을 던져버리고,
Exception Handling을 회피하는 방식이다.
해당 방식은 호출 부분에서 Exception을 Handling 하는 것이 더 바람직 하거나,
해당 로직에서 처리하지않고 Exception을 회피하고 throw 하는 것에 확신이 들 경우만 사용해야 한다.
try {
// Exception 발생 가능 로직
}
catch(???Exception e) {
// Exception 로깅
// 복구 로직
throw CustomSpecificExceptionJustLikeNotAllowedUserException("이런상황~");
}
예외 전환 방식
은 위 코드와 같이 특정 Exception 발생 시 새로운 Exception으로 전환하여,
호출 부분으로 throw 하여 호출 부분에서 Exception을 Handling 할 때,
어떤 Exception인지 더욱 분명히하여 해당 Exception에 대한 Handling을 더욱 수월히 하는 방식이다.
CheckedException
처럼 복구 불가능 Exception을 catch
하여,
UncheckedException
으로 전환하여 Handling 함으로 써,
다른 계층에서 CheckedException
을 일일히 선언하지 않아도 되게 할 수도 있다.
Exception을 Handling 할 때 중요한 사항들이 있는데 아래와 같다.
try {
// Exception 발생 가능 로직
} catch(???Exception e) {
}
위 코드와 같이 Exception을 catch
한 후에 아무 로직 없이 catch
만 하는 것은 적합하지 않다.
Log를 출력하거나, 해당 Exception 발생 로직을 원상 복구 시키는 로직을 첨가하는 등,
catch
만 수행하지 않고 해당 Exception에 대한 처리를 해주어야 한다.
try {
// Exception 발생 가능 로직
} catch(???Exception e) {
throw e;
}
위 경우와 마찬가지로 Log나 적절한 로직을 추가하여야 하고,
단순히 catch
후에 throw
시키는 것은 적합하지 않다.
try {
// Exception 발생 가능 로직
} catch (IOException e) {
e.printStackTrace()
}
Tomcat
에서 e.printStackTrace()
로 Console에 찍히는 log는,
{TOMCAT_HOME}/logs/catalina.out
에만 저장된다.
Logging Framework
를 이용하면 파일을 쪼개는 정책을 설정할 수 있고,
여러 Server의 log를 한곳에서 모아서 보는 System을 활용할 수도 있다.
e.printStackTrace()
대신 LoggingFramwork
를 활용하자.
slf4j, commons logging, log4j, logback ...
Exception의 추적성과 유지보수 성을 높이기 위해서,
e.toString()
이나 e.getMessage()
로 마지막 Exception Message만 남기기보다,
전체 Exception Stack을 다 넘기는 편이 좋다.
대표적인 slf4j의 log.error()
역시 e.printStackTrace()
처럼 Exception의 stack도 남긴다.
위에서 정리한 Exception Handling 기법들을 고려하여 조금 더 세심한 Handling을 해보자.