자바 직렬화 (Serializable) 와 구현할 때의 문제점

Damongsanga·2024년 2월 12일
0

CS 스터디 질문으로 "Java Serializable로 데이터를 통신할 때 문제점을 설명하시오" 라는 내용으로 자료를 찾아본 후 공부하여 정리한 내용이다.
생각해보니 개발하면서 Serializable과 inputStream, OutputStream을 사용한 적이 그리 많지 않았던 것 같다🤔
(파일 입출력 받을 때 정도?)
자바 직렬화는 언제 사용하면 좋은지, 이에 따른 문제점과 해결방안은 무엇인지 공부해보았다.

해당 글은 https://velog.io/@alsgus92/Java-Serialize에-좀-더-깊이-알아보자 글과 이펙티브 자바, 배달의 민족 기술 블로그를 참고하여 작성하였다. 참조 링크는 맨 하단에 두었다. 레쯔꼬!

📌 요약

Java 직렬화는 Java 시스템간의 data 전달시에만 사용하라
직렬화 구현시에는 시스템 호환성을 고려하여 JSON 형태의 직렬화 방법을 고민해야한다
긴 시간 외부에 저장하는 의미있는 데이터는 자바 직렬화를 사용하지 마라

직렬화

  • Java를 기준으로 설명하면 객체를 바이트의 배열로 변환하여 파일, 메모리, 데이터베이스를 통해서 송수신이 가능하도록 하는 것

  • JVM(Java Virtual Machine 이하 JVM)의 메모리에 있는 객체 데이터를 바이트 형태로 변환하는 기술(직렬화)과 직렬화된 바이트 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 기술(역직렬화)

  • 직렬화 조건
    1. primitive type
    2. Serializable 인터페이스 상속한 참조형 객체

  • ObjectOutputStream (Java 직렬화 사용)

    Member member = new Member("김배민", "deliverykim@baemin.com", 25);
        byte[] serializedMember;
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
                oos.writeObject(member);
                // serializedMember -> 직렬화된 member 객체 
                serializedMember = baos.toByteArray();
            }
        }
        // 바이트 배열로 생성된 직렬화 데이터를 base64로 변환
        System.out.println(Base64.getEncoder().encodeToString(serializedMember));
    }
  • 역직렬화 조건

  1. 직렬화 대상이 된 객체의 Class가 현재 project의 class path에 존재하고 import 되어있어야

  2. 직렬화와 역직렬화를 진행하는 시스템이 서로 다를 수 있다는 것을 고려해야

  3. 자바 역직렬화 대상 객체는 직렬화했던 객체와 동일한 serialVersionUID를 가지고 있어야함
    없다면 SHA-1 암호함수를 적용해 자동으로 생성해넣음

    private static final long serialVersionUID = 1L;
  • ObjectInputStream (Java 역직렬화 사용)

    // 직렬화 예제에서 생성된 base64 데이터 
        String base64Member = "...생략";
        byte[] serializedMember = Base64.getDecoder().decode(base64Member);
        try (ByteArrayInputStream bais = new ByteArrayInputStream(serializedMember)) {
            try (ObjectInputStream ois = new ObjectInputStream(bais)) {
                // 역직렬화된 Member 객체를 읽어온다.
                Object objectMember = ois.readObject();
                Member member = (Member) objectMember;
                System.out.println(member);
            }
        }

자바 직렬화 사용

  • 언제 사용하는가?
    • 보통 직렬화 방식 (CSV, XML, JSON) 등은 시스템의 고유 특성과 상관없는 통상적인 시스템에서의 데이터 교환 시 사용
    • 그러나 Java 직렬화 형태의 데이터 교환은 Java 시스템 간의 데이터 교환을 위해서 존재한다.
    • 물론 보통의 직렬화 방식을 사용해도 되나 개발자의 편의를 위해서 택하는 것
    • 복잡한 데이터 구조의 클래스 객체도 직렬화 기본 조건만 지키면 쉽게 직렬화, Data type을 자동으로 맞춰주어 편함
  • 어디서 사용하는가?
    • 캐시
      • DB 리소스 절약을 위해 메모리나 파일 등의 저장소에 반복 요청되는 데이터를 저장해두어 응답할 수 있다
    • Servlet 기반의 WAS 통신
      • 단순 서블릿 메모리 위에서 운용하면 직렬화가 불필요하나 파일로 저장하거나 세션 클러스터링, DB 저장을 하게되면 세션 자체가 직렬화되어 전달

자바 직렬화의 문제점

  • 클래스 구조 변경 불가
    - 클래스 변경 시 직접 serialVersionUID 값을 관리해주어야한다
    - 클래스에서 필드 하나만 변경되어도 자동 지정되는 serialVersionUID값이 달라져서 역직렬화가 불가능해지기 때문이다
    - 역직렬화시 필드 추가, 타입 변경 등 클래스 구조가 변경되면 java.io.InvalidClassException 발생
    - 때문에 장시간 외부에 저장될 정보들은 Java 직렬화 사용을 지양해야
    - 역직렬화된 클래스가 언제 변경이 될지 모르고 이 값은 쓰레기 값이 되거나 언제 예외가 터질지 모르는 지뢰가 됨
    - 그러다보니 개발자가 직접 컨트롤할 수 없는 객체(프레임워크, 라이브러리)를 직렬화하는 것을 추천하지 않음

  • 직렬화 Data 용량 문제
    - 직렬화시에 필요한 클래스의 메타정보도 필요하기 때문에 상대적으로 다른 포맷에 비해 용량이 큼
    - 2배에서 최대 10배까지도..!
    - 이렇게 직렬화 data로 서버 or DB 통신을 하면 트래픽이 급증 가능
    - 스프링에서 기본적으로 지원하는 캐시모듈 (Spring Data Redis, Spring Session, ..) 등이 기본적으로 자바 직렬화 형태로 제공됨
    - JSON 같은 다른 형태로 직렬화하여 바꿔주는 것을 고려해야

  • 호환성
    - 자바 직렬화를 사용하여 외부 데이터를 저장하면 자바에서만 읽을 수 있음
    - JSON으로 저장되어있다면 MySQL 이나 Redis에서도 다른 언어로 읽을 수 있다

  • 버그와 보안 구멍 증가
    - 직렬화는 자바의 객체 생성 기본 메커니즘인 생성자를 우회하는 객체 생성 기법
    - 생성자가 숨어있음으로 "생성자에서 구축한 불변식을 보장해야하고, 생성 도중 공격자가 객체 내부를 들여다 볼 수 없도록 해야한다"는 사실을 떠올리기 어려워짐
    - 신버전 릴리스시 테스트할 부분이 늘어남 (신버전 직렬화하여 구버전으로 역직렬화할 수 있는지 등..)

  • 내부 클래스는 직렬화를 구현하면 안된다
    - 내부 클래스는 컴파일러가 자동으로 추가하는 필드들이 있음 (바깥 인스턴스 참조와 유효 범위 안 지역변수 값을 저장하기 위한 필드)
    - 때문에 내부 클래스에 대한 기본 직렬화의 형태가 분명하지 않음
    - 단, 정적 멤버 클래스는 구현 가능하다

참조

https://velog.io/@alsgus92/Java-Serialize에-좀-더-깊이-알아보자
https://techblog.woowahan.com/2550/
https://techblog.woowahan.com/2551/
이펙티브 자바 아이템 86 Serializable을 구현할 지는 신중히 결정하라

profile
향유하는 개발자

0개의 댓글

관련 채용 정보