본문 바로가기
JAVA/Effective Java

item 90. 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라

by Garonguri 2022. 9. 15.
728x90

 

 

Serializable은 물론 편리하다. 그러나 버그와 보안 문제가 일어날 가능성이 커질 수 밖에 없다.

이럴 때 '직렬화 프록시 패턴' 을 사용하자.

직렬화 프록시는 readObject 의 방어적 복사보다 강력하다.


[ 직렬화 프록시 패턴을 사용하는 방법 ]

  1. 바깥 클래스의 논리적 상태를 정밀히 표현하는 중첩 클래스를 설계하고, private static 으로 선언하기
    1. 위 '중첩 클래스'가 바로 바깥 클래스의 직렬화 프록시다.
    2. 중첩 클래스의 생성자는 단 하나여야 하며, 바깥 클래스를 매개변수로 받는다.
  2. 중첩 클래스의 생성자는 인수로 넘어온 인스턴스 데이터를 복사한다.
    1. 일관성 검사나 방어적 복사는 필요하지 않다.
    2. 직렬화 프록시의 기본 직렬화 형태는 바깥 클래스의 직렬화 형태로 쓰기에 이상적이다.
  3. 바깥 클래스와 직렬화 프록시 모두 Serializable을 구현했다고 선언해야 한다.

[ 직렬화 프록시를 사용하면 얻을 수 있는 장점 ]

  • 직렬화는 생성자를 이용하지 않고도 인스턴스를 생성하는 기능을 제공하는데, 이 패턴은 직렬화의 이런 특성을 제거한다.
  • 역직렬화된 인스턴스가 불변식을 만족하는지 검사할 수단을 강구하지 않아도 된다.
  • 가짜 바이트 스트림 공격과 내부 필드 탈취 공격을 프록시 수준에서 차단해준다
  • 필드들을 final로 선언해도 되므로 바깥 클래스를 진정한 불변으로 만들 수 있다.
  • 어떤 필드가 기만적인 직렬화 공격의 목표가 될 지 고민하지 않아도 된다.
  • 역직렬화때 유효성 검사를 하지 않아도 된다
  • 역직렬화한 인스턴스와 원래의 직렬화된 인스턴스의 클래스가 달라도 정상 작동한다. (은근 쓸모 있다.)

[ 직렬화 프록시 패턴의 한계 ]

  • 클라이언트가 멋대로 확장할 수 있는 클래스에는 적용할 수 없다
  • 객체그래프 순환이 있는 클래스에는 적용할 수 없다.
    • (이런 객체 메서드를 직렬화 프록시 readResolve 안에서 호출하려 하면 ClassCastException이 발생한다.)
    • 직렬화 프록시만 가졌을 뿐, 실제 객체가 만들어진 것은 아니기 때문이다.
  • 방어적 복사보다 느리다.
    • 약 14%

 

 

핵심 정리

제3자가 확장할 수 없는 클래스라면 가능한 한 직렬화 프록시 패턴을 사용하자.
이 패턴이 아마도 중요한 불변식을 안정적으로 직렬화해주는 가장 쉬운 방법이다.
728x90

댓글