Java - Serialize(직렬화)
이번 포스팅은 직렬화에 대한 이야기를 해보려고 합니다.
일단 이 부분이 왜 필요하다고 느꼈냐면 Java는 JVM위에서 작동합니다.
그러나 JVM에서 작동하던 객체를 다른 환경으로 옮길려면 어떻게 옮길까요?
컴퓨터는 저희가 코드를 짜듯이 보내면 알 수 없을 것입니다.
그래서 직렬화라는 기법을 이용하여 데이터를 주고 받을 수 있습니다.
❓ 왜 직렬화를 할까?
직렬화는 우리가 휴대전화로 통화를 한다고 생각하면 됩니다.
그때 목소리를 전기적인 신호로 바꿔 상대방에게 전달하고 그것을 받은 상대방은 그것을 해석해 주인에게 전달합니다.
직렬화는 정보를 자유롭게 옮길 수 있다는 큰 장점이 있습니다.
그리고 우리는 데이터를 값 형식 데이터와 참조 형식 데이터로 나눕니다.
참조 형식은 주소값(0x...)을 참조하는데 이러한 주소값을 다른 컴퓨터로 보낸다면?
네 바로 다른 값이 튀어나오겠죠? 그래서 오직 값 형식 데이터만을 직렬화해서 보내야합니다.
❓ 직렬화가 뭐지
자바 시스템 내부에서 사용되는 Object또는 Data를 외부 자바 시스템에서도 사용가능하도록 Byte로 변환한 기술.
즉 JVM에서 쓰던 것을 다른 머신에서 사용 가능하도록 Byte로 변환해주는 기술을 말합니다.
📖 예제
직렬화를 구현하려면 일단 스트림에대한 이해가 필요합니다.
스트림은 데이터를 이상적으로 흐르게 할 수 있는 통로같은 역할을 합니다.
이러한 스트림을 이용해 입.출력을 구현해보도록 하겠습니다.
ByteArrayOutputStream
바이트 배열에 데이터를 입출력하는데 사용되는 스트림입니다.
주로 다른 곳 입출력전 임시저장소로 사용되어집니다.
ObjectOutputStream
이것은 우리가 쓸 출력 스트림을 설정해주면 그것에 맞는 객체 스트림을 생성해줍니다.
우리는 Array를 Byte로 나타내주는 객체 스트림을 생성하면 됩니다.
그리고 객체내의 writeObject를 이용해 객체를 써줍니다.
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
@SpringBootTest
public class SerializeTest {
Logger log = (Logger) LoggerFactory.getLogger(SerializeTest.class);
@Test
public void Test1() throws IOException {
Member member = new Member("황지훈","ghkdwlgns612@naver.com",27);
byte[] serializeMember;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
log.info("baos = {}", baos);
//baos =
ObjectOutputStream oos = new ObjectOutputStream(baos);
log.info("oos = {}", oos);
//oos = java.io.ObjectOutputStream@2b51696b
oos.writeObject(member);
serializeMember = baos.toByteArray();
log.info("serializeMember = {}",serializeMember);
//serializeMember = [-84, -19, 0, 5, 115, 114, 0, 23, 99, 111, 109, 46, 101, 120, 97, 109, 112, 108, 101, 46, 100, 101, 109, 111, 46, 77, 101, 109, 98, 101, 114, 119, -62, -128, 121, 108, -109, 39, -12, 2, 0, 3, 73, 0, 3, 97, 103, 101, 76, 0, 5, 101, 109, 97, 105, 108, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 76, 0, 4, 110, 97, 109, 101, 113, 0, 126, 0, 1, 120, 112, 0, 0, 0, 27, 116, 0, 22, 103, 104, 107, 100, 119, 108, 103, 110, 115, 54, 49, 50, 64, 110, 97, 118, 101, 114, 46, 99, 111, 109, 116, 0, 9, -19, -103, -87, -20, -89, -128, -19, -101, -120]
}
}
class Member implements Serializable {
private String name;
private String email;
private int age;
public Member(String name, String email, int age) {
this.name = name;
this.email = email;
this.age = age;
}
@Override
public String toString() {
return String.format("Member{name='%s', email='%s', age='%s'}", name, email, age);
}
}
위의 코드에서 보듯이 baos객체는 바로 생성되지 않습니다.
그래서 ObjectOutputStream과 ByteArrayOutputStream 을 세트로 사용해주면 되겠습니다.(데코레이션패턴)
그리고 주의해야 될 사항은 직렬화하고싶은 Object는 Serializable클래스를 상속받아야합니다.
역직렬화는 그럼 InputStream으로 받고 Write대신 Read를해서 읽어들이면 되겠죠?
근데 자바에서의 직렬화를 살펴보기 전에 일반적인 데이터의 직렬화방법을 살펴보겠습니다.
1. CSV(Comma-Separated Values)
해석 그대로 콤마를 기준으로 텍스트 데이터를 구분하는 직렬화방법입니다.
이 방법은 다량의 데이터를 직렬화 시킬 때 많이 사용합니다. 아래는 예시입니다.
Member member = new Member("황지훈", "ghkdwlgns612@naver.com", 27);
String csv = String.format("%s,%s,%d",member.getName(), member.getEmail(), member.getAge());
System.out.println(csv);
2. JSON
JSON은 너무 유명한 데이터 형식이죠. KEY, VALUE형태로 구조적인 데이터를 직렬화 할 경우 많이
사용합니다. 이것은 KEY,VALUE는 콜론(:), 각 속성은 쉼표(,)로 구분되어지고 있습니다.
{
"dayOfWeek": [
{ "Sunday": 0 }, { "Monday": 1 }, { "Tuesday": 2 }, { "Wednesday": 3 },
{ "Thursday": 4 }, { "Friday": 5 }, { "Saturday": 6 }
]
}
아래의 JSON에는 몇 가지 특징이 있습니다.
객체는 중괄호({}), 배열은 대괄호([])로 나눠져 있습니다. 이러한 형태를 JSON이라고 합니다.
근데 여기서 이러한 데이터 직렬화방법이 있는데 왜 JAVA만의 직렬화를 사용하는 것일까요?
물론 상황에 따라서는 안 써도 됩니다.
그러나 JVM끼리 네트워킹 할 경우 다양한 라이브러리를 제공하기에 편하게 데이터를 주고 받을 수 있다는 장점이있습니다.
그럼 이러한 JAVA 직렬화를 언제, 어디서 사용하면 되는지 알아보도록 하겠습니다.
그럼 이러한 JAVA 직렬화를 언제, 어디서 사용하면 되는지 알아보도록 하겠습니다.
🔑 JAVA 직렬화의 이용
JAVA직렬화는 JVM메모리에만 상주해있던 데이터를 영속화시킬 때 사용합니다.
JVM이 종료되면 데이터는 휘발합니다. 그래서 그 때 JAVA 직렬화 방법을 이용합니다.
대표적인 예시를 3가지 살펴보겠습니다.
1. 서블릿 세션(Servlet Session)
세션이란 일정 시간동안 상태를 유지시켜주는 기술입니다. 서버에 상태를 저장하는 기술입니다.
이러한 세션은 서블릿에서는 HttpSession이라는 인터페이스로 관리되어집니다.
HttpSession session = request.getSession(); //세션을 가져오는 구문
String userId = (String) session.getAttribute("속성명") //속성명을 통해서 데이터를 호출
session.setAttribute("속성명",속성값) //해당 세션 속성에 데이터를 넣음
이렇게 위의 세션에 값을 저장하고 페이지를 이동하면서 그 값을 getAttribute로 불러서 사용 할 수 있습니다.
이런 경우에 자바의 직렬화가 적용됩니다. 예제는 링크 참조해주시면 되겠습니다.
2. 캐시(Cache)
캐시의 경우는 세션과 달리 클라이언트에 저장되어집니다.
우리는 많은 객체들을 DB에서 꺼내사용 할 경우가 많습니다.
하지만 사용 할 때마다 계속 객체를 호출할 수 없습니다.
그래서 동적인 객체가 아닌 정적인 객체들을 주로 캐시에 담아서 DB자원을 아껴줍니다.
동적인 객체는 계속 수정하기 때문에 DB와의 트랜잭션이 계속 발생합니다.
그래서 동적인 객체는 비효율적입니다. 예제는 링크 참조해주시면 되겠습니다.
3. 자바 RMI(Remote Method Invocation)
최근에는 많이 사용이 안되지만 자바 직렬화의 대표적인 기술이라고 합니다.
잘은 모르지만 원격 시스템간의 메세지 교환을 위해서 사용하는 자바 기술입니다.
아래의 그림과 같이 작동합니다.
최종적으로 원격 시스템의 메소드를 로컬 시스템의 메소드처럼 활용 할 수 있습니다.
이렇게 자바의 직렬화에 대해서 알아봤습니다.
이러한 직렬화는 휘발성을 가진 데이터를 어딘가에 저장하고 싶을 경우
혹은 다른 머신으로 객체나 정보를 이동하고 싶은 경우 사용하면 유용 할 것 같습니다.
긴 글 읽어주셔서 감사합니다.