cleanUrl: /posts/implement-serializable-with-great-caution-effective-java-86
share: true
자바에 대해 무언가 궁금한 것이 있다면 역시 Effective Java 를 보는 것이 확실한것 같다. 이 item 은 정말 Serializable 을 구현해야 하는지, 무엇이 필요한지, 주의할 것은 무엇인지를 간단히 살펴본다.
<aside>
💡 직렬화: 데이터를 Stream 으로 전송할 수 있는 상태로 만든다 (ex. 파일)
역직렬화: Stream 으로 전송 받은 데이터를 객체로 만든다
</aside>
- 어떤 클래스에 직렬화를 구현하고자 하면 Serializable 을
implements Serializable
만 붙이면된다.
- 매우 간단해 보이지만 그렇지 않다.
- 짧게 보면 쉽게 만들 수 있지만, 길게 본다면 값비싼 일이다.
- Serializable 을 구현하면 릴리즈 이후에 수정이 어렵다(여기서 릴리즈는 서버 릴리즈가 아님)
- HashMap java 11
- HashMap java 1.8
- Serializable 을 구현하면 byte stream encoding 도 하나의 공개 API 가 된다.
private
, package-private
인스턴스 필드들도 API 로 공개하는 꼴이 된다. (캡슐화 깨짐)
- 그래서 영원히 관리해야 하는 대상이 된다.
- 시간이 지나 내부 구현을 수정하게 되면 원래의 직렬화 형태와 달라지게 된다.
- 만약 한 쪽은 구버전 instance 를 직렬화 하고, 한쪽은 신버젼 클래스로 역직렬화 하는 일이 발생된다면..?
- 직렬화를 구현하고자 한다면 감당할 수 있을 만큼의 고품질의 직렬화 형태로 설계해야 한다.
<script async src="<https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js>"></script>
<ins class="adsbygoogle"
style="display:block; width: 100%;"
data-ad-format="fluid"
data-ad-layout-key="-fb+5w+4e-db+86"
data-ad-client="pub-8946038251809377"
data-ad-slot="5490836264"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
직렬화가 클래스 개선을 방해하는 예시들
직렬 버젼 UID(serial Version ID)
- 모든 직렬화된 클래스는
serialVersionUID
라는 이름의 static final long
필드의 식별 번호를 부여받는다.
- 이 번호를 명시하지 않으면 runtime 에서 sha-1 을 적용하여 자동으로 클래스에 집어넣는다.
- 클래스 이름, 구현한 interface, 컴파일러가 자동 생성하여 넣는 것을 포함하며 대부분의 class member 가 대상이다.
- 그래서 나중에 method 를 추가하여 이들중 하나라도 수정되면 UID 도 깨지게 되므로 자동생성보단 직접 넣는것이 좋다.
버그와 보안 구멍이 생길 위험이 높아진다
- 직렬화는 language 의 매커니즘을 우회하는 객체 생성 기법이다.
- 역직렬화는 일반 생성자의 문제가 그대로 적용되는 '숨은 생성자' 이다.
- 이 역직렬화 생성자는 전면에 드러나지 않기 때문에 생성자에 적용되는 규칙을 적용하겠다고 생각하기 어렵게 된다.
- 생성자에서 구축한 불변식을 모두 보장해야 한다
- 생성 도중 공격자가 객체 내부를 들여다볼 수 없도록 해야 한다.
- 역직렬화를 이용하면 불변식은 깨지고, 허가되지 않은 접근에 노출된다.
클래스의 신버전을 일리즈 할때 테스트할 것이 늘어난다.
- 신버젼 instance 를 직렬화 하고, 구버젼으로 역직렬화가 되는지 (하위 호환성) 검사가 필요하다