- ๋ชฉ์ฐจ
- ์๋ฐ ์ง๋ ฌํ ์ดํดํ๊ธฐ - ์ฌ์ฉ์ ์ฃผ์ํ ์ ๊ณผ ์ฌ์ฉ์ ์ถ์ฒํ์ง ์๋ ์ด์
- 1 ์๋ฐ ์ง๋ ฌํ์ ์ญ์ง๋ ฌํ
- 2 ์๋ฐ ์ง๋ ฌํ์ ์ญ์ง๋ ฌํ ์ฃผ์์ฌํญ
- 3 ์ ์ฌ์ฉํ์ง ๋ง๋ผ๋ ๊ฒ์ธ๊ฐ?
- ์ฐธ๊ณ
์ง๋ ฌํ๋ ์๋ฐ ์์คํ ์์ ์ฌ์ฉ๋๋ ๊ฐ์ฒด ๋๋ ๋ฐ์ดํฐ๋ฅผ ์ธ๋ถ์ ์๋ฐ ์์คํ ์์๋ ์ฌ์ฉํ ์ ์๋๋ก ๋ฐ์ดํธ ์คํธ๋ฆผ ํํ๋ก ๋ณํํ๋ ๊ฒ์ด๋ค.
์ญ์ง๋ ฌํ๋ ๋ฐ๋๋ก ์ง๋ ฌํ๋ฅผ ํตํด ๋ณํ๋ ๋ฐ์ดํธ ์คํธ๋ฆผ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ฒด๋ก ๋ณํํ๋ ๊ฒ์ด๋ค.
์ด๋ฌํ ์ง๋ ฌํ์ ์ญ์ง๋ ฌํ ๊ธฐ์ ์ ๋ฉ๋ชจ๋ฆฌ์ ์์นํ ๋ฐ์ดํฐ๋ฅผ ๋์คํฌ์ ์ ์ฅ์ํค๊ฑฐ๋, ๋คํธ์ํฌ ํต์ ์ ์ฌ์ฉํ๊ธฐ์ํ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉ๋๋ค.
ํ์ฌ ์น์์์ ์ฃผ๋ ฅ์ผ๋ก ์ฌ์ฉ๋๋ JSON๋ ์ฌ๋ฌ ์ฅ๋น๋ค์ด ๋คํธ์ํฌ ํต์ ์ ์ํด ๋ง๋ค์ด์ง ์ง๋ ฌํ ํฌ๋งท์ค์ ํ ์ข ๋ฅ๋ผ๊ณ ๋ณผ ์ ์๋ค.
์ด๋ฒ ๊ธ์ ์๋ฐ์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ JDK ์ง๋ ฌํ ๋ฐฉ์์ ๋ํด์ ์์๋ณธ๋ค.
๊ทธ๋ฆฌ๊ณ ์๋ฐ ์ง๋ ฌํ ์ฌ์ฉ์ ์ฃผ์ํด์ผํ ์ ๊ณผ ์ ์ฌ์ฉ์ ์ถ์ฒํ์ง ์๋์ง ์ ๋ฆฌํด๋ณธ๋ค.
์ง๋ ฌํ/์ญ์ง๋ ฌํ ํ๋ก์ธ์ค๋ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ๋์ด์ผํ๋ค.
์ฆ, A ์๋ฐ ์์คํ ์์ ์ง๋ ฌํํ ๋ด์ฉ์ ๋ค๋ฅธ B ์๋ฐ ์์คํ ์์ ์ญ์ง๋ ฌํํ ์ ์์ด์ผํ๋ค.
์๋ฐ ์ง๋ ฌํ๋ ์ด๋ฅผ ์ง์ํ๋ฉฐ, ๋์ ์ง๋ ฌํ/์ญ์ง๋ ฌํ ๋์ ํด๋์ค Serializable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผํ๋ค.
๐โโ๏ธ ์ง๋ ฌํ - ๊ฐ์ฒด to ๋ฐ์ดํธ ์คํธ๋ฆผ ๋ฐ์ดํฐ (ObjectOutputStream)
public final void writeObject(Object o) throws IOException;์๋ฐ ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํธ ์คํธ๋ฆผ ๋ฐ์ดํฐ๋ก ์ง๋ ฌํํ ๋ ObjectOutputStream.writeObject์ ์ฌ์ฉํ๋ค.
๋ฆฌํด ๊ฐ์ด ์ ์์ด? ํ ์ ์๋๋ฐ, ObjectOutputStream์ java.io.OuputStream์ ์์๋ฐ์ผ๋ฏ๋ก, ํด๋น ๊ฐ์ฒด ์์ฑ์ OutputStream์ ์ฃผ์
๋ฐ๋๋ค.
๊ทธ๋ฆฌ๊ณ writeObject๋ฅผ ์คํํ๋ฉด ์ฃผ์
๋ฐ์ OutputStream์ ์ง๋ ฌํ๋ ๋ฐ์ดํธ ์คํธ๋ฆผ ๋ฐ์ดํฐ๋ฅผ writeํ๋ค. (์ ์กํ๋ค.)
์๋ ์์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ์ดํดํ๊ธฐ ์ฝ๋ค :)
๐โโ๏ธ ์ญ์ง๋ ฌํ - ๋ฐ์ดํธ ์คํธ๋ฆผ ๋ฐ์ดํฐ to ๊ฐ์ฒด (ObjectInputStream)
public final Object readObject() throws IOException, ClassNotFoundException;์ง๋ ฌํ๋ ๋ฐ์ดํธ ์คํธ๋ฆผ ๋ฐ์ํฐ๋ฅผ ๋ค์ ์๋ฐ ๊ฐ์ฒด๋ก ์ญ์ง๋ ฌํํ ๋ ObjectInputStream.readObject๋ฅผ ์ฌ์ฉํ๋ค.
ObjectInputStream๋ java.io.InputStream์ ์์๋ฐ๊ณ ์๋ค. ๊ทธ๋ฌ๋ฏ๋ก ์ง๋ ฌํ๋ ๋ฐ์ดํธ ์คํธ๋ฆผ ๋ฐ์ดํฐ์ Input ํ์์ ๋ฐ๋ผ InputStream์ ์ฃผ์
ํ๊ณ readObject๋ฅผ ํธ์ถํ๋ฉด ๋๋ค.
๐โโ๏ธ ์ง๋ ฌํ ์ญ์ง๋ ฌํ ์์
์ฌ๋ฌ๊ฐ์ง ์ค๋ช ๋ณด๋ค ์์๋ฅผ ํตํด ์ง๋ ฌํ๋ฅผ ์ดํด๋ณด์.
์ฐ์ ์ง๋ ฌํ/์ญ์ง๋ ฌํํ ๊ฐ๋จํ ๊ฐ์ฒด๋ฅผ ์์ฑํด์ค๋ค.
Person.java
@Getter
@AllArgsConstructor
public class Person implements Serializable {
public static final Person EMPTY = new Person("EMPTY", 0, "EMPTY");
private String name;
private int age;
private transient String description;
}๊ทธ๋ฆฌ๊ณ ํ ์คํธ ์ฝ๋๋ฅผ ์๋์ ๊ฐ์ด ์์ฑํด์ค๋ค.
Simple_Serialization_Deserialization_Test.java
@Test
void whenSerializingAndDeserializing_thenCorrect() throws IOException, ClassNotFoundException {
// given
Person person = new Person("binghe", 30, "์ค๋ช
");
// when
// serializing ์ง๋ ฌํ
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(person); // write (์ง๋ ฌํ)
oos.flush(); oos.close();
byte[] serializedPerson = baos.toByteArray();
System.out.println(Base64.getEncoder().encodeToString(serializedPerson));
// deserializing ์ญ์ง๋ ฌํ
ByteArrayInputStream bais = new ByteArrayInputStream(serializedPerson);
ObjectInputStream ois = new ObjectInputStream(bais);
Object objectMember = ois.readObject(); // read (์ญ์ง๋ ฌํ)
Person deserializedPerson = (Person) objectMember;
// then
assertThat(deserializedPerson.getName()).isEqualTo(person.getName());
assertThat(deserializedPerson.getAge()).isEqualTo(person.getAge());
// transient๋ ์ญ์ง๋ ฌํ ๋์ง ์๋๋ค. (์ญ์ง๋ ฌํํ๋ฉด null๋ก ์ด๊ธฐํ๋๋ค.)
assertThat(deserializedPerson.getDescription()).isNull();
assertThat(deserializedPerson.EMPTY).isEqualTo(person.EMPTY);
}์คํํด๋ณด๋ฉด ๋ฌธ์ ์์ด ํ ์คํธ๊ฐ ํต๊ณผํ๋ค :)
์ด๋ ๊ฒ ๊ต์ฅํ ์ฝ๊ฒ ์ง๋ ฌํ/์ญ์ง๋ ฌํ๋ฅผ ํ ์ ์๋ค.
๐โโ๏ธ ์๋ฐ ์ง๋ ฌํ/์ญ์ง๋ ฌํ๋ static, transient ๊ฐ์ ์ง๋ ฌํํ์ง ์๋๋ค.
์ ์์์์ ์ ์ ์๋ฏ์ด, static, transient ๊ฐ์ ์๋ฐ์์ ์ง๋ ฌํํ์ง์๋๋ค.
ํนํ transient๋ ์ญ์ง๋ ฌํ์ null๋ก ์ด๊ธฐํ๋๋ฏ๋ก ์ฃผ์ํด์ผํ๋ค.
์๋ฐ ์ง๋ ฌํ/์ญ์ง๋ ฌํ๋ ์์ ๊ฐ์ด ์ฝ๊ฒ ๋น๊ต์ ์ฌ์ฉํ ์ ์์ง๋ง (?) ์ฃผ์ํด์ ์ฌ์ฉํด์ผํ๋ค.
์๋ฐ ์ง๋ ฌํ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง๋ ฌํ/์ญ์ง๋ ฌํ ๋์์ด java.io.Serializable๋ฅผ ๊ตฌํํด์ค์ผํ๋ค.
์ด์ ๊ด๋ จํด์ ์ฃผ์ํด์ผํ ๋ถ๋ถ์ด ์๋ค.
๐โโ๏ธ ์กฐํฉ ๊ด๋ จ
Serializable๋ฅผ ๊ตฌํํ๊ณ ์๋ ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํ/์ญ์ง๋ ฌํํ๊ธฐ์ํด์ , ํด๋น ๊ฐ์ฒด๊ฐ ์ฐธ์กฐํ๋ ๋ชจ๋ ๊ฐ์ฒด๋ Serializable๋ฅผ ๊ตฌํํด์ผํ๋ค.
Person.java, Address.java
@Getter
@AllArgsConstructor
public class Person implements Serializable {
private String name;
private int age;
private transient String description;
private Address address;
}
// Address๋ Person์ ์ํด ์ฐธ์กฐ๋์ง๋ง Serializable์ ๊ตฌํํ์ง ์๊ณ ์๋ค.
@Getter
@AllArgsConstructor
public class Address {
private String address;
}Serializable_Composition_Test.java
@Test
void whenSerializing_thenThrowNotSerializableException() throws IOException {
// given
Address address = new Address("์์ธ"); // not implement serializable object
Person person = new Person("binghe", 30, "์ค๋ช
", address);
// when, then
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
assertThatThrownBy(() -> oos.writeObject(person))
.isInstanceOf(NotSerializableException.class);
}์ ์ฝ๋์์ Address๋ Person์ ์ํด ์ฐธ์กฐ๋์ง๋ง Serializable์ ๊ตฌํํ์ง ์๊ณ ์๋ค.
๊ทธ๋์ ํ
์คํธ์ฝ๋ ์คํ์ NotSerializableException๊ฐ ๋์ ธ์ง๋ค.
์ ๋ฆฌํ๋ฉด, ๋ง์ฝ ์ฐธ์กฐํ๋ ๊ฐ์ฒด๋ค์ด Serializable๋ฅผ ๊ตฌํํ๊ณ ์์ง ์์ผ๋ฉด, ์์ ๊ฐ์ด NotSerializableException ์์ธ๊ฐ ๋ฐ์ํ๋ค.
์ด๋ Collection์ ์์๋ ํฌํจ์ด๋ค. Collection์ ์์๋ ๋ชจ๋ Serializable์ ๊ตฌํํด์ค์ผํ๋ค.
๐โโ๏ธ ์์ ๊ด๋ จ
Serializable๋ฅผ ๊ตฌํํ๋ ํด๋์ค๋ฅผ ์์ํ๋ ํ์ ํด๋์ค๋ ์๋์ผ๋ก ๋ชจ๋ Serializable๋ฅผ ๊ตฌํํ๊ฒ๋๋ค.
์ด๋ ๋ชจ๋ ํ์ ํด๋์ค๋ Serializable๋ก๋ถํฐ ์์ ๋ก์ธ ์ ์์ผ๋ฉฐ, ์๋ฐ ์ง๋ ฌํ ์ฌ์ฉ์ ์ฃผ์ํด์ผํ ์ ์ ๋ชจ๋ ์ฃผ์ํด์ค์ผํ๋ค๋ ๊ฒ์ด๋ค.
์ฆ, ํ์ ํด๋์ค๋ ๋ชจ๋ Serializable์ด ์๋์ผ๋ก ๋ชจ๋ ๊ตฌํํ๊ฒ๋๋ฏ๋ก, ์ด๋ฅผ ์ธ์งํ๊ณ ์์ด์ผํ๋ค.
2-2 ์ญ์ง๋ ฌํ์ ๋ฉค๋ฒ ๋ณ์ ํ์ ์ด ๋ณ๊ฒฝ๋๊ฑฐ๋ ํจํค์ง ์์น๊ฐ ๋ณ๊ฒฝ๋๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
๐โโ๏ธ ์๋ฐ ์ง๋ ฌํ๋ ์ง๋ ฌํ์ ๊ฐ์ฒด์ ๋ฉํ ์ ๋ณด๋ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ๊ฐ์ด ์ ์ฅํ๋ค.
์ง๋ ฌํํ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ Base64๋ก ์ธ์ฝ๋ฉํ ๋์ฝ๋ฉํ ๊ฒฐ๊ณผ
๏ฟฝ๏ฟฝsr๏ฟฝ/com.binghe.ex05.SoftwareDeveloper= ๏ฟฝn;๏ฟฝ๏ฟฝ๏ฟฝL๏ฟฝ๏ฟฝlanguaget๏ฟฝ๏ฟฝLjava/lang/String;xr๏ฟฝ$com.binghe.ex05.Person๏ฟฝ๏ฟฝ}f๏ฟฝ=๏ฟฝ๏ฟฝ๏ฟฝI๏ฟฝ๏ฟฝageL๏ฟฝ๏ฟฝnameq๏ฟฝ~๏ฟฝ๏ฟฝxp๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝt๏ฟฝ๏ฟฝbinghet๏ฟฝ๏ฟฝjava
์์ ๊ฐ์ด String, com.binghe.ex05.Person๋ฑ๋ฑ์ ๋ฉํ ์ ๋ณด๋ฅผ ๋ชจ๋ ๊ฐ์ด ์ ์ฅํ๋ค.
๐โโ๏ธ ์ญ์ง๋ ฌํ์ ํด๋น ๋ฉํ ์ ๋ณด๋ฅผ ํ์ฉํ์ฌ ์ญ์ง๋ ฌํํ๋ค.
์ง๋ ฌํ๋ ์ ๋ณด๋ฅผ ๋ค์ ์ญ์ง๋ ฌํํ ๋, ํด๋น ๋ฉํ ์ ๋ณด๋ฅผ ํ์ฉํ์ฌ ์ญ์ง๋ ฌํ๋ฅผ ์งํํ๋ค.
๐โโ๏ธ ๋ฌธ์ ๋ ๋ฉํ ์ ๋ณด์ ๋์์๋ ๋ฉค๋ฒ ๋ณ์์ ํ์ ์ด ๋ณ๊ฒฝ๋๊ฑฐ๋, ํจํค์ง ์์น๊ฐ ๋ณ๊ฒฝ๋๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
Person.java
package comm.binghe.ex05;
@Getter
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private Address address;
}Address.java
package comm.binghe.ex05;
@Getter
public class Address implements Serializable {
private String address;
public Address(String address) {
this.address = address;
}
}์ด์ Person์ ์ง๋ ฌํํ๋ค. ๊ทธ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ๋ค.
<!-- ์ง๋ ฌํํ Base64๋ก ์ธ์ฝ๋ฉํ ๊ฒฐ๊ณผ๋ฅผ ๋์ฝ๋ฉํ ๊ฒฐ๊ณผ -->
๏ฟฝ๏ฟฝsr๏ฟฝ"com.binghe.ex05.Person๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝI๏ฟฝ๏ฟฝageL๏ฟฝ๏ฟฝaddresst๏ฟฝ%Lcom.binghe.ex05/Address;L๏ฟฝ๏ฟฝnamet๏ฟฝ๏ฟฝLjava/lang/String;xp๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝsr๏ฟฝ#com.binghe.ex05.AddressJtฬ$๏ฟฝ๏ฟฝ๏ฟฝL๏ฟฝ๏ฟฝaddressq๏ฟฝ~๏ฟฝ๏ฟฝxpt๏ฟฝ๏ฟฝseoult๏ฟฝ๏ฟฝbinghe
์ด๋ Address์ ํจํค์ง ์์น๋ฅผ ๋ณ๊ฒฝํด๋ณธ๋ค.
com.binghe.ex05 -> com.binghe.ex05.address
๊ทธ๋ฆฌ๊ณ ์์์ ์ง๋ ฌํํ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์ญ์ง๋ ฌํํด๋ณด๋ฉด ClassNotFoundException๊ฐ ๋ฐ์ํ๋ค.
์ง๋ ฌํํ ๊ฒฐ๊ณผ์์ ์ญ์ง๋ ฌํ์ com.binghe.ex05.Address๋ฅผ ์ฐพ๋๋ฐ, Address๋ com.binghe.ex05.address.Address๋ก ํจํค์ง๊ฐ ๋ณ๊ฒฝ๋์๊ธฐ๋๋ฌธ์ ํด๋์ค๋ฅผ ์ฐพ์ ์ ์๋ค๋ ์์ธ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด๋ค.
๐โโ๏ธ ํจํค์ง๋ฟ๋ง ์๋๋ผ ํ์
์ด ๋ณ๊ฒฝ๋๋ฉด InvalidClassException๊ฐ ๋์ ธ์ง๋ค.
์ฌ์ง์ด int๋ฅผ long์ผ๋ก ๋ณ๊ฒฝํด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
Person.java
@Getter
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
}์ Person์ ์ง๋ ฌํํ age์ ํ์
์ long์ผ๋ก ๋ณ๊ฒฝํด๋ InvalidClassException๊ฐ ๋ฐ์ํ๋ค.
๋น์ฐํ ์ฐธ์กฐ ํ์
์ ๊ฐ์ฒด๋ ๋ณ๊ฒฝ๋๋ฉด InvalidClassException๊ฐ ๋ฐ์ํ๋ค.
๐โโ๏ธ ์ ๋ฆฌ
- ์๋ฐ๋ ์ง๋ ฌํ์ ๋ฉค๋ฒ ๋ณ์์ ํจํค์ง์ ํ์ ์ ๋ฉํ ์ ๋ณด๋ก ์ ์ฅํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ญ์ง๋ ฌํ์ ์ด๋ฅผ ํ์ฉํ์ฌ ์ญ์ง๋ ฌํํ๋ค.
- ๋ง์ฝ ์ญ์ง๋ ฌํ์ ๊ธฐ์กด์ ํด๋์ค ๋ฉค๋ฒ๋ณ์ ํจํค์ง ์์น๊ฐ ๋ณ๊ฒฝ๋์์ผ๋ฉด, ํด๋น ํด๋์ค๋ฅผ ์ฐพ์ ์ ์์ผ๋ฏ๋ก
ClassNotFoundException๊ฐ ๋ฐ์ํ๋ค. - ๋ง์ฝ ์ญ์ง๋ ฌํ์ ๊ธฐ์กด์ ํด๋์ค ๋ฉค๋ฒ๋ณ์ ํ์
์ด ๋ณ๊ฒฝ๋์์ผ๋ฉด, ์ญ์ง๋ ฌํ์ ๋ณํํ ์ ์์ผ๋ฏ๋ก
InvalidClassException๊ฐ ๋ฐ์ํ๋ค. - ์ด์ธ์๋, ์ญ์ง๋ ฌํํ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์ ์์ ์ผ๋ก ์์ ํ์ฌ ๋ณด์์ ์ธ ๊ณต๊ฒฉ๋ ๊ฐ๋ฅํ๋ค๊ณ ํ๋ค.
์ง๋ ฌํ์ ์ญ์ง๋ ฌํํ ๋์ ํด๋์ค์ ๋ฒ์ ์ด ์๋ก ๋ค๋ฅด๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
๐โโ๏ธ ์์ ์ดํด๋ณธ Person ํด๋์ค๋ฅผ ๊ธฐ์ค์ผ๋ก ์ญ์ง๋ ฌํ์ ํด๋์ค ๊ตฌ์กฐ ๋ณ๊ฒฝ ๋ฌธ์ ๋ฅผ ์ดํด๋ณธ๋ค.
Person.java
@Getter
public class Person implements Serializable {
private String name;
private int age;
}Person์ ์ง๋ ฌํํ๊ณ Base64๋ก ์ธ์ฝ๋ฉํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ๋ค.
<!-- name: binghe, age: 30 ์ผ๋ก ๊ฐ์ฒด ์์ฑํ ์ง๋ ฌํ ๋ฐ Base64 ์ธ์ฝ๋ฉ -->
rO0ABXNyACRleDA0X3NlcmlhbGl6YXRpb25fdmVyc2lvblVJRC5QZXJzb26/7herOgpoOAIAAkkAA2FnZUwABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cAAAAB50AAZiaW5naGU=
์ด ๋ฌธ์์ด์ ๋ฐ๋ก ์ญ์ง๋ ฌํํ๋ฉด Person์ผ๋ก ์ ๋ณํ๋๋ค. (๋ฌผ๋ก ํ
์คํธํ ๋์ ๋ฐ๋์ ํจํค์ง๋ ๋์ผํด์ผํ๋ค.)
๊ทธ๋ ๋ค๋ฉด Person ํด๋์ค์ ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ๊ณ ์ง๋ ฌํํ๋ฉด ์ด๋ป๊ฒ๋ ๊น? (ex. ๋ฉค๋ฒ๋ณ์ ์ถ๊ฐ)
Person.java
@Getter
public class Person implements Serializable {
private String name;
private int age;
// ์ง๋ ฌํํ ์์ฑ ์ถ๊ฐ
private String description;
}Person์ description์ด๋ผ๋ ํ๋๋ฅผ ์ถ๊ฐํ๊ณ , ์ด์ ์ name๊ณผ age๋ง ์กด์ฌํ๋ ์ํ์์ ์ง๋ ฌํํ Person์ ๋ค์ ์ญ์ง๋ ฌํํด๋ณธ๋ค.
๊ทธ๋ผ ์๋์ ๊ฐ์ด InvalidClassException์ด ๋ฐ์ํ๋ค.
๊ฐ๋จํ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด์ ํ์ธํด๋ณด์.
Serializable_UID_Test.java
@Test
void whenDeserializingChangedObject_thenThrowInvalidClassException() throws IOException, ClassNotFoundException {
// given (name, age๋ง ๊ฐ์ง ex04_serialization_versionUID.Person ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํํ base64 ๋ฐ์ดํฐ)
String deserializedPersonBase64 = "rO0ABXNyACRleDA0X3NlcmlhbGl6YXRpb25fdmVyc2lvblVJRC5QZXJzb26/7herOgpoOAIAAkkAA2FnZUwABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cAAAAB50AAZiaW5naGU=";
byte[] deserializedPersonBase = Base64.getDecoder().decode(deserializedPersonBase64);
// when
ByteArrayInputStream bais = new ByteArrayInputStream(deserializedPersonBase);
ObjectInputStream ois = new ObjectInputStream(bais);
// then
assertThatThrownBy(() -> ois.readObject())
.isInstanceOf(InvalidClassException.class);
// ois.readObject();
}๋ฌธ์ ์์ด ํต๊ณผํ๋ค :)
์์ธํ ์์ธ ๋ฉ์์ง๋ ์๋์ ๊ฐ๋ค.
java.io.InvalidClassException: ex04_serialization_versionUID.Person; local class incompatible: stream classdesc serialVersionUID = -4616726543827572680, local class serialVersionUID = 7715257156664215348
๋ณดํต ๊ฐ๋ฐ์๊ฐ ์ํ๋ ๊ฒ์ description ๋ฉค๋ฒ ๋ณ์๊ฐ ์ถ๊ฐ๋์ด๋ ๊ธฐ์กด ๋ฉค๋ฒ ๋ณ์๋ ๊ทธ๋๋ก ์ฑ์์ง๊ณ ์๋ก์ด ๋ฉค๋ฒ ๋ณ์๋ null๋ก ๋๋ ๊ฒ์ด๋ค.
ํ์ง๋ง ์์ธ๊ฐ ๋ฐ์ํ๋ค.. ์์ธ๋ฅผ ์ ์ฝ์ด๋ณด๋ฉด Person์ serialVersionUID๊ฐ ์ผ์นํ์ง์์์ ๋ฐ์ํ ๊ฒ์ ์ ์ ์๋ค.
์ฌ๊ธฐ์ ์ ์ ์๋ ๊ฒ์ ์๋ฐ๋ ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํํ ๋ serialVersionUID์ ์์ฑํ๊ณ , ์ญ์ง๋ ฌํํ ๋์ ์์ฑํ serialVersionUID์ ๊ฐ์ง์์ผ๋ฉด InvalidClassException๋ฅผ ๋์ง๋ค๋ ๊ฒ์ด๋ค.
์ง๋ ฌํํ ๋์ ํด๋์ค ๋ฒ์ ๊ณผ ์ญ์ง๋ ฌํํ ๋์ ํด๋์ค ๋ฒ์ ์ด ์๋ก ๋ค๋ฅด๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค๋ ์๋ฏธ.
๐โโ๏ธ ์๋ฐ ์ง๋ ฌํ ์คํ์ ์ดํด๋ณด๋ฉด ์ง๋ ฌํ ๊ฐ์ฒด์ serialVersionUID๋ฅผ ํญ์ ์ ์ํด์ฃผ๋ ๊ฒ์ ์ถ์ฒํ๋ค.

์ถ์ฒ: https://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100
SUID (serialVersionUID)๋ ํ์ ๊ฐ์ด ์๋์ง๋ง, ์ง์ ๊ธฐ์ ํ์ง ์์ผ๋ฉด ๋ด๋ถ์ ์ผ๋ก SUID ์ ๋ณด๊ฐ ์ถ๊ฐ๋๋ฉฐ, ์๋ฐ ์ง๋ ฌํ ์คํ ๊ทธ๋๋ก ์๋์ผ๋ก ์์ฑ๋ ํด๋์ค์ ํด์ ๊ฐ์ด ์ฌ์ฉ๋๋ค.
๋ฌธ์ ๋ SUID๊ฐ ์๋ก ๋ค๋ฅด๋ฉด ์ง๋ ฌํ/์ญ์ง๋ ฌํ์ ํธํ๋์ง ์๋๋ค๋ ๊ฒ์ด๋ค... ๊ทธ๋์ ์ ์คํ์์๋ ํธํ ๊ฐ๋ฅํ ํด๋์ค๋ SUID ๊ฐ์ด ๊ณ ์ ๋์ด์์ด์ผํ๋ค๊ณ ํ๋ค.
๊ทธ๋ฆฌ๊ณ "์กฐ๊ธ์ด๋ผ๋ ์ญ์ง๋ ฌํ ๋์ ํด๋์ค ๊ตฌ์กฐ๊ฐ ๋ฐ๋๋ฉด ์๋ฌ ๋ฐ์ํด์ผ ๋๋ค."์ ๋์ ๋ฏผ๊ฐํ ์์คํ
์ด ์๋ ์ด์์ ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ ๋์ ์ง์ serialVersionUID๊ฐ์ ๊ด๋ฆฌํด์ฃผ๋ ๊ฒ์ ์ถ์ฒํ๋ค๊ณ ํ๋ค.
์ฆ, ๋ฏผ๊ฐํ ์์คํ
์ด ์๋๋ผ๋ฉด ์๋์ ๊ฐ์ด ์ง๋ ฌํ ๋์์ ํด๋์ค์ serialVersionUID๋ฅผ ๋ช
์ํ๋ ๊ฒ์ ์ถ์ฒํ๋ค.
Person.java
@Getter
public class Person implements Serializable {
// SUID ๊ฐ ๋ช
์.
private static final long serialVersionUID = 1L;
private String name;
private int age;
// ์ง๋ ฌํํ ์์ฑ ์ถ๊ฐ
private String description;
}์ด๋ ๊ฒ SUID๋ฅผ ์ ์ํด์ฃผ๋ฉด, ๋ฉค๋ฒ ๋ณ์๋ฅผ ์ถ๊ฐํ์๋ null๋ก ๋ฃ์ด์ฃผ๋ฉฐ, ๋ฉค๋ฒ ๋ณ์๋ฅผ ์ญ์ ํด๋ ์์ธ ์์ด ํด๋น ๊ฐ ์์ฒด๋ฅผ ์ ์ธํ๊ณ ๋๋จธ์ง ๊ฐ๋ค๋ง ์ญ์ง๋ ฌํํด์ค๋ค.
๐โโ๏ธ ์ ๋ฆฌ
- ์๋ฐ๋ ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํํ ๋ ์ง๋ ฌํ ๊ฒฐ๊ณผ์
serialVersionUID์ ์์ฑํ์ฌ ๋ฉํ ์ ๋ณด๋ก ์ ์ฅํ๋ค. - ์ฌ์ฉ์๊ฐ ๋ฐ๋ก ์ง์ ํด์ฃผ์ง์์ผ๋ฉด ๋ด๋ถ์ ์ผ๋ก ํด๋์ค์ ํด์ ๊ฐ์ ์ฌ์ฉํ์ฌ ์ ์ฅํ๋ค.
- ๋ฌธ์ ๋ ์ญ์ง๋ ฌํ์์ ์ ์ฅ๋ SUID์ ํ์ฌ ํด๋์ค์ SUID๊ฐ ๋ค๋ฅด๋ฉด
InvalidClassException๊ฐ ๋ฐ์ํ๋ค.- ์ง๋ ฌํ์ ์ญ์ง๋ ฌํํ ๋์ ํด๋์ค์ ๋ฒ์ ์ด ์๋ก ๋ค๋ฅด๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
- "์กฐ๊ธ์ด๋ผ๋ ์ญ์ง๋ ฌํ ๋์ ํด๋์ค ๊ตฌ์กฐ๊ฐ ๋ฐ๋๋ฉด ์๋ฌ ๋ฐ์ํด์ผํ๋ ์๋ฏผํ ์์คํ
"์ด ์๋๋ผ๋ฉด ์ง๋ ฌํํ ํด๋์ค์
serialVersionUID๋ฅผ ๋ช ์ํด์ฃผ๋ ๊ฒ์ด ์ข๋ค.
์ฌ์ค ์๋ฐ ์ง๋ ฌํ์ ๊ด๋ จ๋ ์ด์๋ ๊ต์ฅํ ์ ์๋ ค์ ธ์๋ค.. ๊ทธ๋ฆฌ๊ณ ์ดํํฐ๋ธ ์๋ฐ์ ์์ดํ 85์์ ์๋์ ๊ฐ์ด ๊ฐ์กฐํ๋ค.
- "The best way to avoid serialization exploits is to never deserialize anything."
- "There is no reason to use Java serialization in any new system you write."
๊ฐ๋ฅํ ์ญ์ง๋ ฌํ๋ฅผ ํ์ง ์๋ ๊ฒ์ด ์ข์ผ๋ฉฐ, ์๋ก์ด ์์คํ ์์ ์๋ฐ ์ญ์ง๋ ฌํ๋ฅผ ์ฌ์ฉํ ์ด์ ๊ฐ ์ ํ ์๋ค๋ ๊ฒ์ด๋ค.
์๋ฐ ์ญ์ง๋ ฌํ ์ฌ์ฉ์ ์ถ์ฒํ์ง ์๋๋ค๊ณ ๊ฐํ๊ฒ ๊ฐ์กฐํ๋ค.
๊ทธ๋์ ํ์๋ ๋ช๋ช ์๋ฃ๋ฅผ ์ฐธ์กฐํ๊ณ ์ฌ์ฉํด๋ณด๋ฉด์ JDK ์ง๋ ฌํ ์ถ์ฒํ์ง ์๋ ์ด์ ๋ฅผ ์ ๋ฆฌํด๋ณด์๋ค.
- ๋ฒ์ ๋ ์ด์
- ์ฌ์ฉ์ ์ฃผ์ ์ฌํญ์์ ์๊ธฐํ๋ฏ์ด, ์ง๋ ฌํํ ๋์ ์ญ์ง๋ ฌํํ ๋์ ํด๋์ค ๋ฒ์ ๋์ด ๋ค๋ฅด๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
- ๋ฌธ์ ๋ ์ปดํ์ผ ํ์์ ์ด๋ฅผ ํ์ธํ ๋ฐฉ๋ฒ์ด ์์ผ๋ฉฐ, ๋ฐํ์์ ์ญ์ง๋ ฌํํ ๋์ผ ์์ธ๊ฐ ๋ฐ์ํ๊ฒ๋๋ค.
- ๋ํ, ์ง๋ ฌํํ๋ ํด๋์ค์ ๋ณ๊ฒฝ์ผ๋ก ์ธํ ๋ฒ์ ์ด ๋ฐ๋๋ ๊ฒฝ์ฐ๋ ๊ต์ฅํ ๋ง๋ค... ์ง๋ ฌํ๋ก์ธํด ์ด๋ฌํ ๋ณ๊ฒฝ์ด ์์ ๋กญ์ง ์๋ ๊ฒ์ด ์ข์ง ์๋ค๊ณ ์๊ฐ๋ ๋ค.
- ๋ฌผ๋ก SUID๋ฅผ ์ง์ ๊ด๋ฆฌํด์ ๋ฒ์ ๋ ์ด์๋ฅผ ํด๊ฒฐํ ์ ์์ง๋ง, ์ข์ ๊ตฌ์กฐ๋ ์๋๋ผ๊ณ ์๊ฐ๋ ๋ค.
- Serializable ์ง์ฅ
Serializable์ ๊ตฌํํ ํด๋์ค๋ง ์ง๋ ฌํ/์ญ์ง๋ ฌํ ๋ ์ ์๋ค.- ๋ฌธ์ ๋ ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๊ณ ์๋ ํด๋์ค๊ฐ ์ง๋ ฌํ/์ญ์ง๋ ฌํํ๋ ค๋ฉด ์ฐธ์กฐํ๋ ๊ฐ์ฒด๋ ๋ชจ๋
Serializable์ ๊ตฌํํด์ผํ๋ค๋ ๊ฒ์ด๋ค. (์์๋ ๋์ผ) - ์ด๋ ์ฌ์ฉ์ ์ฃผ์ ์ฌํญ์๋ ์ ๋ฆฌํ ๋ด์ฉ์ด๊ธฐ๋ํ๋ฐ, ์ด ํน์ง์ผ๋ก์ธํด ๋ณต์กํ ๊ฐ์ฒด ๊ทธ๋ํ๋ฅผ ๊ฐ์ง๊ฒฝ์ฐ
Serializable์ง์ฅ์ ๋น ์ง ์ ์๋ค. - ๋ํ, ํ์ ์๊ฐ์ Serializable์ ์์กดํ๋ ์ฝ๋ ์์ฒด๊ฐ JDK์ ๊ธฐ๋ณธ ์คํ์์๋ POJO์ ๋น์นจํฌ์ฑ์ ๊นจํธ๋ฆฌ๋๊ฒ ์๋๊ฐ ์๊ฐ๋ ๋ค.
- ์ฉ๋ ๋ฌธ์
- ์ฌ์ฉ์ ์ฃผ์ ์ฌํญ์์ ์ ์ ์๋ฏ์ด, ์๋ฐ ์ง๋ ฌํ๋ ์ง๋ ฌํ์ ๋ฉค๋ฒ๋ณ์์ ํจํค์ง์ ํ์ ์ ๋ฉํ ์ ๋ณด๋ก ์ ์ฅํ๋ค.
- ์ด๋ ์บ์ ์๋ฒ์ ๊ฐ์ ๊ณณ์ ์ฌ์ฉ๋๋ค๋ฉด ์ฉ๋ ๋ฌธ์ ๋ก ์ด์ด์ง ์ ์๋ค. ํญ์ ์กฐ์ฌํด์ ์ฌ์ฉํด์ผํ๋ค.
- ํธํ์ฑ ๋ฌธ์
- ์๋ฐ ์ง๋ ฌํ๋ ์๋ฐ ์์คํ ๊ฐ์ ์ง๋ ฌํ์ ์ญ์ง๋ ฌํ๋ง ์ง์ํ๋ค.
- ์๋ฐ์์๋ง ์ฝ์ ์ ์์ผ๋ฏ๋ก, ๋ค๋ฅธ ์์คํ ๊ณผ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๊ฐ์ ํธํ์ด ์ ํ ์๋๋ค.
- ๋ณด์ ์ด์
- ์ง๋ ฌํ๋ ๋ฐ์ดํฐ๊ฐ ์ ๋๋ก ๊ฒ์ฆ๋์ง ์์ ๊ฒฝ์ฐ ๋ณด์ ์ ์ฉ์ ์ทจ์ฝํ ์ ์๋ค.
- ๊ณต๊ฒฉ์๋ ๋ฐ์ดํฐ๊ฐ ์ญ์ง๋ ฌํ๋ ๋ ์คํ๋ ์ ์๋ ์ง๋ ฌํ๋ ๋ฐ์ดํฐ์ ์ ์ฑ ์ฝ๋๋ฅผ ์ฝ์ ํ ์ ์๋๋ฐ, ์ด๋ ์ธ๋ถ์ ํต์ ํ๋ ์ฝ๋๋ฅผ ๋ฃ๋๋ค๋ฉด ์์นซ ํดํน๋์ด ๋ณด์์ ์ผ๋ก ํฐ ์ด์๊ฐ ๋ ์ ์๋ค.