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

✅ 핵심 요약

클라이언트가 여러분의 불변식을 깨뜨릴 수도 있다는 가정 하에, 가변 객체를 외부에서 받을 때나 반환할 때는 반드시 방어적 복사를 수행해야 합니다. 이는 클래스 내부 상태의 안전과 일관성을 유지하는 데 필수입니다.


📚 필수 개념 요약

개념
설명

가변 객체

상태 변경 가능 (Date, List, Map, 배열 등)

불변 객체

상태 변경 불가 (String, Integer, LocalDate, Instant)

캡슐화

외부에서 내부 상태를 제어하지 못하도록 막는 원칙

불변식

클래스가 언제나 만족해야 하는 상태 조건 (start < end)

TOCTOU 문제

검사 시점(Time of Check)과 사용 시점(Time of Use) 사이에 상태가 변하는 문제


💡 핵심 포인트

  • 생성자에서 받는 모든 가변 객체는 복사해서 사용해야 한다.

  • 유효성 검사보다 먼저 복사해야 TOCTOU 공격을 피할 수 있다.

  • clone()은 서브클래스 오염 위험이 있어 사용하지 말 것.

  • Getter로 내부 필드 그대로 반환하면 객체의 캡슐화가 깨진다.

  • 성능이 문제된다면, 대신 클라이언트에게 문서로 책임 전가하되 명시적으로 작성할 것.


🔍 장점 vs 단점

장점
단점

✅ 클래스의 불변성 보장

⛔ 방어적 복사로 인한 성능 오버헤드

✅ 외부 변경으로부터 상태 보호

⛔ 복사 코드 반복 증가 (보일러플레이트)

✅ 멀티스레드 환경에서도 안정적

⛔ 매번 복사에 따른 객체 생성 비용

✅ 실수로 인한 버그 예방


❗ 자주 발생하는 실수

  1. 복사를 안 하고 필드에 그대로 할당

  2. 유효성 검사 후 복사 → TOCTOU 문제 노출

  3. Getter에서 원본 필드 반환 → 외부에서 내부 상태 변경 가능

  4. clone() 사용 → 타입 오염 발생 위험

  5. 복사 비용이 크다고 생략 후 문서화 안 함 → 유지보수자 혼란


🔧 예제 코드 (Date 기반 Period 클래스)

import java.util.Date;

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

    // 생성자에서 방어적 복사 후 검증
    public Period(Date start, Date end) {
        this.start = new Date(start.getTime()); // clone() 대신 생성자 사용
        this.end = new Date(end.getTime());
        
        if (this.start.after(this.end)) {
            throw new IllegalArgumentException("시작 시간이 종료 시간보다 늦을 수 없습니다.");
        }
    }

    // getter에서도 복사
    public Date getStart() {
        return new Date(start.getTime());
    }

    public Date getEnd() {
        return new Date(end.getTime());
    }
}

🔍 만약 복사를 하지 않으면?

Period p = new Period(start, end);
end.setYear(1970); // 외부에서 변경해버리면 내부 불변식이 깨짐!

✅ 결론

방어적 복사는 단순한 예외 대응이 아니라, 객체의 일관성과 불변식을 지키기 위한 기본 전략입니다. 객체가 외부와 어떤 데이터를 주고받든 항상 “내부 상태는 내가 책임진다”는 태도로 코드를 작성하세요.


🎯 느낀점 (강사 코멘트)

객체를 "복사해서 쓰는 것"이 왜 중요한지 잘 몰랐습니다. 객체를 공유하면 의도치 않게 내부 상태가 바뀔 수 있다는 걸 알게 됐습니다.

  • Date 같은 가변 객체를 그대로 넘기면, 외부 코드가 나도 모르게 내 객체 상태를 바꿀 수 있다는 사실.

  • '클라이언트는 내 코드를 망가뜨릴 수도 있다'는 생각을 전제로 코드를 짜야 한다고 느낌.

  • 실제로 이런 실수를 방지해준다면 꼭 필요한 수고라는 걸 느낌.

  • 방어적 복사는 객체의 안전한 사용법에 대한 중요한 태도


Last updated