직렬화란?
문득 개발을 하면서 갑자기 그런 생각이 들었습니다
자바 직렬화가 도대체 뭐지?
Spring을 공부하면서 ObjectMapper를 쓰는데 직렬화한다는 것을 알게되고, 갑자기 궁금해져서 글을 쓰게됬어요 ㅎㅎ
그래서 직렬화란
Java 시스템 내에서 사용되는 객체를 외부의 Java 시스템에서도 사용할 수 있도록 바이트(byte) 형태로 데이터를 변환하는 기술과
바이트로 변환된 데이터를 다시 객체로 변환하는 기술을 통틀어서 이야기 합니다!
조금 더 상세하게 이야기하면..!
JVM의 메모리에 상주되어 있는 데이터를 Byte형태로 변환하는 것을 의미합니다!
그럼 어떻게.?
Java의 Interface중 하나인 Serializable을 상속받으면 사용이 가능합니다.!
package com.huisam.springstudy.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.io.Serializable;
import java.time.LocalDateTime;
@AllArgsConstructor
@Getter
@Builder
@EqualsAndHashCode
public class Order implements Serializable {
public static final long serialVersionUID = 42L;
private Long id;
private String name;
private String product;
private Integer price;
private String address;
private LocalDateTime orderedTime;
}
네 요렇게 사용하시면 됩니다~! 참 쉽죠?
사용법은 간단해 보이나, serialVersionUID를 지정해주지 않으면 정말 큰일납니다
왜냐하면, 다른 시스템끼리 통신해서 직렬화/역직렬화를 진행하는데
같은 객체라도 serialVersion이 다르면 역직렬화시 다른 객체로 판단해버리기 때문입니다!
그럼 직렬화랑 역직렬화에 대한 방법을 적어볼까요?
// serailize
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(order);
byteArray = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
직렬화 하는 방법은 byteArrayOutputStream을 활용해서 write해주면 됩니다!
// deserialize
try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
ObjectInputStream ois = new ObjectInputStream(bais)) {
final Object object = ois.readObject();
afterOrder = (Order) object;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
역직렬화 하는 방법은 byteArrayInputStream을 활용해서 read해주면 됩니다!
이 과정이 귀찮으시죠?ㅎㅎ
apache재단의 commons-lang3 라이브러리에 이미 다 구현이 되어있습니다
가져다 쓰시죠 ㅎㅎ
// serailize
final byte[] byteArray = SerializationUtils.serialize(order);
// deserialize
afterOrder = SerializationUtils.deserialize(byteArray);
그럼 이제 만들었으니, order와 afterOrder가 정말로 같은지 테스트 해볼까요?
class EntityTest {
@Test
@DisplayName("테스트이름")
void test_order_serializable() {
/* given */
Order order = Order.builder()
.id(1L)
.name("huisam")
.address("Seoul")
.product("사과")
.price(123)
.orderedTime(LocalDateTime.now())
.build();
Order afterOrder = Order.builder().build();
/* when */
// serailize
final byte[] byteArray = SerializationUtils.serialize(order);
// deserialize
afterOrder = SerializationUtils.deserialize(byteArray);
/* then */
assertThat(afterOrder).isEqualTo(order);
}
}
본 테스트를 돌리면 통과됩니다~!ㅎㅎ
이로써 직렬화 역직렬화에 대한 검증이 끝났군요
언제 써야 되니.?
일단 주로 직렬화가 사용될 때는.. 객체의 영속성(Persistence)를 보장하고 싶을 때 많이 사용됩니다!
시스템이 사라지거나, 장애가 발생하여 죽어도 데이터가 없어지지 않기 때문이죠!
주로 다음과 같은 항목에서 사용합니다!
- 서블릿 세션(Servlet Session)
- 캐시(Cache): 데이터베이스에서 조회하는 동작보다 캐시에서 꺼내오는 동작이 더 저렴하기 때문이다!
- Java RMI(Remote Method Invocation): 원격 시스템간의 메세지 교환을 위해 사용된다
데이터를 전송하고 보장하는데에 있어서 이렇게나 좋다는 것을 알게 되었는데요..
그럼 이거 정말 막 쓰고 다녀도 될까요?
정말 안전할까요.?
직렬화는 단점을 알고 써라
데이터베이스의 저장하는 경우 장기간 저장될 확률이 매우 높은데, 이때는 사용하는 것을 지양해야 합니다!
역직렬화된 객체가 언제, 어디서 변할지 모르는 위험을 가지고 있고, 이는 반드시 예외로 귀속되기 때문입니다!
프레임워크단에서의 직렬화는 최대한 안쓰는 것이 좋습니다
프레임워크에서의 객체는 보통 개발자들이 건드리지도 않고, 건드린다 해도 다른 시스템에서는 안건드릴 확률이 매우 높습니다
개발자가 마음대로 serialVersionUID를 수정해서 조작한다면, 이는 버그로 이어질 확률이 높아져요ㅠㅠ
그렇기 때문에, 변경이 잦은 객체에서는 절대로 쓰지 마라 입니다
그래서 나만의 규칙은?
그래서 제가 직렬화를 사용하기로 한 규칙은
- 외부 저장소로 저장되는 데이터는 짧은 만료시간이 아니면 사용하지 않는다
- 역직렬화시 예외가 생길 수 있다는 가능성을 숙지하고 사용한다
- 자주 변경되는 Domain로직에는 사용하지 않는다
- 긴 만료시간을 가지는 데이터는 Json으로 사용한다!
입니다
모두들 자기들만의 규칙을 만들어서 사용하는 개발자가 되셨으면 좋겠습니다 ^-^
오늘도 즐거운 코딩하세요~!
참고자료
자바 직렬화, 그것이 알고 싶다
'Developer > Kotlin & Java' 카테고리의 다른 글
Java8 자바 안정적인 비동기 처리 - CompletableFuture (0) | 2020.05.22 |
---|---|
Java8 - Stream과 함수형 인터페이스(lambda 표현식)에 대해서 (0) | 2020.05.01 |
SOLID - DIP(Dependency Inversion Principle)란 : 의존성 역전 원칙 (8) | 2019.11.27 |
SOLID - ISP(Interface Segregation Principle)란? : 인터페이스 분리 원칙 (6) | 2019.11.26 |
SOLID - LSP(Liskov Substitution Principle)이란? 리스코프 치환 원칙 (0) | 2019.11.22 |