[Java] 직렬화(Serialization)

김주경·2021년 9월 29일
0
post-thumbnail
post-custom-banner

포폴 플젝에서 편의를 위해 모듈을 만들던 중 문득 '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;
        }
}

// Main
public static void main(String[] args) throws IOException, ClassNotFoundException {
        Product product = new Product("고양이 모래", "PD0001", 30000); // Model 객체
        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 쪼가리가 될 수 있다. 또한 이는 어떠한 예외를 낳을지 모른다.

마무리

정리하면서 느낀점을 간단명료하게 정리해보겠다.

  1. 웬만하면 JSON 쓰자.
  2. 자주 변경될 클래스에는 Serializable 옵션을 제외하자
  3. 이걸로 DB에 저장할 생각은 웬만하면 말자
  4. 사실 객체를 Byte [] 화시키는 다른 옵션들이 존재한다. 이걸 애용하자.
  5. 예전에 회사 일을 하면서 데이터 select 시 자꾸 byte array로 불러와서 애먹었는데, 이걸 알았다면 해결하는데 더 수월하지 않았을까...
  6. 자바 공부좀 해라.
profile
안냐세온
post-custom-banner

0개의 댓글