포폴 플젝에서 편의를 위해 모듈을 만들던 중 문득 'serialVersionUID' 이녀석은 뭐하는 녀석일까 궁금해졌다. 하지만 요녀석을 완벽하게 이해하기 위해선 우선 직렬화에 대한 이해가 먼저임. 이 녀석은 나중에 알아보고 우선 자바의 직렬화 개념에 대해 알아보자.
serialVersionUID에 대한 포스팅 - 내 벨로그
직렬화 (Serialization)란 무엇인가?
일단 사전적 정의 부터 알아보자
- 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 시스템에서도 사용할 수 있도록 Byte 형태로 데이터를 변환하는 기술.
- 혹은 Byte화 된 데이터를 다시 객체로 돌리는 기술(역직렬화)
- JVM의 메모리에 상주한 객체 데이터(Heap이나 Stack에 있는)를 바이트 형태로 변환하는 기술 및 바이트 형태의 데이터를 객체로 변환하여 JVM에 상주시키는 기술
한줄요약
객체 혹은 데이터를 Byte형태로 변환하거나, Byte형태의 데이터를 다시 객체화 하는 기술
직렬화를 왜 쓸까?
그럼 이녀석을 왜 쓸까?
- 직렬화를 사용하게되면 Byte 형식으로 저장되기에 서버간, OS간, 혹은 다른 포맷간 객체를 주고받을때 사용할 수 있다. 다만 Java 직렬화는 JSON, CSV와는 달리 주로 Java 내에서 데이터를 주고 받을 때 사용되곤 한다.
- 직렬화를 이용하면 객체는 Byte 형식으로 전환되기 때문에, 참조값으로 주고받지 않고 Byte type의 Primitive 데이터 묶음이 된다. 이렇게 되면 파일 저장 및 네트워크 전송에서 활용 할 수 있다.
- 이러한 특성때문에 종종 서블릿 세션에서 파일저장, DB저장 하는 동작에서 사용하곤 한다.
또한 캐시에 저장할때도 사용하곤 함
transient
transient라는 예약어를 활용하면 직렬화에서 예외를 둘 수 있다. 이건 간단하게 예시를 알아보자
public class Product implements Serializable{
private String name;
private transient String product_code;
private int price;
public Product(String name, String code, int price){
this.name = name;
this.product_code = code;
this.price = price;
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Product product = new Product("고양이 모래", "PD0001", 30000);
String serialData = serialize(product);
deSerialize(serialData);
}
이런식으로 임의의 객체 클래스에 transient 예약어를 사용한 변수를 두고 직렬화 후 역직렬화를 하면 null이 뜨는 것을 볼 수 있다.
위의 코드를 기준으로 log를 찍었다고 가정했을 때, 결과값은 아래와 같을 것이다.
Product{name='고양이모래', product_code=null, price='30000'}
이런식으로 직렬화에서 예외를 둘 수 있기 때문에 특정값을 보안, 또는 기타 이유로 제외하고 싶다면 활용할 수 있다.
직렬화의 위험
문제가 많은 녀석이다. 검색해보면 최대한 사용을 지양하라는 글들이 많은데 그 중에서 남용하면 안되는 몇 가지 이유를 알아보자
1. 버전관리 문제 및 보안 문제
- 만일 어떤 API를 만들어서 배포를 했을 때, 몇몇 객체에서 직렬화를 이용했다고 가정해보자. 버전 업이 되어 특정 객체에 변수가 추가됐다고 하면, 버전이 다른 API 간 송수신을 하는 상황에서 문제가 발생할 수 있다. 예를 들어 ver1. 에서 없던 변수가 ver2.에 추가 된다면, 역직렬화를 할 경우 추가된 변수가 null이 나올 수 도 있는 상황.
- 보안 이슈 역시 있다. private으로 접근 제한을 건다고 할 지라도, 직렬화가 되는 과정에서 공개가 되어버리기 때문에 정보은닉의 의미가 없어진다.
2. 어디로 튈지 모르는 역직렬화
- 직렬화된 데이터를 역직렬화하는 과정에서 문제가 발생할 수 있다. 직렬화 개념은 자바 하부시스템에서 직간접적으로 쓰이고 있는데, 만일 출신성분이 불분명한 스트림을 역직렬화 한다면, 이것이 원격코드실행, 혹은 DDos 공격으로 이어질 수 있다.
- 1번과 관련된 문제인데, 클래스 변경에 굉장히 엄격하다. 정확히는 클래스 내의 타입에 엄근진한 편인데, 클래스 내 변수를 int -> long, 또는 String -> StringBuffer 로 변경했을 시 예외를 뿜어낸다.
3. 용량문제
- 직렬화 시, 타입에 대한 정보 역시 포함하고 있기에, 다른 포맷에 비해 용량이 비대하다. 만일 클래스 덩어리가 커지면 그에 비례해 늘어남...
- 같은 정보를 담고 있는 객체를 JSON, 직렬화로 각각 운용한다면, 작게는 2배에서 크게는 10배까지도 용량 차이가 나온다고 한다. 참조 - 배민 기술 블로그
4. DB로 저장할 경우
- 위에 기술한 문제들로 인해, DB로 저장해야하는 객체는 직렬화를 웬만하면 피하는게 좋다.
- 버전업이 되면서 직렬화를 사용하는 클래스에 변동이 있을 경우, 이를 DB에 저장하고 불러오는 과정에서 에러가 발생할 수 있다.
- 특히나 DB에서 데이터를 불러오는 과정에서, 역직렬화를 해 다시 객체화 시킬때 저장됐을 당시의 객체와, 현 객체가 다를 경우 기존에 저장된 데이터는 사실상 쓸모없는 0과 1 쪼가리가 될 수 있다. 또한 이는 어떠한 예외를 낳을지 모른다.
마무리
정리하면서 느낀점을 간단명료하게 정리해보겠다.
- 웬만하면 JSON 쓰자.
- 자주 변경될 클래스에는 Serializable 옵션을 제외하자
- 이걸로 DB에 저장할 생각은 웬만하면 말자
- 사실 객체를 Byte [] 화시키는 다른 옵션들이 존재한다. 이걸 애용하자.
- 예전에 회사 일을 하면서 데이터 select 시 자꾸 byte array로 불러와서 애먹었는데, 이걸 알았다면 해결하는데 더 수월하지 않았을까...
- 자바 공부좀 해라.