아이템 76. 가능한 한 실패 원자적으로 만들라

wisdom·2023년 3월 11일
0

Effetctive Java

목록 보기
76/80
post-thumbnail

실패 원자적(failure-atomic) 이란, 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지하는 것을 말한다.

메서드를 실패 원자적으로 만드는 방법

1. 불변 객체로 설계하기

불변 객체는 태생적으로 실패 원자적이다. 불변 객체의 상태는 생성 시점에 고정되어 변하지 않기 때문에, 메서드가 실패하면 새로운 객체가 만들어지지는 않을 수 있으나 기존 객체가 불안정 상태에 빠지는 일은 없다.

2. 작업 수행 전에 매개변수의 유효성 검사하기 (아이템 49)

객체의 내부 상태를 변경하기 전에 잠재적 예외의 가능성 대부분을 걸러낼 수 있는 방법이다.

Stack.pop 메서드 예를 보자.

public Object pop() {
	if (size == 0) 
    	throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null;
    return result;
}

또한 이와 비슷한 취지로 실패할 가능성이 있는 모든 코드를 객체의 상태를 바꾸는 코드보다 앞에 배치하는 방법도 있다. 계산을 수행해보기 전에는 인수의 유효성을 검사해볼 수 없을 때 앞서의 방식에 덧붙여 쓸 수 있는 기법이다.

3. 객체의 임시 복사본에서 작업 수행하기

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

예를 들어, 리스트를 정렬하는 메서드에서 입력 리스트의 원소들을 배열로 옮겨 담아 정렬을 수행한다. 배열을 사용할 때 반복문에서 원소들에 훨씬 빠르게 접근할 수 있기 때문이다.

4. 작업 도중 발생하는 실패를 가로채는 복구 코드 작성하기

복구 코드를 통해 작업 전 상태로 되돌리는 방법이다. 주로 내구성(durability)을 보장해야 하는 자료구조에서 쓰인다. 이 방법은 자주 쓰이지는 않는다.

실패 원자적으로 만들지 못하는(않는) 경우

실패 원자성은 일반적으로 권장되지만, 항상 달성할 수 있는 것은 아니다.

두 스레드가 동기화 없이 같은 객체를 동시에 수정한다면 그 객체의 일관성이 깨질 수 있다. (ConcurrentModificationException 을 잡아냈다고 해도 그 객체가 여전히 쓸 수 있는 상태라고 가정해서는 안 된다)

Error 는 복구할 수 없으므로 AssertionError 에 대해서는 실패 원자적으로 만들려는 시도조차 할 필요가 없다.

또한 실패 원자적으로 만들 수 있더라도, 실패 원자성을 달성하기 위해 비용이나 복잡도가 아주 큰 연산이라면 반드시 그리 해야 하는 것은 아니다.

주의사항

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

profile
백엔드 개발자

0개의 댓글