손 타이핑을 통한 학습용 글
출처: https://gruda.tistory.com/entry/serialization-
자바에서 객체를 파일에 저장하거나 네트워크 상에서 객체를 전송하는 데 유용합니다.
Java에서 직렬화 및 역직렬화란 무엇입니까?
자바에서 Serialization(직렬화)란 객체를 데이터 스트림으로 변환하는 것이고, Deserialization(역직렬화)란 데이터 스트림을 객체로 다시 변환하는 것입니다. 직렬화는 객체를 전송하거나 저장할 때 유용하며, 자바에서는 Serializable 인터페이스를 구현하여 직렬화를 지원합니다. 하지만 보안상의 문제를 유발할 수 있기 때문에 주의하여 사용해야 합니다.
Java에서 직렬화 및 역직렬화를 구현하는 방법은 무엇입니까?
Serializable인터페이스를 구현하여 구현할 수 있습니다. 해당 인터페이스를 구현한 클래스의 객체는 ObjectOutputStream을 이용하여 직렬화하고, ObjectInputStream을 이용하여 역직렬화 할 수 있습니다. 이를 통해 객체를 데이터 스트림으로 변환하고 다시 객체로 변환하는 과정을 수행할 수 있습니다.
직렬화란 객체를 연속적인 바이트 스트림(Byte Stream)으로 변환하는 것.
즉, 객체를 메모리 상에서 상태 정보와 함께 저장할 수 있는 형태로 변환하는 것.
Serializable 인터페이스를 통해 구현한다. 이를 상속받으면 직렬화가 가능한 객체가 되며, ObjectOutputStream 클래스를 이용하여 직렬화를 수행할 수 있다.
import java.io.*;
public class Serialization implements Serializable{
private int num;
private String str;
public Serialization(int num, String str) {
this.num = num;
this.str = str;
}
public static void main(String[] args) {
Serialization obj = new Serialization(10, "Hello");
try {
FileOutputStream fos = new FileOutputStream("object.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
oos.close();
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
ObjectoutputStream클래스를 이용해 Serialization 객체를 파일에 저장했다.
직렬화된 바이트 스트림을 객체로 변환하는 것. ObjectInputStream 클래스를 이용하여 저장된 파일을 열어, 객체로 변환한다.
import java.io.*;
public class Deserialization implements Serializable {
private int num;
private String str;
public Deserialization(int num, String str) {
this.num = num;
this.str = str;
}
public void printData() {
System.out.println("num : " + num + ", str : " + str);
}
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("Object.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Deserialization obj = (Deserialization)ois.readObject();
obj.printData();
ois.close();
fis.close();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
파일을 InputStream으로 읽어들인 후 객체의 정보 출력
직렬화와 역직렬화는 네트워크 통신에서도 사용된다.
클라이언트와 서버가 통신할 때, 클라이언트가 보낸 객체를 서버에서 사용하려면 해당 객체를 역질렬화해야 한다.
public class NetworkCommunication implements Serializable {
private int num;
private String str;
public NetworkCommunication(int num, String str) {
this.num = num;
this.str = str;
}
public void printData() {
System.out.println("num : " + num + ", str : " + str);
}
public void server(){
// ServerSocket
try {
ServerSocket server = new ServerSocket(1234);
System.out.println("Server started");
while(true){
Socket socket = server.accept();
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
NetworkCommunication obj = (NetworkCommunication) ois.readObject();
obj.printData();
ois.close();
socket.close();
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public void client{
try {
Socket socket = new Socket("localhost", 1234);
ObjectOutputStream oss = new ObjectOutputStream(socket.getOutputStream());
NetworkCommunication obj = new NetworkCommunication(10, "Hello");
oss.writeObject(obj);
oss.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
// ServerSocket
}
}
클라이언트 : 객체를 직렬화하여 서버로 전송
서버 : 전송된 직렬화된 객체를 역직렬화하여 객체 상태정보 이용
직렬화와 역직렬화는 보안 문제가 발생할 수 있다.
네트워크 전송 간 악의적인 조작이 일어날 수 있음.
이러한 문제를 해결하기 위해 Serializable 인터페이스에서 보안성을 높이는 기능을 제공.
public class SecuredCommunication implements Serializable {
private int num;
private String str;
private transient String password;
public Transient(int num, String str, String password) {
this.num = num;
this.str = str;
this.password = password;
}
private void writeObject(ObjectOutputStream oss) throws IOException {
oss.defaultWriteObject();
String encryptedPassword = encrypt(password);
oss.writeObject(encryptedPassword);
}
private readObject(ObjectInputStream ois) {
ois.defaultReadObject();
String encryptedPassword = (String) ois.readObject();
password = decrypt(encryptedPassword);
}
private String encrypt(String str){
// 암호화 코드
}
private String decrypt(String str){
//복호화 코드
}
}
writeObject와 readObject는 객체를 직렬화하거나 역직렬화 할 때 호출되는 메소드들이다. writeObject는 객체를 직렬화하기 전에 호출되며, readObject는 객체를 역직렬화한 후 호출된다.
따라서, 디크립트 된 이후 객체를 저장한다.