오늘의 키워드
직렬화, serialVersionUID
직렬화는 데이터의 구조나 객체의 상태를 지속되게 만드는 프로세스라고 한다.
JVM의 메모리에 저장된 객체가 네트워크나 데이터베이스와 통신하기 위해서는 서로 다른 시스템 간에 이해할 수 있는 형태로 변환이 필요하다. 즉, 연속적인 바이트 형태로 변환이 필요하다고 한다.
직렬화(Serialization)
: 객체를 연속적인 데이터로 변환하는 것
( 반대는 역직렬화: 스트림으로부터 데이터를 읽어서 객체를 만드는 것)
자바 프로그래밍의 직렬화(Serialization)는 객체의 상태를 바이트 스트림(stream of bytes) 으로 변환하는 메커니즘을 사용한다. 역직렬화(Deserialization)는 바이트로 변환된 데이터를 원래대로 실제 Java 객체를 메모리에 재생성하는 반대 과정이다.
바이트 스트림: 직렬화를 통해 객체를 이진 형태로 변환
- 객체를 데이터 스트림으로 만드는 것
- (객체에 저장된 데이터를 스트림에 쓰기위해 연속적인 데이터로 변환하는 것)
- 객체의 인스턴스 변수들의 값을 일렬로 나열하는 것

💡 객체를 저장한다는 것?
객체는 클래스에 정의된 인스턴스 변수의 집합 → 클래스변수나 메서드가 포함되어있지 않다.
⇒ 객체를 저장한다는 것은 모든 인스턴스변수의 값을 저장한다는 것이다.
저장했던 객체를 다시 생성하려면, 객체 생성 후 저장했던 값을 읽어서 생성한 객체의 인스턴스 변수에 저장하면 된다.인스턴스 변수의 타입이 참조형이라면 모두 저장하기가 어려움 → ObjectInputStream , ObjectOutputStream 사용
ObjectInputStream , ObjectOutputStream
ObjectInputStream은 java.io.Serializable인터페이스를 지원하는 객체만 스트림에서 생성되어 읽을 수 있으며, 해당 스트림으로부터 들어온 객체 유형이 JVM에 있는 클래스와 일치하는지 확인한다.
ObjectInputStream은 이전에 ObjectOutputStream을 사용해서 바이트로 변환된 직렬화 객체 데이터를 읽는데 사용된다.
만약 클래스의 버전이나 구조 등이 변경되어 호환성에 문제가 생긴다면 역직렬화 도중에 InvalidClassException와 같은 예외가 발생할 수 있다.(아래쪽에서 확인)
- 직렬화(스트림에 객체를 출력) - ObjectOutputStream 사용
- 역직렬화(스트림으로부터 객체를 입력) - ObjectInputStream 사용
- 각각 InputStream과 OutputStream을 직접 상속
- 보조스트림이다 → 객체 생성 시 입출력(직렬화/역직렬화)할 스트림 지정해야 한다.
ObjectInputStream(InputStream in)
ObjectOutputStream(OutputStream out)
📌 writeObject: 직렬화할 때
직렬화하려는 클래스가 Serializable 인터페이스를 구현하지 않았거나, 직렬화할 수 없는 멤버 변수가 있다면 NotSerializableException이 발생한다.
📌readObject: 역직렬화할 때
클래스가 변경되었거나 readObject 메서드에서 예외가 발생하여 객체 복원이 실패하면 WriteAbortedException이 발생한다.
- 직렬화 - 객체를 파일에 저장하는 방법
FileOutputStream fos = new FileOutputStream("objectflie.ser");
//기반스트림 - 출력할 스트림 생성
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(new userlnfo());
// 객체 출력시 객체가 파일에 직렬화되어 저장
// objectfile.ser이라는 파일에 UserInfo객쳬를 직렬화하여 저장
- 역직렬화 - 파일에 저장된 객체를 다시 읽어오는 방법
FileInputStream fis = new FilelnputStream("objectfi1e.ser");
ObjectInputStrearn in= new ObjectInputStream(fis);
UserInfo info = (UserInfo)in.readObject();
//반환 타입이 Object라서 객체 원래의 타입으로 형변환 해주어야함.
- ObjectInputStream 과 ObjectOutputStream 메서드
- writeObject와 readObject를 오버라이딩하면 직렬화를 마음대로 할 수 있다.

직렬화 가능한 클래스 만들기 - Serializable
자바에서는 직렬화를 할 때, 아래의 Serializable 인터페이스를 구현해서 사용한다.
- 직렬화가 가능한 클래스를 만드는 방법
- 직렬화하고자 하는 클래스가 java.io.Serializable인터페이스를 구현하면 된다.
public interface Serializable {}
//빈 인터페이스
//직렬화를 고려하여 작성한 클래스인지를 판단하는 기준이 됨
transient, static 변수는 직렬화 제외
- tansient예약어를 선언한 변수는 Serializable의 대상에서 제외된다.
패스워드를 보관하고 있는 변수가 있다고 가정했을때, 이 변수가 외부 저장소에 저장되거나, 네트워크로 전송된다면 보안 상 큰 문제가 발 생할 수 있기 때문이다. - static은 객체의 상태가 아닌 클래스의 상태이기 때문에 직렬화 과정에서 포함되지 않는다.
serialVersionUID: 직렬화가능한 클래스의 버전관리
- 역직렬화를 할 때 클래스의 내용이 변경된 경우 역직렬화 실패
- 예외
java.io.InvalidClassException: UserInfo;
local class incompatible:
stream classdesc serialVersionUID = 6953673583338942489,
local class serialVersionUID = -6256164443556992367
============
직렬화 할 때의 클래스 버전과 역직렬화 할 때의 클래스 버전이 다르다는 것
- 직렬화 할때 클래스에 정의된 멤버들의 정보를 이용해 클래스버전(serialVersionUID)라는 클래스의 버전을 자동생성해서 내용에 포함한다.
- 역직렬화 할때 버전을 비교해서 확인한다.
- 클래스 버전을 수동으로 관리하려면 클래스 내에 serialVersionUID를 정의 해야한다.
class MyData implements java.io.Serializable {
static final long serialVersionUlD = 3518731767529258119L;
//이렇게 정의하면 클래스 내용 바뀌어도 자동 변경 안됨.
// 서로다른 클래스간 같은 값을 갖지 않도록
// serialver.exe를 이용해 생성된 값을 사용하는 것이 보통 int valuel;
}
하지만 클래스의 구조가 변경되어서(변수명 변경) 이전 버전의 클래스로 저장된 객체를 현재 클래스로 역직렬화하면 데이터 유실이 발생할 수 있다.
(변수명이 변경(a →c)되었기 때문에 이전 버전에서 사용된 변수명을 찾을 수 없기 때문)
따라서 클래스를 생성할 때부터 serialVersionUID를 명시적으로 선언하해야지 클래스의 구조가 변경되어도 호환성을 유지할 수 있다.
이 serialVersionUID은 클래스마다 다른 값을 가져야 다른 클래스와 충돌이 나지 않는다.
그래서 개발자가 임의로 값을 정하는 것보다 serialVersionUID값을 자동으로 생성시켜주는 serialver.exe의 명령어를 사용하는게 좋다고한다.
참고:
왜 자바 직렬화는 Serializable 인터페이스가 있어야할까?
자바의 신 책에는 DTO에 Serializable를 꼭 구현해야 된다고 되어 있다. 하지만 나는 Spring boot 프로젝트를 했을때 한번도 구현하지 않았는데도, 문제가 없었던것 같다. 어떤 관련이 있는지 "직렬화"라
strong-park.tistory.com
'TIL' 카테고리의 다른 글
| [TIL] 2025.05.12 (0) | 2025.05.12 |
|---|---|
| [TIL] 2025.03.10 서브모듈, 서브트리 (0) | 2025.03.10 |
| [TIL] 2025.03.05 B트리 (0) | 2025.03.05 |
| [TIL] 2025.03.04 옵저버(Observer) 패턴 (0) | 2025.03.04 |
| [TIL] 2025.02.28 아키텍처 (0) | 2025.02.28 |