직렬화에 대한 이해

Leo·2024년 4월 7일
0

직렬화를 찾아보게된 이유


JPA를 사용해서 복합키 클래스를 정의할 때, @IdClass, @EmbeddedIdSerializable을 필수적으로 구현해야 했습니다.

  1. 복합키 객체는 왜 직렬화가 가능해야 하는가?
  2. 엔티티는 직렬화가 필요 없는가?

위 의문에 대한 해답과 직렬화에 대한 학습을 정리한 내용입니다.

직렬화


직렬화란?

직렬화란 컴퓨터의 메모리 상에 존재하는 데이터를 파일로써 저장하거나, 통신하는 다른 컴퓨터에게 알맞은 형식에 맞추어 전달하기 위해 바이트 스트림 형태로 만드는 것을 의미합니다.

자바 직렬화를 언제, 왜 사용할까?

직렬화 종류는 자바 직렬화, CSV, XML, JSON 등이 있습니다.
JSON은 특히 오버헤드가 적고, Javascript와 연동이 되기 때문에 api에서 유용하게 사용되고 있습니다. 그렇다면 자바 직렬화는 언제, 왜 사용할까요?

  • 자바 직렬화는 왜 사용할까요 ?
    자바 직렬화는 자바 시스템간의 통신에 최적화되어 있습니다. 따라서 직렬화 기본 조건만 지키면 큰 작업없이 타입일치와 직렬화, 역직렬화가 진행됩니다.

  • 자바 직렬화는 언제 사용할까요?
    JVM 메모리에만 상주되어 있는 객체 데이터를 영속화 가 필요할 때 사용됩니다.

  1. 세션 클러스터링, DB에 세션을 저장하는 옵션 등을 선택하게 되면 세션 자체가 직렬화 되어 저장됩니다.
  2. Ehcache, Redis, Memcashed와 같은 캐시 라이브러리 사용시, 데이터를 직렬화하여 저장합니다.(단, 직렬화만 사용해서 저장하지는 않습니다.)
  3. 자바 RMI(Remote Method Invocation)

직렬화 방법

자바 직렬화 방법은 java.io.ObjectOutputStream 객체를 사용합니다.

Member member = new Member("홍길동", "송파구", 25);
        byte[] serializedMember = new byte[0];
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
             
            oos.writeObject(member);
            serializedMember = baos.toByteArray();
            
        } catch (IOException ex) {
            e.printStackTrace();
        }

역직렬화 방법

try (ByteArrayInputStream bais = new ByteArrayInputStream(serializedMember);
     ObjectInputStream ois = new ObjectInputStream(bais)) {
     
    Member deserializedMember = (Member) ois.readObject();
    System.out.println("Deserialized Member: " + deserializedMember);
    
} catch (IOException | ClassNotFoundException e) {
    e.printStackTrace();
}

직렬화 주의사항

직렬화, 역직렬화를 진행할 때, 객체의 구조가 다르다면 java.io.InvalidClassException 이 발생합니다. 직렬화를 진행할 때, 객체에 serialVersionUID 를 명시하지 않아도 직렬화 과정에서 객체의 구조로 해시값을 만들어 serialVersionUID를 생성합니다.

serialVersionUID이 같더라도, 데이터 타입이 변경된다면 InvalidClassException이 발생합니다.

따라서 직접 serialVersionUID를 직접 관리하는 것을 권장합니다.

또한 자주 변경되는 클래스의 객체, 외부에 장기간 저장될 정보, 프레임워크, 라이브러리 객체에는 자바 직렬화를 지양하는게 좋습니다. 위와 같은 에러가 발생할 수 있기 때문입니다.

프레임 워크 자체에서 serialVersionUID를 가지고 있기는 예로, spring security 의 SecurityContextImpl 클래스는 시큐리티 버전에 따라 UID를 변경하는데, 해당 객체를 세션서버, 캐시서버에 직렬화로 저장했다가 문제가 발생할 수 있습니다.

또한 자바직렬화는 타입등의 클래스의 메타정보를 포함하고 있기 때문에 다른 직렬화에 비해 용량이 큰편입니다. 따라서 용량에 대한 문제가 있다면 다른 포멧의 직렬화를 고려 해야합니다.

정리하자면 직렬화 대상은 만료시간이 짧고, 자주 변경되지 않는 데이터만 사용하는게 바람직 합니다.

1. 복합키 객체는 왜 직렬화가 가능해야 하는가?


복합키를 적용하는 JPA 스펙입니다.

  • primary key class는 반드시 public 접근제어자를 갖는 기본생성자를 가져야한다.
  • 기본키 필드의 접근제어자는 public or pro-tected 여야 한다.
  • 기본키 클래스는 직렬화가 가능 해야한다.
  • primary key class는 Equals, hashCode 메서드를 구현 해야합니다.
  • id class를 사용하는 경우 타입과 이름이 일치해야 합니다.

2. 엔티티는 직렬화가 필요 없는가?


If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the
entity class must implement the Serializable interface.

출처 - http://jcp.org/aboutJava/communityprocess/final/jsr317/index.html

JPA 스펙에는 Entity 객체에서 떨어져 보내질 때(Entity를 세션에 저장하는 상황), 직렬화 인터페이스를 필수로 구현해야 하고, Entity에 미리 구현하는 것을 권장 한다고 합니다.

하지만 entity 자체를 직렬화 해서 사용하는 경우가 거의 없기 때문에 필요한 경우가 아니라면 직렬화를 구현할 필요는 없을 것 같습니다.

참고

https://techblog.woowahan.com/2551/

0개의 댓글