[Java] 직렬화(Serialization)이 뭘까?

BlackBean99·2022년 12월 31일
2

Java

목록 보기
3/3
post-thumbnail

Kafka를 공부하다가 Producer가 Topic으로 데이터를 넘기거나 Consumer 가 데이터를 받아올 때, DeSerialization을 한다는 내용을 봤어요. JDBC에도 나오고 DB에도 나오는 핵심 개념 Serialization을 이해해야 그 개념을 더 음미할 수 있어요.

자 가볼까요?

먼저 Serializable이 뭔지를 이해해야 하는데 퍼킹!

public interface Serializable {

}

아무것도 없다. 그럼 뭘 implements 해야하는데??

개발할때는 파일을 저장, 읽기, 다른 서버나, DB에 데이터를 넣는 등 한 서버에서 끝나는게 아니라 다른 서버로 어떤 것을 보낼 경우가 많죠.
하지만 보낼때 Java는 Binary한 형태로 JVM의 Runtime Data Area ( Heap or Stack 메모리 ) 에 있는 데이터를 바이트 형태로 변환해서 보내줍니다.
통신을 위해 불가피하다 이거에요

  • ps) 직렬화 ( 객체 -> 바이트 ) , 역직렬화 ( 바이트 -> 객체 ) 를 모두 포함한 개념입니다.
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);
    }
}

이런식으로 활용해서 쓸 수는 있습니다.
바이트로 변환해준다는 개념으로 이해하니 쉽네요

내부 동작 로직을 더 자세히 살펴보면

직렬화(Serialization)

  • 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));
}

역직렬화(Deserialize)

직렬화랑 달리 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);
        }
    }
}

어디에 쓰는데?

  1. 서블릿 세션(Servlet Session)
  • 파일이나 데이터를 저장, DB에 저장, 등등 어떤 데이터로 보낼 때, 데이터를 직렬화 해서 보낸다.
  1. 캐시
  • Redis, Memcached, Ehcache 등등에서 사용된다.
  1. 자바 RMI (Remote Method Invocation)
  • 원격 시스템 간의 메시지 교환을 위해 사용하는 자바에서 지원하는 기술이다.

Serialization의 문제점? 및 해결.. 법?

이 직렬화의 문제점또한 있습니다.
각 전송하는 시스템이 다를 때, 직렬화는 수행되지 않고
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형태로 바꾸는 것을 고려해야합니다.

결론


  • 자주 변경되는 데이터는 직렬화를 사용하지 말자
  • 장기간 사용하는 정보는 직렬화하지 말자.
  • JSON을 통해 직렬화하는 방법이 경량화 돼서 좋습니다.

Reference

https://techblog.woowahan.com/2551/

profile
like_learning

0개의 댓글