자바 직렬화의 대안을 찾으라
- 직렬화 위험을 회피하는 가장 좋은 방법은 아무것도 역직렬화하지 않는 것이다.
- 차선책은 신뢰할 수 없는 데이터는 절대 역직렬화하지 않는 것이다.
- 새로운 시스템에서 자바 직렬화를 써야 할 이유는 전혀 없다.
- 객체 역직렬화 필터링(java.io.ObjectInputFilter)를 사용하자. (블랙리스트 보다는 화이트리스트 방식을 추천한다.)
Serializable을 구현할지는 신중히 결정하라
- Serialiazable을 구현하면 릴리스한 뒤에는 수정하기 어렵다.
- 버그와 보안 구멍이 생길 위험이 높아진다.
- 해당 클래스의 신버전을 릴리스할 때 테스트할 것이 많아진다.
- Serializable 구현 여부는 가볍게 결정할 사안이 아니다.
- 상속용으로 설계된 클래스는 대부분 Serializable을 구현하면 안 되며, 인터페이스도 대부분 Serializable을 확장해서는 안 된다.
- 내부 클래스는 Serializable을 구현하지 말아야 한다. (정적 멤버 클래스는 Serializable을 구현해도 된다.)
커스텀 직렬화 형태를 고려해보라
- 먼저 고민해보고 괜찮다고 판단될 때만 기본 직렬화 형태를 사용하라.
- 객체의 물리적 표현과 논리적 내용이 같다면 기본 직렬화 형태라도 무방하다.
- 객체의 물리적 표현과 논리적 표현의 차이가 클 때 기본 직렬화 형태를 사용하면 크게 4가지 문제가 생긴다.
- 공개 API가 현재의 내부 표현 방식에 영구히 묶인다.
- 너무 많은 공간을 차지할 수 있다.
- 시간이 너무 많이 걸릴 수 있다.
- 스택 오버플로를 일으킬 수 있다.
- 객체의 논리적 상태와 무관한 필드라고 확신할 때만 transient 한정자를 생략해야 한다.
- 객체 전체 상태를 읽는 메서드에 적용해야 하는 동기화 메커니즘을 직렬화에도 적용해야 한다.
- 어떤 직렬화 형태를 택하든 직렬화 가능 클래스 모두에 직렬 버전 UID를 명시적으로 부여하자.
- 구버전으로 직렬화된 인스턴스들과의 호환성을 끊으려는 경우를 제외하고는 직렬 버전 UID를 절대 수정하지 말자.
readObject 메서드는 방어적으로 작성하라
- 객체를 역직렬화할 때는 클라이언트가 소유해서는 안 되는 객체 참조를 갖는 필드를 모두 방어적으로 복사해야 한다.
- readObject 메서드를 작성할 때는 언제나 public 생성자를 작성하는 자세로 임해야 한다.
- readObject 메서드 작성 지침
- private이어야 하는 객체 참조 필드는 각 필드가 가리키는 객체를 방어적으로 복사하라. 불변 클래스내의 가변 요소가 여기 속한다.
- 모든 불변식을 검사하여 어긋나는 게 발견되면 InvalidObjectException을 던진다. 방어적 복사 다음에는 반드시 불변식 검사가 뒤따라야 한다.
- 역직렬화 후 객체 그래프 전체의 유효성을 검사해야 한다면 ObjectInputValidation 인터페이스를 사용하라.
- 직접적이든 간접적이든, 재정의할 수 있는 메서드는 호출하지 말자.
인스턴스 수를 통제해야 한다면 readResolve보다는 열거 타입을 사용하라
- 불변식을 지키기 위해 인스턴스를 통제해야한다면 가능한 한 열거 타입을 사용하자.
- readResolve를 인스턴스 통제 목적으로 사용한다면 객체 참조 타입 인스턴스 필드는 모두 transient로 선언해야 한다.
직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라
- 직렬화 프록시 패턴이 중요한 불변식을 안정적으로 직렬화해주는 가장 쉬운 방법일 것이다.
'책 > Effective Java' 카테고리의 다른 글
동시성 (0) | 2020.03.28 |
---|---|
예외 (0) | 2020.03.27 |
일반적인 프로그래밍 원칙 (0) | 2020.03.26 |
메서드 (0) | 2020.03.24 |
람다와 스트림 (0) | 2020.03.20 |