Skip to content

Latest commit

 

History

History
120 lines (108 loc) · 5.83 KB

ch07_예외처리.md

File metadata and controls

120 lines (108 loc) · 5.83 KB

7. 오류 처리

  • 깨끗한 코드와 오류처리는 확실한 연관성을 가진다.
  • 오류 처리에 대해 알아보자

오류 코드보다 예외를 사용하라

  • 오류 코드 사용시 호출코드가 복잡해 진다
  • 따라서 오류가 발생하면 예외를 던지는 편이 더 낫다
public class DeviceController {
    public void sendShutDown() {
        DeviceHandle handle = getHandle(DEV1);
        // 디바이스 상태를 점검한다.
        if (handle != DeviceHandle.INVALID) {
            // 레코드 필드에 디바이스 상태를 저장한다.
            retrieveDeviceRecord(handle);
            //디바이스가 일시정지 상태가 아니라면 종료한다.
            if (record.getStatus() != DEVICE_SUSPENDED) {
                pauseDevice(handle);
                clearDeviceWorkQueue(handle);
                closeDevice(handel);
            } else {
                logger.log("Device suspended. Unable to shut down");
            }
        } else {
            logger.log("Invalid handle for: " + DEV1.toString());
        }
    }
}
-----------------------------------------------------------
public class DeviceController {
    public void sendShutDown() {
        try {
            tryToShutDown();
        } catch (DeviceShutDownError e) {
            logger.log(e);
        }
    }
 
    private void tryToShutDown() throws DeviceShutDownError {
        DeviceHandle handle = getHandle(DEV1);
        DeviceRecord record = retrieveDeviceRecord(handle);
 
        pauseDevice(handle);
        clearDeviceWorkQueue(handle);
        closeDevice(handle);
    }
 
    private DeviceHandle getHandle(DeviceId id) {
        ...    	
        throw new DeviceShutDownError("Invalid handle for:" + id.toString());
        ...
    }
}
  • 무엇보다 디바이스를 종료하는 알고리즘과 오류를 처리하는 알고리즘이 분리되어
  • 개념적인 파악이 훨씬 수월하다

Try-Catch-Finally 문부터 작성하라

  • try 문은 범위를 지정한다는 특징과 트랙잭션과 비슷한 느낌을 주기도 한다.
  • 저자는 먼저 강제로 예외를 일으키는 테스트 케이스를 작성한 후 테스트를 통과하게 코드를 작성하는 방법을 권장한다.
public List<RecordedGrip> retrieveSection(String sectionName) {
    try {
        FileInputStream stream = new FileInputStream(sectionName); // 1)
        stream.close();
    } catch (FileNotFoundException e) { // 2)
        throw new StorageException("retrieval error", e);
    }
    return new ArrayList<RecordedGrip>();
}

Unchecked 예외를 사용하라

  • 굉장히 중요한 로직은 checked 예외를 사용하여 상위로 전파하는 것도 하나의 방법이다.
  • 하지만 현재 대세는 Unckecked 예외이며 C++, C#, 파이썬 루비는 checked 가 없다
  • 제일 큰 이유는 OCP(Open Closed Principle) 위반이다.
  • 메서드에서 checked 예외를 던지면 상위 선언부의 모든 예외 부분을 수정해야 한다.
  • 즉 하위단계에서 코드를 변경하면 상위 단계 메서드 선언부를 전부 고쳐야 한다.
  • 경로상의 모든 함수가 하위 함수에서 던지는 예외를 알아야 하므로 캡슐화도 깨진다.
  • 일반 적인 애플리케이션은 의존성이라는 비용이 이익보다 크다

예외에 의미를 제공하라

  • 예외를 던질때는 전 후 상황을 충분히 덧붙여라.
  • 오류 메시지에 정보를 담아 예외와 함께 던지자.
  • 실패한 연산이름, 실패 유형도 언급하자
  • 로깅 기능도 활용하자

호출자를 고려해 예외 클래스를 정의하라

  • 오류를 잡아내는 분류하는 방법은 굉장히 많다
  • 하지만 프로그래머에게 가장 중요한 것은 오류를 잡아내는 방법이 되어야 한다.
  • 오류 처리 방식은 보통 다음과 같다
  1. 오류를 기록한다
  2. 프로그램을 계속 수행해도 좋은지 확인한다
  • 호출 라이브러리 API 를 감싸 예외 유형 하나를 반환할 수 있다.
  • 즉 감싸기 기법을 추천하고 있다.
  • 외부 API 를 감싸면 외부 라이브러리와 프로그램 사이에서 의존성이 줄고 추후 다른 라이브러리로 갈아타도 비용이 적다. 감싸기 클래스에서 테스트 코드를 넣어 테스트 할 수도 있다.
  • 프로그래머스에서 훈님 말로는 checked Exception 를 감싸기 메서드를 활용하려 unchecked Exception 으로 처리하라는 말도 하였다.

정상 흐름을 정의하라

  • 비즈니스 논리와 오류 처리 분리는 중요한 부분이다
  • 예외가 논리를 따라가기 어렵게 한다면 특수 사례 패턴(Special Case Pattern) 을 활용하자

Special Case Pattern

  • 클래스를 만들거나 객체를 조작해 특수 사례를 처리하는 방식이다.
  • 그러면 클라이언트 코드가 예외적인 상황을 처리할 필요가 없어진다.
  • 왜냐하면 클래스나 객체가 상황을 캡슐화해서 처리하기 때문이다.
  • 간단한 예시로 get 메서드는 내부 구현을 드러내기 때문에 사용하지 말라고 했다. 대신에 해당 비즈니스 로직을 엔티티 객체 내부에 메서드로 사용하고 해당 메서드의 이름을 메세지로 전달하여 구현을 추상화하는 방법이 있다.

null 을 반환하지 마라

  • null 은 Java 의 고질적인 문제
  • Optional 을 활용하자
  • null 반환을 해야 하면 예외를 던지거나 특수 사례 객체를 반환하자
  • 외부 API 는 감싸기 메서드를 구현하는 것을 고려하자.
  • Collections.emptyList() 와 같이 빈 자료구조를 반환하는 방식이 코드가 더 깔끔하다

null 을 전달하지 마라

  • 전달하면 해당 null 가 있어 코드가 복잡하다
  • assert 를 활용하여 문서화도 가능하다
  • 근본적인 문제해결은 하니다
  • null 을 인자로 전달하는 거 자체를 금지하자