JAVA์ ๊ฐ์ฒด ์ง๋ ฌํ(Serialization)์ JSON ์ง๋ ฌํ
JAVA์ ๊ฐ์ฒด ์ง๋ ฌํ(Serialization)์ JSON ์ง๋ ฌํ
๐ ๋ค๋ฃจ๋ ๋ด์ฉ
- ์ง๋ ฌํ์ ์ญ์ง๋ ฌํ์ ๊ฐ๋
- ObjectInputStream๊ณผ ObjectOutputStream
- Serializable
- transient
- ์ง๋ ฌํ/์ญ์ง๋ ฌํ์ ๊ตฌํ
- serialVersionUID
- Jackson์ ์ด์ฉํ JSON ์ง๋ ฌํ
์ง๋ ฌํ, ์ญ์ง๋ ฌํ๋?
์ง๋ ฌํ
- ๊ฐ์ฒด์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ I/O ์คํธ๋ฆผ์ ์ฐ๊ธฐ(์ถ๋ ฅ) ์ํด ์ฐ์์ ์ธ(serial) ๋ฐ์ดํฐ๋ก ๋ณํํ๋ ๊ฒ
์ญ์ง๋ ฌํ
- I/O ์คํธ๋ฆผ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์(์ ๋ ฅ) ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๊ฒ
์๋ฐ์์ ์ง๋ ฌํ์ ์ญ์ง๋ ฌํ๋ ๊ฐ์ฒด๋ฅผ ํ์ผ๋ก ์ ์ฅํ๊ฑฐ๋ ๋คํธ์ํฌ๋ฅผ ํตํด ์ ์กํ๊ธฐ ์ํด ์ ๊ณต๋๋ ๊ธฐ๋ฅ์ด๋ค.
๊ฐ์ฒด๋ '์ธ์คํด์ค ๋ณ์์ ์งํฉ'์ด๋ฏ๋ก ๊ฐ์ฒด๋ฅผ ์ ์ฅ/์ ์กํ๋ ๊ฒ์ ๊ฐ์ฒด์ ์ธ์คํด์ค ๋ณ์์ ๊ฐ์ ์ ์ฅ/์ ์กํ๋ ๊ฒ๊ณผ ๋์ผํ๋ค.
๊ถ๊ทน์ ์ผ๋ก ๊ฐ์ฒด์ ์ง๋ ฌํ๋ ์๋ํํฐ์์ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ๊ฒ์ด๋ค.
๊ฐ์ฒด ์ง๋ ฌํ๋ JVM์์ ์๋ฐ ๊ฐ์ฒด๋ฅผ ๋ด๋ณด๋ด๋(์ถ๋ ฅํ๋) ๊ฐ๋จํ ๋ฐฉ๋ฒ์ด๋ค. ์ง๋ ฌํ๋ ๊ฐ์ฒด๋ ํ์ผ๋ก ์ ์ฅํ๊ฑฐ๋, ๋คํธ์ํฌ๋ก ์ ์กํ๊ฑฐ๋, ๋ค๋ฅธ I/O ์ธํฐํ์ด์ค์ ๋ด๋ณด๋ผ ์ ์๋ค.
ObjectInputStream๊ณผ ObjectOutputStream
์๋ฐ์์๋ ๊ฐ์ฒด์ ์ง๋ ฌํ, ์ญ์ง๋ ฌํ๋ฅผ ์ํด ObjectInputStream๊ณผ ObjectOutputStream์ ์ ๊ณตํ๋ค.
๊ฐ์ฒด๋ฅผ ์คํธ๋ฆผ์ ์ฐ๊ฑฐ๋(์ถ๋ ฅ) ์ฝ๋ ๊ธฐ๋ฅ(์ ๋ ฅ)์ ์ ๊ณตํ๋ ๋ณด์กฐ์คํธ๋ฆผ(๊ธฐ๋ฐ์คํธ๋ฆผ ํ์)
ObjectOutputStream extends OutputStream
- ์ง๋ ฌํ : ์คํธ๋ฆผ์ ๊ฐ์ฒด๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํด ์ฌ์ฉ
- writeObject()
ObjectInputStream extends InputStream
- ์ญ์ง๋ ฌํ : ์คํธ๋ฆผ์ผ๋ก๋ถํฐ ๊ฐ์ฒด๋ฅผ ์ ๋ ฅ๋ฐ๊ธฐ ์ํด ์ฌ์ฉ
- readObject() : Object๋ก ๋ฆฌํด๋๋ฏ๋ก ํ๋ณํ ํ์
writeObject()/readObject()๋ฟ๋ง ์๋๋ผ primitive(๊ธฐ๋ณธ, ์์) ํ์ ์ ๋์ํ๋ ๋ฉ์๋๋ ์ ๊ณต๋จ
์ฐธ๊ณ ๋ก ๋ ๋ค๋ฅธ ๋ณด์กฐ ์คํธ๋ฆผ ์ค DataInputStream/DataOutputStream์ Object ํ์ ์ ์ ์ธํ๊ณ writeByte(), wirteChar(), writeUTF()๋ readByte(), readChar(), readUTF()์ ๊ฐ์ด ๊ธฐ๋ณธ์ ์ธ ๋ฐ์ดํฐ ํ์ ์ ์ ์งํ๋ฉฐ ์ฝ๊ฑฐ๋ ์ฐ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋๋ฐ, ์ง๋ ฌํ์ ๊ธฐ๋ฐ ๊ฐ๋ ์ด ๋ ์คํธ๋ฆผ์ด๋ค.
๊ฐ์ฒด ์ง๋ ฌํ, ์ญ์ง๋ ฌํ์ ๊ฐ๋จํ ์์
// ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํํ์ฌ ํ์ผ์ ์ ์ฅ
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("objectfile.ser"));
oos.writeObject(new Member());
// ํ์ผ๋ก๋ถํฐ ๊ฐ์ฒด๋ฅผ ์ฝ์ด ๊ฐ์ฒด ์์ฑ
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("objectfile.ser"));
Member member = (Member) ois.readObject(); // Object๋ก ๋ฆฌํด๋๋ฏ๋ก ๋ช
์์ ํ๋ณํ ํ์
Serializable
java.io.Serializable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํด๋์ค๋ง ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํํ ์ ์๋ค.
Serializable ์ธํฐํ์ด์ค๋ ๊ตฌํํด์ผํ๋ ๋ฉ์๋๊ฐ ์๋, ๊ฐ์ฒด์ ์ง๋ ฌํ๋ฅผ ๋ช ์ํ๋ ์ฉ๋์ ๋งํฌ ์ธํฐํ์ด์ค์ด๋ค.
๋ค์์ ์์(is-a), ํฌํจ(has-a) ๊ด๊ณ์์์ ์ง๋ ฌํ ์กฐ๊ฑด(๋ฌธ๋ฒ)์ ์ ๋ฆฌํ ๊ฒ์ด๋ค.
Serializable ์ธํฐํ์ด์ค๋ JVM์๊ฒ ์ด ํด๋์ค๊ฐ ์ง๋ ฌํ๋ ์ ์๋ค๋ ๊ฒ์ ์๋ ค์ฃผ๋ ์ญํ ์ ํ๋ค.
๐ ์์(is-a)
๋ถ๋ชจ ํด๋์ค๊ฐ Serializable์ ๊ตฌํํ๋ฉด
- ์์ ํด๋์ค๋ ํญ์ ์ง๋ ฌํ๊ฐ ๊ฐ๋ฅํ๋ค.
- ์์ ํด๋์ค๋ฅผ ์ง๋ ฌํํ๋ฉด ๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์๋ ํจ๊ป ์ง๋ ฌํ๋๋ค.
์์ ํด๋์ค๋ง Serializable์ ๊ตฌํํ๋ฉด
- ๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์๋ ์ง๋ ฌํ๋์ง ์๋๋ค.
- ๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์๋ ์ง๋ ฌํ ๋์์ ํฌํจํ๋ ค๋ฉด ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
1. ์กฐ์ ํด๋์ค๊ฐ Serializable์ ๊ตฌํํ๋ค.
2. ์ง๋ ฌํ ์ฝ๋๋ฅผ ์ง์ ๊ตฌํํ๋ค.
๐ ํฌํจ(has-a)
ํด๋์ค๊ฐ ์ง๋ ฌํํ ์ ์๋ ๊ฐ์ฒด๋ฅผ ์ธ์คํด์ค ๋ณ์๋ก ์ฐธ์กฐํ๊ณ ์์ผ๋ฉด(๋ณ์ ํ์ ์ด ์๋ ์ค์ ๊ฐ์ฒด์ ํ์ ์ ์ํด ๊ฒฐ์ ๋จ)
- ์ง๋ ฌํ ๋ถ๊ฐ(NotSerializableException)
- ์ง๋ ฌํ ๋ถ๊ฐํ ์ธ์คํด์ค ๋ณ์์๋ transient๋ฅผ ๋ถ์ฌ ์ ์ธํ๊ณ ์ง๋ ฌํ ๊ฐ๋ฅ
transient
์์ ๊ฐ์ด ์ง๋ ฌํํ๋ ค๋ ํด๋์ค์ ์ง๋ ฌํํ ์ ์๋ ๊ฐ์ฒด๋ฅผ ํฌํจํ๊ณ ์์ผ๋ฉด transient๋ฅผ ๋ถ์ฌ ์ง๋ ฌํ ๋์์์ ์ ์ธํ๊ณ ์ง๋ ฌํํ ์ ์๋ค.
- ์ง๋ ฌํ ๋์์์ ์ ์ธ
- transient ํ๋๋ ์ญ์ง๋ ฌํ ์ null(์ฐธ์กฐํ) ๋๋ ํ์ ์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋ฐํ๋๋ค.
public class Member implements Serializable {
String name;
transient String password; // ์ง๋ ฌํ ๋์์์ ์ ์ธ๋์ด ์ง๋ ฌํ ์ null ์ฒ๋ฆฌ
}
์ง๋ ฌํ์ ArrayList
์ง๋ ฌํ ์์์ ์ญ์ง๋ ฌํ ์์ ์์๋ฅผ ์ผ์น์์ผ์ผ ํ๊ธฐ ๋๋ฌธ์(๊ฐ์ฒด a, b c๋ฅผ ์ง๋ ฌํํ๋ฉด ์ญ์ง๋ ฌํ์์๋ a, b, c ์์ผ๋ก ์ํ) ๋ง์ ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํํ ๋๋ ์์๋ฅผ ์ ๊ฒฝ์ฐ์ง ์์๋ ๋๋ ArrayList์ ๊ฐ์ ์ปฌ๋ ์ ์ ๋ด์ ์ง๋ ฌํํ๋ ๊ฒ์ด ์ข๋ค.
// ๊ฐ๋ณ ์ง๋ ฌํ, ์ญ์ง๋ ฌํ
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serialize_test.ser"));
oos.writeObject(new Member("Kate", 30, "1234"));
oos.writeObject(new Member("Jason", 23, "5678"));
oos.writeObject(new Member("Aaron", 35, "9012"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("serialize_test.ser"));
System.out.println(ois.readObject());
System.out.println(ois.readObject());
System.out.println(ois.readObject());
System.out.println("==========================================");
// ArrayList์ ๋ด์ ์ง๋ ฌํ
List<Member> memberList = new ArrayList<>();
memberList.add(new Member("Kate", 30, "1234"));
memberList.add(new Member("Jason", 23, "5678"));
memberList.add(new Member("Aaron", 35, "9012"));
oos.writeObject(memberList);
System.out.println(ois.readObject());
๐ฅ ์คํ ๊ฒฐ๊ณผ
Member{id=1001, name='Kate', age=30, password='null'}
Member{id=1002, name='Jason', age=23, password='null'}
Member{id=1003, name='Aaron', age=35, password='null'}
==========================================
[Member{id=1004, name='Kate', age=30, password='null'}, Member{id=1005, name='Jason', age=23, password='null'}, Member{id=1006, name='Aaron', age=35, password='null'}]
์ง๋ ฌํ/์ญ์ง๋ ฌํ ์ง์ ๊ตฌํํ๊ธฐ
ObjectOutputStream์ writeObject(), ObjectInputStream์ readObject()๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ๋จํ๊ฒ ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํ, ์ญ์ง๋ ฌํํ ์ ์์ง๋ง ๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์๋ ์ง๋ ฌํ๋์ง ์๋๋ค๋ ์ ์ฝ ์ฌํญ์ด ์๋ค. ๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์๋ ํจ๊ป ์ง๋ ฌํํ๋ ค๋ฉด Serializable์ ๊ตฌํํ ์์ ํด๋์ค์ writeObject(), readObject()๋ฅผ ์ ์ธํ์ฌ ๊ตฌํํด์ผ ํ๋ค.
private void writeObject(ObjectOutputStream oos) throws IOException {
// ๋ถ๋ชจ ์ง๋ ฌํ
oos.writeUTF(๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์ ์ด๋ฆ); // String
oos.writeInt(๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์ ์ด๋ฆ); // int
oos.writeDouble(๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์ ์ด๋ฆ); // double
...
// ์๊ธฐ ์์ ์ง๋ ฌํ
oos.defaultWriteObject();
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// ๋ถ๋ชจ ์ญ์ง๋ ฌํ
๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์ ์ด๋ฆ = ois.readUTF(); // String
๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์ ์ด๋ฆ = ois.readInt(); // int
๋ถ๋ชจ ํด๋์ค์ ์ธ์คํด์ค ๋ณ์ ์ด๋ฆ = ois.readDouble(); // double
...
// ์๊ธฐ ์์ ์ง๋ ฌํ
ois.defaultReadObject();
}
serialVersionUID - ํด๋์ค ๋ฒ์ ๊ด๋ฆฌ
- ์ง๋ ฌํ ์ํ ์ ํด๋์ค ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๋์ผ๋ก ์์ฑ๋๋ ํด๋์ค์ ๋ฒ์ ๊ฐ
- ์ญ์ง๋ ฌํ ์ํ ์ ์ด ๊ฐ์ ๋น๊ตํด์ ์ผ์นํด์ผํจ
- ๋ค๋ฅผ ๊ฒฝ์ฐ InvalidClassException ๋ฐ์
- ์ง๋ ฌํ์ ์ํฅ์ด ์๋ static ๋ณ์๋ transient ๋ณ์ ์ถ๊ฐ ์ InvalidClassException ๋ฐฉ์ง ํ์
- ์ด๋ฅผ ์ํด ์ง๋ ฌํ ํด๋์ค์ serialVersionUID๋ฅผ ์ง์ ์ ์
- ์ง์ ์ ์ํ๋ฉด ์๋์ผ๋ก ์์ฑํ์ง ์๊ณ ์ด๋ฅผ ์ฌ์ฉํจ
- ๋ณดํต jdk์ ํฌํจ๋ serialver ๋ฐ์ด๋๋ฆฌ๋ฅผ ์ฌ์ฉํด์ ์์ฑ
JSON ์ง๋ ฌํ, ์ญ์ง๋ ฌํ
์ง๊ธ๊น์ง๋ ๊ฐ์ฒด ์ง๋ ฌํ๋ฅผ JVM๊ณผ ObjectInputStream/ObjectOutputStream์ ์์ํ๋ ๋ฐฉ์์ด์์ง๋ง XML, JSON๊ณผ ๊ฐ์ ํฌ๋งท์ ์ด์ฉํ ์ง๋ ฌํ๋ ๊ฐ๋ฅํ๋ค. ์ด๋ก ์ธํ ์ฅ์ ์ ๋ค๋ฅธ ํ๊ฒฝ, ๋ค๋ฅธ ์ธ์ด๋ก ๋ง๋ค์ด์ง ์ดํ๋ฆฌ์ผ์ด์ ๊ณผ๋ ํต์ ์ด ๊ฐ๋ฅํด์ง๋ค๋ ๊ฒ์ด๋ค.
JSON ํ์ฑ, ์ฒ๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฐ ๊ฐ์ฒด๋ฅผ JSON์ผ๋ก ์ง๋ ฌํํ๊ฑฐ๋ JSON ๋ฐ์ดํฐ๋ฅผ ์๋ฐ ๊ฐ์ฒด๋ก ์ญ์ง๋ ฌํํ ์ ์๋ค. ์ฌ๊ธฐ์๋ Jackson์ ์ฌ์ฉํ๋ค.
(1) Object to JSON(์ง๋ ฌํ)
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
java
List<Member> memberList = new ArrayList<>();
memberList.add(new Member("Kate", 30, "1234"));
memberList.add(new Member("Jason", 23, "5678"));
memberList.add(new Member("Aaron", 35, "9012"));
ObjectMapper mapper = new ObjectMapper();
// object to json
mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
mapper.writeValue(new File("test.json"), memberList);
test.json
[
{
"id": 1001,
"name": "Kate",
"age": 30
},
{
"id": 1002,
"name": "Jason",
"age": 23
},
{
"id": 1003,
"name": "Aaron",
"age": 35
}
]
(2) JSON to Object(์ญ์ง๋ ฌํ)
java
ObjectMapper mapper = new ObjectMapper();
// json to object
mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
mapper.writeValue(new File("test.json"), memberList);
System.out.println(mapper.readValue(new File("test.json"), new ArrayList<Member>().getClass()));
configure()๋ transient ์ฒ๋ฆฌ๋ฅผ ์ํด ์ถ๊ฐํ ์ฝ๋์ด๋ค.
ํ๋์ ์ง์ @JsonIgnore ์ ๋ ธํ ์ด์ ์ ๋ถ์ฌ๋ ๋๋ค.
๐ฅ ์คํ ๊ฒฐ๊ณผ
[{id=1001, name=Kate, age=30}, {id=1002, name=Jason, age=23}, {id=1003, name=Aaron, age=35}]
Process finished with exit code 0