Skip to content

Files

Latest commit

 

History

History
94 lines (64 loc) · 3.8 KB

아이템71_필요_없는_검사_예외_사용은_피하라.md

File metadata and controls

94 lines (64 loc) · 3.8 KB

필요 없는 검사 예외 사용은 피하라

자바에서 예외는 크게 Checked ExceptionUnchecked Exception으로 나뉩니다.

그 중 Checked Exception은 컴파일 시점에 예외를 처리하는 방식인데요, 클라이언트가 예외를 발생시키면 프로그래머가 안정적으로 문제를 해결할 수 있기에 과거에는 많이 선호하는 예외 처리 방식이었습니다.

하지만 catch 블럭의 사용과 스트림 안에서 직접 예외 처리를 해줄 수 없다는 부분에서 Checked Exception의 사용에 대한 부담감이 높아졌다고 합니다.

책에서 아래와 같은 예시가 나옵니다.

} catch(CheckedException e) {
    throw new AssertionError();    
}

} catch(CheckedException e) {
    e.printStackTrace();
    System.exit();
}

위 두 Checked Exception 처리 방식이 올바를까요? 저는 아니라고 생각합니다.

Checked Exception은 예외가 발생하더라도 프로그래머가 의미있게 프로그램을 해결할 수 있도록 만드는 것이 중요하다고 생각합니다. 파일명이 존재하지 않는다는 Checked Exception이 정상적인 default 파일명을 입력할 수 있게 만드는 것처럼 말이죠.

이건 제 생각이었고 이제부턴 이펙티브 자바의 이야기를 집중해보겠습니다.

하나만 체크한다면 부담스럽다

Checked Exception 하나를 위해 예외처리를 위한 API를 만드는 것은 사용자 친화적이지 못합니다. 사용하는 시점에 try블럭을 추가해야 할 뿐더러 Stream의 사용까지 제한되기 때문입니다. 그래서 이런 상황에는 다른 방법을 고민해봐야 합니다.

빈 옵셔널 반환

직접 Custom Exception을 만든다고 가정해보겠습니다.

public class MemoryRepository {

    private static final Map<Long, String> repo = new HashMap<>();
    private static Long id = 0L;

    public void save(Long id, String name) {
        repo.put(++id, name);
    }

    public String findById(Long id) throws CustomCheckedException {
        return repo.get(id);
    }
}

위 코드를 실행하는 부분에서는 꼭 try - catch블럭을 사용하여 예외처리를 해줘야합니다.

try {
    memoryRepository.findById(1L);
} catch (CustomCheckedException e) {
    e.printStackTrace();
}

매우 귀찮게 되는 것이죠. 이 예외처리의 핵심은 null값이 넘어오는 이슈가 발생하는 것을 막는 것이라고 가정하겠습니다.

그러면 아래처럼 쉽게 문제를 해결할 수 있죠.

public Optional<String> findById(Long id) {
    return Optional.of(repo.get(id));
}

혹은 기본 반환값, 비검사 예외처리 등을 해줄 수 있을 것입니다. (충분한 정보를 제공할 수 없기 때문에 지양하기도 함)

메서드를 쪼개라

또 다른 방법으로는 Checked Exception을 던지는 메서드를 두개로 쪼개 UnChecked Exception으로 바꾸는 방법입니다.

문제가 발생하는 지점을 boolean으로 받아 true일 경우에만 로직을 실행시키는 것이죠.

if (obj.isPermitted(args)) {
    obj.action(args);
} ...

만약 멀티 스레드 상황에서 동기화 없이 위 방법대로 리팩터링이 된다면, if문의 시점과 action의 시점 사이에 상태가 변경될 수도 있고, 중복 수행이 발생할 수도 있어 이런 문제는 잘 해결해야합니다. (이렇게 리팩토링 하지 않으면 될듯?)

정리

간단하게 정리하자면 안정성을 높이기 위해서는 Checked Exception 사용이 좋지만 최대한 UnChecked Exception 을 사용하려 해봅시다 예외상황에서 catch 블럭에서 유의미하게 복구할 방법이 없다면 UnChecked Exception을 사용합시다!