우아하게 예외 처리하기
목차
1. 예외 처리 방식
2. Unchecked Exception을 사용하라
3. Exception 잘 쓰기
4. 실무 예외 처리 패턴
5. 오픈소스 속 Exception 살펴보기
1. 예외 처리 방식
오류 코드를 리턴하지 말고, 예외를 던져라
2. Unchecked Exception을 사용하라
Exception을 상속하면 Checked Exception 명시적인 예외처리가 필요하다.
- ex) IOException, SQLException
RuntimeException을 상속하면 Unchecked Exception 명시적인 예외처리가 필요하지 않다.
- ex) NullPointerException, IllegalArgumentException
<Effective Java> Exception에 관한 규약
자바 언어 명세가 요구하는 것은 아니지만, 업계에 널리 퍼진 규약으로 Error 클래스를 상속해 하위 클래스를 만드는 일을 자제하자.
즉, 사용자가 직접 구현하는 unchecked throwable은 모두 RuntimeException의 하위 클래스여야 한다.
Exception, RuntimeException, Error를 상속하지 않는 throwable을 만들 수도 있지만, 이러한 throwable은 정상적인 사항보다 나을 게 하나도 없으면서 API 사용자를 헷갈리게 할 뿐이므로 절대로 사용하지 말자
c#, c++, python, 루비는 확인된 예외(Checked Exception)가 없음에도 불구하고 안정적인 소프트웨어를 구현하기에 무리가 없다.
3. Exception 잘 쓰기
- 예외에 메시지 담자 :
오류가 발생한 원인과 위치를 찾기 쉽도록, 예외를 던질 때는 전후 상황을 충분히 덧붙인다.
실패한 연산 이름과 유형 등 정보를 담아 예외를 던진다.
- exception wrapper
4. 실무 예외 처리 패턴
- getOrElse :
* 예외 대신 기본 값을 리턴한다.
1. null이 아닌 기본 값 :
복수형의 데이터를 가져올 때는 데이터의 없을 의미하는 컬랙션을 리턴하면 된다. null보다 size가 0인 컬렉션이 훨씬 안전한다.
2. 도메인에 맞는 기본 값 :
예외 처리를 데이터를 제공하는 쪽에서 처리해 호출부 코드가 심플해진다.
코드를 읽어가며 논리적인 흐름이 끊기지 않는다.
도메인에 맞는 기본값을 도메인 서비스에서 관리한다.
3. 도메인에 맞는 기본값이 없다면? getOrElseThrow 이용
- getOrElseThrow :
* null 대신 예외를 던진다.(기본 값이 없다면)
1. 데이터를 제공하는 쪽에서 null 체크를 하여, 데이터가 없는 경우엔 예외를 던진다.
2. 호출부에서 매번 null 체크를 할 필요 없이 안전하게 데이터를 사용할 수 있다.
3. 호출부의 가독성이 올라간다.
- 파라미터의 null을 점검하라 :
1. null을 리턴하는 것도 나쁘지만 null을 메서드로 넘기는 것은 더 나쁘다.
2. 메서드에서 null을 파라미터로 받지 못하게 한다.
- 실무에서는 보통 자신의 예외를 정의한다 :
1. 에러 로그에서 stacktrace 해봤을 때 우리가 발생시킨 예외라는 것을 바로 인지할 수 있다.
2. 다른 라이브러리에서 발생한 에러와 섞이지 않는다. 기본 IllegalArgumentException을 던지는 것보다 우리 예외로 던지는게 어 느 부분에서 에러가 났는 지 파악하기 용이하다.
3. 우리 시스템에서 발생한 에러의 종류를 나열할 수 있다.
5. 오픈소스 속 Exception 살펴보기
apps-android-commons
public boolean findBookmark(Bookmark bookmark) {
if (bookmark == null) {//Avoiding NPE's
return false;
} // null이면 예외를 던지지않고 false를 리턴한다.
// Bookmark 존재여부에 대한 메서드의 목적에 부합하다.
Cursor cursor = null;
ContentProviderClient db = clientProvider.get();
try {
cursor = db.query(
BookmarkPicturesContentProvider.BASE_URI,
Table.ALL_FIELDS,
Table.COLUMN_MEDIA_NAME + "=?",
new String[]{bookmark.getMediaName()},
null);
if (cursor != null && cursor.moveToFirst()) {
return true;
}
} catch (RemoteException e) {
// This feels lazy, but to hell with checked exceptions. :)
// ReomoteException을 RuntimeException으로 바꿔서 던졌다.
throw new RuntimeException(e);
} finally {
// finally 블록에서 리소스를 close 처리한다.
// 리소스를 사용했다면 반드시 finally 블록에서 닫아줘야 한다.
if (cursor != null) {
cursor.close();
}
db.release();
}
return false;
}
'Book > Clean Code' 카테고리의 다른 글
[Clean Code] Chapter 09 (0) | 2022.03.21 |
---|---|
[Clean Code] Chapter 08 (0) | 2022.03.20 |
[Clean Code] Chapter 06 (0) | 2022.03.13 |
[Clean Code] Chapter 05 (0) | 2022.03.12 |
[Clean Code] Chapter 04 (0) | 2022.03.12 |