# 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를 반드시 추가해야 한다.

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

### Serializable을 구현하지 않는 상위 클래스

* 상속용 클래스에서 직렬화를 지원하지 않지만, 하위 클래스에서 직렬화를 지원하려 한다면 부담이 늘어난다.
* 이런 클래스를 역직렬화하려면 상위 클래스의 매개변수가 없는 생성자를 제공하거나, 하위 클래스에서 직렬화 프록시 패턴을 사용해야 하기 때문이다.

### 내부 클래스와 직렬화

* 내부 클래스에는 바깥 인스턴스의 참조와 유효 범위 안의 지역변수 값들을 저장하기 위해 컴파일러가 생성한 필드들이 **자동으로 추가**되어 기본 직렬화 형태가 유동적이다.
* 정적 멤버 클래스는 컴파일러에 의해 자동 추가되는 필드가 따로 없으므로 Serializable을 구현해도 괜찮다.
