반응형
CS 기본 지식 공부 과정입니다.
우아한 형제들 기술 블로그에 내용이 잘 정리되어있어 참고했습니다
https://techblog.woowahan.com/2550/
1. 직렬화란?
- 자바 내부에서 사용되는 객체 혹은 데이터를 외부 자바 시스템에서도 사용할 수 있게 byte 형태의 데이터로 변환하는 기술입니다
- 반대로 byte 형태로 변환된 데이터를 자바 객체, 데이터로 다시 변환하는 것을 역직렬화라고 한다
- 필요성
- 자바 시스템 간의 데이터 교환을 위해 사용한다 ( 시스템 독립적이기 위해서는 CSV, JSON, ProtoBuffer 등등 사용 )
- 직렬화의 기본 조건만 지키면 별도의 큰 코딩없이(java.io.Serializable만 구현해주면) 쉽게 데이터 교환이 가능하다
2. 직렬화 방법
- 직렬화 조건
- 자바 기본형 타입 ( primitive type ), Wrapping 클래스도 가능합니다
- java.io.Serializable 구현한 클래스
- Serializable 구현 클래스 예제
// MyObject import java.io.Serializable; import java.time.LocalDateTime; import java.util.StringJoiner; public class MyObject implements Serializable { private Long id; private String name; private int age; private LocalDateTime registerTime; public MyObject(long id, String name, int age, LocalDateTime registerTime) { this.id = id; this.name = name; this.age = age; this.registerTime = registerTime; } public long getId() { return id; } public String getName() { return name; } public int getAge() { return age; } public LocalDateTime getRegisterTime() { return registerTime; } @Override public String toString() { return new StringJoiner(", ", MyObject.class.getSimpleName() + "[", "]") .add("id=" + id) .add("name='" + name + "'") .add("age=" + age) .add("registerTime=" + registerTime) .toString(); } }
- 직렬화 예제
- ByteArrayOutputStream과 ObjectOutputStream을 이용하여 "MyObject"를 직렬화할 수 있습니다
- Serializable이 구현되지 않은 객체를 직렬화하면 Exception이 발생합니다
// Exception java.io.NotSerializableException: com.my.toyproject.serializable.MyObject
- 테스트 코드 예제
// JUnit5 test @Test void serializableTest() throws IOException { MyObject myObject = new MyObject(1L, "주지민", 30, LocalDateTime.now()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(myObject); byte[] results = baos.toByteArray(); System.out.println(Base64.encodeBase64String(results)); }
- 추가: Base64 인코딩 필요성
- Base64란 8비트 이진 데이터(예를 들어 실행 파일이나, ZIP 파일 등)를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 문자열로 바꾸는 인코딩 방식을 가리키는 개념
- Base64를 사용하게 되면 6bit당 2bit의 Overhead가 발생하여 전송해야 될 데이터의 크기가 약 33% 정도 늘어나고, 인코딩과 디코딩의 추가 연산까지도 필요하게 되는데 굳이 사용하는 이유는?
- 문자를 위한 byte 코드를 전송해야할 때, 공통된 포맷으로 변경하여 시스템 독립적으로 동일하게 전송 또는 저장되는 걸 보장하기 위해 사용하여 아래와 같은 문제점을 해결
- ASCII는 7 bits Encoding인데 나머지 1bit를 처리하는 방식이 시스템 별로 상이합니다.
- 일부 제어 문자의 경우 시스템 별로 다른 코드값을 가진다
3. 역직렬화 방법
- 위에서 설명했듯이 byte 형태로 변환된 데이터를 자바 객체, 데이터로 다시 변환하는 것을 역직렬화라고 합니다
- 역직렬화 예제
- ByteArrayInputStream과 ObjectInputStream을 이용하여 역직렬화를 할 수 있습니다
- 테스트 코드 예제
// Deserializable byte[] bytes = Base64.decodeBase64(encodedBase64); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bais); Object deserializableObject = ois.readObject(); MyObject deserializableMyObject = (MyObject) deserializableObject; System.out.println(deserializableMyObject);
4. 유의사항
- serialVersionUID 관리
- 자바 직렬화 스펙 정의
- SUID(serialVersionUID) 필수 값은 아니다.
- 호환 가능한 클래스는 SUID값이 고정되어 있다.
- SUID가 선언되어 있지 않으면 클래스의 기본 해쉬값을 사용한다
- 즉 직접 기술하지 않아도 내부 적으로 serialVersionUID 정보가 추가되며, 자동으로 생성된 클래스의 해쉬 값이 serialVersionUID로 사용된다는 것을 확인 할 수 있습니다
- 따라서 serialVersionUID를 기술하지 않고 객체의 스펙을 변경하게되면 serialVersionUID도 같이 변경이 되고, 기존 스펙으로 직렬화를 했던 데이터를 역직렬화 요청시 java.io.InvalidClassException이 발생하게 됩니다
- serialVersionUID의 값이 동일하면 멤버 변수 및 메서드 추가는 크게 문제가 발생하지 않습니다
- 멤버 변수 제거 및 이름 변경 또한 오류는 발생하지 않지만 데이터는 누락됩니다
- 결론
- 외부(DB, 캐시 서버, NoSQL 서버 등)에 장기간 저장될 정보는 자바 직렬화 사용을 지양
- 역직렬화 대상의 클래스가 언제 변경이 일어날지 모르는 환경에서 긴 시간 동안 외부에 존재했던 직렬화된 데이터는 쓰레기가 될 가능성이 높습니다
- 자주 변경되는 클래스는 사용을 자제하자
- 자바 직렬화 스펙 정의
728x90
반응형
'Tech > Java' 카테고리의 다른 글
팀원들과 동일한 코드 스타일을 적용해보자!! (0) | 2021.08.08 |
---|---|
ObjectMapping 기술, MapStruct는 뭘까? (0) | 2021.08.08 |
올바른 Optional 사용법 (0) | 2021.08.08 |