잘못 알고 있었던 직렬화와 역직렬화
---
layout: post
category: example
---
2023.12.16 - 잘못 알고 있었던 직렬화와 역직렬화
JPA 를 사용하여 데이터베이스에 있는 데이터를 가져와 객체로 변환하는 과정에서 에러가 발생했다..
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.Instant` from String ”2012-02-01”: Failed to deserialize java.time.Instant: (java.time.format.DateTimeParseException) Text ’2012-02-01’ could not be parsed at index 10
직렬화와 역직렬화는 바이트코드에서 객체로의 정보교환만을 의미하는 것으로 알고 있었는 데 String 에서 Object 로의 변환 역시 역직렬화라고 표현하는 것을 보았다.
알고보니 직렬화, 역직렬화는 그런 의미가 아니였다는 것을 깨달았고 관련된 내용을 정리해보았다.
컴퓨터는 0 과 1 만 안다.
사실 데이터에는 객체가 없다. 우리가 통신에서 주고 받는 데이터는 사실 바이트 코드로 대부분 0과 1로 이루어져 있다.
객체는 사람들이 이해하기 쉽게 하기 위해 만든 추상적인 개념에 불과하다. 메모리에는 객체가 없다. 0과 1로 이루어진 데이터만 있을 뿐이다.
코드를 작성하는 에디터에 코드를 작성하고 저장버튼을 누르자. 그리고 다음날 저장된 파일을 에디터를 통해 열면 저장된 모습 그대로의 코드가 있을 것이다. 이건 어찌보면 당연한 거다. 저장을 했으니 말이다.
우리는 에디터를 통해 연 파일을 편집하고 있다고 생각하지만 이는 “직렬화” 라는 과정을 통해 실제 파일에 코드(실제로는 바이트코드)를 작성한 것이다. 해당 파일을 에디터로 여는 행위는 “역직렬화”라는 과정을 통해 파일에 있는 바이트 코드를 역직렬화를 통해 에디터가 읽어드릴 수 있는 형태로 바꾸게 된 것이다.
직렬화와 역직렬화
즉, 직렬화란 객체를 파일이나 통신할 수 있는 쉬운 형태로 변환하는 과정을 말한다. 통신할 수 있는 쉬운형태는 바이트코드가 될 수도 있고 JSON 이나 XML 같이 규격화된 또 다른 통신 형태로 정해놓은 형태일 수도 있다.
역직렬화는 직렬화의 반대로, 직렬화된 정보를 다시 원래대로 객체의 형태로 되돌리는 것을 의미한다.
즉, 바이트코드에서 오브젝트로, 오브젝트에서 바이트코드로 정보의 형태를 바꾸는 것만이 직렬화, 역직렬화를 뜻하는 것이 아니였다.
객체를 JSON, XML, 파일 이 모든 것이 해당된다.
###
파일을 주고 받는 것 또한 직렬화와 역직렬화가 사용된다.
A 라는 컴퓨터와 B 라는 컴퓨터가 서로 파일을 주고 받는 다고 가정해보자.
A 와 B는 각자 컴퓨터 스팩도 다르고 사용하는 프로그래밍 언어도 다르지만, 0과 1이라는 바이트 코드를 전기적 신호로 바꾸어 회선을 통해 전달받을 수 있다.
그렇기 때문에 전달하고자 하는 파일을 먼저 0과 1이라고 하는 바이트코드로 “직렬화” 한다. 0과 1의 바이트코드로 변환된 데이터는 회선을 통해 전기적인 신호로 바뀌어 전달되게 된다.
파일 데이터를 전기적신호를 0과1의 바이트코드로 전달받은 다른 컴퓨터는 이를 “역직렬화”를 통해 바이트데이터를 실제로 존재하는 오브젝트(파일)로 바꾸어 전달받는다.
###
JSON 데이터 주고 받기
또 다른 예로 브라우저에서 서버와 통신을 주고 받을 때 주로 JSON 형태의 데이터를 주고 받는다.
오브젝트 파일에서 JSON 파일로 직렬화 하여 전송하고 다운로드 받은 JSON 파일에서 역직렬화 한다.
###
이제는 이해되는 에러메시지
Cannot deserialize value of type `java.time.Instant` from String ”2012-02-01”: Failed to deserialize java.time.Instant: (java.time.format.DateTimeParseException) Text ’2012-02-01’ could not be parsed at index 10
이 오류 메시지는 java.time.Instant 객체로의 역직렬화 중에 발생한 문제를 설명하고 있다. java.time.Instant는 날짜와 시간을 표현하는 자바의 클래스 중 하나인데, 주어진 문자열 “2012-02-01”을 Instant 객체로 변환할 수 없다는 것을 나타낸다. 즉, “역직렬화” 할 수 없다는 것이다.
java.time.Instant는 ISO 8601 형식에 맞춰진 문자열을 사용하여 날짜와 시간을 나타내고 있다. 그렇기 때문에 역직렬화의 대상이 되는 문자열은 반드시 “YYYY-MM-DDTHH:MM:SSZ”와 같은 형식이어야 한다. 그러나 “2012-02-01”은 시간 정보가 없는 날짜 정보만을 가지고 있기 때문에 Instant로 역직렬화할 때 오류가 발생했던 것이다.
(사실 이러한 문제는 JSON 을 Object 로 변환 할 때도 빈번하게 일어나는 문제이다. 보통 이런 경우 JSON 을 Object 로 직렬,역질렬화를 도와주는 라이브러리를 사용하게 되는 데 위의 문제와 마찬가지로 JSON 에 포맷에 맞지 않는 경우 오브젝트 변환 시 “역직렬화”에 실패했다는 메시지를 자주 볼 수 있을 것이다.)
자바 객체 <-> 파일
Person 이라는 객체를 생성한 후 객체를 직렬화 하여 파일에 저장합니다. 이후 파일에 저장된 객체를 역직렬화 하여 읽어옵니다.
import java.io.*;
// Serializable 인터페이스를 구현하여 직렬화 가능하도록 만듭니다.
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class SerializationExample {
public static void main(String[] args) {
// Person 객체 생성
Person person = new Person("Alice", 30);
// 객체를 직렬화하여 파일에 저장
try (FileOutputStream fileOut = new FileOutputStream("serialized_person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(person);
System.out.println("Person 객체를 직렬화하여 저장했습니다.");
} catch (IOException e) {
e.printStackTrace();
}
// 파일에서 객체를 역직렬화하여 읽어옴
Person loadedPerson = null;
try (FileInputStream fileIn = new FileInputStream("serialized_person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn)) {
loadedPerson = (Person) in.readObject();
System.out.println("Person 객체를 역직렬화했습니다.");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
// 역직렬화된 Person 객체 정보 출력
if (loadedPerson != null) {
System.out.println("이름: " + loadedPerson.getName());
System.out.println("나이: " + loadedPerson.getAge());
}
}
요약
오브젝트를 직렬화하고 역직렬화하는 데 사용되는 포맷을 굉장히 많다. 하지만 핵심은 추상적인 오브젝트를 구체적이고 저장가능한 형태로 정보를 바꾸어 주는 역할을 한다는 것이다.