개발하는 두더지

[Effective Java 규칙74] Serializable 인터페이스를 구현할 때는 신중하라 본문

Java,Android

[Effective Java 규칙74] Serializable 인터페이스를 구현할 때는 신중하라

덜지 2018. 11. 21. 14:34

[Effective Java 규칙74] Serializable 인터페이스를 구현할 때는 신중하라

Effective Java 2/E 책과 구글링을 통해 내용을 정리하고 개인적인 견해가 포함된 글입니다.



클래스 선언부에 implements Serializable 만 붙이면 직렬화 가능한 객체를 만들 수 있다. 지금까지 이 키워드를 사용해서 직렬화 객체를 만들고 intent에 데이터를 보내는 용도로 사용해 왔다. 바이트 스트림으로 변경해서 전송하는 직렬화 기능에는 문제는 없다. Serializable을 구현하면 클래스의 private, package-private 객체 필드도 공개가 되어서 정보 은닉을 위해 필드의 접근 권한은 최소화해야 한다는 규칙이 깨져버리게 된다. 


그리고 모든 직렬화 가능 클래스에는 고유한 식별 번호가 붙는다 serialVersionUID 라는 static final long 필드 타입을 선언 또는 자동 생성하여 넣어준다. 이 값을 명시적으로 적어주지 않으면 시스템은 복잡한 과정을 거쳐서 식별 번호를 만들어서 붙여준다. 자동 생성되는 식별 번호는 클래스 이름, 해당 클래스가 구현하는 모든 인터페이스 이름, 해당 클래스의 모든 public과 protected 멤버에 영향을 받는다. 만약 이중 하나라도 변경되면 식별 번호는 자동으로 변경되게 된다. 그럼 나중에 호환성이 깨져서 InvalidClassException이 발생할 가능성이 있다. 그래서 반드시 serialVersionUID는 직접 명시해주는 것이 좋다.


그리고 버그나 보안 취약점이 발생할 가능성이 높아진다. 불변식 훼손(invariant corruption)이나 불법 접근(illegal access) 문제에 쉽게 노출된다는 것이다.


그리고 새 버전 클래스를 내놓기 위한 테스트 부담이 늘어난다. 이진 호환성 외에 의미 호환성까지 테스트를 해야하며 직렬화-역직렬화가 버전에따라 정상적으로 이루어지는지 확인을 전부 해줘야한다.


그리고 상속을 염두하고있는 클래스는 Serializable을 구현하지 않는 것이 좋다. 인터페이스도 가급적이면 Serializable을 상속하지 말아야 한다. 하지만 Serializable을 구현하는 객체만으로 무엇인가 작업하는 경우에는 유용하다. Throwable, Component, HttpServlet 등이 상속을 고려해 설계된 클래스중에서 Serializable을 구현한 클래스다. Throwable에서는 원격 메서드 호출에서 발생하는 예외를 서버에서 클라이언트로 전달하기 위해 Serializable이 사용된다. Component에서는 GUI를 전송, 보관, 복원하기 위해서이며, HttpServlet은 세션 상태를 캐시하기 위해서 사용된다. 0

물론 장점이 없는 것은 아니다. 직렬화를 통해 객체를 전송하거나 저장할때 유용하다. 대부분의 컬렉션 클래스와 Date, BigInteger 같은 값 클래스는 Serializable을 구현해야하는 클래스도 있기때문에 작업이 아주 쉬워진다.







Comments