자바 "직렬화" 확실히 알고 가기

henrywoo·2022년 5월 26일
21
post-thumbnail

🤔 자바 직렬화란?

자바 직렬화란 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터 변환하는 기술변환된 데이터를 다시 객체로 변환하는 기술(역직렬화)을 아울러서 칭한다.

시스템적으로 JVM의 Runtime Data Area(Heap 또는 스택 영역)에 상주하고 있는 객체 데이터를 바이트 형태로 변환하는 기술직렬화된 바이트 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 형태를 말하기도 한다.

✅ Serializable

public interface Serializable() {
}

우리가 만든 클래스가 파일에 읽거나 쓸 수 있도록 할 때, 다른 서버로 보내고 받을 수 있도록 할 때 반드시 이 인터페이스(Serializable)를 구현해야 한다.

  • 생성한 객체를 파일로 저장할 때
  • 저장한 객체를 읽을 때
  • 다른 서버에서 생성한 객체를 받을 때

한마디로, Serializable 인터페이스를 구현하면 JVM에서 해당 객체는 저장하거나 다른 서버로 전송할 수 있게 해준다.

👩‍👦 Serializable이 없는 경우의 직렬화

보통의 경우는 직렬화가 불가능하다.
하지만 Serializable을 implement한 클래스를 상속받은 경우에는 가능하다.

transient란?

보통 클래스의 멤버변수 전부 직렬화 대상에 해당된다.
하지만 보안 상의 문제나 기타 이유로 멤버 변수의 일부를 제외하고 싶다면 transient를 사용하면 된다.

public class User implements Serializable {
    private String id;
    private transient String password;
    private String email;

    //....
}

다른 객체를 멤버변수로 가지고 있다면

int, long, String 등 기본 자료형 뿐만 아니라 다른 객체를 멤버변수로 사용하는 경우가 많다.

public class User implements Serializable {
    private String id;
    private transient String password;
    private String email;

    ItemInfo itemInfo;
    Calendar regDate;

    //....
}

위 코드에서 ItemInfo , Calendar 클래스들 중 Serializable 인터페이스를 구현한 클래스가 하나라도 없다면 java.io.InvalidClassException 예외가 발생한다.
다시 말해서, 멤버 변수의 클래스 중 Serializable을 구현하지 않은 클래스가 있을 때 직렬화를 할 수 없다.

✅ serialVersionUID

직렬화하면 내부에서 자동으로 SerialVersionUID라는 고유의 번호를 생성하여 클래스의 버전을 관리한다.
UID는 직렬화와 역직렬화할 때 중요한 역할을 한다.
이 값이 맞는지 확인 후 처리하기 때문에, SerialVersionUID가 맞지 않는다면java.io.InvalidClassException 예외가 발생하게 된다.

  • serialVersionUID는 필수 값은 아니다.
  • 호환 가능한 클래스는 serialVersionUID 값이 고정되어 있다.
  • serialVersionUID가 선언되어 있지 않으면 클래스의 기본 해쉬값을 사용하게 된다.

특별한 문제가 없으면 Java 직렬화 버전 serialVersionUID 값은 개발자가 직접 관리해줘야 한다.

🤔 자바에서 CSV, JSON 말고 굳이 직렬화를 사용해야 하는 이유?

결론을 이야기하자면 정답은 없다.
목적에 따라 적절히 써야 한다. 직렬화의 장단점은 무엇일까?
아래에서 살펴보자.

자바 직렬화의 장점

자바 직렬화는 자바 시스템 개발에 최적화되어있다.
복잡한 데이터 구조의 객체라도 직렬화 기본 조건만 지키면 큰 작업 없이 바로 직렬화가 가능하다.
특히 데이터 타입이 자동으로 맞춰지기 때문에 관련 부분에 큰 신경을 쓰지 않아도 된다.
그렇게 역직렬화되면 기존 객체처럼 바로 사용 가능하다. 개발자 만만세!!

자바 직렬화의 단점 (유의할 점?)

특별한 문제가 없으면 자바 직렬화 버전(serialVersionUID) 값은 개발자가 직접 관리해야 한다.
역직렬화 대상 클래스의 멤버 변수 타입 변경을 지양해야 한다. 자바 역직렬화는 타입에 엄격하다. (나중에라도 타입이 변경되면 예외를 다 신경써야 한다)

외부(DB, 캐시 서버 등)에 장기간 저장될 정보는 자바 직렬화 사용을 지양해야 한다.
(역직렬화 대상 클래스가 언제 변경될지 모르는 환경에 긴 시간동안 외부에 존재한다는 것 → 언제 예외가 발생할지 모르는 지뢰 시스템)

개발자가 직접 컨트롤할 수 없는 클래스의 객체에 대해서는 직렬화를 지양해야 한다. (보통 프레임워크, 라이브러리 등에서 제공하는 클래스의 객체) 그런 객체는 보통 serialVersionUID를 가지고 있어서 편의상 직렬화시켜 바로 DB에 저장하는데, 이 부분에서 문제가 발생한다.

  1. 프레임워크 또는 라이브러리가 버전업을 하면서 serialVersionUID를 변경
  2. 테스트할 때는 멀쩡하다가 운영에 반영

이와 관련된 예시는 스프링 시큐리티의 SecurityContextImpl 클래스가 있다.
serialVersionUID 값이 스프링 시큐리티의 버전 값이기 때문에 버전이 변경될 때마다 신경쓰인다고 한다.

🙌 결론

자주 변경되는 클래스의 객체에는 자바 직렬화를 사용하지 않는 것이 좋다.
변경에 취약하기 때문에 생각지도 못한 예외가 발생할 가능성이 높다.
특히 역직렬화가 되지 않을 때와 같은 예외처리는 기본적으로 해두는 것을 추천한다.
물론 직렬화 대상 클래스 변경과 같은 문제는 자바 직렬 화만 일어나는 문제는 아니다만
자바 직렬화 기술은 중간에 끼어들 여지가 없는 블랙박스에 가까워서 변경 부분에 취약한 문제가 존재한다.

읽어주셔서 감사합니다 😀🙏

레퍼런스

https://devlog-wjdrbs96.tistory.com/268
https://flowarc.tistory.com/entry/Java-객체-직렬화Serialization-와-역직렬화Deserialization
https://velog.io/@sa1341/Java-직렬화를-하는-이유가-무엇일까
https://techblog.woowahan.com/2550/
https://techblog.woowahan.com/2551/

profile
가보자 가보자

1개의 댓글

comment-user-thumbnail
2022년 6월 5일

backward compatibility 에 대한 언급도 있으면 좋을 것 같습니다.

답글 달기