-
item 86 Serializable을 구현할지는 신중히 결정하라책/이펙티브 자바 2022. 4. 26. 11:28
ITEM 86 Serializable을 구현할지는 신중히 결정하라
클래스의 인스턴스를 직렬화가 가능하게 하려면 Serializable을 구현하면 된다
쉽게 적용 가능하기 때문에 개발자가 특별히 신경 쓰지 않아도 된다고 생각 할 수 있지만 사실은 훨씬 더 복잡하다
직렬화를 지원하는게 짧게 보면 쉬워 보이지만 길게 보면 아주 값 비싼 일이다
Serializable을 구현하면 릴리스한 뒤에는 수정하기 어렵다
- 클래스가 Serializable을 구현하면 직렬화된 바이트 스트림 인코딩도 하나의 공개 API가 된다
- 클래스가 많이 사용되게 되면 그 직렬화 형태도 다른 공개 API 처럼 영원히 지원해야 하는 것이다
- 커스텀 직렬화 형태를 설계하지 않고 자바의 기본 방식을 사용하면 직렬화 형태는 최소 적용 당시 클래스의 내부 구현 방식에 영원히 묶인다
- 기본 직렬화 형태에서는 클래스의 private와 package-private 인스턴스 필드마저 API로 공개하는 모습이 된다(캡슐화가 깨짐)
- 필드로의 접근을 최대한 막아 정보를 은닉하는게 불가능해진다
- 직렬화 가능 클래스를 만들려면 길게 보고 감당할 수 있을 만큼 고품질의 직렬화 형태도 주의해서 함께 설계해야 한다
Serializable 구현의 두 번째 문제는 버그와 보안 구멍이 생길 위험이 높아진다는 점이다
- 객체는 생성자를 사용해 만드는게 기본이며 직렬화는 언어의 기본 메커니즘을 우회하는 객체 생성 기법이다
- 기본 방식을 따르든 재정의해 사용하든 역직렬화시 생성자의 문제가 그대로 적용된다(숨은생성자)
- 생성자가 바로 보이지 않기 때문에 생성자에서 구축한 불변식을 모두 보장해야 하고 생성 도중 공격자가 객체 내부를 들여다 볼 수 없도록 해야 한다는 사실을 떠올리기 어렵다
- 기본 역직렬화를 사용하면 불변식 깨짐과 허가되지 않은 접근에 쉽게 노출 된다는 뜻
Serializable 구현의 세 번째 문제는 해당 클래스의 신버전을 릴리스할 때 테스트할 것이 늘어난다는 점이다
- 직렬화 가능 클래스가 수정되면 신버전 인스턴스를 직렬화한 후 구버전으로 역직렬화가 가능한가, 반대의 경우도 가능한가 검사해야한다
- 테스트해야 할 양이 직렬화 가능 클래스 수, 릴리스 횟수에 비례해 증가한다
- 양방향 직렬화, 역직렬화가 모두 성공하며 원래 객체를 제대로 복사하는지 확인이 필요하다
- 최초 클래스 설계시 커스텀 직렬화 형태를 잘 설계 했으면 테스트 부담이 줄어든다
Serializable 구현 여부는 가볍게 결정할 사안이 아니다
- 객체를 전송하거나 저장할 때 자바 직렬화를 이용하는 프레임워크용으로 만든 클래스면 선택의 여지가 없다
- Serializable을 반드시 구현해야 하는 다른 클래스의 컴포넌트로 쓰일 클래스도 마찬가지다
- Serializable 구현에 따르는 비용이 적지 않으니 클래스를 설계할 때마다 그 이득과 비용을 잘 따져봐야 된다
- BigInteger, Instant 같은 값 클래스와 컬렉션 클래스는 Serializable를 구현하는 경우가 많으며 스레드 풀 처럼 동작하는 객체를 표현하는 클래스는 Serializable를 구현하지 않는 경우가 많다
상속용으로 설계된 클래스는 대부분 Serializable을 구현하면 안되며 인터페이스도 대부분 Serializable을 확장해서는 안된다
- 이 규칙을 따르지 않으면 그런 클래스를 확장하거나 그런 인터페이스를 구현하는 이에게 커다란 부담을 지우게 된다
- 이 규칙을 어겨야 하는 경우도 있는데 예로 Serializable을 구현한 클래스만 지원하는 프레임워크를 사용하는 상황이다
Serializable을 구현하지 않기로 한 경우 주의사항
- 상속용 클래스인데 직렬화를 지원하지 않으면 그 하위 클래스에서 직렬화를 지원하려 할 때 부담이 늘어난다
- 보통은 이런 클래스를 역직렬화하려면 그 상위 클래스는 매개변수가 없는 생성자를 제공해야 하는데 이런 생성자를 제공하지 않으면 하위 클래스에서는 어쩔수 없이 직렬화 프록시 패턴을 사용해야하니 조심하자
내부 클래스는 직렬화를 구현하지 말아야 한다
- 내부 클래스에는 바깥 인스턴스의 참조와 유효 범위 안의 지역변수 값들을 저장하기 위해 컴파일러가 생성한 필드들이 자동으로 추가된다
- 익명 클래스와 지역 클래스의 이름을 짓는 규칙이 언어 명세에 나와 있지 않듯이 이 필드들이 클래스 정의에 어떻게 추가 되는지 정의되지 않는다
- 내부 클래스에 대한 기본 직렬화 형태는 분명하지 않지만 정적 멤버 클래스는 Serializable을 구현해도 된다
Serializable은 구현한다고 선언하기는 쉽지만 그건 눈속임일 뿐이며 한 클래스의 여러 버전이 상호작용할 일이 없고 서버가 신뢰할 수 없는 데이터에 노출될 가능성이 없는 등 보호된 환경에서만 쓰일 클래스가 아니라면 Serializable 구현은 아주 신중하게 이뤄져야 한다
상속할 수 있는 클래스라면 주의사항이 더욱 많아진다
'책 > 이펙티브 자바' 카테고리의 다른 글
item 88 readObject 메서드는 방어적으로 작성하라 (0) 2022.04.28 item 87 커스텀 직렬화 형태를 고려해보라 (0) 2022.04.28 item 85 자바 직렬화의 대안을 찾으라 (0) 2022.04.26 item 84 프로그램의 동작을 스레드 스케줄러에 기대지 말라 (0) 2022.04.25 item 83 지연 초기화는 신중히 사용하라 (0) 2022.04.25 - 클래스가 Serializable을 구현하면 직렬화된 바이트 스트림 인코딩도 하나의 공개 API가 된다