Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ Item 86 ] Serializable을 구현할지는 신중히 결정하라 #85

Open
qkrqudcks7 opened this issue Jul 2, 2022 · 0 comments
Open
Assignees
Labels
12장 직렬화

Comments

@qkrqudcks7
Copy link

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

Serializable

어떤 클래스의 인스턴스를 직렬화 할 수 있게 하려면 클래스 선언에 implements Serializable만 덧붙이면 된다.


Serializable 구현의 문제점

1. Serializable을 구현하면 릴리스한 뒤에는 수정하기 어렵다.

  • 클래스가 Serializable을 구현하면 직렬화된 바이트 스트림 인코딩(직렬화 형태)도 하나의 공개 API 된다.
  • 따라서 커스텀 직렬화 형태를 설계하지 않고 자바의 기본 방식을 사용하면 직렬화 형태는 최소 적용 당시 클래스의 내부 구현 방식에 영원히 묶여 버린다.
// 이런 클래스가 있다고 예를 들어보자
public class Member implements Serializable {
      private String name;
      private String email;
      private int age;
    // 생략
  }

위 클래스를 직렬화를 했을 때

ANyibG9nLmV4ABpWhhbi5YW0xLk1lbWJlcgAAA3b293YAABAg ...

위 클래스에 code 속성을 추가 한 다음 직렬화한 데이터를 역직렬화 하면 ?

public class Member implements Serializable {
      private String name;
      private String email;
      private int age;
      // code 속성을 추가
      private int code;
  }
  • java.io.InvalidClassException 발생한다. serialVersionUID 가 다르기 때문이다.
  • 위 상황처럼 뒤늦게 클래스 내부 구현을 손보면 원래의 직렬화 형태가 달라져 역직렬화에 실패한다.

해결 방법

serialVersionUID를 명시적으로 선언하여 클래스 변경으로 인한 역직렬화 문제를 해결할 수 있다.

public class Member implements Serializable {
      static final long serialVersionUID = 1L;
      private String name;
      private String email;
      private int age;
      // code 속성을 추가
      private int code;
  }

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

  • 객체는 생성자를 사용해 만드는게 기본인데, 기본 역직렬화를 사용하면 불변식 깨짐과 허가되지 않는 접근에 쉽게 노출된다.
  • 왜냐하면 역직렬화는 숨은 생성자인데 생성자가 만족해야 하는 불변식이나 객체 생성중 객체 내부에 공격자가 접근할 수 없도록 해야 한다는 것을 잊기 쉽다.

3. 해당 클래스의 신버전을 릴리스할 때 테스트 할 것이 늘어난다.

  • 직렬화 가능 클래스가 수정되면 신버전 인스턴스를 직렬화한 후 구버전으로 역직렬화 할 수 있는지, 그 반대도 가능한지 검사해야 한다.
  • 따라서 테스트 할 양이 직렬화 가능 클래스의 수와 릴리스 횟수에 비례해 증가한다.
  • 다만 클래스를 처음 제각할 때 커스텀 직렬화 형태를 잘 설계해놨다면 이러한 테스트 부담을 줄일 수 있다.

4. Serializable 구현 여부는 가볍게 결정할 사안이 아니다.

  • 단, 객체를 전송하거나 저장할 때 자바 직렬화를 이용하는 프레임워크용으로 만든 클래스라면 선택의 여지가 없다. Serializable을 반드시 구현해야 하는 다른 클래스의 컴포넌트로 쓰일 클래스도 마찬가지다.
  • 하지만 이를 구현하는데 따르는 비용이 적지 않으니, 클래스를 설계할 때마다 그 이득과 비용을 잘 저울질 해야 한다.

5. 상속용으로 설계된 클래스는 대부분 Serializable을 구현하면 안되며, 인터페이스도 대부분 Serializable을 확장해서는 안된다.

  • 다만 Serializable을 구현한 클래스만 지원하는 프레임워크를 사용하는 상황이라면 어쩔수 없다.
  • 대표적으로 상속용으로 설계된 클래스 중 Serializable을 구현한 예로 Throwble과 Component가 있다.

6. 내부클래스는 직렬화를 구현하지 말아야 한다.

  • 내부 클래스에는 바깥 인스턴스의 참조와 유효 범위 안의 지역변수 값들을 저장하기 위해 컴파일러가 생성한 필드들이 자동으로 추가된다.
  • 이 필드들이 클래스 정의에 어떻게 추가 됬는지 정의되지 않았기 때문에 기본 직렬화 형태가 분명하지 않아 이용 불가하다.
  • 단 정적 멤버 클래스는 Serializable을 구현해도 된다.

핵심정리

  • Serializable은 구현한다고 선언하기는 쉽지만, 그것은 눈속임일 뿐이다.
  • 보호된 환경에서 쓸 클래스가 아니라면 Serializable 구현은 아주 신중하게 이뤄져야 한다.
  • 상속할 수 있는 클래스라면 주의사항이 더 많아진다.
  • static 멤버 클래스 : Serializable 구현 가능
  • 익명 내부 클래스, 지역 내부 클래스 : Serializable 구현 불가능
@qkrqudcks7 qkrqudcks7 added the 12장 직렬화 label Jul 2, 2022
@qkrqudcks7 qkrqudcks7 self-assigned this Jul 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
12장 직렬화
Projects
None yet
Development

No branches or pull requests

1 participant