Serializable(3)
날짜: 2021년 6월 13일
학습목표
저번 포스팅까지 자바의신 교재를 통한 직렬화에 대한 공부는 끝마쳤다. 언제나 그랬듯이, 이게 뭔가?.. 그냥 문법을 익히는 정도로만 끝난 것 같다. 이번 포스팅에서는 자바 직렬화에 대해서 아래의 항목들을 좀더 자세히 공부해보자.
- 자바 직렬화는 왜 쓰는가?
- 직렬화의 종류
- 자바 직렬화는 어디에 쓰이는가?
- 자바 직렬화의 장단점
- DMP Serializable 사례 분석
자바 직렬화는 왜 쓰이는가?
직렬화가 왜? 쓰이는지 알아보기 이전에 먼저 직렬화의 종류들에 대해서 알아보자.
문자열 형태의 직렬화
직접 데이터를 문자열 형태로 확인 가능한 직렬화다. 범용적인 API나 데이터를 변환해 추출할 때 많이 사용된다.
표형태의 다량의 데이터를 직렬화 시 CSV가 많이 쓰이고, 구조적인 데이터는 이전에는 XML을, 최근에는 JSON형태를 많이 쓰고 있다. CSV와 JSON형태에 대해서 알아보자.
- CSV
- 데이터를 표현하는 가장 많이 사용되는 방법 중 하나로 콤마(,) 기준으로 데이터를 구분한다.
김대한,이한국,박코리아
- 자바에서 의 사용법예제에서는 문자열로 단순히 변경했다. 자바에서는 Appache Commons CSV, opencsv등의 라이브러리 등을 이용 할 수 있다.
Member member = new Member("김대한",29,"한국"); //Member객체를 csv로 변환 String csv = String.format("%s, %d, %s", member.getName(), member.getAge(), member.getCountry()); System.out.println(csv);
- JSON
- 최근에 가장 많이 사용하는 포맷으로, 자바스크립트(ECMAScript)에서 쉽게 사용 가능하고, 다른 데이터 포맷 방식에 비해 오버헤드가 적기 때문에 인기가 많다.
{ name : "김대한", age : 29, country : "한국" }
- 자바에서 사용방법JSON도 물론 이렇게 직접 문자열을 만들일은 거의 없다. 자바에서는 Jackson, GSON등의 라이브러리를 이용해 변환할 수 있다.
Member member = new Member("김대한", 29, "한국"); // member객체를 json으로 변환 String json = String.format( "{\"name\":\"%s\",\"age\":%d,\"country\":\"%s\"}", member.getName(), member.getEmail(), member.getAge()); System.out.println(json);
이진 직렬화 방법
데이터 변환 및 전송 속도에 최적화해 별도의 직렬화 방법을 제시하는 구조다. 직렬화 뿐아니라, 전송 방법에 대한 부분도 있지만, 지금은 직렬화 부분에 대해서만 알아보자.
이진 직렬화의 종류로는 ProtocolBuffer, Apache Avro등이 있다. 이중 프로토콜 버퍼에 대해서 알아보자.
- ProtocolBuffer(프로토콜 버퍼)
- 프로토콜 버퍼는 구글에서 제안한 플랫폼 독립적인 데이터 직렬화 플랫폼이다.
- 프로토콜 버퍼는 특정 언어 또는 플랫폼에 종속되지 않는 방법을 구현하기 위해 직렬화 하기 위한 데이터를 표현하기 위한 문서가 따로있다.
- 자바에서 사용방법이렇게 기술된 member.proto 문서를 프로토컬 버퍼 컴파일러를 이용해 개발하기 원하는 언어로(여기서는 자바) 변환해야 한다.
- 프로토콜 버퍼 컴파일러는 별도 설치 혹은 Maven, Gradle을 이용한다.
자바 직렬화와 다른 점은 데이터 스펙을 표현하기 위한 문서가 존재하는 부분이고, 그 외에는 대동소이하다.Member member = Member.newBuilder() .setAge(29) .setName("김대한") .setCountry("한국") .build(); ByteArrayOutputStream baos = new ByteArrayOutputStream() member.writeTo(baos); // 프로토콜 버퍼 직렬화된 데이터 byte[] serializedMember = baos.toByteArray();
- 컴파일 하게되면 다음과 같이 프로토콜 버퍼형태의 Member 클래스가 생성된다.
- 프로토콜 버퍼 컴파일러는 별도 설치 혹은 Maven, Gradle을 이용한다.
message Member { required string name = 1; required int32 age = 2; optional string country = 3; }
그래서 왜 자바 직렬화를 쓰는데?
그럼 다시 자바 직렬화를 왜 사용하는지 생각해보자. CSV,JSON, 프로토콜 버퍼 등은 시스템의 고유 특성과 상관없는 대부분의 시스템에서의 데이터 교환 시 많이 사용된다.
그.러.나
자바 직렬화 형태의 데이터 교환은 자바 시스템 간의 데이터 교환을 위해서 존재한다. 고 생각하면 된다.
https://woowabros.github.io/experience/2017/10/17/java-serialize.html
??.. 그럼 자바에서도 CSV랑 JSON을 쓰면되지 왜 자바 직렬화를 쓰나??
- 사실 정답은 없다. 그저 목적에 따라 적절하게 써야하기 때문이다.
그럼, 앞서 말한 직렬화 방식과는 자바 직렬화의 장,단점을 알아보자.
- 자바 직렬화의 장점
- JSON 또는 CSV 등 형태의 포맷을 이용하면 직렬화 또는 역직렬화시에 특정 라이브러리를 도입해야 쉽게 개발이 가능하며, 구조가 복잡하면 직접 매핑시켜줘야 하는 작업도 포함해야한다.
- 자바직렬화는 자바시스템에서 개발에 최적화 되어있다. 복잡한 데이터 구조의 클래스 객체라도 직렬화의 기본조건(Serializable구현)만 지키면 바로 직렬화 및 역직렬화가 가능하다.
- 데이터 타입또한 자동으로 맞춰지기 때문에, 큰 신경을 쓰지 않아도 된다.
- 직렬화된 객체를 역직렬화시 기존 객체처럼 바로 사용할수 있다.
- 자바 개발시 상당히 편리함!
- 자바 직렬화의 단점
- 역직렬화시 클래스 구조 변경 문제
- Serializable와NIO(2)에서 알아본 객체 저장후 클래스 정보 변경시 serialVersionUID 불일치의 문제다. 즉, 개발자가 직접 serialVersionUID를 관리해줘야 하는 부담이 생긴다.
- 또한 serialVersionUID(이하 SUID)를 관리한다 할지라도, 변수의 타입이 바뀌면 또다시 오류가 발생한다.
즉, 나중에라도 클래스 정보 변경에 대한 예외를 다 신경써야한다.
- 용량문제
- 자바 직렬화는 기본적으로 타입에 대한 정보 등, 클래스의 메타정보도 가지고 있기 때문에, 다른 직렬화 포맷에 비해 상대적으로 용량이 크다. 특히 클래스 안에 클래스 또 리스트 등의 형태의 객체를 직렬화하게 되면, 내부에 참조하고 있는 모든 클래스의 메타정보를 가지게 되 용량이 비대해 진다. 그래서 JSON같은 최소의 메타정보만 가지고 있는 포맷에 비해 최소 2배 ~ 최대10배까지도 차이가 날수있다.
- 직렬화 된 데이터를 메모리 서버(Redis, Memcached)에 저장하는 형태를 가진 시스템에서 용량문제가 두드러진다. 메모리 서버 특성상 용량이 크지 않기 때문에, 핵심만 요약해서 기록하는 형태가 효율적이다. 시스템의 규모가 커지는 시점에서는 반드시 직렬화의 형태에 대해 고민해보자.
- 역직렬화시 클래스 구조 변경 문제
자바 직렬화는 언제 그리고 어디서 쓸까?
JVM 메모리에만 상주된 객체 데이터를 그대로 영속화(Persistence)가 필요할 때 사용한다. 시스템이 종료되더라도 사라지지 않는 장점을 가지며, 영속화 된 데이터기 때문에 네트워크로 전송도 가능하다. 또, 필요할때는 직렬화된 객체를 가져와 바로 역직렬화해 객체를 사용할 수있다. 그럼 이런 장점과 특성을 가진 자바 직렬화는 어디서 쓰일까?
- 서블릿 세션 (ServletSession)그래서 세션에 필요한 객체는 Serializable을 구현하는것을 추천한다.
→ DMP프로젝트에서 Serializable을 구현해야 했던 이유다!! - 서블릿 깃반의 WAS들은 대부부 세션의 자바 직렬화를 지원한다. 물론 단순히 세션을 서블릿 메모리 위에서 운용한다면 직렬화가 필요없지만, 파일로 저장하거나 세션 클러스터링, DB를 저장하는 옵션 등을 선택하면 세션 자체가 직렬화 되어 저장되어 전달된다.
- 캐시(Cache)
- 자바 시스템에서 퍼포먼를 위해 캐시(Ehcache, Redis, Memcached,...) 라이브러리 시스템을 많이 사용한다. 이렇게 캐시 할 부분을 자바 직렬화된 데이터를 저장해서 사용하게 된다. 물론 자바 직렬화만 이용하지는 않지만, 가장 간편해서 많이 사용한다.(외부 서버에는 쓰지않도록 하자!..)
- 자바RMI(Remote Method Invocation)이 원격시스템의 메소드를 호출 시 전달하는 메세지(보통 객체)를 자동으로 직렬화 시켜 사용한다.
- 자바RMI를 간단히 이야기하면 원격 시스템 간의 메세지 교환을 위해 사용하는 자바에서 지원하는 기술이다. 보통 원격 시스템과의 통신을 위해 IP와 포트를 이용해 소켓통신을 하지만, RMI는 그 부분을 추상화해 원격에 있는 시스템의 메소드를 로컬 시스템의 메소드인 것처럼 호출할 수 있다.
총정리
- 특별한 문제가 없으면 SUID는 개발시 개발자가 직접 관리해야한다.
- SUID값이 동일하면 맴버변수 및 메소드의 추가는 문제가 없으나, 제거 및 이름변경은 오류는 일으키지 않지만, 데이터는 누락된다.
- 역직렬화 대상의 클래스 맴버변수 타입변경을 지양해야한다. → 바로 오류발생, 나중에라도 타입변경이 됬을때, 이미 이전에 직렬화 된 데이터가 존재한다면, 그에 따른 예외를 모두 신경써야한다.
- 외부(DB,캐시 서버, NoSQL 서버 등)에 장기간 저장될 정보는 사용을 지양해야한다. 역직렬화 대상의 클래스가 언제 변경될지 모르기 때문이다. 긴 시간 외부에 존재했던 직렬화 데이터는 쓰레기 데이터가 될 가능성이 높다.또, 언제 예외가 발생할지 모르는 지뢰 시스템이 될 수 도 있다.
- 개발자가 직접 컨트롤하지 못하는 객체에 대해서는 지양해야한다.
- 직접 컨트롤하지 못하는 객체들이란 : 프레임워크 또는 라이브러리에서 제공하는 객체
- 이런 객체들은 직접 SUID를 가지고 있기도 한데, 개발시 편의상 직렬화 시켜 DB 또는 캐시 서버에 바로 저장시 이때 많은 문제가 발생한다.
- 예시
- 프레임워크 또는 라이브러리가 버전업을 하면서 SUID 변경
- 테스트 시에는 발생 안하다가 운영에 반영
- 용량문제가 있으므로, 트래픽이 많이 증가하는 서비스의 경우 다른 포맷을 고려하자.
Feedback
자바 직렬화의 장점과 단점 등에 대해서 알아볼수 있었고, 또 다른 직렬화 방법에는 뭐가 있는지, 어떤 상황에 어떤 직렬화를 써야할지 대략적으로 생각해볼수 있던 포스팅이었다.
알게된 점
- 자바 직렬화는 왜 쓰는가?
- 자바 직렬화는 자바 시스템 개발시 매우 편리하다!
- 직렬화의 종류
- 문자열 직렬화 : CSV,JSON
- 바이트코드 직렬화 : ProtocolBuffer 등..
- 자바 직렬화는 어디에 쓰이는가?
- 캐시 : 캐시되는 부분을 자바 직렬화로 저장한다.
- 서블릿 세션 → 이부분 때문에 DMP 프로젝트에서 Serializable 구현을 했어야 했다.
- 자바RMI
- 자바 직렬화의 장단점
- 자바 시스템에 최적화되어 간편하게 사용할 수있다.
- SUID문제와, 클래스변경에 매우 민감하다. 예외처리를 모두 생각해야함
- 외부서버에 저장은 지양해야한다. → 변화에 매우 취약하므로...
- 용량이 다른 포맷에 비해 상대적으로 크다 → 클래스 메타정보를 가지고 있기 때문이다.
- DMP Serializable 사례 분석
- 세션 클러스터링에 의해 세션들이 직렬화 되었기 때문이었다!!
알아야할 점
- 세션 클러스터링이 뭔지 간만 보자!
References
자바 직렬화, 그것이 알고싶다. 실무편 - 우아한형제들 기술 블로그
자바 직렬화, 그것이 알고싶다. 훑어보기편 - 우아한형제들 기술 블로그
자바의신 VOL2
'프로그래밍 > Java' 카테고리의 다른 글
NIO(2) (0) | 2021.06.14 |
---|---|
NIO(1) (0) | 2021.06.14 |
Serializable(2) (0) | 2021.06.13 |
Serializable(1) (0) | 2021.06.11 |
Java I/O (0) | 2021.06.11 |