CHAPTER 22. 객체 직렬화 알아가기
오늘은 Java의 직렬화, 역직렬화에 대해서 알아봅시다.
■ 직렬화(serialization)란?
자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 byte 형태로 데이터를 변환하는 기술입니다. 또한 JVM(Java Virtual Machine)의 메모리에 상주(힙 또는 스택)되어 있는 객체 데이터를 byte 형태로 변환하는 기술이라고도 합니다.
그렇다면 직렬화는 왜 배워야 할까??
우리는 파일에 텍스트를 기록하고, 이전 데이터를 기록하는 방법은 많이들 알고 계시겠습니다. 그런데 만약 이런 종류의 데이터들이 아니라 객체를 파일로 저장하거나 읽어올 수 있을까요?? 직렬화만 배운다면 할 수 있습니다.
가 ) 직렬화(serialization) 클래스 구현하기
직렬화를 하려면 우선 Serializable 인터페이스를 implements 구현해야 합니다.
직렬화를 수행할 땐 두가지만 알고 있으면 됩니다.
- 직렬화가 가능한 클래스를 구현하기
- 직렬화가 된 클래스의 객체를 쓰고 읽는 Stream 준비하기
자 그럼 예제를 통해서 알아봅시다.
<예제 1>
//객체 직렬화를 하려면 Serializable 인터페이스를 구현해야 함
//객체 직렬화는 메모리에 저장된 객체를 파일로 저장하거나 네트워크로 전송할 때 사용
import java.io.Serializable;
// MVC패턴에서 쓰이는 용어
// 1) DTO : Data Transfer Object(데이터를 전달하는 객체, 결과물을 저장)
// 2) VO : Value Object, DTO의 개념이지만 생성자 없이 getter,setter로만 구성됨
// 3) DAO : Data Access Object(데이터 조작 객체, DB와 연결해서 데이터를 구하는 비지니스로직단)
public class MemberDTO implements Serializable {
private String userid;
private String name;
private String passwd;
private int age;
private String email;
//생성자 2개, getter/setter , toString()
public MemberDTO(){}
public MemberDTO(String userid, String name, String passwd, int age, String email) {
this.userid = userid;
this.name = name;
this.passwd = passwd;
this.age = age;
this.email = email;
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "MemberDTO{" +
"userid='" + userid + '\'' +
", name='" + name + '\'' +
", passwd='" + passwd + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
클래스 이름을 MemberDTO로 만들었는데, 여기서 DTO란 데이터를 전달하면서 결과물을 저장해 주는 객체입니다. DTO는 MVC패턴에서 쓰이는 용어로 추후에 Spring 포스팅으로 찾아뵙겠습니다.
직렬화에 대해서 이해하기 쉽게 생성자 2개, get/set, toString()을 활용했습니다.
그렇다면 이제 구현해볼까요??
<예제 2>
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class MemberUse {
public static void main(String[] args) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;//메모리에 있는 객체를 파일로 저장시켜주는 객체
MemberDTO m1 = new MemberDTO("kim", "김길동", "1234", 30, "kim@gmail.com");
MemberDTO m2 = new MemberDTO("lee", "이길동", "1234", 20, "lee@gmail.com");
MemberDTO m3 = new MemberDTO("park", "박길동", "1234", 40, "park@gmail.com");
try {
//직렬화 : 메모리 -> 프로그램 -> 파일
fos = new FileOutputStream("c:\\test\\object.dat");
oos = new ObjectOutputStream(fos);
oos.writeObject(m1);//m1객체를 파일에 저장
oos.writeObject(m2);//m2객체를 파일에 저장
oos.writeObject(m3);//m3객체를 파일에 저장
System.out.println("객체를 파일에 저장했습니다.");
} catch (Exception e) {
e.printStackTrace();
}
//역직렬화(deserialization) : 파일 =>프로그램=> 메모리
FileInputStream fis = null;
ObjectInputStream ois =null;
try {
fis = new FileInputStream("c:\\test\\object.dat");
ois = new ObjectInputStream(fis);
MemberDTO dto1 = (MemberDTO)ois.readObject();
MemberDTO dto2 = (MemberDTO)ois.readObject();
MemberDTO dto3 = (MemberDTO)ois.readObject();
System.out.println(dto1);
System.out.println(dto2);
System.out.println(dto3);
}catch (Exception e){
e.printStackTrace();
}
}
}
예제 코드들을 보시면 Stream 활용을 많이 하는데, 이전 포스팅을 참고해주시면 좋을 거 같습니다..!!
직렬화를 좀 더 쉽게 설명하자면 아래와 같습니다.
//직렬화 : 메모리 -> 프로그램 -> 파일
그렇다면 출력결과를 확인해 봅시다.
위 출력으로는 Output(역직렬화(Deserialization))은 출력이 정상적으로 나왔습니다.
그럼 Input(직렬화(serialization))은 잘 출력되었을까요??
위 예제 코드를 구현해 보시면 정상적으로 출력되는 걸 확인할 수 있습니다.
나 ) 직렬화(serialization) 장점
- 자바 직렬화는 자바 시스템에서 개발에 최적화되어 있습니다.
- 복잡한 데이터 구조의 클래스의 객체라도 직렬화 기본 조건만 지키면 큰 작업 없이 바로 직렬화, 역직렬화가 가능합니다.
- 데이터 타입이 자동으로 맞춰지기 때문에 역직렬화가 되면 기존 객체처럼 바로 사용이 가능합니다.
그럼 장점에서 말하는 역직렬화는 어떤 의미가 있을까??
다 ) 역직렬화(Deserialization)
Byte로 변환된 data를 원래대로 Object나 data로 변환하는 기술을 역직렬화(Deserialization)라고 부릅니다. 또한 직렬화된 바이트 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 형태를 의미합니다. 즉, 객체화하여 파일이나 네트워크로 write 할 때는 직렬화(serialization)를 거쳐서 전달이 되고, 반대로 읽어올 때는 역직렬화(Deserialization)를 거쳐서 가져오게 됩니다.
역직렬화(Deserialization)를 수행할 땐 세 가지만 알고 있으면 됩니다.
- 직렬화 대상이 된 객체의 클래스가 클래스 패스에 존재해야 하며 import 되어 있어야 합니다.
- 중요한 점은 직렬화와 역직렬화를 진행하는 시스템이 서로 다를 수 있다는 것을 반드시 고려해야 합니다.
- 자바 직렬화 대상 객체는 동일하게 가지고 있어야 합니다.
※ <예제 2> 코드 참고 해보세요!
라 ) 직렬화(serialization) 사용 시 주의할 점
- 외부 저장소로 저장되는 데이터는 짧은 만료 시간이 아니라면 자바 직렬화 사용을 지양합니다.
- 역직렬화 시 반드시 예외가 생갈 수 있다는 점을 진지하고 개발해야 합니다.
- 자주 변경되는 비즈니스적인 데이터에 대해 자바 직렬화 사용을 지양합니다.
- 긴 만료 시간을 가지는 데이터는 JSON 등 다른 포맷을 사용하여 저장합니다.
■ 비순차 접근 파일(RandomAccessFile)
이전 포스팅에서 살펴본 I/O Stream을 이용하면 파일에 순차적으로 입출력 작업을 수행할 수 있습니다. 하지만 순차적인 접근이 아닌 임의의 지점에 접근하여 작업을 수행하고 싶다면, RandomAccessFile 클래스를 사용하면 됩니다.
RandomAccessFile 클래스는 파일만을 대상으로 하며, 임의의 지점에서 입출력을 동시에 수행할 수 있습니다.
RandomAccessFile 클래스의 생성자에는 인수로 파일의 이름뿐만 아니라 파일 모드까지 함께 전달해야 하는데, 파일 모드란 파일의 사용 용도를 나타내는 문자열입니다. 자바에서 사용할 수 있는 대표적인 파일 모드는 다음과 같습니다.
파일 모드 | 설 명 |
"r" | 파일을 오로지 읽는 것만 가능한 모드로 개방합니다. |
"w" | 파일의 쓰기 전용입니다. |
"rw" | 파일을 읽고 쓰는 것이 모두 가능한 모드로 개방합니다. 만약 파일이 없으면 새로운 파일을 생성합니다. |
예제 코드로 알아봅시다.
<예제 3>
import java.io.RandomAccessFile;
public class RandomFile {
public static void main(String[] args) {
String str = null;
try { // r:읽기전용, w:쓰기전용, rw:읽기,쓰기
RandomAccessFile file = new RandomAccessFile("c:\\test\\rand.txt","rw");
System.out.println(file.getFilePointer());
//getFilePointer() : 파일포인터(파일을 어디까지 읽었는지 가리켜 줌)
file.seek(8);// 8번째 인덱스 부터
System.out.println(file.getFilePointer());//8
file.write("HTML".getBytes());
System.out.println(file.length());//문자길이(한글은 3바이트의 길이값을 가짐)
System.out.println(file.getFilePointer());
while (file.getFilePointer() < file.length()){
// 파일의 내용보다 파일포인터의 위치값이 적으면 반복
str = file.readLine();
//한글처리
//String(문자열,캐릭터셋) 인코딩방식 변환
//iso-8859-1 , 8859_1 : 서유럽 언어 인코딩 방식
//ms949(eucKr) : 2바이트(완성형)
//utf-8 : 3바이트 , 초성(1byte), 중성(1byte), 종성(1byte) - 조합형
str = new String(str.getBytes("8859_1"),"utf-8");
}
System.out.println(file.length());
System.out.println(file.getFilePointer());
file.close(); // 파일 저장시점
System.out.println("파일이 저장되었습니다.");
}catch (Exception e){
e.printStackTrace();
}
}
}
위 코드를 실행 전에 먼저 예제 파일을 생성해서 txt 안에 "Hello!! Java" 문구를 준비했습니다.
<예제 3> 코드를 실행해보면 결과적으로 8번 인덱스 자리에 있는 Java 문구를 HTML로 수정되어야 성공입니다.
결과는 아래와 같습니다.
마치며
직렬화, 역직렬화할 경우 객체들의 순서가 중요합니다. ArratList 등을 활용하면 손쉽게 할 수 있습니다!
항상 직렬화 가능한 클래스들의 상태를 확인하시는 것도 중요합니다.
'[ JAVA ] > JAVA' 카테고리의 다른 글
[ Java ] File 클래스 (0) | 2023.01.16 |
---|---|
[ Java ] Socket Programming (0) | 2023.01.13 |
[ Java ] I/O Stream (2) | 2023.01.10 |
[ Java ] GUI 프로그래밍 응용 (2) | 2023.01.09 |
[ Java ] GUI 프로그래밍 개념 (2) | 2023.01.07 |