직렬화(Serialization)와 역직렬화(Deserialization)이란 무엇인가요?

김상욱·2024년 11월 14일

직렬화(Serialization)와 역직렬화(Deserialization)이란 무엇인가요?

직렬화와 역직렬화는 데이터를 전송하거나 저장하기 위해 객체 데이터를 변환하고 복원하는 과정

직렬화는 객체의 상태를 바이트 스트림으로 변환. 이를 통해 파일로 저장하거나 네트워크를 통해 전송. -> 데이터를 영구적으로 저장하거나 네트워크를 통해 전송하기 위해 객체를 바이트 형태로 변환

import java.io.*;

class Person implements Serializable {
    private String name;
    private int age;

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

public class SerializationExample {
    public static void main(String[] args) {
        Person person = new Person("John", 30);
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(person); // 직렬화 수행
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

역직렬화는 직렬화된 바이트 스트림을 다시 객체로 복원하는 과정
-> 저장되거나 전송된 데이터를 복원하여 프로그램 내에서 다시 사용할 수 있도록 개체로 변환

import java.io.*;

public class DeserializationExample {
    public static void main(String[] args) {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person person = (Person) ois.readObject(); // 역직렬화 수행
            System.out.println(person);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

직렬화하려는 객체는 Serializable 인터페이스를 구현해야 한다. 특정 필드를 직렬화하지 않으려면 transient 키워드를 사용할 수 있다.

네트워크 통신과 데이터 저장 뿐만 아니라 캐싱 시에도 저장하기 위해 사용한다.

직렬화된 데이터는 해당 클래스의 버전 정보를 포함하기 때문에 클래스 구조가 변경되면 역직렬화가 실패할 수 있다. 이를 방지하기 위해 serialVersionUID를 명시적으로 설정
serialVersionUID : 직렬화된 클래스의 고유 식별자 역할을 하는 상수 -> 이를 비교하여 확인
이를 명시적으로 설정하지 않으면 클래스의 구조를 기반으로 자동생성되는데 이 때, 클래스의 구조가 변경되면 자동 생성된 serialVersionUID 값도 변경됨. 이러면 호환성 문제 발생할 수 있음. 변경되어도 serialVersionUID가 같으면 호환 가능.


직렬화(Serialization)와 역직렬화(Deserialization)는 네트워크 통신, 파일 저장 및 복원, 데이터 교환 같은 실무에서 중요한 역할을 하므로, 신입 백엔드 개발자로서 실습해볼 가치가 있습니다. 특히, 객체 데이터를 전송하거나 저장하는 상황은 빈번히 발생하므로, 아래 실습 아이디어를 통해 이를 더 잘 이해하고 실무에 대비할 수 있습니다.


실습 아이디어

1. 객체 파일 저장 및 복원 실습

  • 목표: Java에서 객체를 직렬화하여 파일에 저장하고 다시 역직렬화하여 객체로 복원.
  • 실습 내용:
    • 객체를 파일(.ser 확장자)로 저장.
    • 저장된 파일에서 객체를 다시 읽어 복원.
    • 클래스에 serialVersionUID를 추가하고, 필드 변경 후 호환성 실험.

코드 예제:

import java.io.*;

// Serializable 인터페이스를 구현한 클래스
class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "Employee{name='" + name + "', age=" + age + "}";
    }
}

public class SerializationPractice {
    public static void main(String[] args) {
        Employee employee = new Employee("Alice", 25);

        // 직렬화
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.ser"))) {
            oos.writeObject(employee);
            System.out.println("직렬화 완료: " + employee);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 역직렬화
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("employee.ser"))) {
            Employee deserializedEmployee = (Employee) ois.readObject();
            System.out.println("역직렬화 완료: " + deserializedEmployee);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  • 추가 실험:
    • Employee 클래스에 새로운 필드(예: String department)를 추가 후 역직렬화 시도.
    • serialVersionUID를 변경 후 역직렬화 시도.

2. 네트워크 통신에 직렬화 적용

  • 목표: 직렬화된 객체를 네트워크를 통해 송수신.
  • 실습 내용:
    • 서버와 클라이언트 간의 통신에서 객체를 직렬화하여 전송하고 역직렬화로 복원.
    • 네트워크를 통해 객체 데이터를 전송하면서 직렬화의 필요성과 데이터를 어떻게 안전하게 다룰지 실습.

코드 예제:

  • 서버:
import java.io.*;
import java.net.*;

public class Server {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(12345)) {
            System.out.println("서버 대기 중...");
            Socket socket = serverSocket.accept();

            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
            Employee employee = (Employee) ois.readObject();

            System.out.println("클라이언트로부터 받은 데이터: " + employee);

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  • 클라이언트:
import java.io.*;
import java.net.*;

public class Client {
    public static void main(String[] args) {
        try (Socket socket = new Socket("localhost", 12345)) {
            Employee employee = new Employee("Bob", 30);

            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
            oos.writeObject(employee);

            System.out.println("서버로 객체 전송 완료: " + employee);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. JSON 직렬화/역직렬화 실습

  • 목표: 객체를 JSON 형식으로 직렬화/역직렬화하여 REST API 요청과 응답 시 활용.
  • 실습 내용:
    • Java 라이브러리(Gson, Jackson 등)를 사용해 객체를 JSON 문자열로 변환.
    • JSON 문자열을 다시 객체로 변환.

Gson 예제:

import com.google.gson.Gson;

class User {
    private String username;
    private int level;

    public User(String username, int level) {
        this.username = username;
        this.level = level;
    }

    @Override
    public String toString() {
        return "User{username='" + username + "', level=" + level + "}";
    }
}

public class JsonSerialization {
    public static void main(String[] args) {
        Gson gson = new Gson();

        // 객체를 JSON으로 변환
        User user = new User("player1", 10);
        String json = gson.toJson(user);
        System.out.println("JSON 직렬화: " + json);

        // JSON을 객체로 복원
        User deserializedUser = gson.fromJson(json, User.class);
        System.out.println("JSON 역직렬화: " + deserializedUser);
    }
}

4. Spring에서 직렬화 활용 실습

  • 목표: Spring MVC에서 직렬화를 활용한 REST API 구축.
  • 실습 내용:
    • Spring Boot 프로젝트를 설정하고, 객체 데이터를 JSON으로 직렬화하여 클라이언트로 응답.
    • 클라이언트 요청에서 JSON 데이터를 역직렬화하여 객체로 변환.

REST Controller 예제:

@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/user")
    public User getUser() {
        return new User("player2", 20); // JSON 직렬화되어 반환
    }

    @PostMapping("/user")
    public String createUser(@RequestBody User user) { // JSON 역직렬화
        return "Received user: " + user;
    }
}

실습으로 얻을 수 있는 경험

  1. 직렬화의 실제 동작 이해:

    • 파일 저장, 네트워크 전송, JSON 변환 등 다양한 시나리오에서 직렬화와 역직렬화를 직접 경험.
  2. 데이터 교환 포맷 학습:

    • JSON, XML 등 다양한 포맷과 직렬화의 차이점 및 장단점 이해.
  3. 호환성 문제 해결:

    • serialVersionUID와 클래스 구조 변경 시의 문제를 경험하여 실무 대비.
  4. 백엔드 API 설계:

    • RESTful API와 JSON 직렬화를 활용한 데이터 전송 설계.
  5. 네트워크 통신:

    • 서버와 클라이언트 간 객체 전송을 통해 네트워크 프로그래밍 실습.

추가 실습 아이디어

  • 캐싱 시스템과 직렬화: 객체 데이터를 Redis나 Memcached에 저장하고 복원.
  • 스프링 세션 데이터 직렬화: 분산 환경에서 세션 데이터를 직렬화하여 공유.
  • 성능 비교 실습: JSON, XML, ProtoBuf(Protocol Buffers) 등 다양한 직렬화 포맷의 성능 차이 분석.

이런 실습은 신입 개발자로서 직렬화와 관련된 백엔드 기술 스택을 쌓는 데 큰 도움이 될 것입니다. 필요하면 추가 가이드를 요청해주세요! 🚀

0개의 댓글