-
JAVA 직렬화(Serializable)란?JAVA&SPRING 2020. 6. 17. 14:07
-
우리가 SPRING 개발을 하다 보면 클래스에 implements Serializable 되어있는 VO(Value Object)가 많이 보았을 것이다.
-
그냥 남들이 쓰니까 써야 한다고 생각하고 쓰는 개발자도 있을 것이고 나 또한 그랬는데 Serializable를 왜 쓰느냐라는 얘기를 듣고 대답할 수 없어서 직렬화에 대해서 공부를 했다.
✔ 직렬화
- 자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 바이트 형태로 데이터를 변환하는 기술로써, JVM의 메모리에 상주되어 있는 객체 데이터를 바이트 형태로 변환하는 기술이다.
- 다차원의 자료를 파일로 저장하거나 네트워크로 보내기에 알맞게 일차원으로 펼치고 다시 원래대로 되돌리는 것을 직렬화(serialization)이라고 부른다.
- 역직렬화는 직렬화의 반대를 의미한다.(바이트 => data, object)
🔸 직렬화의 종류
- 직렬화의 종류에는 직렬화되는 자료가 어떤 종류인지를 나타내는 데이터 모델이 있고, 이 데이터 모델이 언제 결정되느냐에 따라서 두 종류로 나눌 수 있다.
◼ schematic
-
데이터 모델이 고정되어 있어서 나중에도 별로 바뀌지 않는다는 걸 알고 있고, 직렬화 포맷을 데이터 모델마다 생성해서 쓸 수 있다.
-
schematic에는 프로토콜 버퍼나 Cap'n Proto가 있다.
-
schematic 한 직렬화 방법은 성능을 우선하느냐와 데이터 크기를 우선하느냐에 따라 나눌 수 있는데 어느 쪽이든 schemaless 한 직렬화보다는 효율적이다.
◼ schemaless
-
대부분의 데이터 모델을 포함하는 범용 데이터 모델을 쓰는 접근이다.
-
재귀가 없고 순서가 있는 순열 및 순서가 없는 집합만 가지고 있는 것들로 JSON이나 MessagePack 같은 것들에 해당한다.
-
XML이 DTD나 XML Schema 같은 것과 결합해서 스키마가 존재하게 되면 schematic 하고 그렇지 않으면 schemaless 하다고 할 수 있다.
📌 더 자세한 내용은 https://j.mearie.org/post/122845365013/serialization 참조
✔ 자바에서의 직렬화(Serializable)
-
자바 직렬화는 개발에 최적화되어 있기 때문에 복잡한 데이터 구조의 클래스의 객체라도 직렬화 기본 조건만 지키면 큰 작업 없이 바로 직렬화를 사용할 수 있다.
-
자바에서 직렬화는 JVM 메모리에서만 상주되어있는 객체 데이터를 그대로 영속화가 필요할 때 사용된다.
-
이러한 장점 때문에 네트워크 전송도 가능하고 필요할 때 직렬화된 객체 데이터를 가져와서 역직렬 화하여 객체를 바로 사용할 수 있게 해 준다.
◼ 서블릿 세션(Servlet Session)
- 서블릿 기반의 WAS(톰캣, 웹로직 등) 대부분 세션의 자바 직렬화를 지원한다.
- 파일로 저장하거나 세션 클러스터링, DB에 저장하는 옵션 등을 선택하게 된다면 세션 자체가 직렬화 되어 저장되어 전달된다.
◼ 캐시(Cache)
- 캐시 할 부분을 자바 직렬화된 데이터를 저장해서 사용된다.
◼ RMI(Remote Method Invocation)
- RMI는 원결 시스템 간의 메시지 교환을 위해서 사용하는 자바에서 지원하는 기술이다.
- 원격의 시스템의 메서드를 호출 시에 전달하는 메시지(객체)를 자동으로 직렬화 시켜 사용된다.
🔸 자바의 Serializable
- 자바에서 java.io.Serializable 인터페이스를 보면 구현해야 하는 메서드가 없다.
- 그 이유는 Serializable 인터페이스를 구현한 구현체가 직렬화 대상이다라는 것을 JVM에게 알려주는 역할만 하기 때문이다.
- 때문에 이 상황에서 불필요한 직렬화는 오버헤드의 원인이 되기도 하지만, Serializable은 직렬화 작업을 하는 것이 아니라 단지 해당 클래스의 인스턴스는 직렬화할 수 있다는 것을 선언하는 것 뿐이기 때문에 문제가 되지 않는다. (참고로, 자바의 ArrayList나 HashMap 등 기본 컬렉션 구현체가 모두 Serializable을 구현하고 있는 것도 비슷하다.)
public class MemberVo implements Serializable{ private static final long serialVersionUID = 2868210232929931052L; private String memName; private String memPass; private String memPhone; private int memAge; ... }
◼ serialVersionUID가 필요한 이유
-
직렬화 과정에서 serialVersionUID의 버전이 포함되게 되고, 역직렬화 과정에서 Java class에 선언되어 있는 serialVersionUID의 버전과 서로 동일한지를 체크를 한다.
1. serialVersionUID는 기본적으로 필수 값은 아니다.
2. 호환 가능한 클래스는 serialVersionUID값이 고정되어 있다.
3. serialVersionUID가 선언되어 있지 않으면 클래스의 기본 해시값(SHA-1)을 사용한다. (해시 알고리즘은 참고 링크에)-
◾ Serializable클래스가 serialVersionUID를 명시적으로 선언하지 않으면 내부적으로 serialVersionUID 정보가 추가된다.
-
◾ 그러나 기본 serialVersionUID 계산은 컴파일러 구현에 따라 달라질 수 있는 클래스 세부 사항에 매우 민감하므로 예기치 않은 결과를 초래할 수 있으므로 모든 Serializable 클래스는 serialVersionUID 값을 명시적으로 선언하는 것이 좋다. (참고)
-
◾ 잠재적으로 네트워크를 넘나들거나 파일로 저장할 가능성이 조금이라도 있는 클래스는 기본적으로 Serializable을 구현해서 의도를 명확하게 밝히는 것이 좋은 개발 습관이다.
◼ serialVersionUID 버전 체크를 하는 이유
-
◾ 버전이 바뀌면 객체의 상태가 조금이라도 바뀌었다는 것을 의미하고 결국 역직렬화 과정에서 오류가 발생한다.
-
◾ 때문에 클래스를 변경할 때에 직접 serialVersionUID 값을 관리해주어야 클래스 변경 시 혼란을 줄일 수 있다.
-
◾ serialVersionUID 값이 동일할 때 문제가 발생하는 경우 (굉장히 엄격하다.)
-
멤버 변수명은 같은데 멤버 변수 타입이 바뀔 때
public class MemberVo implements Serializable{ private static final long serialVersionUID = 2868210232929931052L; private String memName; private String memPass; // StringBuilder로 변경 private StringBuilder memPhone; private int memAge; ... }
-
원시 타입으로 변경
public class MemberVo implements Serializable{ private static final long serialVersionUID = 2868210232929931052L; private String memName; private String memPass; private String memPhone; // int => long private long memAge; ... }
-
◾ serialVersionUID 값이 동일하면 멤버 변수 및 메서드 추가는 크게 문제 되지 않는다. 그리고 멤버 변수 제거 및 이름 변경은 오류는 발생하지 않지만 데이터는 누락된다.
public class MemberVo implements Serializable{ private static final long serialVersionUID = 2868210232929931052L; private String memName; private String memPass; private String memPhone; // 추가 private String memBirth; // 제거 //private int memAge; ... }
-
하지만, 위 경우에도 구버전에서 신버전 클래스를 읽는 경우에 문제를 일으킬 수 있다고 비호환적이라고 한다.
(📌 참고 : https://stackoverflow.com/questions/16261383/delete-field-from-old-java-class-implementing-serializable#answer-16261562)
◼ 호환성
- 자바 직렬화를 이용해서 외부 데이터를 저장하면 자바에서만 사용가능하다.
- 긴 만료시간을 가진 데이터는 JSON등 다른 포맷을 사용하여 저장한다.
- 📌 참고 : https://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html
'JAVA&SPRING' 카테고리의 다른 글
JAVA Spring의 @SuppressWarnings 어노테이션 (0) 2020.06.16 -