86. Serializable을 구현할지는 신중히 결정하라

sojukang·2022년 4월 4일
0

Serializable 그저 바라만 보자

소주캉

직렬화가 뭐야?🤔

  • 자바가 객체를 바이트 스트림으로 인코딩하고(직렬화) 그 바이트 스트림으로부터 다시 객체를 재구성하는(역직렬화) 메커니즘이다.
  • 직렬화된 객체는 다른 VM에 전송하거나 디스크에 저장한 후 나중에 역직렬화할 수 있다.

객체를 저장한다는 게 무슨 의미인가?

  • 새로운 객체는 오직 인스턴스 변수로만 구성된다.
    • 객체의 동일성 판정은 두 객체의 인스턴스 변수의 값이 같고 다름을 비교한다!
  • 클래스 변수나 메서드는 static 영역에 이미 적재되어 있다.
  • 따라서 객체를 저장한다는 것은 객체의 모든 인스턴스 변수의 값을 저장한다는 의미이다.
  • 객체를 저장, 전송하기 위한 방법으로 직렬화가 등장한다.

직렬화 쓰지 말라는데?😮

직렬화가 등장한 이유

  • 프로그래머가 어렵지 않게 분산 객체를 만들 수 있다고 생각했다.

직렬화가 나쁜 이유👎

  • 생성자가 보이지 않는다.
  • API와 구현 사이 경계가 모호해진다.(캡슐화? 확장성? 그런 거 없음)
  • 잠재적인 정확성 문제, 성능, 보안, 유지보수성이 안좋다.
    • readObject 메서드는 클래스패스 안의 거의 모든 타입의 객체를 만들 수 있다.
    • 바이트 스트림 역직렬화 과정에서 메서드는 그 타입들 안의 모든 코드를 수행할 수 있다.
    • 코드 전체가 공격 범위에 들어간다.

직렬화 위험 피하는 방법

쓰지 마라면 쓰지마~🤯

아무것도 역직렬화 하지 말자.

  • 우리가 새로 만드는 코드에서 직렬화를 써야 할 이유는 전혀 없다.
  • 객체와 바이트 시퀀스를 변환해주는 다른 메커니즘을 사용하자.

그럼 다른 메커니즘은 뭔데??

  • 직렬화가 아닌 다른 객체를 저장할 방법을 쓰자!!!
    • JSON(박재성씨 아님)
    • 프로토콜 버퍼(Protocol Buffers, protobuf)

직렬화보다 뭐가 더 좋은데??

  • 임의 객체 그래프를 자동으로 직렬화/역직렬화하지 않는다.
  • 속성 - 값 쌍의 집합으로 구성된 간단하고 구조화된 데이터 객체를 사용한다.
  • 기본 타입 몇 개와 배열 타입만 지원한다.

이래도 쓸거야?? 이래도?? 그러면...

  • 객체 역직렬화 필터링(java.io.ObjectInputFilter) 사용하자.
    • 데이터 스트림이 역직렬화되기 전에 필터를 설치한다.
    • 특정 클래스를 받아들이거나 거부할 수 있다.
      • 화이트리스트 방식을 사용하자(블랙리스트 노노)

Serializable 구현할 때 주의점🚧

문제점

구현 하면 수정하기 어려워진다👎

  • Serializable 구현하면 직렬화 결과물도 공개 API가 된다.
  • API 수정하면 직렬화 결과물도 수정해야 한다.
    • 예시(arrayList)
    • 직렬화 가능한 클래스를 수정해도 직렬화-역직렬화가 가능하게 하려면 serialVersionUID를 다른 클래스와 다른 유일한 값으로 수동으로 넣어주어야한다.
    • 그렇지 않으면 수정 전후 자동 생성된 UID가 충돌하여 직렬화-역직렬화가 실패한다.
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;
  • private, default 인스턴스 필드마저 공개된다(캡슐화 깨짐).
  • OCP는 개나 줘버리게 된다.

버그와 보안 구멍 생길 위험이 높아진다👎

  • 역직렬화는 생성자를 거치지 않는 우회 객체 생성 기법이다.
  • 검증이 원활하게 이뤄지지 않을 수 있다.

테스트가 늘어난다👎

  • Serializable 구현 클래스 수정되면 신버전과 구버전 사이에 직렬화-역직렬화 가능한지 검증해야 한다.

대처 방안

상속용 클래스, Interface에는 Serializable 구현하지 말자🏴‍☠️

  • 위와 같은 문제점으로 기능 확장이 힘들어진다.
  • 응~ 객체지향 안하면 그만이야~ 라면 적극적으로 구현하자.

내부클래스도 하지 말자🏴‍☠️

  • 내부 클래스는 바깥 인스턴스의 참조와 유효 범위 안의 지역변수 값들을 저장하기 위해 컴파일러가 생성한 필드들이 자동으로 추가된다.
  • 자동으로 추가된 이 필드들의 직렬화 형태는 분명하지 않다.

이런 것까지 해야되나? 싶으면 하라
이거 해도 되나? 싶으면 하지 마라

비공식 야전 교본, 개발자에게도 적용

궁금한데?

그럼 왜 자바에는 그렇게 Serializable 구현한 API가 많은가?

  • 단지 레거시 때문인가?
  • 객체를 캐싱하거나, 원격 메서드를 호출할 수 있다.
  • 자바 내의 전송에서는 JSON이나 XML을 이용한 전송이 비효율적이다. 해당 parser가 있어야 하기 때문이다.

참고

profile
기계공학과 개발어린이

0개의 댓글