Item 90. Serialization Proxy Pattern
직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라
Serializable
인터페이스를 구현하는 순간 정상적인 생성자 이외의 방법으로 인스턴스를 생성할 수 있게 되면서, 버그나 보안 문제에 노출되게 된다.
이런 위험을 크게 줄여주는 기법으로 직렬화 프록시 패턴이 있다.
구현 방법
직렬화 프록시 패턴의 구현 방법은 다음과 같다.
바깥 클래스의 논리적 상태를 표현하는 중첩 클래스를 설계해
private static
으로 선언중첩 클래스의 생성자는 단 하나로 제한하며, 바깥 클래스를 매개변수로 받아 단순히 넘어온 인스턴스 데이터를 복사
바깥 클래스와 직렬화 프록시 모두
Serializable
을 구현
위처럼 구현된 각각의 메서드들은 아래와 같은 역할을 수행하면서 보안 문제를 해결하게 된다.
writeReplace
: 바깥 클래스 인스턴스 대신 직렬화 프록시 객체를 반환하여 직렬화 중에 생성자 호출을 막음readObject
: 역직렬화가 직접적으로 이루어지지 않도록 예외를 던져 프록시 객체를 통해서만 역직렬화가 이루어지도록 함readResolve
: 역직렬화된 프록시 객체를 바깥 클래스 인스턴스로 대체하여 반환
장점
직렬화 프록시 패턴을 사용하면 아래와 같은 장점을 얻을 수 있다.
진정한 불변 클래스: 멤버 필드 final 선언하여 불변성 보장 가능
역직렬화 유효성 검사 불필요: 직렬화 대상이 직접적으로 되지 않기 때문에 역직렬화 과정에서 유효성 검사를 할 필요가 없음
역직렬화 인스턴스 != 원래의 인스턴스: 역직렬화된 인스턴스가 원래의 인스턴스와 다르게 되어도 문제가 없음
이 중 3번에 대한 장점은 EnumSet
클래스를 사용한 예시를 통해 확인할 수 있다.
EnumSet 클래스의 직렬화 프록시 패턴
EnumSet
클래스 생성 시 RegularEnumSet
과 JumboEnumSet
두 가지 인스턴스를 반환하게 되는데, 이 두 인스턴스의 차이는 다음과 같다.
RegularEnumSet
인스턴스: 64개 이하의 원소를 가질 때 사용JumboEnumSet
인스턴스: 64개 이상의 원소를 가질 때 사용
64개짜리 열거 타입을 가진 RegularEnumSet
인스턴스를 직렬화하고 원소를 추가한 후 역직렬화하면 JumboEnumSet
인스턴스가 생성되어야 하는데,
이를 위해 EnumSet
클래스는 직렬화 프록시 패턴을 사용해 적절한 인스턴스를 생성하도록 구현되어 있다.
Last updated