serialVersionUID와 InvalidClassException
[!info] 직렬화, 역직렬화는 보통 json을 이용해서 처리하게 되고, 그렇게 하는 것이 좋아보인다.
json이라는 명확한 표준이 있어 이식성도 좋고, 변환 로직도 심플해서 아래와 같은 잠재적인 문제를 피할 수 있기 때문이다.
- 어떤 사정으로 인해 json serialization 하지 못하는 경우, java에서는 Serializable 구현하고 이를 이용해서 직렬화 하게 되는데…
- 직렬화/역직렬화 시, 클래스와 객체의 동일성 판단이 json에 비해 매우 민감하기 때문에 주의해야 한다.
1
2
3
4
5
6
7
8
import dev.umbum.Address;
// 변경 전 User
public class User implements Serializable {
private String name;
private String email;
private Address address;
}
위 User
클래스의 객체를 직렬화 해서 redis에 올린 다음, Address
의 위치가 변경되어(내용은 변경되지 않았다) import
주소를 아래와 같이 변경하면, 역직렬화 시 InvalidClassException
발생한다.
1
2
3
4
5
6
7
8
import dev.umbum.model.Address;
// 변경 후 User
public class User implements Serializable {
private String name;
private String email;
private Address address;
}
- 필드가 아무것도 변경되지 않았기 때문에 역직렬화 성공할 것이라고 예상하지만,
import
구문만 달라져도 역직렬화에 실패한다. int
->long
으로 변경하는 것도 json이라면 호환이 되지만, java 직렬화는InvalidClassException
발생한다.
호환이 된다/안된다 판단은? serialVersionUID
참고
- 호환 가능 판단은
serialVersionUID
를 보고 판단하게 되는데, 명시적으로 지정하지 않더라도Serializable
인터페이스가 있으면 컴파일러가 알아서 계산하게 된다.- 클래스, 필드, 메서드 등 종합적으로 보고 컴파일러가 계산하며 컴파일러 세부 구현에 따라 값이 또 다를 수 있다는게 단점이다.
import
구분을 변경하거나, type을 변경하는 등 Incompatible changes가 있었다면, 생성되는serialVersionUID
값이 달라지게 된다.- e.g.
-2649918656647101333 -> 3656463606950018647
로 변경
- e.g.
- 어떤 변경이
serialVersionUID
값을 달라지게 만드는지는, compatible, Incompatible 변경 spec 참고 - 그래서 docs 에서는 아예,
serialVersionUID
를 지정해서 처리하는 것을 권장하고 있다.
It is strongly recommended that all serializable classes explicitly declare
serialVersionUID
values, since the defaultserialVersionUID
computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpectedserialVersionUID
conflicts during deserialization, causing deserialization to fail.
직접 serialVersionUID를 생성 할 수 있는 방법이 있을까?
This post is licensed under CC BY 4.0 by the author.