지난 글 : [프로그래밍 언어/JAVA] - JAVA 입문 - 보조 스트림
직렬화와 역직렬화
클래스의 인스턴스가 생성되면 인스턴스의 상태(인스턴스 변수 값)는 계속 변하게 된다. 헌데 인스턴스의 어느 순간 상태를 그대로 저장하거나 네트워크를 통해 전송할 일이 있을 수 있다. 이를 직렬화(serialization)라 한다. 그리고 저장된 내용이나 전송받은 내용을 다시 복원하는 것을 역직렬화(deserialization)라 한다. 간단히, 직렬화란 인스턴스 내용을 연속 스트림으로 만드는 것이다. 스트림으로 만들어야 파일에 쓸 수도 있고 네트워크로 전송할 수도 있다. 따라서 직렬화 과정에서 하는 일은 인스턴스 변수 값을 스트림으로 만드는 것이다. 자바에서는 보조 스트림 ObjectInputStream과 ObjectOutputStream을 사용해 비교적 쉽게 구현할 수 있다. 생성자는 아래와 같다.
저장할 파일이나 전송할 네트워크 등의 기반 스트림을 매개변수로 받아 인스턴스 변수 값을 저장하거나 전송한다.
직렬화에 사용할 Person 클래스를 만들고 인스턴스로 생성한 후 파일에 썼다가 복원하는 코드를 작성해보자.
직렬화에 사용할 Person 클래스를 만들었다. 이 클래스는 이름과 직업을 생성자의 매개변수로 받는다. 위 코드에서 writeObject( ) 메서드를 호출하면 personChoi와 personKim의 값이 파일에 쓰인다. 이때 serial.out 파일을 열어 보면 아래처럼 읽을 수 없는 내용으로 저장되어 있다.
다시 원래 상태로 복원할 때는 readObject( ) 메서드를 사용해 저장된 순서대로 읽어 들인다. 이때 readObject( )의 반환 값이 Object이므로 원래 자료형인 Person으로 형 변환을 한다. 또 역직렬화를 할 때 크래스 정보가 존재하지 않을 수 있으니 ClassNotFoundException도 처리해야 한다.
Serializable 인터페이스
프로그램을 실행하면 위 출력 결과 사진처럼 오류가 발생한다. 직렬화는 인스턴스 내용이 외부로 유출되는 것이므로 프로그래머가 직렬화를 하겠다는 의도를 표시해야 한다. 따라서 Person 클래스에 마커 인터페이스(marker interface)인 Serializable 인터페이스를 아래처럼 추가한다.
Serializable 인터페이스는 '이 클래스를 직렬화하겠다'는 의미로만 해석하면 된다. 출력 결과를 보면 처음 생성한 클래스 내용이 그대로 읽히고 복원된 것을 알 수 있다.
transient 예약어
직렬화 대상이 되는 클래스는 모든 인스턴스 변수가 직렬화되고 복원된다. 헌데 직렬화 될 수 없는 클래스(Socket 클래스는 직렬화 될 수 없음)가 인스턴스 변수로 있거나 직렬화하고 싶지 않은 변수가 있을 수 있다. 그럴 때 transient 예약어를 사용한다. 그럼 해당 변수는 직렬화되고 복원되는 과정에서 제외된다. transient 예약어를 사용한 변수 정보는 그 자료형의 기본 값으로 저장되므로 객체 자료형인 경우 null 값이 된다. 위 코드에서 Person의 job 변수를 아래처럼 바꾸고 실행해보자.
이 코드의 출력 결과는 아래와 같다.
job 내용이 저장되지 않았음을 알 수 있다.
serialVersionUID를 사용한 버전 관리
객체를 역직렬화할 때, 직렬화할 때의 클래스와 상태가 다르면 오류가 발생한다. 그 사이 클래스가 수정되었거나 변경되었다면 역직렬화를 할 수 없기 때문이다. 따라서 직렬화할 때 자동으로 serialVersionUID를 생성해 정보를 저장한다. 그리고 역직렬화할 때 serialVersionUID와 비교하는데 만약 클래스 내용이 변경되었다면 클래스 버전이 맞지 않는다는 오류가 생긴다. 그런데 작은 변경에도 클래스 버전이 계속 바뀌면 네트워크로 서로 객체를 공유해 일하는 경우 번거롭게 매번 클래스를 새로 배포해야 한다. 이런 경우 클래스의 버전 관리를 개발자가 할 수 있다고 한다. 자바에서 제공하는 자바 설치 경로의 bin\serialver.exe를 사용하면 아래처럼 serialVersionUID가 생성된다. 이 정보를 클래스 파일에 적어주면 된다. 이 기능을 이클립스에서는 자동으로 제공한다.
여기서 두 번째 Add generated serial version ID를 선택하면 아래처럼 직렬화를 위한 버전 번호가 자동으로 생성된다.
만약 직렬화의 대상이 되는 클래스 정보가 바뀌고 이를 공유해야 하는 경우 버전 정보를 변경하면 된다고 한다. 아직 실무에 못 가봤기에 어떤 경우인지 상상이 안된다.
Externailzable 인터페이스
직렬화하는 데 사용하는 또 다른 인터페이느 Externalizable은 Serializable 인터페이스와는 달리 프로그래머가 구현해야 할 메서드가 있다. 객체의 직렬화와 역직렬화를 프로그래머가 직접 세밀하게 제어하고자 할 때 메서드에 그 내용을 구현한다. name 속성을 가진 Dog 클래스에 Externalizable을 구현하면 아래와 같다.
Externalizable 인터페이스를 구현하면 writeExternal( ) 메서드와 readExternal( ) 메서드를 구현해야 한다. 또 복원할 때 디폴트 생성자가 호출되므로 티폴트 생성자를 추가해 줘야 한다. 읽고 쓰는 내용은 프로그래머가 직접 구현한다.
참고 서적 : 자바 프로그래밍 입문 - 박은종
'프로그래밍 언어 > JAVA' 카테고리의 다른 글
JAVA 입문 - 학점 산출 프로그램 만들기 (0) | 2022.06.21 |
---|---|
JAVA 입문 - 그 외 입출력 클래스 (0) | 2022.06.20 |
JAVA 입문 - 보조 스트림 (0) | 2022.06.18 |
JAVA 입문 - 문자 단위 스트림 (0) | 2022.06.16 |
JAVA 입문 - 바이트 단위 스트림 (0) | 2022.06.15 |