질문 배경
자바가 처음 등장한 이래로, 애플리케이션 세션 간에 객체를 저장하는 문제가 발생했습니다. 네트워크를 통해 전송하거나 데이터베이스 또는 디스크에 기록하는 등의 작업을 위해 Serializable 인터페이스가 고안되어 객체를 바이트 스트림으로 변환하고 다시 변환할 수 있게 되었습니다 (직렬화 및 역직렬화).
문제점
클래스가 Serializable 인터페이스를 구현하지 않으면, 해당 인스턴스를 직렬화하려 할 때 예외가 발생합니다. 더군다나, 직렬화는 필드 값뿐만 아니라 그 상태도 저장하므로 잘못된 구현은 예기치 않은 취약성, 객체 복구 오류, 데이터 손실을 초래할 수 있습니다.
해결책
Serializable 인터페이스는 마커 인터페이스입니다. 즉, 메서드를 포함하지 않습니다. 올바른 직렬화를 위해서는 serialVersionUID를 명시적으로 선언하고, 직렬화되지 않아야 할 필드에 대해 transient 키워드를 사용하는 것이 좋습니다.
코드 예시:
import java.io.Serializable; public class User implements Serializable { private static final long serialVersionUID = 1L; private String username; private transient String password; // 직렬화되지 않음 public User(String username, String password) { this.username = username; this.password = password; } }
주요 특징:
transient 사용으로 특정 필드의 직렬화를 방지serialVersionUID를 반드시 명시적으로 선언해야 하나요?
아니요, 필수는 아니지만 선언하지 않으면 자동으로 생성됩니다. 그러나 클래스에 변경이 있을 경우 이전 직렬화된 버전을 복구하는 과정에서 InvalidClassException이 발생할 수 있습니다. 따라서 이 필드는 항상 명시적으로 선언하는 것이 좋습니다.
정적 필드는 직렬화되나요?
아니요, 정적 필드는 직렬화되지 않고, 인스턴스 필드만 직렬화됩니다. 정적 필드는 클래스에 속하며, 객체에 속하지 않기 때문에 직렬화 및 역직렬화 시 값이 저장되거나 복구되지 않습니다.
Serializable을 구현하지 않는 필드를 가진 객체를 직렬화할 수 있나요?
아니요, 비정적 필드 중 하나라도 직렬화되지 않거나 transient로 선언되어 있지 않으면 직렬화 시 NotSerializableException 예외가 발생합니다.
transient 없이 민감한 데이터 직렬화 (예: 비밀번호)사용자 캐시 애플리케이션에서 serialVersionUID를 명시하지 않고, 코드 수정을 통해 User 클래스의 구조를 변경했습니다. 애플리케이션 시작 시 InvalidClassException이 발생하고 모든 직렬화된 데이터가 손실되었습니다.
장점:
단점:
유사한 프로젝트에서 항상 serialVersionUID를 명시하고 직렬화되지 않는 모든 필드를 transient로 설정했습니다. 이 덕분에 구조가 미세하게 변경된 후에도 직렬화된 객체가 성공적으로 로드되었습니다.
장점:
단점: