본문 바로가기
Java/About Java

[Java] 직렬화와 역직렬화

by seaweed_one 2023. 2. 21.
728x90

안녕하세요. 씨위드입니다.

저는 요즘 Kerberos 를 이용한 사용자 인증과 관련된 업무를 진행 중인데요.

프로젝트를 진행하며 transient이라는 키워드를 사용하게 되었습니다.

해당 키워드는 직렬화 과정에서 제외하고싶은 데이터에 사용하게 되는데요.

transient이라는 키워드가 생소하신 분들도 있을 것 같아 키워드를 소개하며 추가로 이와 연관된 직렬화와 역직렬화에 대하여 알아보려 합니다. 

먼저 자바의 직렬화(Serialization)와 역직렬화(Deserialize)의 개념에 대하여 알아볼까요?

 

Serialization

먼저 일반적 의미의 직렬화란 데이터의 포맷을 변환하는 기술입니다.

자바에서 말하는 직렬화는 객체를 전송 가능한 형태로 변환하는 것을 의미합니다.

일반적으로는 시스템 내부에서 사용되는 객체 혹은 데이터를 외부의 자바 시스템에서 사용할 수 있도록 자바 byte 형태로 변환하는 기술로 직렬화된 객체는 네트워크 전송 혹은 파일 저장을 위해 활용될 수 있습니다.

 

Deserialize

역직렬화는 예상하셨겠지만 직렬화된 byte 상태의 데이터를 다시 객체의 형태로 만드는 것을 의미합니다.

직렬화된 데이터를 객체로 변환하여 JVM에 로드합니다.

 

직렬화 예외

위에서 언급한 transient 키워드가 등장합니다.

이 예약어는 직렬화 과정에서 제외하고 싶은 경우에 선언하는데요.

저의 경우와 같이 비밀번호와 같은 보안 정보 등 데이터를 전송하고 싶지 않을 때 선언한다면 직렬화 과정에서 제외할 수 있습니다.

 

직렬화 & 역직렬화의 사용 

직렬화 클래스 생성 

java.io.Serializable 인터페이스를 구현해야 합니다.

Serializable를 이용해 클래스를 하나 생성해 보았습니다.

pwd 객체는 직렬화 제외 테스트를 위해 transient 객체로 선언해 보았습니다.

 

Serialization & Deserialize

먼저 직렬화는 java.io.ObjectOutputStream, 역직렬화는 java.io.ObjectInputStream을 사용하여 진행할 수 있습니다.

아래는 제가 작성한 예시 코드입니다.

직렬화와 역직렬화를 위한 로직을 함수로 생성해 보았습니다.

해당 코드를 이용해서 위에서 만든 객체를 직렬화하고 또 역직렬화해보겠습니다.

직렬화 전 객체, 직렬화 후 객체, 그리고 역직렬화 후 객체를 각각 출력하는 로직을 작성하였습니다.

위의 코드를 실행해 볼까요?

직렬화 전 객체 정보와 직렬화 후의 정보 그리고 역직렬화 후의 객체까지 출력을 해보았습니다.

출력문을 통해 byte로 직렬화된 객체가 역 직렬화를 통해 다시 User 객체로 변환된 것을 보실 수 있습니다.

또한 pwd 객체는 transient 키워드를 사용하였기 때문에 직렬화에서 제외되어 역직렬화를 수행하더라도 값이 null로 표시되었습니다.

역직렬화 객체는 형변환을 통해 기존 User 클래스에 존재하던 getter 함수도 사용할 수 있습니다.

 

직렬화의 제약사항 

다만 역직렬화를 진행하기 위해서는 직렬화 대상 클래스가 java class path에 존재해야 하며 import 된 상태여야 합니다.

또한 기존 클래스 구조에 변화가 생긴다면 역직렬화 시 에러가 발생할 수 있습니다.

따라서 자주 변경되는 데이터에 대해서는 직렬화 사용을 지양하는 것이 바람직합니다.

 

오늘은 이렇게 직렬화와 역직렬화에 대해서 알아보았는데요.

위의 예시 코드는 하단에 첨부해 두겠습니다.

직렬화와 역직렬화에 대해서 궁금하신 분들은 직접 실행해 보세요.

import java.io.Serializable;

public class User implements Serializable {
  private String id;
  private transient String pwd;

  public User(String id, String pwd) {
    this.id = id;
    this.pwd = pwd;
  }

  @Override
  public String toString() {
    return String.format("User{id='%s', pwd='%s'}", id, pwd);
  }

  public String getId() {
    return id;
  }

  public String getPwd() {
    return pwd;
  }
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class Test {
  public static void main(String[] args) {
    User user = new User("seaweed", "TStory");
    try {
    
      //직렬화
      byte[] serializedObject = serialize(user);
      System.out.println("직렬화 전 객체=" + user);
      System.out.println("직렬화 후 객체=" + Arrays.toString(serializedObject));
      System.out.println("--------------------------------------");
      
      //역직렬화
      Object deserializeObj = deserialize(serializedObject);
      System.out.println("역직렬화 객체=" + deserializeObj);

      //형변환
      User userClass = (User) deserializeObj;
      userClass.getId();
      userClass.getPwd();
    } catch (IOException | ClassNotFoundException e) {
      e.printStackTrace();
    }
  }

  private static byte[] serialize(User user) throws IOException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
    objectOutputStream.writeObject(user);
    return outputStream.toByteArray();
  }

  private static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
    try (ByteArrayInputStream byteInputStream = new ByteArrayInputStream(bytes)) {
      try (ObjectInputStream objectInputStream = new ObjectInputStream(byteInputStream)) {
        return objectInputStream.readObject();
      }
    }
  }
}
728x90