CS 스터디 (Java) 12 - 직렬화에서 SerialVersionUID 를 선언해야하는 이유는?
SerialVersionUID를 선언하는 이유는 직렬화된 객체를 역직렬화할 때 클래스의 버전이 같은지 확인하기 위해서이며, 선언하지 않으면 클래스 구조가 바뀔 때 역직렬화 오류(InvalidClassException)가 발생할 수 있기 때문입니다.
왜 SerialVersionUID가 중요한가?
- 직렬화(Serialization)는 자바 객체의 상태를 바이트 스트림으로 변환하여 저장하거나 전송할 수 있게 해주는 핵심 기능입니다.
- 특히 웹 애플리케이션에서 세션을 공유하거나, 캐시, 메시지 브로커 등을 사용할 때 자주 사용됩니다.
- 이러한 환경에서는 객체를 직렬화한 후 역직렬화하는 과정이 빈번하게 발생하므로, 객체 구조가 변경되어도 안정적인 호환성을 유지하는 것이 중요합니다.
직렬화는 무엇을 기준으로 판단하는가
- 자바 직렬화에서 중요한 사실은, 클래스가 변경되더라도 serialVersionUID 값이 동일하면 JVM은 역직렬화를 시도합니다.
- JVM은 직렬화된 데이터를 역직렬화할 때, 클래스 이름, 패키지, 필드 구조를 비교하지 않습니다.
- 오직 serialVersionUID 값이 저장된 클래스와 현재 클래스 간에 일치하는지만을 판단합니다.
- 따라서 클래스 구조가 완전히 같더라도 UID가 다르면 역직렬화기 실패(InvalidClassException)합니다.
Spring Security 버전 업 시 직렬화 호환성 주의사항
- Spring Security는 세션에 인증 정보를 직렬화된 객체 형태로 저장하고, 이후 요청에서 해당 정보를 역직렬화하여 인증 상태를 유지합니다. 이때, 프레임워크를 업그레이드할 경우 내부 클래스(SecurityContextImpl, Authentication, User 등)의 serialVersionUID가 변경되면 기존 세션 데이터를 역직렬화하지 못해 InvalidClassException이 발생할 수 있습니다.
호환 가능한 클래스 변경
- 필드 추가: 새 필드는 기본값으로 초기화되어 역직렬화 가능
- 클래스 제거: 삭제된 클래스의 필드는 무시되거나 기본값으로 처리
- Serializable 구현 추가: 새로 직렬화 가능, 역직렬화 시 기본값으로 초기화
- 접근 지정자 변경: 접근 수준 변화는 영향 없음
- static/transient → 일반 필드: 새 필드로 간주되어 기본값 처리
호환 불가능한 클래스 변경
- 필드 제거: 기존 데이터에 있던 필드를 찾지 못해 오류 가능
- 클래스 위치 변경: FQCN이 달라져 역직렬화 실패
- 일반 필드 → static/transient: 필드가 직렬화되지 않아 손실 발생
- 필드 타입 변경: 타입 불일치로 역직렬화 실패
- Serializable 구현 제거: 더 이상 직렬화 불가, 호환성 상실
결론
직렬화에서 serialVersionUID를 선언해야 하는 이유는, 자바 직렬화 메커니즘이 객체를 역직렬화할 때 클래스의 호환성을 판단하는 기준이 클래스 구조가 아니라 serialVersionUID 값이기 때문입니다. 만약 이 값을 명시적으로 선언하지 않으면, 컴파일러가 클래스 구조에 따라 자동으로 UID를 생성하는데, 이는 클래스에 사소한 변경이 있어도 UID가 변경될 수 있어 동일한 클래스임에도 불구하고 역직렬화가 실패하고 InvalidClassException이 발생할 수 있습니다. 특히 웹 애플리케이션에서 세션, 캐시, 메시지 큐 등 직렬화가 사용되는 다양한 환경에서는 클래스 변경이 빈번하게 일어나므로, serialVersionUID를 명시적으로 선언하여 버전 호환성을 명확히 관리하는 것이 안정적인 시스템 운영에 필수적입니다.