Java

자바 직렬화 Serialization

변위니 2025. 1. 7. 02:09

 

객체를 바이트 스트림으로 변환하여 저장하거나 전송할 수 있게 하는 과정

Java 시스템 내부에서 사용되는 데이터를 외부의 Java 시스템에서 사용할 수 있도록 바이트 형태로 변환하는 것을 의미한다.

 

 

구현 방식

public interface Serializable() {}

 

 

 

자바에서 직렬화를 구현하기 위해서는 java.io.Serializable 인터페이스를 구현해야 한다.

필드 중에 직렬화하지 말아야 할 필드는 transient 키워드를 사용하여 표시하자.

import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = 1L; // 클래스 버전 관리용
    private String username;
    private transient String password; // 직렬화에서 제외
}

 

1. Serializable 인터페이스 구현

직렬화할 클래스는 java.io.Serializable 인터페이스를 구현한다.

 

 

2. transient 키워드 사용

직렬화하지 않을 필드는 transient 키워드를 사용하여 표시한다.

예를 들어, 암호와 같은 민감한 정보를 직렬화에서 제외하려면 transient를 사용한다.

 

 

3. 직렬화 및 역직렬화

ObjectOutputStream, ObjectInputStream 클래스를 사용하여 객체를 파일에 저장하거나 읽어올 수 있다.

  • 직렬화: 객체를 파일에 저장할때 ObjectOutputStream을 사용한다.
  • 역직렬화: 저장된 파일에서 객체를 읽어올때 ObjectInputStream을 사용한다.

 

 

자바 직렬화와 JSON, CSV

데이터를 직렬화하는 방법에는 자바 직렬화말고도 CSV, XML, JSON도 있다. 

표 형태의 데이터를 저장, 전송할 때는 CSV를, 네트워크에서 구조적인 데이터를 전송(API)할 때는 주로 JSON을 사용한다.

 

이런 방법도 있는데 왜 자바 직렬화를 사용할까?

자바 직렬화는 자바의 내장 메커니즘으로, 같은 자바 시스템에서의 데이터 전송과 저장에 최적화되어있다. 무엇보다 데이터 타입이 자동으로 맞춰지기 때문에, 역직렬화시 바로 기존 객체처럼 사용이 가능하다. 따라서 개발자가 일일이 타입을 맞추지 않아도 된다.

 

 

 

JSON은 언어와 플랫폼에 독립적이기 때문에, 다양한 시스템 간에 데이터를 주고받을 때 유용하다. JSON은 사람이 읽을 수 있는 형식으로 데이터를 표현하며, JSON을 사용하면 데이터 타입을 명시적으로 지정하지 않아도 자동으로 맞춰진다. JSON의 직렬화와 역직렬화는 객체를 쉽게 변환할 수 있어, 다양한 환경에서 데이터를 쉽게 사용할 수 있다.

 

CSV는 간단한 데이터를 텍스트 형식으로 저장하는 데 유용하며, 데이터가 표 형식으로 구성되어 있고, 크기가 작은 데이터에 적합하다. 하지만 복잡한 객체 구조나 중첩 데이터를 표현하는 데는 한계가 있다.

 

하지만 무조건 자바 직렬화를 사용하는 것이 아닌 때에 따라서 맞게 사용해야 한다.

 

 

 

 


언제 사용할까

객체 데이터를 영속화하고 전송할 수 있는 특성으로 여러 곳에서 사용된다.

서블릿 세션

서블릿 기반의 WAS는 대부분 자바 직렬화를 지원한다.

사용자의 상태 정보를 유지하기 위해 세션을 사용하며, 세션 데이터를 DB나 파일에 저장할 때 자바 직렬화를 통해 외부 저장소에 저장할 수 있다.

캐시

Redis 같은 캐시 시스템에서 데이터를 저장할 때, 자바 객체를 직렬화하여 바이트 스트림으로 변환한 후 저장한다.

자바 RMI

원격 메서드 호출을 위한 기술로, 네트워크를 통해 다른 컴퓨터에 있는 메서드를 로컬 컴퓨터에서 호출하는 것처럼 사용할 수 있도록 한다. 이때, 소켓통신을 이용하여 IP와 포트를 통해 연결하는 방식이 아닌, 이 과정을 자동으로 추상화하여 사용자가 쉽게 원격 메서드를 호출할 수 있도록 한다.

 

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

public class SerializationExample {
    public static void main(String[] args) {
        User user = new User("john_doe", "password123");

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
            oos.writeObject(user); // 객체를 파일에 직렬화
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}​

 

 

ObjectInputStream의 readObject()

직렬화된 파일에서 객체를 읽어올 때 사용한다.이 메서드는 직렬화된 데이터를 역직렬화하여 원래의 객체로 복원한다. Object 타입을 반환하기 때문에, 읽어온 객체를 원래의 클래스 타입으로 형 변환해야 한다.

역질렬화할 때 해당 클래스가 클래스패스에 없는 경우 ClassNotFoundException이 발생할 수 있다.또한 클래스의 구조나 패키지 이름이 변경된 경우에도 발생할 수 있다.직렬화된 객체가 다른 JVM 버전이나 다른 시스템에서 직렬화된 경우, 특정 클래스에 의존할 때도 예외가 발생할 수 있다.

 

SerialVersionUID

직렬화된 객체의 버전을 식별하는 고유 식별자

클래스에 새로운 필드가 추가, 제거등 변경 사항이 있을 때, 재정의하지 않으면 InvalidClassException 예외가 발생할 수 있다. 여러 버전의 클래스가 존재할 수 있는 경우 SerialVersionUID를 사용하여 호환성을 유지할 수 있기 때문에 직렬화시 버전 불일치의 문제를 방지할 수 있다.

 

Externalizable 인터페이스

Externalizable 인터페이스를 구현하면, 직렬화 및 역질렬화 과정에서, 사용자가 직접 writeExternal()과 readExternal() 메서드를 구현해야 한다. 또한 직렬화할 필드와 방법을 명시적으로 제어할 수 있어 불필요한 데이터를 직렬화하지 않을 수 있다. 이 과정에서 성능 최적화가 가능하며 직렬화된 데이터의 구조를 명확하게 정의할 수 있다.

 

 

 


transient

transient로 선언된 필드는 ObjectOutputStream을 사용하여 객체를 직렬화할 때 데이터 스트림에 저장되지 않는다.역직렬화 과정에서

는 transient로 선언된 필드는 기본값으로 초기화된다.int 타입의 transient 필드는 0으로, boolean 타입의 transient 필드는 false로 초기화된다. 직렬화가 필요한 데이터와 필요하지 않은 데이터를 구분할 때 유용하다.

 

transient의 동작

transient 키워드가 적용된 필드가 사용자 정의 객체나 참조형 변수를 가지면, 직렬화 시 해당 필드의 값은 저장되지 않으며, 역직렬화 후에는 null이 되거나 기본값으로 초기화된다.

민감한 정보나, 직렬화가 필요 없는 데이터에 사용한다.하지만, transient로 지정된 필드는 역직렬화 후 제대로 복원되지 않기 때문에, 필요한 경우 readObject메서드에서 직접 복원 로직을 구현해야 한다.

 

readObject 메서드를 구현해서 transient 필드를 복원 시 주의할 부분

readObject에서 복원 로직을 정확하게 구현해야 한다.직렬화되지 않기 때문에 복원시 적절한 값을 설정해야 하며, 직렬화와 역직렬화시 필드의 순서가 맞아야 한다.그리고 복원과정에서 발생할 수 있는 예외를 적절히 처리해야 한다.

 

Serializable 인터페이스 내부의 writeObject 메서드

writeObject() 메서드에서 transient 필드는 직렬화되지 않기 때문에, 이 메서드에서는 transient 필드를 저장하지 않아야 한다.

 

 



'Java' 카테고리의 다른 글

index와 B-tree 그리고 Hash Index  (0) 2025.01.07
StringBuffer & StringBuilder  (0) 2025.01.07
System.out.println()와 로그  (1) 2025.01.07
synchroinzed와 DeadLock  (0) 2025.01.07
Generic과 Object  (0) 2025.01.07