Programming/Java

Serialize(직렬화)는 언제 쓰는걸까?

KOOCCI 2022. 4. 1. 01:13

목표 : Serialize(직렬화)는 언제 쓰는건지 알아보자!

 

오늘의 목표에 따라, Serialize에 대해 알아보자.

Java를 사용할 때, 정확하게는 Spring boot 로 서버개발을 하고 JPA 등 객체에 대한 연동을 할 때 Serialize는 언제 어디선가 보고 듣는다.

Node.js의 Express로 서버를 배울 때는 JSON 규격만 신경을 썼고, Flask로 서버를 공부할 때도 별로 신경쓰지 않던 부분이지만 최근 Spring boot 로 개발하는 일이 잦아지면서, Serialize에 대한 개념이 어떤지에 대해 궁금해졌다.

특히, 최근에 실시간 음성 스트리밍에 대한 업무를 진행하면서, 다음 질문을 받았다.

"혹시 JAVA로 개발하시나요? JSON이 아니라, Byte Array로 Serialze해서 규격이 나가도 되는 구조일지 확인이 필요해요"

내 대답은 "JSON이 좋은데, 꼭 그렇게 해야하나요...?" 였다.

과연, 난 잘 알고 대답한걸까 싶어 공부해보기로 했다.

한국어로 봤을 때는 어려운 직렬화라는건 대체 뭘까? 

 

난 최근에 어디서 이걸 봤지?

Jackson을 볼 때, 보았던 것 같다.

Jackson에서 ObjectMapper 를 사용하고, ObjectMapper 는 text를 json으로 바꾸거나, json을 text로 바꾸는 경우가 많다.

ObjectMapper objectMapper = new ObjectMapper();

User user = new User();
user.setName("홍길동");
user.setAge(10);

String json = objectMapper.writeValueAsString(user);
System.out.println(json);

ObjectMapper를 확인하면, Serializable 인터페이스가 implement되는 것을 알 수 있다.

인터페이스를 보면, 메소드나 필드가 없이 비어있으며, 직렬화 가능의 의미 식별 정도만 한다.

(마커 인터페이스라고도 한다) 즉, 직렬화해야한다는 사실을 JVM에 알려주는 용도다.

 

만들어낸 객체를 그냥 프린트하면, 그 주소가 나온다. (ToString 오버라이딩 안했다는 가정에서)

User user = new User();
System.out.println(user);

//com.example.test.UserTest$User@3d00c2ae

이 주소는 hashCode 값이며, hasCode는 주소값 기반으로 생성된 값이라, 그 주소라고 표현할 수 있다.

JVM에서 메모리(스택 혹은 힙) 영역에 올라온 데이터를 그냥 외부로 전송하면 당연히 에러가 난다.

전달받는 쪽에서는 알 방법이 없기 때문이다.

따라서, 외부에서 읽을 수 있는 처리가 필요하다.

그 과정이 Serialize 이며, 객체 데이터를 바이트 형태로 변환한다. 그리고 JAVA에서는 Serializable 인터페이스를 구현하는 것으로 표현한다.

 

그럼 JAVA끼리만 사용하는건가?

"java에서 Serialize를 한 다음, 외부로 보내면 그 쪽에선 어떻게 Deserialize 하지?"

"JSON과 다른건가? JSON도 직렬화한다는 표현을 쓰는데?"

위에 내가 가장 많이 본, ObjectMapper 예시를 든 이유기도 하다.

용어가 헤깔릴 수 있기 때문이다.

javascript 에서 json을 string으로 직렬화하라고 하면 다음과 같다.

console.log(JSON.stringify({ x: 5, y: 6 }));
// expected output: "{"x":5,"y":6}"

그 반대의 경우도, JSON.parse를 써서 처리한다.

이런 부분 때문에 많이 헤깔렸는데, 결과적으로는 자바 직렬화는 자바 시스템 간 데이터 전송을 위함이다.

 

그럼 위에.. ObjectMapper는... 왜있지...?

결과는 간단하다. Serialize가 가능할 뿐이다.

ObjectMapper 내부에는 JsonGenerator 라는 abstract class가 있어, 해당 클래스를 통해 json으로 직렬화를 한다.

즉, 위 javascript처럼 간단히 동작할 수 있는 게 jackson 라이브러리다.

다만, Serializable의 구현을 통해, 자바간 직렬화도 가능하도록 하였다.

 

굳이.. 쓸 필요가 있을까?

JVM 메모리에서만 상주된 데이터를 Persistence(영속화)가 필요할 때 사용이 된다.

가장 자주 사용하는 곳은 서블릿 세션캐시 라고 하니 아래 블로그를 확인하면 좋을 듯 하다.

https://techblog.woowahan.com/2550/

 

자바 직렬화, 그것이 알고싶다. 훑어보기편 | 우아한형제들 기술블로그

{{item.name}} 자바의 직렬화 기술에 대한 대한 이야기입니다. 간단한 질문과 답변 형태로 자바 직렬화에 대한 간단한 설명과 직접 프로젝트를 진행하면서 겪은 경험에 대해 이야기해보려 합니다.

techblog.woowahan.com

 

그런데.. 제약이 많아보인다.

우선, serialVersionUID 는 유명한 스펙이다.

더보기

The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException. A serializable class can declare its own serialVersionUID explicitly by declaring a field named serialVersionUID that must be static, final, and of type long:

위 serialVersionUID를 고정하더라도 중간에 클래스가 변경되었을 때는 예외 케이스를 미리 체크해야한다.

타입 등 변경되었을 때 Deserialize가 안되는 경우가 생겨버릴 수 있다.

 따라서, 오래 저장하고 있어야 하는 곳에 사용할만한 내용도 아니다.

 

결론

그래서 언제쓰냐... 한다면, 사실 잘 모르겠다.

당장 이펙티브 자바 책을 봐도, 직렬화는 위험하고, 대안을 찾으라고 한다...

아직까지는 프로젝트 내에 크게 신경써서 개발해보지 못한 점도 있다.

다만, 적어도 내가 헤깔릴뻔한 내용은 정리하고 넘어가도록 하자.