본문 바로가기

CS/Java

직렬화(Serialization)

SSAFY 프로젝트를 진행하면서 몇몇 클래스에 Serializable 인터페이스를 상속하였는데, 이번 기회에 직렬화에 대해 자세히 알아보자.

 

개발을 진행하다 보면 다음과 같은 상황이 발생한다.

 -  생성한 객체나 데이터를 파일로 저장한다.

 -  저장한 객체나 데이터를 읽어온다.

 -  다른 서버에서 생성한 객체나 데이터를 받아온다.

 

우리가 생성한 객체나 데이터를 파일로 읽거나 쓸 때, 다른 서버로 보내거나 받을 때 직렬화가 꼭 필요하다.

직렬화는 왜 필요할까?

대부분의 OS의 프로세스 구현은 서로 다른 가상 메모리 주소 공간을 갖기 때문에 Object 타입의 참조값(주소값)을 전달한다면 사용할 수 없다. 때문에 서로 다른 메모리 공간 사이의 데이터 전달을 위해서는 주소값이 아닌 Byte 형태로 직렬화(변환)된 객체 데이터를 전달해야한다. 그리고 직렬화된 데이터를 받아 사용하려면 역직렬화를 거처 사용해야한다.

정리하자면, 직렬화는 JVM의 메모리에서만 상주되던 객체 데이터를 그대로 영속화(Persistence)할 때 사용된다.

직렬화 예시

class Member implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private String password;

    public Member(String name, String password){
            this.name = name;
            this.password = password;
    }

    @Override
    public String toString() {
            return "Member{" +
                    "name='" + name + '\'' +
                    ", password='" + password + '\'' +
                    '}';
    }
}

java.io.Serializable 인터페이스를 상속받는다.

serialVersionUID를 설정한다.

 

String path = "/Users/tak/test.md";

Member member = new Member("꽁탁", "1234");

try{
    FileOutputStream fos = new FileOutputStream(path);
    ObjectOutputStream oos = new ObjectOutputStream(fos);

    oos.writeObject(member);
} catch (Exception e){
    e.printStackTrace();
}

java.io.ObjectOutputStream을 이용하여 직렬화하고 파일에 저장한다.

역직렬화 예시

public static void main(String[] args) {
    String path = "/Users/tak/test.md";

    try {
        FileInputStream fis = new FileInputStream(path);
        ObjectInputStream ois = new ObjectInputStream(fis);

        Object obj = ois.readObject();
        Member member = (Member)obj;

        System.out.println(member);
    } catch (Exception e){
        e.printStackTrace();
    }
}

 

위와 같이 역직렬화를 통해 데이터를 받아오는 것을 확인할 수 있다.

그렇다면, Serializable를 상속받을 때 serialVersionUID는 무엇일까?

serialVersionUID

자바에서 직렬화 대상 객체는 동일한 serialVersionUID(SUID)를 가지고 있어야 한다. 

직렬화 과정에서는 SUID 선언이 없어도 된다. 내부적으로 클래스의 구조 정보를 이용해 자동으로 생성된 해쉬값이 할당된다. 때문에 클래스의 멤버 변수가 추가되거나 삭제되면 SUID 가 변경되는데, SUID는 직렬화와 역직렬화 과정에서 값이 서로 맞는지 확인한 후에 처리하기 때문에 이 값이 맞기 않다면 InvalidClassExcetpion 예외가 발생한다.

따라서, SUID는 직접 관리하는 것을 권장한다.

 

 

SSAFY의 프로젝트를 진행하면서 한 가지 의문이 들었다.

실수로 DTO 클래스에 Serializable를 상속받지 않았는데, 정상적으로 DB에 저장되고 DB에서 데이터를 가져와 사용할 수 있었다.

그래서 찾아본 결과 서블릿 기반의 WAS(톰캣, 웹로직 등)들은 대부분 세션의 자바 직렬화를 지원하고 있다고 한다.

따라서, DB에 저장하는 시점에 세션 자체가 직렬화되어 전달되는 것 같다.

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

로깅 시 System.out.println()을 사용하지 않는 이유  (0) 2023.08.13
new String()과 리터럴("")  (1) 2023.08.12
String, StringBuffer, StringBuilder  (0) 2023.08.12
equals()와 hashCode()  (0) 2023.08.11
Java 컴파일 과정  (0) 2023.05.18