예외처리는 개발자의 숙명인것 같다.초반엔 이 개념에 대해 크게 고려를 안했던것 같은데 개발을 하면서 적절한 처리를 하는것이 추후, 시간을 낭비하지 않는 방법이라는 것을 알기에 토비의 스프링 중 예외처리 부분은 따로 정리한다.
예외를 처리할 때 피해야할 것들
1> 예외 블랙홀
try {
...
} catch (Exception e) {
}
기껏하여 예외를 잡아, try~catch를 사용해놓고, catch 문에 아무런 액션을 하지않는 경우,
이 경우에는 개발자 및 운영자가 예외가 발생하였는지를 못알아차릴 뿐만 아니라 예외가 발생하여도 아무런 대책을 취하지 못하는 최악의 방법이다.
try {
...
} catch (Exception e) {
sytem.out.println(e); 또는 e.printStackTrace();
}
이 역시, 로그에 뿌리기만 할뿐, 지나가면 못잡는 예외처리이다. 필자는 이 경우에 차라리 잡지말고, 상위클래스에 throws로 예외를 전달하여 책임을 전가하라고 한다.
2> 무의미한 throws
public void method1() throws Exception () {
method2();
}
public void method2() throws Exception () {
method3();
}
...
모든 예외를 위로 던져 버리는 throws 역시, 심각한 문제점이 있다. 무조건 복붙하여 넣었는지..아니면 이 예외를 어느선에서 잡아야 하는지 에외를 다룰 수 있는 기회를 박탈해버린다.
예외의 종류와 특징
1>Error
시스템에 비정상적인 상황이 발생했을 경우에 사용, 주로 JVM에서 발생시키는것이기 잡아도 대응방법이 없다. 따라서 이 에러에 대한 처리는 신경쓰지 않아도 된다.
2>Exception과 체크예외
Exception에서는 체크예외와 언체크예외로 나눠진다. 이 둘의 구분은 RuntimeException을 상속했느냐 아니냐의 차이다.
RuntimeException을 상속했다면, 언체크예외이다. 일반적으로 예외라면, 체크예외를 말한다. 사용할 메소드가 체크예외를 발생시킨다면 try~catch로 잡든지, throws를 사용하여 위에 던져야한다.
-> 일부에서 이에대한 비난이 있다. 강제하여 오류를 잡게하기 떄문에 불필요한 throws를 남발할 수 있기 때문이다.
3> RuntimeException과 언체크/런타임예외
명시적인 예외처리를 강제하지 않기 때문에 언체크예외라고 불린다. 에러와 마찬가지로 런타임예외는 catch문으로 잡거나 trhows로 던지지 않아도 된다. 오류가 있을때 발생하도록 의도된 것들이기 때문이다. nullpointException이나 illegalArgumentException 은 개발자가 주의하면 피할 수 있다. 개발자의 부주의에 의해서 발생하는 것이기 때문에 발생하도록 만든것이 런타임 예외다. 충분히 예상할 수 있는 상황에서 발생한 예외이므로 예외처리를 하지 않는다.
예외 처리 방법
그렇다면 이러한 예외들을 어떻게 처리하여야 할까? 다음과 같은 방법이 있다.
1> 예외복구
예외상황을 파악하고 문제를 해결해서 정상상태로 돌려놓는 것이다. 예외로 인해 기본작업 흐름이 불가능하다면, 다른 작업흐름으로 자연스럽게 유도해주는 것이다. 예를 들어 파일 전송이 되지 않으면 사용자에게 파일 업로드를 다시하라는 로직을 추가하거나, 네트워크 접속이 원할하지 않으면 일정 조건을 두어 해당 횟수만큼 재시도를 하도록 유도하여야 한다.
int maxTry = MAX_RETRY;
while(maxretry --> 0) {
try {
.. //예외 발생 가능성이 있는 코드
return;
}
catch(Exception e) {
// 로그출력, 정해진 시간만큼 대기
}
finally {
//리소스 반납, 정리작업
}
throw new RetryFailedException; // 최대 재시도 횟수를 넘기면 excception 발생
}
2> 예외처리 회피
자신이 직접 처리하지않고 throws로 예외를 던지는 방법이다. 또는 try~catch로 로그만 남기고 throw로 예외를 던지는 방법이 있다. 하지만 긴밀한 협력관계가 아니면 이는 다른 메소드에게 책임을 전가하는 무책임한 회피가 될 수 있음을 기억하자.
public void add() throws Exception {
}
public voi add() throws Exception {
try {
...
} catch (Exception e){
//로그출력
throw e;
}
}
3> 예외 전환
발생한 예외를 그대로 던지는게 아닌 적절한 예외로 전환하여 던진다.
이에 대한 목적은 두가지가 있다. 첫째, 의미를 분명하게 해줄 수 있는 예외로 바꿔준다. 예를 들어, id가 중복인 예외라면 sql익셉션보다 idDuplication예외를 선언하여 던지는것이 더 의미가 분명할 것이다.
둘째, 예외처리를 강제하는 체크예외를 언체크예외로 바꾸려는 경우 사용한다. 어차피 잡기 불가능한 예외라면, 런타임예외로 포장하여 던지게 하여 다른 계층이 코드를 작성할때 불필요한 throws를 작성하는것을 막아준다. 코드에서는 런타임예외로 던지고, 에외처리 서비스 등을 이용하여 관리자에게 메일로 남기거나 로그로 던지는 등의 추가 로직을 넣는것이 바람직한다.
출처: 토비의 스프링3.1 vol1