목차

  1. 그냥 RuntimeException으로 감싸면 되는거 아냐?
  2. throwable-interfaces
  3. 마치며

checked exception과 unchecked exception이 대체 뭐고, 무슨 차이가 있는지에 대한 내용은 과감히 생략합니다.

그냥 RuntimeException으로 감싸면 되는거 아냐?

맞습니다.

1
2
3
4
5
6
7
public List<List<Object>> getSheetValues(String spreadSheetId, String sheetName, String range) {
try {
return sheets.spreadsheets().values().get(spreadSheetId, sheetName + "!" + range).execute().getValues();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

(앞서 포스팅했던 구글API 관련하여 구글시트API의 실 사용 예입니다.)
네 저러면 되죠. 하지만 한 클래스에 저런류의 메소드가 아주 많다면?
보기 싫어집니다. 저만 그런건진 모르겠습니다만 { } 이 많아지는게 참 거슬립니다.
또 중복되는 코드가 많아지는데 저것을 공통화 하기도 참 애매한 현실이 너무 싫습니다.

1
2
3
4
5
6
7
data.stream().map(p -> {
try {
someCheckedExceptionThrow(p);
} catch (Exception e) {
throw new RuntimeException(e)
}
}).collect(Collectors.toList());

아니면 이런상황을 생각해봅시다.
stream.map을 수행할때 checked exception을 발생시키는 메소드를 태워야할 때..
정말 보기만 해도 화가 솟구칩니다.
좋은 방법이 없을까 찾아보던 찰나…

throwable-interfaces

https://github.com/StefanLiebenberg/throwable-interfaces

이런 솔루션을 발견했습니다.
쉽게말해서 java8의 java.util.function 안에 있는 FunctionInterface들에 Throwable 처리를 해준 라이브러리입니다.
작동 방식이 궁금하시면 저 깃헙 코드를 참고해주시면 되고요.
제가 위에 든 예시에 이 라이브러리르 적용했을때 어떻게 되는지 보겠습니다.

1
2
3
4
5
public List<List<Object>> getSheetValues(String spreadSheetId, String sheetName, String range) {
return SupplierWithThrowable.castSupplierWithThrowable(
() -> sheets.spreadsheets().values().get(spreadSheetId, sheetName + "!" + range).execute().getValues()
).get();
}

위의 예시입니다. try - catch 블럭이 사라졌습니다.

1
2
3
data.stream()
.map(FunctionWithThrowable.castFunctionWithThrowable(p -> someCheckedExceptionThrow(p))
.collect(Collectors.toList());

아래 예시입니다. 역시 사라졌습니다.
그런데 메소드가 더 길어져서 불편하다고요..?
static import를 사용해서 앞에 class 명은 떼버릴 수 있습니다.
그리고 한 클래스에 저런 try-catch 블록이 계속 중복되는 코드를 넣어야 할땐 훨씬 깔끔해 보일 수 있습니다.

마치며

공식이 아닌 서드파티 개발자의 라이브러리 사용하기 vs 그냥 try - catch 사용하기
머릿속에서 이 두가치관이 싸우고 있지만 거기서 조금이나마 이기는쪽을 선택하시길 바랍니다.