본문 바로가기

프로그래밍/JAVA

자바 - 직렬화 (Serializable)

자바에서는 객체를 손쉽게 저장하거나 전송할 수 있는데, 이를 가능하게 하는 기술이 바로 직렬화입니다.

원래 객체는 힙 안에 존재하게 되는데 직렬화를 하게 되면 그 모양이 약간 변형되게 됩니다. 이렇게 하게 되면 자바에서 저장 및 전송에 아주 편리하고 유리하다는 장점이 있지만, 동시에 다른 프로그램에서는 이를 분석하기 힘들다는 단점이 있습니다. (물론 직렬화를 통해 어떤식으로 변형이 되는지 그 원리만 잘 알고 있다면 다른 프로그램에서도 얼마든지 읽고 쓰는 것이 가능할 것입니다.)


1.Serializable
직렬화를 하기 위해서는 저장하거나 전송할 객체에 Serializable이라는 인터페이스를 구현해 주어야 합니다. 이 Serializable 인터페이스는 구현할 메소드가 하나도 없기 때문에 그냥 implements Serializable만 써주면 됩니다.

아래의 코드가 Serializable을 구현한 Car클래스입니다.
public class Car implements Serializable {
   /* Car 클래스의 내용 */
}


위와 같이 Serializable을 구현해 줘야 그 객체를 직렬화해서 저장하거나 전송이 가능하지, 구현하지 않을 경우에는 런타임 에러가 발생할 것입니다.

객체가 직렬화되면 그 객체 내부에 있는 모든 객체들도 직렬화되게 됩니다. 그런데 Car객체 내부에 있는 다른 객체가 있다 하더라도 전송이나 저장하기 싫은 변수도 있을 수 있습니다. 이럴 경우에는 transient라는 키워드를 사용하면 됩니다.
예를 들어 Car 클래스에 비밀번호를 담고 있는 password 변수가 있어서 이는 전송하고 싶지 않다고 하겠습니다. 그러면 아래와 같이 코드를 작성할 수 있을 것입니다.

public class Car implements Serializable {
   transient private String password;
   /* Car 클래스의 내용 */
}
위와 같이 코드를 작성하면 직렬화를 하더라도 password의 내용은 직렬화되지 않게 됩니다. 이 transient 키워드는 primitive 변수에도 사용이 가능하고, non-primitive변수에도 사용이 가능합니다.


2.스트림(Stream)
자바에서는 여러종류의 스트림(Stream) 클래스가 있는데 이 스트림 클래스를 이용해서 직렬화된 객체를 파일로 저장하거나 전송하는 등 여러가지 작업을 할 수 있는 것입니다.

다음의 코드는 car객체를 data.dat라는 파일에 넣기 위한 코드입니다.
Car car = new Car();
try {
   FileOutputStream fos = new FileOutputStream("data.dat");
   ObjectOutputStream oos = new ObjectOutputStream(fos);
   oos.writeObject(car);
   oos.close();
} catch(Exception e) {
}

코드를 잘 보면 먼저 FileOutputStream 객체를 생성합니다. 이는 파일에 출력하기 위한 스트림입니다.
그리고 ObjectOutputStream 객체인 oos를 생성하는데 이는 객체를 연결하는 스트림이라 생각하면 되겠습니다.
oos가 객체를 저장할 수 있게 하는데 혼자서는 파일이나 소켓에 넘기는 것이 불가능합니다. 그래서 oos를 생성할 때 fos를 인자로 넣음으로써 객체를 파일에 저장할 수 있도록 하는 것입니다.
그러고나면 oos.writeObject(car)를 하게 되는데 이렇게 하면 실제로 파일에 car객체를 저장하는 것입니다. 저장이 끝나면 oos를 닫아줘야 합니다. oos를 닫아주면 그 아래에 있는 fos는 자동으로 닫히기 때문에 신경쓰지 않아도 됩니다.

이를 그림으로 살펴보면 아래와 같습니다.
사용자 삽입 이미지
위 그림에서 보면 car객체를 data.dat라는 파일에 넣기 위해서 먼저 ObjectOutputStream을 거치고 그 다음에 FileOutputStream을 거쳐서 data.dat파일에 들어간다는 것을 알 수 있습니다.

아래의 코드는 data.dat라는 파일에서 car객체에 값을 읽어보는 코드입니다.
Car newCar = new Car();
try {
   FileInputStream fis = new FileInputStream("data.dat");
   ObjectInputStream oos = new ObjectInputStream(fis);
   ois.readObject(car);
   ois.close();
} catch(Exception e) {
}

코드는 크게 바뀐 부분이 없습니다. Ouput대신에 모두 Input으로 바뀌었고, 객체를 쓸 때에는 writeObject()라는 메소드가 사용되었는데 객체에서 읽어올 때에는 readObject()라는 메소드가 사용됩니다.

이에 대한 그림도 아래에 나와 있습니다.
사용자 삽입 이미지
위의 그림을 잘 보면 data.dat라는 파일에서 newCar라는 객체에 불러오는데, 먼저 FileInputStream에서 ObjectInputStream을 거쳐서 newCar에 불러 온다는 것을 알 수 있습니다.