개발/Java

자바 직렬화 / 역직렬화 정리

Louisus 2021. 8. 12. 15:36
728x90

직렬화

  • 자바 직렬화란 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터 변환하는 기술과 바이트로 변환된 데이터를 다시 객체로 변환하는 기술(역직렬화)을 아울러서 이야기합니다.
  • 시스템적으로 이야기하자면 JVM(Java Virtual Machine 이하 JVM)의 메모리에 상주(힙 또는 스택)되어 있는 객체 데이터를 바이트 형태로 변환하는 기술과 직렬화된 바이트 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 형태를 같이 이야기합니다.
public class Member implements Serializable {
        private String name;
        private String email;
        private int age;

        public Member(String name, String email, int age) {
            this.name = name;
            this.email = email;
            this.age = age;
        }

        // Getter 생략

        @Override
        public String toString() {
            return String.format("Member{name='%s', email='%s', age='%s'}", name, email, age);
        }
    }

// 직렬화 방법
Member member = new Member("김배민", "deliverykim@baemin.com", 25);
    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));
}

역직렬화

  • 직렬화 대상이 된 객체의 클래스가 클래스 패스에 존재해야 하며 import 되어 있어야 합니다.

    중요한 점은 직렬화와 역직렬화를 진행하는 시스템이 서로 다를 수 있다는 것을 반드시 고려해야 합니다.(같은 시스템 내부이라도 소스 버전이 다를 수 있습니다. 이 부분은 다시 자세히 이야기하겠습니다.)

  • 자바 직렬화 대상 객체는 동일한 serialVersionUID 를 가지고 있어야 합니다.

      `private static final long serialVersionUID = 1L;`

    자바 직렬화를 아시는 분은 위에서 기술한 예제에서 사용되는 자바 직렬화 대상의 Member 클래스가 serialVersionUID 상수가 없어서 의아하신 분도 계실 겁니다.사실 반드시 기술해야 되는 필수는 아니기 때문에 빼둔 것입니다. 하지만 상당히 중요한 부분이라서 따로 설명하려고 합니다. 이곳에서는 넘어가도록 하겠습니다.

              // 직렬화 예제에서 생성된 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);
              }
          }