50. 적시에 방어적 복사본을 만들라

자바는 안전한 언어?

자바는 C, C++ 같은 네이티브 언어와 다르게 JVM에 의해 관리 받는 메모리를 사용하기 때문에 버퍼 오버런arrow-up-right, 배열 오버런, 와일드 포인터arrow-up-right 같은 메모리 오류에서 안전하다.

자바는 클래스의 객체지향 특성을 통해 클래스 내부를 불변하게 유지할 수 있는 강점이 있다. 하지만 그런 클래스의 불변식을 클라이언트가 깨뜨린다고 가정하고 방어적인 프로그래밍을 해야 한다.

사례: Period 클래스

기간을 표현하는 클래스 - 불변식을 지키지 못함

public final class Period {
    private final Date start;
    private final Date end;

    public Period(Date start, Date end) {
        if (start.compareTo(end) > 0)
            throw new IllegalArgumentException(
                    start + "가 " + end + "보다 늦다.");
        this.start = start;
        this.end = end;
    }

    public Date start() {
        return start;
    }

    public Date end() {
        return end;
    }

    ... // 나머지 코드 생략
}

Data가 가변이라는 사실을 이용하여 Period 인스턴스의 내부를 공격

자바 8 이후로는 Date 대신 Instant classarrow-up-right를 사용하면 된다. 혹은 LocalDateTime이나 ZonedDateTime을 사용해도 된다.

  • Date는 오래된 API이라 더 이상 사용하면 안된다. 하지만 어쩔수 없이 사용해야 하는 상황에서는 방어적 복사(defensive copy) 를 해야 한다.

해결법: 방어적 복사

  • 방어적 복사본이 비효율적일수 있기 때문에 되도록 불변 객체들을 조합하여 객체를 구성해서 방어적 복사를 할 일이 줄여야 한다.

  • 호출자가 컴포넌트 내부를 수정하지 않는다는 확신이 있다면 방어적 복사를 생략할수 있지만 매개변수나 반환값을 수정하지 말아야 함을 문서화해야 한다.

정리

  • 클래스가 클라이언트로부터 받는 혹은 클라이언트로 반환하는 구성요소가 가변이라면 그 요소는 반드시 방어적으로 복사해야함.

  • 복사 비용이 너무 크거나 잘못 수정할 일이 없음을 신뢰한다면 구성 요소 수정에 대한 책임 내용을 문서에 명시해야함.

Last updated