JiwonDev

10. ์ง๋ ฌํ™”

by JiwonDev

2021.08.04 - [๐Ÿ‘€๊ธฐ๋ณธ ์ง€์‹/Java ๊ธฐ๋ณธ์ง€์‹] - ์ž๋ฐ”์˜ ์ง๋ ฌํ™”(Serialization)๋ž€?

 

์ž๋ฐ”์˜ ์ง๋ ฌํ™”(Serialization)๋ž€?

https://www.slideshare.net/sunnykwak90/java-serialization-46382579 ์ž๋ฐ” ์ง๋ ฌํ™” (Java serialization) ์ž๋ฐ” ์ง๋ ฌํ™” ๊ธฐ์ˆ ์— ๋Œ€ํ•œ ์†Œ๊ฐœ ๋ฐ ์ง๋ ฌํ™” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ค์— ๋Œ€ํ•œ ๋น„๊ต ์ž๋ฃŒ www.slideshare.net https://k..

jiwondev.tistory.com

 

# ์ง๋ ฌํ™”์˜ ๋Œ€์•ˆ์„ ์ฐพ์•„๋ผ.

์ง๋ ฌํ™”๋Š” ๋ณด์•ˆ์— ์ทจ์•ฝํ•˜๊ณ , ๊ณต๊ฒฉํ•  ์š”์†Œ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ๋‹ค.

์ด๋Š” ์˜›๋‚  ์˜›์  ์ด์•ผ๊ธฐ๊ฐ€ ์•„๋‹ˆ๋‹ค. 2016๋…„11์›” ์ƒŒํ”„๋ž€์‹œ์Šค์ฝ” ๊ตํ†ต๊ตญ์—์„œ๋Š” ์ง๋ ฌํ™”๋กœ ์ธํ•œ ์ฝ”๋“œ ์‚ฝ์ž…๋•Œ๋ฌธ์— ๋žœ์„ฌ์›จ์–ด ๊ณต๊ฒฉ์„ ๋ฐ›์•„ ์š”๊ธˆ ์ง•์ˆ˜ ์‹œ์Šคํ…œ์ด ์ดํ‹€๊ฐ„ ๋งˆ๋น„๋˜๊ธฐ๋„ ํ•˜์˜€๋‹ค.

 

์ง๋ ฌํ™”์˜ ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ œ๋Š” ๊ณต๊ฒฉ ๋ฒ”์œ„๊ฐ€ ๋„ˆ๋ฌด ๋„“๊ณ , ๋ฐฉ์–ดํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋Š”๋ฐ์— ์žˆ๋‹ค. ์—ญ์ง๋ ฌํ™”๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฑฐ์˜ ๋ชจ๋“  ํƒ€์ž…์˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด๋‚ผ ์ˆ˜ ์žˆ๋Š” ๋งˆ๋ฒ•์˜ ํด๋ž˜์Šค๊ฐ€ ๋˜๋ฉฐ, ์ด๋ฅผ ๋ง‰๋Š”๋‹ค๊ณ  ํ•œ๋“ค ์—ญ์ง๋ ฌํ™”๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๊ฐ์ฒด ์ฒด์ธ์„ ๋ฌดํ•œ๋ฐ˜๋ณตํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์‹œ์Šคํ…œ์„ ๋ป—์–ด๋ฒ„๋ฆฌ๊ฒŒ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋‹ค.

// ์—ญ์ง๋ ฌํ™” ํญํƒ„(deserialization bomb), hashCode ๋ฉ”์„œ๋“œ๋ฅผ 2^100๋ฒˆ์ •๋„ ํ˜ธ์ถœํ•˜๊ฒŒ ๋œ๋‹ค.
static byte[] bomb() {
    Set<Object> root = new HashSet<>();
    Set<Object> s1 = root;
    Set<Object> s2 = new HashSet<>();

    for (int i=0; i < 100; i++) {
        Set<Object> t1 = new HashSet<>();
        Set<Object> t2 = new HashSet<>();

        t1.add("foo"); // t1์„ t2๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋งŒ๋“ ๋‹ค.
        s1.add(t1); s1.add(t2);

        s2.add(t1); s2.add(t2);
        s1 = t1; s2 = t2;
    }
    return serialize(root);
}

์—ญ์ง๋ ฌํ™” ํญํƒ„(deserialization bomb), hashCode ๋ฉ”์„œ๋“œ๋ฅผ 2^100๋ฒˆ์ •๋„ ํ˜ธ์ถœํ•˜๊ฒŒ ๋œ๋‹ค.

์ด์™ธ์—๋„ ์ƒ์„ฑ์ž๋‚˜ ์†Œ๋ฉธ์ž(finalizer)๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ๊ณต๊ฒฉ, ๊ฐ์ฒด์˜ ํ•„๋“œ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ง๋ ฌํ™”๊ฐ€ ๊นจ์ง€๋Š” ๋ฌธ์ œ๋“ฑ์ด ์žˆ๋‹ค.

 

์ง๋ ฌํ™”์˜ ์œ„ํ—˜์„ฑ์„ ํ”ผํ•˜๋Š” ๊ฐ€์ • ์ข‹์€ ๋ฐฉ๋ฒ•์€ '์•„๋ฌด ๊ฒƒ๋„ ์—ญ์ง๋ ฌํ™” ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ' ์ด๋‹ค. ๊ผญ ํ•„์š”ํ•˜๋‹ค๋ฉด ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋งŒ ์ง๋ ฌํ™” ํ•  ์ˆ˜ ์žˆ๋„๋ก [Java9์— ์ถ”๊ฐ€๋œ ๊ฐ์ฒด ์—ญ์ง๋ ฌํ™” ํ•„ํ„ฐ๋ง]๋“ฑ์„ ์‚ฌ์šฉํ•ด๋ณด์ž. ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์ด ์ง๋ ฌํ™” ๋˜๊ธฐ์ „์— ํŠน์ • ํด๋ž˜์Šค๋งŒ ์ˆ˜์šฉ/ ์ œ์™ธํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ง๋ ฌํ™”๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด JSON ๊ฐ™์€ ์ง๋ ฌํ™” ๋ฐ์ดํ„ฐ๋‚˜, ๋งŒ์•ฝ ์„ฑ๋Šฅ์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ํ”„๋กœํ† ์ฝœ ๋ฒ„ํผ๊ฐ™์€ ๋Œ€์•ˆ์ด ์žˆ๋‹ค.

๋‹ค๋งŒ [JSON, ํ”„๋กœํ† ์ฝœ ๋ฒ„ํผ, Java9]์˜ ํ•„ํ„ฐ๋ง์„ ๋‹ค ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ ๋ชจ๋“  ์ง๋ ฌํ™” ๊ณต๊ฒฉ์— ์•ˆ์ „ํ•˜๋‹ค๊ณ  ๋งํ•  ์ˆ˜๋Š” ์—†๋‹ค. ๊ฐ€๋Šฅํ•˜๋ฉด ํด๋ž˜์Šค๊ฐ€ ์ง๋ ฌํ™”๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋„๋ก ๋งŒ๋“ค์–ด์•ผํ•˜๊ณ  ๊ผญ ํ•„์š”ํ•˜๋‹ค๋ฉด ์‹ ๊ฒฝ์จ์„œ ์„ค๊ณ„ํ•˜๋„๋ก ํ•˜์ž.

 

 

# Serializable์„ ๊ตฌํ˜„ํ• ์ง€๋Š” ์‹ ์ค‘ํžˆ ๊ฒฐ์ •ํ•˜๋ผ.

์ž๋ฐ”์—์„œ๋Š” extends Serializable ํ•˜๋‚˜๋งŒ์œผ๋กœ ์‰ฝ๊ฒŒ ์ง๋ ฌํ™”๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋กœ ์ธํ•œ๋Œ€๊ฐ€๋Š” ์ƒ๋‹นํ•˜๋‹ค.

๊ตฌํ˜„ํ•œ ์ˆœ๊ฐ„๋ถ€ํ„ฐ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ–ˆ์„ ๋•Œ ์ง๋ ฌํ™” ๋ฒ„์ „์ด ๊นจ์ ธ ํ™•์žฅ์„ฑ์„ ์žƒ๊ฒŒ๋˜๊ณ , ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ์œ„ํ—˜์„ฑ์„ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค.

 

์ž๋ฐ”์—์„œ๋Š” ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ์ž๋ฅผ ์ด์šฉํ•ด ๋งŒ๋“ ๋‹ค. ์ง๋ ฌํ™”๋Š” ์ด๋Ÿฌํ•œ ์ƒ์„ฑ์ž ๊ธฐ๋Šฅ์„ ์šฐํšŒํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ถˆ๋ณ€์‹์ด ๊นจ์งˆ ์œ„ํ—˜์„ฑ์ด ์žˆ๊ณ , ์บก์Šํ™”๊ฐ€ ๊นจ์ง„๋‹ค. ํŠนํžˆ ์ƒ์†์„ ๋ชฉ์ ์œผ๋กœ ์„ค๊ณ„๋œ ํด๋ž˜์Šค๋Š” Serializable์„ ์‚ฌ์šฉํ–ˆ๋‹ค๊ฐ„ ํฐ์ผ ๋‚  ์ˆ˜ ์žˆ๋‹ค. ๋‚ด๊ฐ€ ์ง๋ ฌํ™”ํ•œ์ง€ ์—ฌ๋ถ€๋„ ๋ชจ๋ฅธ์ฑ„ ์ง๋ ฌํ™”๋œ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  ์žˆ์œผ๋‹ˆ๊นŒ ๋ง์ด๋‹ค.

 

๋˜ํ•œ ๋‚ด๋ถ€ํด๋ž˜์Šค๋„ ์ง๋ ฌํ™” ํ•ด์„  ์•ˆ๋œ๋‹ค. JVM ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๋‚ด๋ถ€ ํด๋ž˜์Šค์˜ outer ํด๋ž˜์Šค์˜ ์ฐธ์กฐ์™€ ์Šค์ฝ”ํ”„๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ํ•„๋“œ๋ฅผ ํ•„์š”๋กœํ•ด์„œ, ์ปดํŒŒ์ผ๋Ÿฌ์—์„œ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•˜๋Š”๋ฐ. ์ด ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜๋Š” ํ•„๋“œ ๋˜ํ•œ ์ง๋ ฌํ™” ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž๊ฐ€ ์˜ˆ์ธกํ•  ์ˆ˜ ์—†๋‹ค. (static inner ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค์–ด์•ผํ•˜๋Š” ์ด์œ ๊ธฐ๋„ ํ•˜๋‹ค. ์ด๋Ÿฌ๋ฉด ๋ฌธ์ œ ํ•ด๊ฒฐ)

 

 

# ์ปค์Šคํ…€ ์ง๋ ฌํ™” ํ˜•ํƒœ๋ฅผ ๊ณ ๋ คํ•ด๋ณด๋ผ

๊ฐ์ฒด๋ฅผ ํ†ต์œผ๋กœ ์ง๋ ฌํ™”ํ•˜์ง€๋ง๊ณ , ์ปค์Šคํ…€ํ•ด์„œ ์‚ฌ์šฉํ•ด๋ผ

์ด์ƒ์ ์ธ ์ง๋ ฌํ™” ํ˜•ํƒœ๋ผ๋ฉด, ์‹ค์ œ ๊ฐ์ฒด์˜ ๋ฌผ๋ฆฌ์ ์ธ ๋ชจ์Šต๊ณผ๋Š” ์™„์ „ํžˆ ๋…๋ฆฝ๋œ ๋…ผ๋ฆฌ์ ์ธ ๋ชจ์Šต๋งŒ์„ ํ‘œํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

public final class StringList implements Serializable {
    private int size = 0;
    private Entry head = null;

    private static class Entry implements Serializable {
        String data;
        Entry next;
        Entry previous;
    }
    // ... ์ƒ๋žต
}

์œ„์˜ ์ฝ”๋“œ๋Š” ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋ฌธ์ž์—ด๋“ค์„ ์ด์ค‘ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋กœ ํ‘œํ˜„ํ–ˆ๋‹ค.

์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ๊ฐ์—†์ด ๋ฌผ๋ฆฌ์ ์œผ๋กœ ์ง๋ ฌํ™”ํ–ˆ๋‹ค๊ฐ€๋Š”, ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด๋‹ค.

  • ์ง๋ ฌํ™”์— ๋ฒ„์ „์ด ์ƒ๊ธด๋‹ค. ๋‚ด๋ถ€ ํ‘œํ˜„์— ์ข…์†์ ์ด๊ฒŒ ๋œ๋‹ค. (์ œ๊ฑฐ ๋ถˆ๊ฐ€๋Šฅ)
  • ์ง๋ ฌํ™” ๋ฐ์ดํ„ฐ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ์“ธ๋ชจ์—†์ด ํฌ๋‹ค
  • ์ง๋ ฌํ™” ์‹œ๊ฐ„์ด ๋งŽ์ด๊ฑธ๋ฆฌ๊ณ , ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ์Šคํƒ์˜ค๋ฒ„ํ”Œ๋กœ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ธฐ ์‰ฝ๋‹ค.

 

์ง๋ ฌํ™”๋ฅผ ์ปค์Šคํ…€ํ•˜์ž.

transient ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€ ํ•„๋“œ๋Š” ๊ธฐ๋ณธ ์ง๋ ฌํ™” ํ˜•ํƒœ์— ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค. ์ฐธ๊ณ ๋กœ [transient ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ]๋Š” ์ •์ƒ์ ์œผ๋กœ ์ง๋ ฌํ™” ๋œ๋‹ค. ๊ทธ๋ž˜์•ผ ์ƒ์œ„/ํ•˜์œ„ํ˜ธํ™˜์ด ๊ฐ€๋Šฅํ•˜๋‹ˆ๊นŒ.

public final class StringList implements Serializable {
    private transient int size = 0;
    private transient Entry head = null;

    // ์ด๋ฒˆ์—๋Š” ์ง๋ ฌํ™” ํ•˜์ง€ ์•Š๋Š”๋‹ค.
    private static class Entry {
        String data;
        Entry next;
        Entry previous;
    }

    // ๋ฌธ์ž์—ด์„ ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€ํ•œ๋‹ค.
    public final void add(String s) { ... }

    /**
     * StringList ์ธ์Šคํ„ด์Šค๋ฅผ ์ง๋ ฌํ™”ํ•œ๋‹ค.
     */
    private void writeObject(ObjectOutputStream stream)
            throws IOException {
        stream.defaultWriteObject();
        stream.writeInt(size);

        // ๋ชจ๋“  ์›์†Œ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๊ธฐ๋กํ•œ๋‹ค.
        for (Entry e = head; e != null; e = e.next) {
            s.writeObject(e.data);
        }
    }

    private void readObject(ObjectInputStream stream)
            throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        int numElements = stream.readInt();

        for (int i = 0; i < numElements; i++) {
            add((String) stream.readObject());
        }
    }
    // ... ์ƒ๋žต
}

 

 

# ์ง๋ ฌํ™”(readObject)์—์„œ ๋ฐฉ์–ด์ ์œผ๋กœ ๋ณต์‚ฌํ•˜๊ณ , ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋„ ํ•ด๋ผ

# ์ง๋ ฌํ™” ๋ฒ„์ „(SUID) ๋ฅผ ๋ช…์‹œํ•ด๋ผ

# ์ง๋ ฌํ™” ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ. ์‹ค์ œ ๊ฐ์ฒด์— ๋ฐ”๋กœ ์—ญ์ง๋ ฌํ™” ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋ง‰์–ด๋ฒ„๋ ค๋ผ.

# ์‹ฑ๊ธ€ํ†ค์„ enum์œผ๋กœ ๋งŒ๋“ค์–ด๋ฒ„๋ ค๋ผ. ์•ˆ์ „๐Ÿ‘

https://madplay.github.io/post/effectivejava-chapter12-serialization

 

[์ดํŽ™ํ‹ฐ๋ธŒ ์ž๋ฐ” 3ํŒ] 12์žฅ. ์ง๋ ฌํ™”

[Effective Java 3th Edition] Chapter12: Serialization

madplay.github.io

 

์ถ”๊ฐ€๋‚ด์šฉ ์ž‘์„ฑ์˜ˆ์ •.

https://hyperconnect.github.io/2019/10/28/jackson-serialize-for-global-caching.html

 

Jackson ์ง๋ ฌํ™” ์˜ต์…˜์˜ ์ ์ ˆํ•œ ํ™œ์šฉ๊ณผ Jackson์— ๊ธฐ์—ฌํ•˜๊ธฐ๊นŒ์ง€ (feat. ๊ธ€๋กœ๋ฒŒ ์บ์‹ฑ)

๊ธ€๋กœ๋ฒŒ์บ์‹ฑ์„ ํ•˜๊ธฐ ์œ„ํ•ด ์ž๋ฐ”์˜ ์˜คํ”ˆ์†Œ์Šค JSON ์ง๋ ฌํ™” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Jackson์˜ ์ง๋ ฌํ™” ์˜ต์…˜์„ ํ™œ์šฉํ•œ ๊ฒฝํ—˜๊ณผ Jackson์— ๊ธฐ์—ฌํ•œ ๊ฒฝํ—˜์„ ์ด์•ผ๊ธฐํ•ฉ๋‹ˆ๋‹ค.

hyperconnect.github.io

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ