item 86) Serializable을 구현할지는 신중히 결정하라
Serializable 구현의 문제점
1) 릴리스 후 수정 어려움
Serializable을 구현하면 릴리스 한 뒤에는 수정하기 어려워진다.
Serializable을 구현한 클래스는 하나의 공개 API가 되므로 구현한 직렬화 형태도 영원히 지원해야 한다.
직렬화 가능 클래스를 만든다면 고품질의 직렬화 형태도 함께 설계해야 한다.
자바의 기본 직렬화 방식을 사용하면 직렬화 형태는 적용 당시 클래스의 내부 구현 방식에 종속된다.
원래의 직렬화 형태를 유지하면서 내부 구현을 수정할 수도 있지만, 이는 어렵고 소스코드도 지저분해진다.
클래스의 private와 package-private 인스턴스 필드마저 API로 공개되어 캡슐화가 깨진다.
나중에 내부 구현을 수정하면 직렬화 형태가 달라져 직렬화-역직렬화에 실패할 수 있다.
ex) serialVersionUID 자동 생성 이슈
Serializable 클래스의 버전을 기억해 클래스와 직렬화 된 객체가 호환되는지 확인하는 식별자
직접 번호를 명시하지 않는 경우 런타임 시 자동으로 생성된다.
자동 생성될 때 클래스의 이름, 구현한 인터페이스 등이 고려되기 때문에 이들 중 하나라도 변경되면 UID 값도 달라진다. 따라서 쉽게 호환성이 깨지고 런타임에 InvalidClassException이 발생한다.
2) 버그와 보안 취약점 발생 위험
기본 역직렬화는 전면에 드러나지 않는 숨은 생성자이기 때문에 불변식 깨짐과 허가되지 않은 접근에 쉽게 노출된다.
3) 신버전 릴리스 시 테스트 요소 증가
직렬화 가능 클래스가 수정되면 신버전/구버전 인스턴스 간의 양방향 직렬화/역직렬화가 가능한지 검사해야 한다.
테스트해야 할 양이 직렬화 가능 클래스 수와 릴리스 횟수에 비례해 증가한다.
4) 구현 여부는 쉽게 결정할 사안이 아니다.
Serializable을 반드시 구현해야 하는 클래스는 구현에 따르는 이득과 비용을 잘 고려해 설계해야 한다.
BigInteger와 Instant 같은 '값' 클래스와 컬렉션 클래스들은 Serializable을 구현하고, 스레드 풀처럼 **'동작'**하는 객체를 표현하는 클래스는 대부분 Serializable을 구현하지 않았다.
💡 값을 나타내는 클래스는 아무래도 Spring의 entity와 비슷하게 외부와 통신하는 경우가 많을 것 같다.
5) 상속용으로 설계된 클래스/인터페이스는 Serializable 확장 불가
해당 클래스/인터페이스를 구현하는 대상에게 Serializable의 문제점들을 그대로 전이하기 때문
하지만 Serializable을 구현한 클래스만 지원하는 프레임워크를 사용하는 상황이라면 이 규칙을 지키지 못하는 경우도 생긴다.
ex) Throwable
Throwable은 서버가 RMI를 통해 클라이언트로 예외를 보내기 위해 Serializable을 구현했다.
RMI(Remote Method Invocation) 자바와 개발환경을 사용하여 서로 다른 컴퓨터들 상에 있는 객체들이 분산 네트워크 내에서 상호 작용하는 객체지향형 프로그램을 작성할 수 있는 방식이다.
직렬화, 확장(extends)이 모두 가능한 클래스에서 불변식을 보장해야 한다면 finalize 메서드를 하위 클래스가 재정의하지 못하도록 자신이 재정의하면서 final로 선언해야 finalizer 공격을 피할 수 있다.
필드 중 원시 값으로 초기화되면 위배되는 불변식이 있다면 readObjectNoData를 반드시 추가해야 한다.
Serializable을 구현하지 않는 상위 클래스
상속용 클래스에서 직렬화를 지원하지 않지만, 하위 클래스에서 직렬화를 지원하려 한다면 부담이 늘어난다.
이런 클래스를 역직렬화하려면 상위 클래스의 매개변수가 없는 생성자를 제공하거나, 하위 클래스에서 직렬화 프록시 패턴을 사용해야 하기 때문이다.
내부 클래스와 직렬화
내부 클래스에는 바깥 인스턴스의 참조와 유효 범위 안의 지역변수 값들을 저장하기 위해 컴파일러가 생성한 필드들이 자동으로 추가되어 기본 직렬화 형태가 유동적이다.
정적 멤버 클래스는 컴파일러에 의해 자동 추가되는 필드가 따로 없으므로 Serializable을 구현해도 괜찮다.
Last updated