모르는 용어 정리

직렬화(Serialization), 역직렬화(Deserialization)란?

참깨빵위에참깨빵_ 2022. 6. 6. 23:30
728x90
반응형

인텐트로 객체를 넘길 때 직렬화라는 걸 사용해서 넘겼던 적이 있다. 예제 코드도 구글에 많이 있어서 그냥 썼었는데, 직렬화라는 용어 자체를 잘 모르는 채 썼기 때문에 공부하고 포스팅하려고 한다.

오라클 공식문서에선 객체 직렬화에 대해 아래와 같이 말하고 있다.

 

https://docs.oracle.com/javase/tutorial/jndi/objects/serial.html

 

Serializable Objects (The Java™ Tutorials > Java Naming and Directory Interface > Java Objects in the Dire

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com

객체를 직렬화한다는 건 객체 상태를 바이트 스트림으로 바꿔서 바이트 스트림을 객체의 복사본으로 되돌릴 수 있음을 의미한다. 자바 객체는 해당 클래스 또는 상위 클래스가 java.io.Serializable 인터페이스 또는 하위 인터페이스인 java.io.Externalizable을 구현하는 경우 직렬화 가능하다. 역직렬화는 직렬화된 객체 형식을 객체의 사본으로 다시 바꾸는 프로세스다. 자바 플랫폼은 직렬화 가능한 객체가 직렬화되는 기본 방법을 지정한다. 클래스는 이 기본 직렬화를 재정의하고 해당 클래스의 객체를 직렬화하는 고유한 방법을 정의할 수 있다. 객체가 직렬화되면 해당 클래스를 식별하는 정보가 직렬화된 스트림에 기록된다. 그러나 클래스의 정의(클래스 파일) 자체는 기록되지 않는다. 필요한 클래스 파일을 찾고 로드하는 방법을 결정하는 것은 객체를 역직렬화하는 시스템의 책임이다...(중략)

 

아래는 코틀린 공식문서에서 설명하는 직렬화다.

 

https://kotlinlang.org/docs/serialization.html

 

Serialization | Kotlin

 

kotlinlang.org

직렬화는 응용 프로그램에서 쓰는 데이터를 네트워크를 통해 전송하거나 DB 또는 파일에 저장 가능한 형식으로 바꾸는 프로세스다. 역직렬화는 외부 소스에서 데이터를 읽고 이를 런타임 객체로 바꾸는 반대 프로세스다. 이들은 제3자와 데이터를 교환하는 대부분의 앱에서 필수적인 부분이다. JSON 및 프로토콜 버퍼와 같은 일부 데이터 직렬화 형식은 특히 일반적이다. 언어 중립적이고 플랫폼 중립적이기 때문에 모든 현대 언어로 작성된 시스템 간에 데이터 교환이 가능하다. 코틀린에서 데이터 직렬화 도구는 별도의 구성요소인 kotlinx.serialization에서 쓸 수 있다...(중략)...모든 코틀린 직렬화 라이브러리는 org.jetbrains.kotlinx 그룹에 속한다...(중략)...kotlinx.serializaiton에는 다양한 직렬화 형식에 대한 라이브러리가 포함돼 있다.

- JSON
- Protocol buffers
- CBOR
- Properties
- HOCON

JSON 직렬화를 제외한 모든 라이브러리는 실험적이므로 예고없이 API가 바뀔 수 있다

 

지금은 코틀린으로 안드로이드 개발을 진행하기 때문에 코틀린 위주로 알아본다.

kotlinx.serialization에 대해선 제트브레인 블로그에서 설명하는 내용이 있으니 관심있다면 읽어보자. 21.05.16에 작성된 글이다.

 

https://blog.jetbrains.com/ko/kotlin/2021/05/kotlinx-serialization-1-2-released/#fast-json

 

kotlinx.serialization 1.2 출시: 고속 JSON 처리, value 클래스 지원, 새롭게 개편된 문서 등 다양한 개선

kotlinx.serialization 1.2가 출시되었습니다! 최신 버전의 멀티 플랫폼 직렬화 라이브러리가 여러 가지 개선 사항을 제공합니다. 주요 내용은 다음과 같습니다. JSON 직렬화가 그 어느 때보다 빨라졌습

blog.jetbrains.com

 

직렬화와 역직렬화가 무엇인지 가볍게 확인했으니 다른 글을 통해서 두 개가 각각 무엇인지 좀 더 확인해본다.

 

https://en.wikipedia.org/wiki/Serialization

 

Serialization - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Conversion process for computer data In computing, serialization (US spelling) or serialisation (UK spelling) is the process of translating a data structure or object state into a form

en.wikipedia.org

직렬화는 데이터 구조 또는 객체 상태를 파일 또는 메모리 데이터 버퍼에 저장하거나 네트워크를 통해 전송 및 다른 컴퓨터 환경 등에서 나중에 재구성된다. 직렬화 형식에 따라 결과로 나온 일련의 비트를 다시 읽으면 원래 객체의 의미상 동일한 사본을 만드는 데 사용할 수 있다. 참조를 광범위하게 쓰는 것과 같은 복잡한 객체의 경우 이 프로세스가 간단하지 않다. 객체 지향 객체의 직렬화에는 이전에 연결된 관련 메서드가 포함되지 않는다. 객체를 직렬화하는 이 프로세스는 일부 상황에서 객체 마샬링이라고도 한다. 일련의 바이트에서 데이터 구조를 추출하는 반대 작업은 역직렬화다.

 

https://hazelcast.com/glossary/serialization/

 

Serialization

Serialization is the process of converting a data object into a series of bytes that saves the state of the object in an easily transmittable form.

hazelcast.com

직렬화는 데이터 객체(데이터 저장소 영역 안에서 표현되는 코드와 데이터의 조합)를 전송하기 쉬운 형태로 객체 상태를 저장하는 일련의 바이트로 바꾸는 과정이다. 이 직렬화된 형태에서 데이터는 다른 데이터 저장소, 응용 프로그램 또는 다른 대상에게 전달될 수 있다
-> 데이터 직렬화는 객체를 쉽게 저장, 전송할 수 있게 바이트 스트림으로 바꾸는 프로세스다.

일련의 바이트에서 데이터 구조 또는 객체를 구성하는 역 프로세스는 역직렬화다. 역직렬화 프로세스는 객체를 재생성하므로 데이터를 더 쉽게 읽고 프로그래밍 언어의 기본 구조로 수정할 수 있다
-> 직렬화, 역직렬화는 함께 작동해서 데이터 객체를 이식 가능한 형식으로 변환, 재작성한다

직렬화를 사용하면 객체 상태를 저장하고 새 위치에 객체를 다시 만들 수 있다. 직렬화는 객체의 저장과 데이터 교환을 모두 포함한다. 객체는 여러 구성요소로 구성돼 있으므로 모든 부분을 저장하거나 전달하려면 일반적으로 상당한 노력이 필요하므로 직렬화는 객체를 공유 가능한 형식으로 캡처하는 표준 방법이다. 이를 통해 객체를 전송할 수 있다

 

다른 글에서도 비슷하게 설명하기 때문에 이제 코드로 확인해본다.

예제로 찾은 코드는 DTO 클래스에 Serializable을 붙인 다음 파일로 만들고, 그 파일의 내용을 getter로 가져오는 코드다.

 

먼저 Account 클래스를 만들었다.

 

import java.io.Serializable;

public class Account implements Serializable {
    private String email;
    private transient String name;
    private String address;

    public Account(String email, String name, String address) {
        this.email = email;
        this.name = name;
        this.address = address;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

 

여기서 transient 키워드는 직렬화 과정에서 빼고 싶은 데이터가 있을 경우 사용하는 키워드다.

비밀번호 같이 보안이 중요한 데이터의 경우 직렬화도 하고 싶지 않을 수 있는데 이 때 사용할 수 있다. 나중에 저 키워드가 제대로 작동했는지 getter로 확인한다.

그리고 메인 클래스를 아래처럼 쓴다.

 

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 객체 쓰기 (IOException만 있어도 됨)
        Account account = new Account("abc@abc.com", "name", "seoul");
        FileOutputStream fos = new FileOutputStream("user.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(account);
        oos.close();

        // 객체 읽기 (ClassNotFoundException 추가해야 함)
        Account readAccount;
        FileInputStream fis = new FileInputStream("user.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        readAccount = (Account) ois.readObject();
        System.out.println("주소 : " + readAccount.getAddress());
        System.out.println("이름 : " + readAccount.getName());
        System.out.println("이메일 : " + readAccount.getEmail());
        ois.close();
    }
}

 

try-catch를 쓰지 않고 메인 메서드 옆에 throws 키워드로 예외 클래스명만 써주면 try-catch가 없어도 빨간 줄이 생기지 않는다.

먼저 위에서 만든 Account 클래스를 적당히 인스턴스화한 다음 FildOutputStream, ObjectOutputStream을 써서 파일을 생성한다. ObjectOutputStream과 ObjectInputStream은 각각 객체를 직렬화, 역직렬화하는 메서드를 가진 스트림이다. 자바에선 객체 안에 저장된 내용을 파일로 저장하거나 네트워크로 전송하려면 객체를 바이트 형태로 만들어야 하는데 이 때 사용한다. 그러려면 객체는 직렬화가 되어있어야 한다.

 

객체 쓰기 부분의 코드를 실행하면 해당 프로젝트의 루트 폴더에 이런 파일이 생긴다.

 

 

이 메모장의 파일을 열면 아래와 같은 외계문자가 나올 것이다.

 

 

깨진 한글은 무시하고 확인해 보면 위에서 만든 Account 클래스와 address, email이란 글자가 보이고 메인 클래스에서 입력했던 문자열들이 보인다.

반면 name 글자는 안 보인다. Account에서 사용했던 transient 키워드가 제대로 작동해서 직렬화 과정 중 해당 데이터를 제외한 것이다.

 

그리고 객체 읽기 부분의 println() 안의 내용을 확인해보면 아래와 같다.

 

 

이름 값은 직렬화에서 제외됐으니 getter로 가져와도 null을 리턴하는 걸 볼 수 있다.

 

마지막으로 JSON 형태의 값을 직렬화, 역직렬화하는 예제도 확인해보자.

 

import java.io.Serializable;

public class Target implements Serializable {
    private String name;
    private int age = 0;

    public Target(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        // JSON 형태로 출력되게 toString() 재정의
        return String.format("{\"name\"=\"%s\",\"age\"=%d}", name, age);
    }
}

 

그리고 이 Target 클래스를 메인 클래스에서 사용한다. 바이트 배열이 추가된 것 말고는 큰 흐름과 쓰이는 클래스는 위와 비슷하다.

 

import java.io.*;
import java.util.Arrays;

public class Test2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // JSON 직렬화
        Target target = new Target("철수", 14);
        byte[] serializableArr = new byte[1024];    // 간단한 정보만 직렬화하기 때문에 byte[] 크기를 1024로 설정해도 무관하다
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(target);

        serializableArr = bos.toByteArray();
        System.out.println("직렬화 전 : " + target.toString());
        System.out.println("직렬화 후 : " + Arrays.toString(serializableArr));

        System.out.println("========================================================================");

        // JSON 역직렬화
        byte[] deserializableArr = Arrays.copyOf(serializableArr, serializableArr.length);
        System.out.println("역직렬화 전 : " + Arrays.toString(deserializableArr));
        ByteArrayInputStream bis = new ByteArrayInputStream(deserializableArr);
        ObjectInputStream ois = new ObjectInputStream(bis);

        Object deserializableObject = ois.readObject();
        Target readTarget = (Target) deserializableObject;
        System.out.println("역직렬화 후 : " + readTarget.toString());
    }
}

 

println()을 확인해보면 아래와 같다. 직렬화 후, 역직렬화 전의 로그는 가로로 너무 길어서 중간에 잘랐다.

 

 

직렬화, 역직렬화에 대한 지식도 지식이지만 toString()을 재정의하고 처음 보는 클래스들을 써 봐서 재밌었다.

 

 

참고한 사이트)

 

https://reakwon.tistory.com/160

 

[자바] 직렬화(Serialization)의 개념과 객체 파일로 저장하기 예제 - ObjectOutputStream, ObjectInputStream

직렬화(Serialization) 우리는 파일에 텍스트를 기록하고, 이진 데이터를 기록하는 방법은 많이들 알고 계시겠습니다. 그런데 만약 이런 종류의 데이터들이 아니라 객체를 파일로 저장하거나 읽어

reakwon.tistory.com

 

https://kkh0977.tistory.com/263

 

137. (java/자바) json형식으로 직렬화 (serialize) 바이트값 포맷, 역직렬화(deserialize) 원본 데이터 확인

/* =========================== */ [ 개발 환경 설정 ] ​ 개발 툴 : Eclipse 개발 언어 : Java /* =========================== */ ​ /* =========================== */ [소스 코드] package AI4; import jav..

kkh0977.tistory.com

 

반응형