객체 또는 데이터 구조를 저장하거나 전송 가능한 형식으로 변환하는 과정을 의미한다.
직렬화는 본래, 자바 언어에서만 활용되는 개념이 아니며, 자바 직렬화의 경우에는 JVM
이 메모리에 상주하고 있는 객체 데이터를 바이트 형태로 변환시켜, 포맷 결과를 필요한 용도에 맞게 사용하는 기술을 의미한다.
역직렬화는 직렬화의 반대 개념으로 직렬화된 데이터를 자바 언어의 객체나 데이터 구조로 변화하는 과정을 의미한다. 자바 역직렬화는 직렬화된 바이트 형태의 데이터를 자바의 객체 혹은 데이터 구조로 변환하여 JVM
에 상주시키는 형태를 의미한다.
바이트스트림
클라이언트나 서버간에 출발직 목적지로 입출력하기 위한 데이터가 흐르는 통로를 의미한다. 자바는 스트림의 기본단위를 바이트로 두어, 네트워크, 데이터베이스로 전송하기 위해 최소 단위인 바이트 스트림으로 변환하여 처리한다.
일반적으로 Serializable
인터페이스를 구현하는 경우 사용이 가능하다. 단 참조 객체를 담고 있는 클래스의 경우, 해당 참조 객체도 역시 Serializable
인터페이스가 구현된 상태여야 온전한 자바 직렬화가 가능해진다. 이는 목적은 다르지만 JPA
에서 보았던 연관관계의 설정과 매우 유사한 구조를 가지고 있다.
물론 자바가 기본적으로 제공하는 참조 타입(String, Collections 등) 은 대부분Serializable
이 적용되어 있어 바로 사용이 가능하다.
public class User implements Serializable {
private int age;
private long id;
private Team team; // 안됨.
}
public class Team {
private long teamId;
private String teamName;
}
만약 참조하는 객체 가운데 직렬화 적용이 불가능한 경우에서 관련된 객체를 직렬화해야하는 경우에는 transient
를 활용하여 해당 참조 타입에 제외시키고 직렬화를 이루는 것이 가능하다.
public class User implements Serializable {
private int age;
private long id;
private transient Team team; // 이건 됨. 단 team 정보는 제외된다.
}
public class Team {
private long teamId;
private String teamName;
}
객체의 직렬화에 사용되는 버전 식별자를 나타낸다. 예를들어 User
클래스와 이를 직렬화하여 저장한 파일이 있다고 하자.
만약 User
클래스를 업데이트하여, 클래스의 구조가 변경되는 경우, 저장한 파일을 역직렬화 할 때 구조가 달라 제대로 변경이 되지 않는 문제가 발생할 수 있다. 이를 위해 변경사항들을 반영한 클래스의 버전 식별자를 직렬화와 함께 저장을 하는 것으로 현재 클래스와 직렬화된 파일 간의 호환성 여부를 확인하는 것이 가능하다. 만약 변환한 데이터와 클래스 간의 SerialVersionUID
가 서로 다르다면, InvalidClassException
을 반환하며, 역직렬화가 이루어지지 않는다.
SerialVersionUID
는 기본적으로 Serializable
인터페이스를 구현하면 클래스의 기본 해시값을 사용하여 SerialVersionUID
값을 생성한다. 물론 개발자가 직접 명시적으로 정의하여 SerializableUID
를 설정하는 것도 가능하다.
자바 직렬화는 자바의 고유 기술인 만큼 자바 시스템에 초점을 맞추어 최적화를 이뤘다. 서로 다른 개발환경을 가진 자바 데이터의 경우에도 둘다 동일한 자바 시스템을 갖추고 있다면, 직렬화와 역직렬화를 통해 데이터를 서로 전송하는 것이 가능하다. 이러한 점은 자바가 플랫폼 독립적인 특징을 더욱 강조하는 부분이라 할 수 있다.
자바는
JVM
을 통해 주어진 코드를 각 개발환경에 맞게 실행할 수 있다는 플랫폼 독립성을 가진 언어이다. 자바 언어가 등장할 당시 다른 개발환경 간 나타나는 호환성 문제 때문에, 이러한 특성을 지닌 자바가 인기가 많아지는 이유가 되기도 했다.
더불어 자바에서 사용하는 List, Set, Map 등과 같이 다양한 컬렉션이나 클래스, 인터페이스들을 별도의 설정없이 그대로 외부로 보내는 것이 가능하다. (물론 다른 언어와는 호환이 안되고, 자바 시스템에만 호환이 될 것이다.)
직렬화는 객체에 저장된 데이터값 외에도 타입 정보, 클래스 메타 정보 등을 함께 저장하기 때문에, 데이터 용량이 큰 편에 속한다. 실무에서 많이 사용하는 데이터 전송 포맷인 JSON
과 비교하면 동일 자원 저장 대비 2배 이상의 차이가 난다고 한다.
클래스가 참조하고 있는 객체도 Serializable
이 적용된 경우라면 함께 직렬화, 역직렬화를 실행하므로, 객체간의 연관관계가 많아지면 많아질수록, 직렬화, 역직렬화 과정에 필요한 연산은 기하급수적으로 많아진다.
더불어, 자바 직렬화 자체는 클래스 객체를 변환시켜 외부에 저장하거나 전송을 하는 목적으로 사용되기 때문에 공개적인 특성을 가지게 된다. 이러한 특성 때문에, 릴리즈 이후에는 클래스 변경이 어려워 유지보수가 어렵고, OOP
특징인 클래스 캡슐화의 의미가 없어진다.
역직렬화 시 버그나 보안에 취약한 특성을 가지기도 한다. 역직렬화는 기존 직렬화된 데이터를 바탕으로 새로운 객체를 생성하는 과정인 만큼, 생성자를 통해 악의적인 코드를 실행한다던지, 허가되지 않은 접근을 푸는 등, 서비스 환경에 공격이 이뤄질수 있다.
참고
☕ 자바 직렬화(Serializable) - 완벽 마스터하기
해피쿠 블로그 - [Java] 직렬화(Serialization)에 대해 알아보자
[Java] 직렬화(Serialization)란 무엇일까?
유원우 - Java 직렬화/역직렬화 | 백엔드 데브코스 4기 | 20230621