우리가 만드는 클래스의 클라이언트가 불변식을 망가뜨리기 위해 최선을 다할 것이라는 가정하에, 방어적으로 프로그래밍을 해야한다.  그러니 클라이언트가 이상한 짓을 해도 안정적으로 동작하는 클래스를 만들기 위해 노력해야 한다.


위의 클래스는 일반적으로 생각하는 변경 불가능 클래스를 작성한 것이다. 과연 이 클래스는 변경이 불가능할까? 아래의 코드를 보자.


Date클래스는 변경 가능 클래스라는 것을 이용하면 위와 같이 불변식을 깨뜨릴 수 있다. 따라서 Period 객체의내부를 보호하려면 생성자로 전달되는 변경 가능 객체를 반드시 방어적으로 복사해서 그 복사본을 Period 객체의 컴포넌트로 이용해야 한다. 즉 Period 생성자는 아래와 같이 작성되어야 한다.


위와 같이 생성자를 변경하면 앞서 살펴본 공격법은 먹히지 않는다. 인자의 유효성을 검사하기 전에 방어적 복사본을 만들었다는 것에 유의하자. 유효성 검사는 복사본에 대해서 시행한다. 이러한 방법은 인자를 검사한 직후 복사본이 만들어지기 직전까지의 시간, 즉 취약시간 동안에 다른 스레드가 인자를 변경해버리는 일을 막기 위한 것이다. (TICTOU 공격, time-of-check/time-of-use 공격이라 부른다)


그런데 위와 같은 방법은 생성자 인자를 통한 공격은 막을 수 있지만, 접근자를 통한 공격은 막을 수 없다. 접근자를 호출하여 얻은 객체를 통해 Period 객체 내부를 변경할 수 있기 때문이다.



위와 같은 공격을 막으려면 변경 가능 내부 필드에 대한 방어적 복사본을 반환하도록 접근자를 수정하여야 한다.



 생성자와 접근자를 이렇게 수정하고 나면 Period는 진정한 변경 불가능 클래스가 된다. 


요약

클라이언트로부터 구했거나 클라이언트에게 반환되는 변경 가능 컴포넌트가 있는 경우, 해당 클래스는 그 컴포넌트를 반드시 방어적으로 복사해야 한다. 복사 오버헤드가 너무 크고 클래스 사용자가 그 내부 컴포넌트를 부적절하게 변경하지 않는다는 보장이 잇을 때는, 방어적 복사를 하는 대신 클라이언트 측에서 해당 컴포넌트를 변경해선 안된다. 


+ Recent posts