자바에서 저장한 Reference Type의 데이터들은 각자 OS마다 다른 메모리 주소를 가리키기 때문에 외부 환경에서의 자바에서 데이터에 접근하기 위해서는 Reference Type으로 저장된 데이터들의 Primitive Type으로의 전환이 필요하다.
시스템적으로는 JVM내의 메모리(힙, 스택)에 상주하는 객체 데이터를 바이트 형태로 변환하는 기술을 직렬화, 반대로 직렬화 되어 있는 바이트 형태의 데이터를 객체 데이터로 변환하여 JVM에 상주시키는 기술을 역직렬화라고 한다.
즉 데이터를 전송, 저장 가능한 형태로 변환하기 위해서 직렬화를 수행한다.
자바에서의 직렬화 & 역직렬화
직렬화 시 Object(객체) 데이터들은 Byte Stream 형태의 데이터로 변환된다. 역직렬화 시 Byte Stream 형태의 데이터들은 Object 형태의 데이터로 변환된다.
직렬화
직렬화를 위해선 데이터의 타입이 기본(primitive) 타입이고 객체가 java.io.Serializable 인터페이스를 상속받아야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 클래스를 직렬화가 가능하도록 Serializable 인터페이스 상속
public class User implements Serializable{
// primitive 타입 데이터
private String name;
private int age;
private String email;
public User(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
@Override
public String toString() {
return String.format("User name: %s, User age: %s, User email: %s", name, age, email);
}
}
|
cs |
직렬화는 java.io.ObjectOutputStream 객체를 이용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public static class Serialize{
public void SerializeTest() {
User user = new User("Pseudocode", 26, "pseudocode@gmail.com");
byte[] serialized; // 바이트 배열 데이터
try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
try(ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(user);
serialized = baos.toByteArray(); // serialized : 직렬화된 객체
}
// 직렬화된 객체를 base64로 변환하여 출력
System.out.println("Base64Serialized: " + Base64.getEncoder().encodeToString(serialized));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
|
cs |
역직렬화
java.io.ObjectOutputStream을 이용해 직렬화 된 데이터를 역직렬화하기 위해서는 아래의 조건을 충족해야 한다.
직렬화가 된 객체의 클래스가 클래스 패스에 존재해야 한다. 반드시 import가 되어 있어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public static class Deserialize {
public void DeserializeTest(String base64Data) {
byte[] Deserialized = Base64.getDecoder().decode(base64Data);
try(ByteArrayInputStream bais = new ByteArrayInputStream(Deserialized)) {
try(ObjectInputStream ois = new ObjectInputStream(bais)) {
// 역직렬화 된 객체 읽어오기
Object objectUser = ois.readObject();
// 형변환
User user = (User) objectUser;
System.out.println(user);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
|
cs |
자바 직렬화를 사용하는 이유?
데이터 직렬화는 문자열 형태의 직렬화, 이진 직렬화 두 가지로 나뉜다. 문자열 형태의 직렬화는 데이터를 우리가 흔히 아는 JSON, CSV(Comma-separated values)과 같은 확인 가능하고 읽을 수 있는 형태로 직렬화하는 것이다.
- 자바에서의 CSV 직렬화
1
2
3
4
|
// 편의를 위해 String으로 변환. 라이브러리 사용해서 csv로 변환가능
User user = new User("Pseudocode", 26, "pseudocode@mail.com");
String csv = String.format("%s,%d,%s", user.name, user.age, user.email);
System.out.println(csv);
|
cs |
- 자바에서의 JSON 직렬화
1
2
3
4
5
|
{
name: "Pseudocode",
age: 26,
email: "pseudocode@mail.com"
}
|
cs |
// jackson 사용해서 json으로 변환
User user = new User("Pseudocode", 26, "pseudocode@mail.com");
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
System.out.println(json);
csv, json같은 문자열 직렬화는 특정 시스템이 아닌 각자 다른 시스템 간에도 원활한 데이터 교환을 위해 사용된다.
반면 자바 직렬화는 자바 시스템 간의 데이터 교환을 위해 사용된다.
그럼 그냥 csv나 json 직렬화를 사용하면 되지 왜 굳이 자바 직렬화를 사용하는 것일까?
자바 직렬화의 장점과 단점
자바 직렬화를 굳이 사용해야 하느냐? 라고 묻는다면 정답은 없다. "There is no silver bullet"이라는 말처럼 상황과 목적에 따라 적절하게 사용하면 된다. 자바 직렬화의 장점과 단점에 대해 알아보자.
장점:
- 기본적으로 자바 직렬화는 자바 시스템 개발에 최적화되어있다.
- json, csv 직렬화는 개발자가 직접 매핑을 해야하지만, 자바 직렬화는 클래스의 구조가 복잡하더라도 직렬화의 조건만 지키면 큰 작업 없이 직렬화가 가능하다. 반대로 역직렬화도 조건만 지키면 쉽게 처리할 수 있다.
- 데이터 타입도 자동으로 맞춰주고 역직렬화를 통해 바로 기존의 자바 객체를 사용할 수 있기 때문에 개발자 입장에서는 굉장히 편하다는 장점이 있다.
단점:
- 자바 클래스 내의 변수가 추가되면 java.io.InvalidClassException 에러가 발생한다. 클래스에 변수를 추가하고 직렬화와 역직렬화를 진행하게 되면 SerialVersionUID가 변경되기 때문에 에러가 발생한다. 따라서 클래스 변수 추가 시에 개발자가 직접 SerialVersionUID를 관리해주어야 한다.
- SerialVersionUID를 직접 관리해줘도 문제가 발생하는 경우가 있다. 클래스의 변수명은 같으나 변수 타입이 바뀌는 경우, 마찬가지로 java.io.InvalidClassException가 발생한다. 자바 직렬화는 타입에 대해 굉장히 엄격하기 때문에 SerialVersionUID 관리를 하더라도 타입은 변경하면 에러가 발생한다.
- 반면 SerialVersionUID를 관리하면 기존에 존재하는 클래스 변수를 삭제하거나 추가하는 것은 가능하다.
장점 | 단점 |
자바 시스템 개발에 최적화 | 클래스 구조 변경 필요시에 개발자가 일일이 SerialVersionUID 관리 |
복잡한 클래스라도 조건만 지키면 손쉬운 직렬화 / 역직렬화 가능 | 클래스 변수 타입 변경 불가능 |
데이터 타입 자동으로 맞춰줌 | 역직렬화 시에 엄격한 데이터 변수 타입 체크 |
직렬화는 규칙만 잘 지키면 편리하지만 SerialVersionUID를 직접 관리해줘야 하는 번거로움, 역직렬화 시 엄격한 변수 타입 체킹 등 신경 써야할 부분이 몇 가지 존재한다. 따라서 자주 변경되는 클래스의 자바 직렬화를 지양하자.
직렬화/역직렬화가 필요한 상황
- 서블릿 세션(Servlet Session)
- 서블릿 기반의 톰캣과 웹로직 같은 WAS들은 대부분 세션의 자바 직렬화를 지원한다.
- 데이터를 파일로 저장하거나 세션 클러스터링, DB에 저장하는 경우 세션 직렬화가 되어 저장되기 때문에 저장하려는 데이터에 Serializable 인터페이스를 구현해 두는 것이 좋다.
- 자바 RMI(Remote Method Invocation)
- 자바 RMI란 원격 시스템 간의 메시지 교환을 위해 자바에서 지원하는 기술이다.
- 일반적으로 원격 시스템 통신을 위해 IP와 포트를 사용하여 소켓 통신을 하지만 RMI로 이 부분을 추상화하여 원격 시스템 메서드를 로컬 시스템 메서드처럼 호출할 수 있다.
- 원격 시스템의 메서드 호출 시의 메서드를 자동으로 직렬화하고, 원격시스템에서는 전달받은 메시지를 역직렬화를 통해 변환하여 사용한다.
- 캐시(Cache)
- DB리소스를 줄이기 위해서 Redis와 같은 외부 라이브러리를 사용하게 되면 캐시할 데이터를 직렬화해서 저장하게 된다.
- 아래는 프로젝트를 할 때 redis 직렬화 설정을 위해 작성한 코드의 일부이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Configuration
@RequiredArgsConstructor
@EnableCaching
public class RedisCacheConfig {
private final RedisConnectionFactory redisConnectionFactory;
@Bean
public CacheManager redisCacheManager() {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
// 키 String 직렬화
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new StringRedisSerializer()))
// 값 String 직렬화
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofHours(48L)); // 캐시 만료 시간 48시간으로 설정
return RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration).build();
}
|
cs |
Reference
https://www.scientecheasy.com/2021/07/serialization-in-java.html/
Serialization in Java | Deserialization, Example - Scientech Easy
Learn Serialization in Java with example, Serializable interface, Deserialization in Java, use of objects serialization, Serialize arrays
www.scientecheasy.com
☕ 자바 직렬화(Serializable) - 완벽 마스터하기
자바의 직렬화 & 역직렬화 직렬화(serialize)란 자바 언어에서 사용되는 Object 또는 Data를 다른 컴퓨터의 자바 시스템에서도 사용 할수 있도록 바이트 스트림(stream of bytes) 형태로 연속전인(serial) 데
inpa.tistory.com
https://techblog.woowahan.com/2550/
자바 직렬화, 그것이 알고싶다. 훑어보기편 | 우아한형제들 기술블로그
{{item.name}} 자바의 직렬화 기술에 대한 대한 이야기입니다. 간단한 질문과 답변 형태로 자바 직렬화에 대한 간단한 설명과 직접 프로젝트를 진행하면서 겪은 경험에 대해 이야기해보려 합니다.
techblog.woowahan.com
https://techblog.woowahan.com/2551/
자바 직렬화, 그것이 알고싶다. 실무편 | 우아한형제들 기술블로그
{{item.name}} 자바의 직렬화 기술에 대한 대한 두 번째 이야기입니다. 실제 자바 직렬화를 실무에 적용해보면서 주의해야 할 부분에 대해 이야기해보려고합니다. 자바 직렬화 실제 업무에서 사용
techblog.woowahan.com
'Java' 카테고리의 다른 글
[Java] Error & Exception(에러와 예외 클래스) (0) | 2024.04.04 |
---|---|
[Java] 문자열 클래스 (0) | 2024.02.03 |
[Java] 오토박싱(Autoboxing) & 오토 언박싱(Autounboxing) (0) | 2024.01.25 |
[Java] Primitive Type & Reference Type (0) | 2024.01.25 |
[Java] Call by value & Call by reference (0) | 2024.01.25 |