Kafka를 공부하다가 Producer가 Topic으로 데이터를 넘기거나 Consumer 가 데이터를 받아올 때, DeSerialization을 한다는 내용을 봤어요. JDBC에도 나오고 DB에도 나오는 핵심 개념 Serialization을 이해해야 그 개념을 더 음미할 수 있어요.
자 가볼까요?
먼저 Serializable이 뭔지를 이해해야 하는데 퍼킹!
public interface Serializable {
}
아무것도 없다. 그럼 뭘 implements 해야하는데??
개발할때는 파일을 저장, 읽기, 다른 서버나, DB에 데이터를 넣는 등 한 서버에서 끝나는게 아니라 다른 서버로 어떤 것을 보낼 경우가 많죠.
하지만 보낼때 Java는 Binary한 형태로 JVM의 Runtime Data Area ( Heap or Stack 메모리 ) 에 있는 데이터를 바이트 형태로 변환해서 보내줍니다.
통신을 위해 불가피하다 이거에요
import java.io.Serializable;
public class A implements Serializable {
private String a;
private int b;
public A(String a, int b) {
this.a = a;
this.b = b;
}
@Override
public String toString() {
return String.format("A is : '%s', B is='%s'", a, b);
}
}
이런식으로 활용해서 쓸 수는 있습니다.
바이트로 변환해준다는 개념으로 이해하니 쉽네요
내부 동작 로직을 더 자세히 살펴보면
java.io.ObjectOutputStream
를 사용한다.public static void main(String[] args){
Member member = new Member("a","b");
byte[] serializedMember;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(member);
// serializedMember -> 직렬화된 member 객체
serializedMember = baos.toByteArray();
}
}
// 바이트 배열로 생성된 직렬화 데이터 -> base64로 인코딩하기
System.out.println(Base64.getEncoder().encodeToString(serializedMember));
}
직렬화랑 달리 ObjectInputStream
를 사용한다.
public static void main(String[] args){
// 직렬화 예제에서 생성된 base64 데이터
String base64Member = "...생략";
byte[] serializedMember = Base64.getDecoder().decode(base64Member);
try (ByteArrayInputStream bais = new ByteArrayInputStream(serializedMember)) {
try (ObjectInputStream ois = new ObjectInputStream(bais)) {
// 역직렬화된 Member 객체를 읽기
Object objectMember = ois.readObject();
Member member = (Member) objectMember;
System.out.println(member);
}
}
}
이 직렬화의 문제점또한 있습니다.
각 전송하는 시스템이 다를 때, 직렬화는 수행되지 않고
java.io.InvalkidClassException
을 던집니다.
호환성을 유지시키기 위해서 SUID(serialVersionUID) 를 정의합니다.
선언되지 않으면 default값이 클래스의 기본 해시값을 사용합니다.
변수 제거, 이름 변경은 오류가 발생하지 않고 데이터가 누락됩니다.
근데 이 빌어먹을 호환성 문제 때문에 Java 가 자료형에 깐깐합니다.
String -> StringBuilder
int -> long
으로 변경해도 역직렬화에 java.io.InvalkidClassException
예외가 터집니다. 그래서 자료형을 아주 잘 맞춰줘야 통신이 가능하다는 이야기입니다.
만약에 변수가 빠지거나 한다면 Exception대신 null값이 들어갑니다.
이렇게 직렬화된 데이터는 json의 형태로 직렬화한 것보다 용량이 약 2배차이납니다.
Spring 에서 제공하는 대부분의 서비스는 Spring Data Redis, Spring Session .. 등은 자바 기본 직렬화를 사용하기 때문에 규모가 커질 때 직렬화를 JSON형태로 바꾸는 것을 고려해야합니다.