직렬화(Serialization)

2024. 12. 12. 09:09·Language/Java
반응형

직렬화(Serialization)란?

각자 PC의 OS마다 서로 다른 가상 메모리 주소를 갖기 때문에, `Referece Type`의 데이터들은 인스턴스를 전달할 수 없다.

이런 문제를 해결하기 위해선 주소값이 아닌 `Byte` 형태로 직렬화된 객체 데이터를 전달해야 한다.

자바에서 직렬화(Serialization)란 객체를 바이트 스트림으로 변환하여 파일, 데이터베이스, 네트워크 등을 통해 저장하거나 전송할 수 있게하는 과정이다.

직렬화된 객체는 모두 파일 저장이나 네트워크 전송 시 파싱이 가능한 기본형(Primitve Type)이 되며, 역직렬화(Deserialization) 과정을 통해 원래 객체로 복원할 수 있다.

 

 

 

직렬화를 하는 경우는 주로 다음과 같다.

  • 객체 지속성
    JVM 메모리에 있는 객체를 영속화 할 수 있다.
  • 네트워크 전송
    객체를 네트워크를 통해 다른 JVM으로 전송할 수 있다.
  • 캐싱
    객체 직렬화하여 저장함으로써 데이터베이스 조회를 줄일 수 있다.

 

 

Serializable 인터페이스 사용 예시

직렬화하려는 클래스에 `Serializable` 인터페이스를 구현해야한다.

`Serializable` 인터페이스는 아무 메서드도 정의하지 않고,

단지 해당 객체가 직렬화 가능하다는 표시로 사용된다. ( = 마커 인터페이스)

public interface Seralizable {}

 

아래는 `Serializable` 인터페이스 사용 예시이다.

class Person implements Serializable {
    private static final long serialVersionUID = 1L; // 직렬화 버전 관리
    private String name;
    private int age;
    private String password;
    // ...
 }

`Serializable` 인터페이스를 상속받은 `Person` 클래스는 직렬화 가능하다는 것을 의미한다.

 

⭐️ 객체 내 직렬화 대상은 `Serializable` 인터페이스를 상속 받은 객체, Primitive Type 데이터이다.

⭐️ static 필드는 직렬화 되지 않는다. 클래스의 상태가 아닌 인스턴스의 상태만 직렬화된다.

  • 만약에, Person 객체 내에 `Serializable`을 상속받지 않은 객체가 멤버 변수로 있다면 이는 직렬화 할 수 없다.
    ㄴ`InvalidClassException`가 발생할 것이다.
  • 참고로, `String`은 Primitibe Type이 아니지만 `Serializable`을 상속받았기 때문에 직렬화 가능하다.
    ㄴ 기본형 래퍼 클래스, 문자열 및 관련 클래스, 컬렉션 클래스, 날짜 및 시간 관련 클래스 등은 `Serializable`을 상속받았다.
  • `serialVersionUID`는 `static`으로 선언되어 직렬화에 포함하지 않는다.
    ㄴ하지만, 직렬화 된 객체에 클래스의 메타데이터로 포함되어 있다.

위의 예시에서는, 클래스의 모든 멤버변수가 직렬화 대상이 된다.

 

 

 

transit 키워드 사용 예시

하지만 위 예시에서 `password` 와 같이 민감한 데이터는 직렬화 시 제외시킬 필요가 있다.

보안상의 이유나 기타 이유로 직렬화 대상에서 제외하고 싶다면 `transient` 키워드를 사용하면 된다. 

class Person implements Serializable {
    private static final long serialVersionUID = 1L; // 직렬화 버전 관리
    private String name;
    private int age;
    private transient String password;
    // ...
 }

위의 예시에서 `password` 변수는 `transient` 키워드를 붙여 직렬화 대상에서 제외된다.

참고로, `transient` 키워드가 포함되어 있는 멤버 변수는 역직렬화 과정에서 기본값으로 초기화된다.

 

 

 

serialVersionUID 예시

위의 예시들에서 `Serializable` 인터페이스를 상속받은 객체의 멤버 변수 중 `serialVersionUID`를 주의 깊게 볼 필요가 있다.

이는 자바에서 직렬화된 객체의 버전을 식별하기 위해 사용되는 고유한 식별자이다.

직렬화된 객체를 역직렬화 할 때 클래스의 호환성을 확인하는데 사용된다.

 

 

 

`serialVersionUID` 주요 목적

  • 버전 관리를 통한 호환성 유지
    직렬화된 객체를 역직렬화할 때, 클래스가 변경되었더라도 동일한 `seriallVersionUID`를 가지면 호환성이 유지가 된다.
  • 클래스 변경 감지
    만약 `seriallVersionUID`가 다른 것을 역직렬화 하게 되면 JVM은 `InvalidClassException`을 던진다.
    이는 클래스가 직렬화된 객체와 호환되지 않음을 나타낸다.

 

 

만약 `seriallVersionUID`를 선언하지 않으면 JVM이 자동으로 생성한다. 하지만 자동생성된 값은 클래스 구조에 따라 달라지므로 클래스가 변경될 때마다 값이 달라질 수 있다. 때문에 개발자가 직접 관리하는 것이 좋다.

 

 

(예시 1) seriallVersionUID 선언하기

class Person implements Serializable {
    private static final long serialVersionUID = 1L; // 직렬화 버전 관리
    private String name;
    private int age;
    private transient String password;
    // ...
 }

위와 같이 `seriallVersionUID`를 명시적으로 선언할 수 있다.

인스턴스 객체와는 독립적인 값이면서 클래스 수준에서 버전 관리를 위해 `static`으로 선언한다. 

이때 이 값을 `final`로 선언하여 변경되지 않도록 보장해야 한다.

 

 

(예시 2) 만약 예시 1에서 새로운 필드를 추가하면? 

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    private transient String password;
    private String email; // 새롭게 추가된 필드
    // ...
 }

email 필드를 추가했음에도 `seriallVersionUID`는 여전히 같다.

때문에 버전이 `1`인 객체를 역직렬화 할 수 있다.

하지만, 역직렬화된 객체는 새 필드 `email`에 대해 기본 값(null)을 갖게 된다.

 

 

(예시 3) 만약 예시 1에서 버전을 달리 하면? 

class Person implements Serializable {
    private static final long serialVersionUID = 2L; // 직렬화 버전 관리
    private String name;
    private int age;
    private transient String password;
    // ...
 }
java.io.InvalidClassException: Person; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

`serialVersionUID`를 2로 변경한 경우, 예시 1에서 직렬화 한 객체를 예시 3의 클래스로 역직렬화 하려고 하면 `InvalidClassException`을 발생시킨다. 자바가 클래스의 `serialVersionUID`값이 달라졌음을 인식하고, 역직렬화를 거부했기 때문에 발생하는 예외이다.

💡 직렬화에 포함되지 않는 `serialVersionUID`. 직렬화 된 객체의 버전을 어떻게 알 수 있는가?
`serialVersionUID`는 `static`으로 선언되기 때문에 직렬화되지 않는다.
대신, 클래스에 대한 메타데이터로 직렬화되는 데이터와 함께 저장된다.
역직렬화 시 자바는 직렬화된 데이터에 포함된 `serialVersionUID`의 버전을 확인하고 호환성을 판단한다.

 

728x90
반응형

'Language > Java' 카테고리의 다른 글

[JSP] JSP(JavaServer Pages)의 정의와 기본 구성 요소  (1) 2025.01.02
record 클래스 (Java 14)  (0) 2024.12.13
동기화(synchronization)와 락(Lock)  (1) 2024.12.11
Java의 스레드(Thread)  (0) 2024.12.10
Error & Exception  (1) 2024.12.09
'Language/Java' 카테고리의 다른 글
  • [JSP] JSP(JavaServer Pages)의 정의와 기본 구성 요소
  • record 클래스 (Java 14)
  • 동기화(synchronization)와 락(Lock)
  • Java의 스레드(Thread)
settong
settong
    250x250
  • settong
    개 발 자 국
    settong
  • 전체
    오늘
    어제
    • 전체보기 (202)
      • Computer Science (50)
        • Network (7)
        • Operating System (18)
        • Data Structure (9)
        • Database (11)
        • Algorithm (5)
      • Language (17)
        • Java (17)
        • Javascript (0)
        • Python (0)
      • Devops (20)
        • AWS (0)
        • Naver Cloud (16)
        • CICD (3)
        • 웹 서버 관리 (1)
      • Front (0)
        • React (0)
      • Backend (5)
        • Spring (5)
      • 코딩 테스트 정복기 (110)
        • 백준 (51)
        • 프로그래머스 (53)
        • 기타 (6)
      • etc (0)
      • 경제 상식 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    github actions
    완전탐색
    ncp200
    Spring Boot
    다익스트라
    lcs
    다이나믹프로그래밍
    백준
    BFS
    집합
    프로그래머스
    분할정복
    Network
    해시
    ncp
    CI/CD
    벨만포드
    DFS
    ncp202
    백트래킹
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
settong
직렬화(Serialization)
상단으로

티스토리툴바