Checked Exception vs UncheckedException
에러와 예외
프로그래밍에서 예외란 입력 값에 대한 처리가 불가능하거나, 프로그램 실행 중에 참조된 값이 잘못된 경우 등 정상적인 프로그램의 흐름을 어긋나는 경우를 말한다. 그리고 자바에서 예외는 개발자가 직접 처리할 수 있기 때문에 예외 상황을 미리 예측하여 핸들링할 수 있다.
그러나 에러는 시스템에 무엇인가 비정상적인 상황이 발생한 경우를 말한다. 주로 자바 가상 머신에서 발생하는 것이며, 예외와 반대로 이를 애플리케이션 코드에서 잡을 수 없다. 에러의 예시로는 OutOfMemoryError, ThreadDeath, StackOverflowError 등이 있다.
자바 예외 구분
checked vs unchecked
이번 글의 제목처럼 Exception은 Checked Exception과 Unchecked Exception으로 구분할 수 있다.
간단하게 RuntimeException을 상속하지 않는 클래스는 Checked Exception이고, 상속한 클래스는 Unchecked Exception으로
분류할 수 있다.
checkedException: 이름 그대로 컴파일시에 체크되는 예외로,명시적인 예외처리를 실행 전에 강제한다.
uncheckedException: checked와 반대로 컴파일시 체크되지않고 런타임시 발생하는 예외로, 명시적인 예외처리가 필요 없다.
*명시적인 예외처리: try-catch or throw를 말한다.
예외처리 방식
- 예외복구 : 예외상황을 파악하고 해당 예외에 따른 문제를 해결한 뒤 정상 상태로 돌려 놓는 방법
- 예외처리회피: throws를 이용하여 호출한 쪽으로 예외처리를 미루는 방식
- 예외전환: try-catch로 받은 뒤 적절한 예외를 다시 throw 해주는 방식
DB와 입출력에서 발생하는 checkedException처리
나는 아래의 피드백 받은 코드에서는 발생하는 checkedException을 회피하고 전부 던졌다.(변경 전)
public Voucher findVoucherV0(long voucherId) throws IOException {
File csv = new File(path);
String line;
BufferedReader br = new BufferedReader(new FileReader(csv));
while ((line = br.readLine()) != null) {
if (matchId(line, voucherId)) {
return stringToVoucher(line);
}
}
throw new NotFoundVoucherException();
}
이는 예외를 처리하는 prensentation계층까지 해당 메소드에 영향을 받는 모든 클래스들은 전부 IOException을 의존해야 해서 유지보수성에 좋지 못했다.
또 호출하는 쪽에서 실제 구현 코드가 inputOut관련된 로직이 짜여져 있는 것을 대략적으로 추측이 가능하기 때문에 캡슐화적인 측면에서도 좋지 못했다. 따라서 나는 두개의 문제점을 해결하고자 코드를 리팩토링 했다.
public Voucher findVoucher(long voucherId) {
File csv = new File(path);
String line;
try (BufferedReader br = new BufferedReader(new FileReader(csv));) {
while ((line = br.readLine()) != null) {
if (matchId(line, voucherId)) {
return stringToVoucher(line);
}
}
} catch (FileNotFoundException e) {
throw new FileNotFoundCustomException();
} catch (IOException e) {
throw new FileInOutException();
}
throw new NotFoundVoucherException();
}
try-catch로 해당 CheckedException을 잡은 뒤, 새로만든 UncheckedException으로 예외를 전환하여 주었다.
이렇게 되니 해당 메소드에 영향을 받는 클래스들에서 파일 입출력에대한 예외를 의존하지 않아도 되었고, 또 캡슐화까지 보장할 수 있었다.
김영한님의 강의를 찾아보니 비슷한 맥락으로 CheckedExcpetion을 Unchecked Exception으로 전환해주는 것을 확인했다.
(계층 누수 멈춰!) 트랜잭션 관련해서는 Rollback에 대한 이유도 존재했다.