티스토리 뷰

728x90
반응형

예외는 진짜 예외 상황에만 사용하라


🧨 잘못된 예외의 사용 방법

  • 아래의 코드를 보고 무슨 일을 하는 코드인지 가늠하기가 힘듭니다. while 문을 무한으로 순회하면서 range 배열의 각 요소마다 climb() 메서드를 호출하는구나, 배열의 범위 밖의 요소를 찾으면 예외가 발생하고 끝나는구나라고 생각할 수 있습니다. 
  • 이 코드는 성능 향상,,,?을 위해 작성했다고 생각할 수 있겠지만, 표준 관용구(for-loop)보다 훨씬 느리다고 합니다.
try {
    int i = 0;
    while(true) {
    	range[i++].climb();
    }
} catch (ArrayIndexOutOfBoundsException e) {
  // ...
}

 

💡 리펙토링

  • 아래처럼 사용하면 try-catch문을 사용하지 않아도 됩니다. 왜 위의 예제 코드처럼 사용했을까요? 그 이유는 잘못된 추론을 근거로 성능을 높여보려고 한 것입니다. JVM은 배열에 접근할 때마다 경계를 넘는지 혹은 넘지 않는지 검사하는데, 일반적으로 반복문도 배열 경계에 도달하면 종료합니다. 따라서 try-catch와 반복문을 함께 사용하면 둘 다 경계를 살펴보는 작업을 하게되는데 이는 같은 일이 중복되므로 둘 중 하나는 무시가 됩니다. 아래는 세 가지 부분에서 잘못된 추론입니다.
  • 예외는 예외 상황에 쓸 용도로 만들어졌기 때문에 JVM 구현자 입장에서는 빠르게 만들 이유가 없습니다. 
  • 코드를 try-catch 블록 안에 넣으면 JVM이 적용할 수 있는 최적화가 제한됩니다.
  • 배열을 순회하는 표준 관용구(for-loop)는 앞서 중복적인 검사를 수행하지 않습니다. JVM이 알아서 최적화해 없애줍니다.
for (Mountain m : range){
    m.climb();
}

 

💡 예외를 사용하는 원칙

  • 예외는 오직 예외 상황에서만 사용해야 합니다. 절대로 일상적인 제어 흐름용으로 사용해서는 안됩니다.
  • 이 원칙은 API 설계에도 적용됩니다. 잘 설계된 API라면 클라이언트가 정상적인 제어 흐름에서 예외를 사용할 일이 없게 해야합니다. 특정 상태에서만 호출할 수 있는 "상태 의존적" 메서드를 제공하는 클래스는 "상태 검사" 메서드도 함께 제공해야 합니다. 
    이와 관련해서는 Iterator 인터페이스의 next(), hasNext() 메서드가 있습니다. 그리고 별도의 상태 검사 덕분에 다음과 같은
    for-loop를 사용할 수 있다고 합니다.
  • 상태 의존적 메서드란 특정 상태에서만 호출할 수 있는 메서드를 의미합니다. (next)
  • 상태 검사 메서드란 상태를 검사하는 메서드를 의미합니다. (hasNext)
for (Iterator<Foo> i = collection.iterator(); i.hasNext(); ) {
	// loop....
}

 

💡 상태 검사 메서드, 옵셔널, 특정 값 중 선택하는 지침

  • 외부 동기화 없이 여러 스레드가 동시에 접근할 수 있거나 외부 요인으로 상태가 변할 수 있다면 옵셔널이나 특정 값을 사용해야 합니다. 상태 검사 메서드와 상태 의존적 메서드를 호출하는 시점에 따라 여러 스레드가 동시에 접근한다면 상태가 변할 수 있기 때문입니다.
  • 성능이 중요한 상황에서 상태 검사 메서드가 상태 의존적 메서드의 작업 일부를 중복해서(동시성) 수행한다면 옵셔널이나 특정 값을 선택해야 합니다. 
  • 그 외의 경우에는 상태 검사 메서드가 더 낫습니다. 가독성 뿐만 아니라 문제가 발생할 경우 발견하기도 쉬워집니다.

 

 

 

 

 

 

728x90
반응형