직렬화(Serialization)

한혜성·2022년 9월 7일
0

Java

목록 보기
1/2

1. 직렬화란?

- 자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 Byte 형태로 데이터를 변환하는 기술

- JVM의 메모리에 상주되어 있는 객체 데이터를 Byte 형태로 변환하는 기술

- 역직렬화는 직렬화의 반대인 Byte -> Data, Object를 의미함.

2. 자바에서의 직렬화(Serializable)

  • JVM의 메모리에만 상주되어 있는 객체 데이터를 그대로 영속화가 필요할 때 사용된다.
    -> 시스템이 종료되더라도 없어지지 않는 장점을 가지며, 영속화된 데이터이기 때문에 네트워크로도 전송 가능하다.
    또한, 필요할 때 직렬화된 객체 데이터를 가져와서 역직렬화하여 객체를 바로 사용할 수 있게 한다.

- 직렬화 구현

public class Member implements Serializable {
	private static final long serialVersionUID = 1L;

        private String name;
        private String email;
        private String phone;
      // 생략
    }

serialVersionUID를 설정해준다.

Member member = new Member("홍길동", "hong@email.com","01012345678");
byte[] serializedMember;

try(ByteArrayOutputStream baos = new ByteArrayOutputStream()){
	try(ObjectOutputStream oos = new ObjectOutputStream(baos)){
    	oos.writeObject(member);
        
        serializedMember = baos.toByteArray();
    }
}

ObjectOutputStream으로 직렬화를 진행한다. Byte로 변환된 값을 저장하면 된다.

- 역직렬화 구현

try(ByteArrayInputStream bais = new ByteArrayInputStream(serializedMember)){
	try(ObjectInputStream ois = new ObjectInputStream(bais)){
    	Object objectMember = ois.readObject();
    
    	Member member = (Member) objectMember;
    }
}

ObjectInputStream으로 역직렬화 진행한다. Byte의 값을 다시 객체로 저장하면 된다.

3. 직렬화 사용시 주의할 점

 public class Member implements Serializable {
	//SUID 선언해주지 않으면 Member에 변화가 생길 시 예외 발생
	private static final long serialVersionUID = 1L;

        private String name;
        private String email;
        private String phone;
      // 생략
    }
  • Member에 새로운 변수를 추가 시 java.io.InvalidClassException 예외가 발생한다. -> serialVersionUID의 정보가 일치하지 않아 발생한 것

- serialVersionUID 관리

: SUID(serialVersionUID)는 필수 값은 아니며, 선언되어 있지 않으면 클래스의 기본 해쉬값을 사용한다.
직렬화 과정에서 SUID의 버전이 포함되게 되고, 역직렬화 과정에서 java class에 선언되어 있는 SUID의 버전과 서로 동일한지 체크하게 된다.
=> SUID 계산은 클래스 세부 사항에 매우 민감하므로 예기치 않은 결과를 초래할 수 있어 SUID 값을 명시적으로 선언하는 것이 좋다.

즉, SUID 버전이 바뀌었다 == 객체의 상태가 조금이라도 바뀌었다 ==> 역직렬화 과정에서 오류 발생

  • SUID값이 동일할 때 문제가 발생하는 경우
    => 멤버 변수명은 같은데 타입이 바뀐 경우
public class Member implements Serializable {
	//SUID 선언해주지 않으면 Member에 변화가 생길 시 예외 발생
	private static final long serialVersionUID = 1L;

        private String name;
        private String email;
        //String -> StringBuilder로 변경
        private StringBuilder phone;
      // 생략
    }
  • SUID 값이 동일하면 멤버 변수 및 메서드 추가는 크게 문제되지 않음. 또한, 멤버 변수 제거 및 이름 변경은 오류는 발생하지 않으나 데이터는 누락된다.

∴ 자바 직렬화 사용할 때 클래스 구조 변경 시 어떤 부분을 확인해야 할까?

  • 특별한 문제가 없다면 serialVersionUID의 값은 개발 시 직접 관리해야 한다.
  • SUID 값이 동일하면 멤버 변수 및 메서드 추가는 크게 문제되지 않으나 제거 및 이름 변경 시 데이터는 누락된다.
  • 역직렬화 대상의 클래스의 멤버 변수 타입 변경은 지양해야한다.
    -> 자바 역직렬화시 타입에 엄격함. 나중에라도 타입 변경이 된다면 직렬화된 데이터가 존재하는 상태에서는 발생할 예외들에 대해 다 신경써야 한다.
  • 외부(DB, 캐시 서버 등)에 장기간 저장될 정보는 자바 직렬화 사용을 지양해야한다.
    -> 역직렬화 대상의 클래스가 언제 변경이 일어날지 모르는 환경에서 긴 시간동안 외부에 존재했던 직렬화된 데이터는 Garbage가 될 가능성이 높음. 언제 예외가 발생할지 모르는 지뢰 시스템이 될 수도 있음.

4. 직렬화 사용 예

  • Servlet Session(서블릿 세션)
    : 서블릿 기반의 WAS(톰캣, 웹로직 등) 대부분 세션의 자바 직렬화를 지원한다.
    서블릿 메모리 위에서 운용한다면 직렬화가 필요없지만, 파일로 저장하거나 세션 클러스터링, DB를 저장하는 옵션 등을 선택하게 된다면 세션 자체가 직렬화되어 저장되어 전달된다.

  • Cache(캐시)
    : 메모리, 외부 저장소, 파일 등의 저장소를 이용해서 데이터 객체를 저장한 후 동일한 요청이 오면 저장된 객체를 찾아서 응답하는 캐시 방식은 캐시할 부분을 자바 직렬화된 데이터로 저장해서 사용된다.

  • RMI(Remote Method Invocation)
    : 원격 시스템간의 메시지 교환을 위해서 사용하는 자바에서 지원하는 기술.
    원격 시스템의 메서드를 호출 시에 전달하는 메시지(객체)를 자동으로 직렬화시켜 사용된다.

[참고자료] 
https://devlog-wjdrbs96.tistory.com/268
https://techblog.woowahan.com/2551/
profile
백엔드하고 싶은 사람 소오오온~~

0개의 댓글