어플리케이션을 실행하다보면 피치 못할 오류가 발생할 수 있는데, 예상되는 오류를 코드로 미리 처리함으로써 어플리케이션의 실행 흐름을 원하는대로 유도할 수 있다. Java에서는 어플리케이션 실행 시 발생할 수 있는 오류들을 클래스로 정의하였다.
0. Error와 Exception
발생할 수 있는 오류는 Error와 Exception, 두 가지 분류로 나뉜다.
Error(에러)
에러는 어플리케이션을 컴파일하거나 실행 도중에 발생할 수 있는 치명적인 오류로, 프로세스에 심각한 문제를 야기시켜 프로세스를 강제로 종료시킬 수 있는 오류이다.
Java에서는 java.lang.Error 클래스의 하위 클래스에서 에러에 관한 내용을 다루고 있다. 대표적으로 StackOverflowError나 OutOfMemoryError 등이 있다.
에러의 경우 주로 JVM에서 발생시키기 때문에 개발자가 애플리케이션 코드 상에서 따로 에러를 처리할 필요는 없으며, 코드로 대응할 수도 없는 경우가 대부분이다.
Exception(예외)
예외는 어플리케이션 코드 상에서 발생할 수 있는 오류들을 의미한다. 에러가 발생 시 복구할 수 없는 심각한 오류라면, 예외는 코드 작성을 통해 발생해도 적절하게 수습하여 어플리케이션의 비정상적인 종료를 막을 수 있다.
Java에서는 java.lang.Exception 클래스의 하위 클래스에서 기본적인 예외에 대한 내용을 다루고 있다. Exception의 경우 CheckedException과 UncheckedException으로 나뉜다.
1. Checked Exception
Checked Exception은 반드시 예외 처리를 해줘야 하는 예외 클래스들이며, 처리를 미리 해주지 않으면 컴파일에 실패한다. 대표적으로 IOException, SQLException, ClassNotFoundException 등이 있다.
IDE에서 개발을 하다보면 개발자를 위해 컴파일이 불가능할 경우 처리가 누락된 부분을 표시하는 등의 도움을 준다. 하지만 반드시 명시적으로 처리를 해야 하는 번거로움이 존재하고, 예외가 반드시 복구가 되지 않는 경우도 있기 때문에 실제 개발 시 반드시 필요한 상황이 아니라면 Checked Exception의 사용은 지양한다고 한다.
2. Unchecked Exception
RuntimeException 클래스를 상속받는 클래스들이다. 런타임 환경에서 발생할 수 있는 예외들로 주로 개발자의 실수로 발생하는 경우가 많다(배열의 범위를 벗어난 탐색, null을 참조하는 등). RuntimeException을 상속받는 예외들은 복구 가능성이 없는 예외들로 컴파일러가 굳이 예외 처리를 강제하지는 않는다(그래서 Unchecked).
3. 예외 처리 방법
에러는 코드 작성의 영역에서 다룰 수 없기 때문에 어쩔 수 없지만, 예외의 경우 발생할 수 있는 예외들을 예상하여 개발자가 미리 처리할 수 있다.
3-1. 예외 복구
예외가 발생하는 문제를 해결하고 정상 상태로 복구하는 것이다.
예를 들어 파일을 읽는 중 IOException이 발생한 경우, 재시도하여 다시 파일을 읽어 예외가 복구되도록 하는 것이 있다.
3-2. 예외 처리 회피
예외를 발생한 메서드에서 직접 처리하지 않고 throws 키워드를 통해 호출한 쪽으로 던져버리는 것이다.
3-3. 예외 전환
회피와 마찬가지로 메서드 밖으로 던져버리지만, 그냥 던지는 것이 아닌 적절한 예외로 변경하여 던진다. 단순히 봤을 때 이 예외가 왜 발생한지 모르는 예외가 아닌 추상화된 의미를 가진 예외로 바꿔 던져 이를 사용하는 서비스 계층에서 의미를 파악하기 쉽게 하기 위함이다.
4. 올바른 Exception Handling 방법
1. 올바른 try-catch
try-catch는 예외가 발생할 가능성이 있는 부분을 try로 감싸고 이에 따라 발생할 수 있는 예외를 잡아(catch) 처리하는 것이다. 이때 예외를 잡기만 하고 이를 처리하지 않는 것은 위험한 행위이다.
} catch (SQLException e) {
//Nothing
//또는 에러를 출력만 한다.
System.out.println(e);
//또는
e.printStackTrace();
}
예외 발생이 서비스에 무관한 것이 아니라면 예외로 인해 이후 어떤 상황이 야기될지 모르기 때문에 예외는 반드시 적절하게 처리를 하거나 해당 작업을 중단하는 등의 조치를 취해야 한다.
2. catch의 순서
try-catch에서 여러 예외가 발생할 수 있는 경우, 발생할 수 있는 좀 더 자세한 예외부터 처리를 해야 한다. 앞쪽에서 더 포괄적인 예외를 적을 경우 더 상세한 예외 처리 블록이 실행이 되지 않는다.
3. 리소스 정리
try 블록 안에서 DB나 파일같은 리소스에 접근하는 경우, 예외가 발생한다면 해당 리소스가 제대로 닫히지 않아 메모리가 낭비되는 등의 문제가 발생할 수 있다.
그렇기에 finally를 통해 사용한 리소스를 정리하거나 아예 try-with-resources를 사용해서 리소스를 정리해줘야 한다.
4. 자세한 예외 명시
throw를 통해 해당 메서드에서 발생할 수 있는 예외를 명시할 수 있다. 이때 발생할 수 있는 예외를 Exception 같은 상위 클래스로 던져버리면 이 메서드를 사용하는 쪽에서 처리하기 복잡해진다.
따라서 발생할 수 있는 예외에 대해 최대한 자세한 예외 클래스로 던져 이를 처리하는 쪽에서 잘 구분할 수 있도록 해야 한다.
5. 발생할 수 있는 예외 기술
예외가 발생하는 메서드는 javadoc에 어떤 예외가 어떤 경우에 발생하는지 명시하는 것이 좋다. 그래야 메서드를 호출하는 쪽에서 적절하게 대응할 수 있기 때문이다.
6. 무책임한 예외 던지기(throws) 금지
예외가 발생했을 때 이를 무작정 상위 메서드에 던져버리는 것은 좋지 못하다.
public void method1() throws Exception {
method2();
...
}
public void method2() throws Exception {
method3();
...
}
public void method3() throws Exception {
...
}
그렇게 된다면 예외가 어떤 이유에서 왜 발생했는지 추적하기 힘들기 때문이다.
참고
https://mangkyu.tistory.com/152
[Java] 체크 예외(Check Exception)와 언체크 예외/런타임 예외 (Uncheck Exception, Runtime Exception)의 차이와
1. 체크 예외(Check Exception)와 언체크 예외/런타임 예외 (Uncheck Exception, Runtime Exception)의 차이 [ 예외(Exception)의 종류 ] 에러(Error) 예외(Exception) 체크 예외(Check Exception) 언체크 예외(Uncheck Exception) 에
mangkyu.tistory.com
https://soft.plusblog.co.kr/160
[Java] 9가지 예외처리 Best Practice - Exception Handling
자바 프로그래밍에서 '예외(Exception)' 처리는 다소 까다로운 주제입니다. 때문에 각 개발팀들은 자신들만의 예외처리 규칙을 만들고 사용하는데요. 일반적으로 자바 프로젝트에서 따르면 좋은
soft.plusblog.co.kr