Java 직렬화 및 역직렬화

JavaBeginner
지금 연습하기

소개

직렬화 (Serialization) 와 역직렬화 (Deserialization) 는 Java 에서 객체를 바이트 스트림으로 변환하고 다시 객체로 복원하는 중요한 프로세스입니다. 직렬화와 역직렬화를 사용하여 객체의 상태를 저장하거나 유지하거나, 네트워크를 통해 객체를 전송할 수 있습니다. 이 Lab 에서는 ObjectInputStreamObjectOutputStream 클래스를 사용하여 Java 에서 객체를 직렬화하고 역직렬화하는 방법을 배웁니다.

Serializable 인터페이스 구현

객체를 직렬화 가능하게 만들려면 Serializable 인터페이스를 구현해야 합니다. 이는 구현해야 할 메서드가 없는 마커 인터페이스입니다. Serializable 인터페이스는 이 클래스의 객체를 직렬화할 수 있음을 JVM 에 나타내는 데 사용됩니다.

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;

    // Constructor and getter/setter methods
}

직렬화할 객체 생성

다음 단계에서 직렬화할 Student 클래스의 객체를 몇 개 생성합니다.

Student student1 = new Student("Alice", 22);
Student student2 = new Student("Bob", 21);

객체 직렬화 수행

객체를 직렬화하려면 ObjectOutputStream의 인스턴스를 생성하고 직렬화하려는 객체를 전달하여 해당 writeObject() 메서드를 호출해야 합니다. 이렇게 하면 객체가 바이트 스트림으로 파일에 기록되며, 나중에 역직렬화에 사용할 수 있습니다.

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;

public class SerializationDemo {
    public static void main(String[] args) {
        try {
            FileOutputStream fileOut = new FileOutputStream("students.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);

            out.writeObject(student1);
            out.writeObject(student2);

            out.close();
            fileOut.close();

            System.out.println("Serialized data is saved in students.ser file");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

객체 역직렬화

객체를 역직렬화하려면 ObjectInputStream의 인스턴스를 생성하고 readObject() 메서드를 호출하여 이전에 직렬화한 파일에서 바이트 스트림을 읽어야 합니다. 그러면 원래 클래스 유형으로 캐스팅할 수 있는 Object가 반환됩니다.

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;

public class SerializationDemo {
    public static void main(String[] args) {
        try {
            FileInputStream fileIn = new FileInputStream("students.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);

            Student student1 = (Student) in.readObject();
            Student student2 = (Student) in.readObject();

            in.close();
            fileIn.close();

            // Print the students
            System.out.println(student1);
            System.out.println(student2);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Transient 필드 처리

클래스에 직렬화하지 않으려는 필드가 있는 경우, 해당 필드를 transient로 선언할 수 있습니다. transient 필드는 직렬화 및 역직렬화 중에 무시됩니다. 예를 들어, 직렬화하지 않으려는 transient 필드를 Student 클래스에 추가해 보겠습니다.

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;
    private transient String password;

    //Constructor, getter/setter methods
}

직렬화 및 역직렬화 사용자 정의

writeObject()readObject() 메서드를 구현하여 직렬화 및 역직렬화 프로세스를 사용자 정의할 수도 있습니다. 이는 transient 필드를 처리하거나, 직렬화 및 역직렬화 중에 추가 처리를 수행하려는 경우에 유용할 수 있습니다.

private void writeObject(ObjectOutputStream oos) throws IOException {
    // Default serialization
    oos.defaultWriteObject();

    // Encrypt password before writing to file
    String encryptedPassword = encrypt(password);
    oos.writeObject(encryptedPassword);
}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    // Default deserialization
    ois.defaultReadObject();

    // Decrypt password after reading from file
    String encryptedPassword = (String) ois.readObject();
    password = decrypt(encryptedPassword);
}

버전 관리 처리

속성을 추가하거나 제거하여 클래스를 업데이트하면 클래스의 버전이 변경되어 이전 객체를 역직렬화할 때 오류가 발생할 수 있습니다. serialVersionUID 필드를 사용하여 클래스에 대한 고유 식별자를 지정함으로써 버전 관리를 처리할 수 있습니다. 이렇게 하면 클래스 정의가 변경되더라도 직렬화 및 역직렬화가 올바르게 작동합니다.

import java.io.Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    //Constructor, getter/setter methods
}

코드 실행

SerializationDemo 클래스를 컴파일하고 다음 명령을 사용하여 터미널에서 실행합니다.

javac SerializationDemo.java
java SerializationDemo

출력은 직렬화된 데이터와 역직렬화된 객체를 보여줍니다.

Student 클래스 수정

필드를 추가하거나 제거하여 Student 클래스를 업데이트합니다.

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private String email; // 필드 추가

    //Constructor, getter/setter methods
}

SerialVersionUID 를 사용한 버전 관리

이제 클래스를 수정했으므로 serialVersionUID를 업데이트하여 버전 관리를 처리해야 합니다. 이렇게 하면 클래스가 수정되기 전에 직렬화된 객체를 여전히 역직렬화할 수 있습니다.

public class Student implements Serializable {
    private static final long serialVersionUID = 2L;

    private String name;
    private int age;
    private String email; // 필드 추가

    //Constructor, getter/setter methods
}

요약

이 랩에서는 ObjectInputStreamObjectOutputStream을 사용하여 Java 에서 객체를 직렬화 (serialize) 및 역직렬화 (deserialize) 하는 방법을 배웠습니다. 또한 transient 필드를 처리하고, 직렬화 및 역직렬화 프로세스를 사용자 정의하며, serialVersionUID를 사용하여 버전 관리를 처리하는 방법도 배웠습니다. 직렬화 및 역직렬화는 Java 에서 데이터를 저장하고 전송하는 데 중요한 프로세스이며, 객체의 현재 상태를 저장하고 필요에 따라 나중에 복원하는 데 사용할 수 있습니다.