Skip to content
This repository was archived by the owner on Mar 7, 2023. It is now read-only.

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

[아이템 86] Serializable을 구현할지는 신중히 고려하라

직렬화의 구현은 쉽다. 클래스 선언에 implements Serializable을 덧붙히는 것으로 직렬화를 구현할 수 있다.

하지만 직렬화를 사용하는 것은 매우 신중하게 결정되어야한다. 직렬화를 구현한 순간부터 많은 위험성을 갖게 되고 확장성을 잃게 된다

Serializable을 구현하면 릴리스한 뒤에는 수정하기 어렵다.

클래스가 Serializable을 구현하면 직렬화된 바이트 스트림 인코딩(직렬화 형태)도 하나의 공개 API가 된다.

그래서 이 클래스가 널리 퍼진다면 그 직렬화 형태도 (다른 공개 API와 마찬가지로) 영원히 지원해야 하는 것이다.

-> 다른 JVM에서 구동되는 프로그램에서 직렬화된 객체를 받아 처리하는 과정을 API라고 표현한 것 같음
직렬화의 목적을 보면 내부뿐만 아니라 다른 외부 JVM 프로그램에서도 직렬화한 객체를 참조할 수도 있으니, 수정이 까다롭다.

직렬화: 데이터 구조나 오브젝트 상태를 동일하거나 다른 컴퓨터 환경에 저장(이를테면 파일이나 메모리 버퍼에서, 또는 네트워크 연결 링크 간 전송)하고, 나중에 재구성할 수 있는 포맷으로 변환하는 과정

2. 버그와 보안 구멍이 생길 위험이 높아진다는 점이다.

객체는 생성자를 사용해 만드는 게 기본이다. 즉, 직렬화는 언어의 기본 메커니즘을 우회하는 객체 생성 기법인 것이다.

기본 방식을 따르든 재정의해 사용하든, 역직렬화는 일반 생성자의 문제가 그대로 적용되는 '숨은 생성자'다.

-> 보안 이슈를 가지게 된다.

3. 해당 클래스의 신버전을 릴리스할 때 테스트할 것이 늘어난다는 점이다.

직렬화 가능 클래스가 수정되면 신버전 인스턴스를 직렬화한 후 구버전으로 역직렬화할 수 있는지, 그리고 그 반대도 가능한지를 검사해야 한다.

따라서 테스트해야 할 양이 직렬화 가능 클래스의 수와 릴리스 횟수에 비례해 증가한다.

-> 양방향 직렬화/역직렬화가 모두 성공하는지, 원래의 객체를 충실히 복제해내는지까지 테스트 해야하기 때문에 테스트 비용이 증가한다.

결론

한 클래스의 여러 버전이 상호작용할 일이 없고 서버가 신뢰할 수 없는 데이터에 노출될 가능성이 없는 등, 보호된 환경에서만 쓰일 클래스가 아니라면 Serializable 구현은 아주 신중하게 이뤄져야 한다.

Serializable을 구현하면 안되는 경우

상속 목적으로 설계된 클래스와 대부분의 인터페이스는 Serializable을 구현하면 안된다.

-> 클래스를 확장하거나 인터페이스를 구현하는 대상에게 앞서 살펴본 위험성을 그대로 전이하기 때문에, 클래스를 상속받거나 인터페이스를 구현하는 사람에게 비용이 증가된다.

하지만 Serializable을 구현한 클래스만 지원하는 프레임워크를 사용해야 한다면 Serializable를 사용해야한다. ->대표적으로 Throwable 클래스가 있는데 서버가 RMI를 통해 클라이언트로 예외를 보내기 위해 Serializable을 구현하였다.

만일 직렬화와 확장이 모두 가능한 클래스를 만든다면, 하위 클래스에서 finalize 메서드를 재정의하지 못하게 해야 한다.

간단하게 자신이 재정의하고 final 키워드를 붙이면 된다. 인스턴스 필드 중 기본값으로 초기화되면 위배되는 불변식이 있다면 아래와 같은 메서드를 추가해야 한다.

private void readObjectNoData() throws InvalidObjectException {
    throw new InvalidObjectException("스트림 데이터가 필요합니다.");
}

RMI(Remote Method Invocation): 원격에 있는 시스템 메서드를 로컬 시스템의 메서드인 것처럼 호출하는 것을 말하며, 메서드의 파라미터로 객체를 전달할 때, 그 객체를 자동으로 직렬화시켜 전달합니다.

finalize(): Garbage Collector를 사용해 힙 영역에서 객체를 삭제하는 기능을 수행하는 메서드