ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] Serialization과 Deserialization
    Java 2022. 1. 3. 21:58

    자바에서 데이터(객체)를 외부로 전송하려면 어떻게 해야할까 ❔❔

    객체를 저장/복원하거나 네트워크 상으로 전송하기 위해서는 객체의 내용을 I/O가 처리할 수 있는 형태로 변환해야 한다. 이를 할 수 있게 해주는 것이 직렬화(Serialization)과 역직렬화(Desirialization)이다.

    직렬화(Serialization)

    직렬화는 객체를 외부로 저장/복원하거나 네트워크 상으로 전송할 수 있도록 바이트 형태로 변환하는 기술을 말한다.

    역직렬화(Deserialization)

    역직렬화는 직렬화를 통해 변환된 바이트 형태를 다시 원상태인 객체로 변환시키는 기술을 말한다.

    직렬화의 특징

    • 객체의 데이터는 파일로 변환되어 저장되어 있기 때문에 프로그램이 종료되어도 언제든지 객체로 변환할 수 있으며, 외부로 보내서 데이터를 공유할 수 있다.
    • 여러 형태(CSV, JSON, 일반 파일 등)로 수행이 가능하다.
    • Java에서 제공하는 직렬화 기능은 오직 Java 프로그램 사이에서만 공유 가능한 데이터이며, 코드를 수정하거나 자바 버전이 달라 클래스 속성이 바뀌게 되면 사용할 수 없다.
    • Java의 JVM에서 자동으로 처리해주기 때문에 수신부와 송신부의 운영체제가 달라도 상관없다.

    직렬화 방법

    클래스의 객체를 직렬화하기 위해서는 Serializable 인터페이스를 implement해야 한다.

    Serializable 인터페이스를 implement해도 따로 구현해야할 내용은 없다.

    public interface Serializable {
    }
    

    Serializable 인터페이스는 단순히 현재 클래스의 객체에 직렬화가 제공되어야 함을 JVM에게 알려주는 역할만 수행한다.

    다음과 같이 Person 클래스가 있다고 가정하고 이 클래스의 객체를 직렬화하여 파일에 저장해보자.

    public class Person implements Serializable {
    
        String name;
        int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    

    다음과 같이 serialization.txt 파일에 person 객체를 직렬화해 저장해준다.

    Person person = new Person("홍길동", 25);
    
    try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("serialization.txt"))){
        out.writeObject(person);
    } catch (Exception e) {
    }

    실행하면 serialization.txt 파일에 다음과 같이 변환된 바이트 형태의 데이터가 저장된 것을 확인할 수 있다.

    역직렬화 방법

    직렬화하여 저장된 바이트 형태의 데이터를 역직렬화하여 불러와보자.

    Person person = null;
    
    try(ObjectInputStream in = new ObjectInputStream(new FileInputStream("serialization.txt"))){
        person = (Person) in.readObject();
    } catch (Exception e) {
    }
    
    System.out.println(person.toString());

    이전에 직렬화하여 변환한 객체의 정보가 원래대로 복구된 것을 확인할 수 있다.

    만약 특정 정보를 저장하고 싶지 않다면 어떻게 해야할까 ❔

    이는 저장하고 싶지 않은 멤버에 transientstatic 키워드를 붙여 제외할 수 있다.

    public class Person implements Serializable {
    
        static String name;
        transient int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }

    아무런 정보가 없기 때문에 기본값으로 출력되는 것을 확인할 수 있다.

    ❗❗❗ 주의해야 할 점 ❗❗❗

    위의 직렬화 특징 중에 직렬화와 역직렬화를 할 때 클래스 속성이 바뀌게 되면 사용할 수 없다고 말했다.

    그렇다면 JVM은 어떻게 두 클래스의 속성이 일치하는지 판단할 수 있을까

    이는 serialVersionUID 값을 통해 판단한다.

    SerialVersionUID 란 ❔

    serialVersionUID는 클래스의 버전을 확인하기 위한 용도로 사용하는 변수이다. serialVersionUID 값을 명시적으로 지정하지 않으면 컴파일러가 계산한 값을 부여하는데, 만약 Serializable 클래스나 Outer 클래스에 변경이 있으면, serialVersionUID 값이 바뀌게 된다. 직렬화할 때와 역직렬화할 때 serialVersionUID 값이 다르면 InvalidClassException이 발생하게 되기 때문에 serialVersionUID를 선언하는 것을 권장한다.

     

    즉, JVM은 직렬화할 때와 역직렬화할 때 SerialVersionUID를 비교하여 두 클래스의 속성이 같은지 판단한다. 아래와 같이 serialVersionUID 값을 1로 설정하고 직렬화하고 이를 다시 역직렬화해보자.

    public class Person implements Serializable {
    
        private static final long serialVersionUID = 1L;
        
        String name;
        int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
        public static void serialize() {
            Person person = new Person("홍길동", 25);
    
            try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("serialization.txt"))){
                out.writeObject(person);
            } catch (Exception e) {
            }
        }
    public static void deserialize() {
        Person person = null;
    
        try(ObjectInputStream in = new ObjectInputStream(new FileInputStream("serialization.txt"))){
            person = (Person) in.readObject();
        } catch (Exception e) {
        }
    
        System.out.println(person.toString());
    }

    그럼 이와 같이 제대로 수행되는 것을 볼 수 있다.

     

    이번에는 serialVersionUID 값을 1로 설정하고 직렬화하고 serialVersionUID 값을 2로 변경하고 역직렬화해보자.

    private static final long serialVersionUID = 2L;

    그럼 이와 같이 InvalidClassException이 발생하고 serialVersionUID가 맞지 않다는 메시지를 볼 수 있다.

    만약 serialVersionUID 값을 직접 설정하지 않고 컴파일러에게 맡긴다면 아래의 경우 위와 같은 예외가 발생할 것이다.

    1. 송신부의 컴파일러와 수신부의 컴파일러가 다를 경우
    2. 직렬화할 때의 클래스 내용과 역직렬화할 때의 클래스 내용이 다를 경우

    따라서 serialVersionUID를 선언해야 위와 같은 예외가 발생하는 것을 막을 수 있다.


    Reference

    https://sorjfkrh5078.tistory.com/89

     

    [Java] 객체 직렬화(Serialization)와 역직렬화(Deserialization)

    객체지향 언어인 Java는 프로그램의 모든 데이터들이 객체로 이루어져 있다고 봐도 무방하다. ​그렇다면 Java로 만든 프로그램의 데이터(객체)를 외부로 전송하려면 어떻게 해야 할까? 네트워크

    sorjfkrh5078.tistory.com

     

    'Java' 카테고리의 다른 글

    [Java] Mutable과 Immutable  (1) 2022.01.04
    [Java] Singleton Pattern  (0) 2022.01.03
    [Java] String, StringBuffer, StringBuilder  (0) 2022.01.03
    [Java] Identity와 Equality  (0) 2022.01.03
    [Java] Primitive type과 Reference type  (0) 2022.01.03

    댓글

Designed by Tistory.