직렬화와 역직렬화는 데이터를 전송하거나 저장하기 위해 객체 데이터를 변환하고 복원하는 과정
직렬화는 객체의 상태를 바이트 스트림으로 변환. 이를 통해 파일로 저장하거나 네트워크를 통해 전송. -> 데이터를 영구적으로 저장하거나 네트워크를 통해 전송하기 위해 객체를 바이트 형태로 변환
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)는 네트워크 통신, 파일 저장 및 복원, 데이터 교환 같은 실무에서 중요한 역할을 하므로, 신입 백엔드 개발자로서 실습해볼 가치가 있습니다. 특히, 객체 데이터를 전송하거나 저장하는 상황은 빈번히 발생하므로, 아래 실습 아이디어를 통해 이를 더 잘 이해하고 실무에 대비할 수 있습니다.
.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();
}
}
}
String department)를 추가 후 역직렬화 시도.serialVersionUID를 변경 후 역직렬화 시도.코드 예제:
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();
}
}
}
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);
}
}
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;
}
}
직렬화의 실제 동작 이해:
데이터 교환 포맷 학습:
호환성 문제 해결:
serialVersionUID와 클래스 구조 변경 시의 문제를 경험하여 실무 대비.백엔드 API 설계:
네트워크 통신:
이런 실습은 신입 개발자로서 직렬화와 관련된 백엔드 기술 스택을 쌓는 데 큰 도움이 될 것입니다. 필요하면 추가 가이드를 요청해주세요! 🚀