[Effective Java]가능한 한 실패 원자적으로 만들라

예외, 여덟 번째 아이템

Posted by SungBeom on May 23, 2020 · 3 mins read

실패 원자성

작업 도중 예외가 발생해도 그 객체는 여전히 정상적으로 사용할 수 있는 상태라면 멋질 것이다. 검사 예외를 던진 경우라면 호출자가 오류 상태를 복구할 수 있을 테니 특히 더 유용할 것이다. 일반화해 이야기하면, 호출된 메소드가 실패하더라도 해당 객체는 메소드 호출 전 상태를 유지해야 한다. 이러한 특성을 실패 원자적(failure-atomic)이라고 한다.

실패 원자성을 얻는 방법

메소드를 실패 원자적으로 만드는 방법은 다양하다. 가장 간단한 방법은 불변 객체로 설계하는 것이다. 불변 객체는 태생적으로 실패 원자적이므로, 메소드가 실패하며 새로운 객체가 만들어지지는 않을 수 있으나 기존 객체가 불안정한 상태에 빠지는 일은 결코 없다. 불변 객체의 상태는 생성 시점에 고정되어 절대 변하지 않기 때문이다.

가변 객체의 메소드를 실패 원자적으로 만드는 가장 흔한 방법은 작업 수행에 앞서 매개변수의 유효성을 검사하는 것이다. 객체의 내부 상태를 변경하기 전에 잠재적 예외의 가능성 대부분을 걸러낼 수 있는 방법이다. 이와 비슷한 취지로 실패할 가능성이 있는 모든 코드를, 객체의 상태를 바꾸는 코드보다 앞에 배치하는 방법도 있다. 계산을 수행해보기 전에는 인수의 유효성을 검사해볼 수 없을 때 앞서의 방식에 덧붙여 쓸 수 있는 기법이다.

실패 원자성을 얻는 다음 방법은 객체의 임시 복사본에세 작업을 수행한 다음, 작업이 성공적으로 완료되면 원래 객체와 교체하는 것이다. 데이터를 임시 자료구조에 저장해 작업하는 게 더 빠를 때 적용하기 좋은 방식이다.

실패 원자성을 얻는 마지막 방법은 작업 도중 발생하는 실패를 가로채는 복구 코드를 작성하여 작업 전 상태로 되돌리는 방법이다. 주로 디스크 기반의 내구성(durability)을 보장해야 하는 자료구조에 쓰이는데, 자주 쓰이는 방법은 아니다.

실패 원자성의 예외

실패 원자성은 일반적으로 권장되는 덕목이지만 항상 달성할 수 있는 것은 아니다. 한편, Error는 복구할 수 없으므로 AssertionError에 대해서는 실패 원자적으로 만드려는 시도조차 할 필요가 없다.

실패 원자적으로 만들 수 있더라도 항상 그리 해야 하는 것도 아니다. 실패 원자성을 달성하기 위한 비용이나 복잡도가 아주 큰 연산도 있기 때문이다. 그래도 문제가 무엇인지 알고 나면 실패 원자성을 공짜로 얻을 수 있는 경우가 더 많다.

메소드 명세에 기술한 예외라면 설혹 예외가 발생하더라도 객체의 상태는 메소드 호출 전과 똑같이 유지돼야 한다는 것이 기본 규칙이다. 이 규칙을 지키지 못한다면 실패 시의 객체 상태를 API 설명에 명시해야 한다.


핵심 정리

호출된 메소드가 실패하더라도 해당 객체는 메소드 호출 전 상태를 유지해야 하며, 이를 실패 원자적이라고 한다. 메소드를 실패 원자적으로 만들기 위해 불변 객체로 설계하거나, 매개변수의 유효성을 검사하거나, 객체의 임시 복사본에서 작업을 수행한 다음, 작업이 성공적으로 완료되면 원래 객체와 교체하거나, 작업 도중 발생하는 실패를 가로채는 복구 코드를 작성하여 작업 전 상태로 되돌리는 방법이 있다. 실패 원자성은 항상 달성할 수 있는 것은 아니며, 실패 원자적으로 만들 수 있더라도 항상 그리 해야 하는 것도 아니지만, 일반적으로 권장되는 덕목이다.