๐Ÿ“‚ JAVA/์ฃผ์š” ๊ฐœ๋…

์ž…์ถœ๋ ฅ(I/O)(2) - ์ง๋ ฌํ™”(Serialization)

Amenable 2023. 1. 29. 23:31

1. ์ง๋ ฌํ™” ๊ฐœ๋… ๐Ÿ“

  ์ง๋ ฌํ™”(Serialization)๋ž€ ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. ์ฆ‰, ํ˜„์žฌ ๋ฐ์ดํ„ฐ(structure, object)์˜ ์ƒํƒœ๋ฅผ ์˜์†์ ์œผ๋กœ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํ™˜๊ฒฝ์œผ๋กœ ์ „๋‹ฌ(๋„คํŠธ์›Œํฌ ํ†ต์‹  ๋“ฑ) ํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋– ํ•œ ์ •ํ•ด์ง„ ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ด๋‹ค. ๋ณ€ํ™˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ์›๋ž˜ ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์€ ์—ญ์ง๋ ฌํ™”(Deserialization)์ด๋ผ๊ณ  ํ•œ๋‹ค. 

// ์ง๋ ฌํ™”๋ฅผ ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ
public class User{
	
	private String name;
	private String id;
	String password;
	
	public User(String name, String id, String password) {
		this.name = name;
		this.id = id;
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", id=" + id + ", password=" + password + "]";
	}
}


public class Main {
	
	private static File target = new File("C:" + File.separator + "Amenable" + File.separator + "User.dat");
	
	public static void main(String[] args) throws IOException {
		write();
	}

	private static void write() {
		User user = new User("์ด๋ฐ”๋‹ค", "Amenable", "1234");
		
		try(ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream(target))){
			objOut.writeObject(user); // Error
			System.out.println("์ €์žฅ ์™„๋ฃŒ");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static void read() {
		try(ObjectInputStream objIn = new ObjectInputStream(new FileInputStream(target))){
			Object obj = objIn.readObject();
			if(obj instanceof User) {
				User user = (User) obj;
				System.out.println(user);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

  ์œ„์˜ ์ฝ”๋“œ๋Š” ์ง๋ ฌํ™”๋ฅผ ์ง„ํ–‰ํ•˜์ง€ ์•Š๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค. ์‹คํ–‰์‹œ์ผœ ๋ณด๋ฉด, "java.io.NotSerializableException"๋ผ๋Š” ์—๋Ÿฌ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜์˜จ๋‹ค. ์ฆ‰, ๊ฐ์ฒด๋ฅผ ํ†ต์‹  ๋ฐ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด์„  ๋ฐ˜๋“œ์‹œ ์ง๋ ฌํ™”๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

 

2. ์ง๋ ฌํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ• ๐Ÿธ

  ์ง๋ ฌํ™”๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•˜๋‹ค. ์ง๋ ฌํ™”ํ•˜๊ณ ์ž ํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ java.io.Serializable์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋„๋ก ํ•˜๋ฉด ๋œ๋‹ค. ๋งŒ์•ฝ ๋ณด์•ˆ์ƒ ์ง๋ ฌํ™”ํ•˜๋ฉด ์•ˆ ๋˜๋Š” ๊ฐ’์ด ์žˆ๋‹ค๋ฉด transient๋ฅผ ๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ธ์Šคํ„ด์Šค๋ณ€์ˆ˜์˜ ๊ฐ’์€ ๊ทธ ํƒ€์ž…์˜ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ง€์ •๋œ๋‹ค. 

// ์ง๋ ฌํ™”๋ฅผ ํ•œ ๊ฒฝ์šฐ
public class User implements Serializable {
	private String name;
	private String id;
	transient String password; // ํƒ€์ž…์˜ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ง๋ ฌํ™” ์ง„ํ–‰
	
	public User(String name, String id, String password) {
		this.name = name;
		this.id = id;
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", id=" + id + ", password=" + password + "]";
	}
}

public class Main {
	
	private static File target = new File("C:" + File.separator + "Amenable" + File.separator + "User.dat");
	
	public static void main(String[] args) throws IOException {
		write();
		read();
	}

	private static void write() {
		User user = new User("์ด๋ฐ”๋‹ค", "Amenable", "1234");
		
		try(ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream(target))){
			objOut.writeObject(user);
			System.out.println("์ €์žฅ ์™„๋ฃŒ");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static void read() {
		try(ObjectInputStream objIn = new ObjectInputStream(new FileInputStream(target))){
			Object obj = objIn.readObject();
			if(obj instanceof User) {
				User user = (User) obj;
				System.out.println(user);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

// ์ €์žฅ ์™„๋ฃŒ
// User [name=์ด๋ฐ”๋‹ค, id=Amenable, password=null]

  ๋งŒ์•ฝ Serializable์„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›๋Š”๋‹ค๋ฉด Serializable์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

public class User implements Serializable {
    int id;
    String name;
}

public class SubUser extends User {
	int country;
}

  ์ง๋ ฌํ™”ํ•˜๊ณ ์ž ํ•˜๋Š” ํด๋ž˜์Šค์— ์ง๋ ฌํ™”๋˜์ง€ ์•Š์€ ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค๋ฉด ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ๋Š” ์œ„์—์„œ ์‚ดํŽด๋ณธ ๊ฒƒ์ฒ˜๋Ÿผ transient๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ง๋ ฌํ™” ๋Œ€์ƒ์—์„œ ์ œ์™ธ๋ฅผ ํ•˜๋ฉด ๋œ๋‹ค. ํ•˜์ง€๋งŒ ํ•ด๋‹น ๊ฐ์ฒด ๋˜ํ•œ ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ๊ฐ’์ด๋ผ๋ฉด ํ•ด๋‹น ๊ฐ์ฒด๋Š” Serializable์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

// ๋ฐฉ๋ฒ•1
public class User implements Serializable {
    private String name;
    transient SubUser subUser = new SubUser();
}

// ๋ฐฉ๋ฒ•2 
public class User implements Serializable {
    private String name;
    SubUser subUser = new SubUser();
}
public class SubUser implements Srializable {
	private String id;
}

 

3. ์ง๋ ฌํ™”๊ฐ€๋Šฅํ•œ ํด๋ž˜์Šค์˜ ๋ฒ„์ „๊ด€๋ฆฌ ๐ŸŽพ

  ์œ„์˜ ์˜ˆ์ œ์—์„œ User์— ๊ตญ๊ฐ€(country)๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด ๋ณด์ž.

public class User implements Serializable {

	private String name;
	private String id;
	transient String password;
	private String country;
	
	public User(String name, String id, String password, String country) {
		this.name = name;
		this.id = id;
		this.password = password;
		this.country = country;
	}
	
	@Override
	public String toString() {
		return "User [name=" + name + ", id=" + id + ", password=" + password + ", country=" + country + "]";
	}
}

  ๊ทธ ํ›„, read()๋ฅผ ํ†ตํ•ด์„œ ๊ฐ’์„ ์ฝ์–ด์˜ค๋ฉด ์–ด๋–ป๊ฒŒ ๋˜์–ด์•ผ ํ• ๊นŒ? ์ผ๋‹จ name, id, password(๊ธฐ๋ณธ๊ฐ’์ด๋ผ๋„ ๊ฐ’์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ž)์€ ์ด์ „์— ์ €์žฅํ•ด ๋†“์•˜์œผ๋‹ˆ๊น ๊ทธ๊ฑธ ๋ถˆ๋Ÿฌ์˜ค๋ฉด ๋˜๊ณ , country๋Š” ์•„์ง ์ €์žฅํ•˜์ง€ ์•Š์•˜์œผ๋‹ˆ๊น null๋กœ ์˜จ๋‹ค๊ณ  ์ƒ๊ฐ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ ์‹คํ–‰์‹œ์ผœ ๋ณด๋ฉด
"java.io.InvalidClassException: test.User; local class incompatible: stream classdesc serialVersionUID = 2429090748191540600, local class serialVersionUID = 2845618176141831242"
์ด ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค. serialVersionUID๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ํ•ด๊ฒฐ์ฑ…๋ถ€ํ„ฐ ๋จผ์ € ๋งํ•˜์ž๋ฉด serialVerisonUID๋ฅผ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

  serialVersionUID๋ž€ ํด๋ž˜์Šค์˜ ๋ณ€๊ฒฝ ์—ฌ๋ถ€๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•œ ์œ ์ผ ํ‚ค์ด๋‹ค. ์ง๋ ฌํ™”๋˜๋Š” ๊ฐ์ฒด์— serialVersionUID๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค. ์œ„์˜ ์˜ˆ์—์„œ๋Š” country๋กœ ์ธํ•œ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•ด ์ปดํŒŒ์ผ ์‹œ์— ์ž๋™์œผ๋กœ ์ƒˆ๋กœ์šด serialVersionUID๋ฅผ ํ• ๋‹น๋ฐ›์•˜๋‹ค.

  ๊ทธ๋ž˜์„œ ์ง๋ ฌํ™”๋˜๋Š” ๊ฐ์ฒด์— ๋Œ€ํ•ด์„œ serialVersionUID๋ฅผ ์„ค์ •ํ•ด ์ค€๋‹ค๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋œ๋‹ค.

// serialVersionUID๋ฅผ ์„ค์ •ํ•˜๊ณ  write()๋ฅผ ํ†ตํ•ด ์ €์žฅ
public class User implements Serializable {

	private static final long serialVersionUID = 2429090748191540600L;
	private String name;
	private String id;
	transient String password;
	
	public User(String name, String id, String password) {
		this.name = name;
		this.id = id;
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", id=" + id + ", password=" + password + "]";
	}
}

public class Main {
	
	private static File target = new File("C:" + File.separator + "Amenable" + File.separator + "User.dat");
	
	public static void main(String[] args) throws IOException {
		write();
	}

	private static void write() {
		User user = new User("์ด๋ฐ”๋‹ค", "Amenable", "1234");
		
		try(ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream(target))){
			objOut.writeObject(user);
			System.out.println("์ €์žฅ ์™„๋ฃŒ");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
// Userํด๋ž˜์Šค์— country ์ถ”๊ฐ€
public class User implements Serializable {

	private static final long serialVersionUID = 2429090748191540600L;
	private String name;
	private String id;
	transient String password;
	private String country;
	
	public User(String name, String id, String password, String country) {
		this.name = name;
		this.id = id;
		this.password = password;
		this.country = country;
	}
	
	@Override
	public String toString() {
		return "User [name=" + name + ", id=" + id + ", password=" + password + ", country=" + country + "]";
	}
}

public class Main {
	
	private static File target = new File("C:" + File.separator + "Amenable" + File.separator + "User.dat");
	
	public static void main(String[] args) throws IOException {
		read();
	}
	
	private static void read() {
		try(ObjectInputStream objIn = new ObjectInputStream(new FileInputStream(target))){
			Object obj = objIn.readObject();
			if(obj instanceof User) {
				User user = (User) obj;
				System.out.println(user);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

// User [name=์ด๋ฐ”๋‹ค, id=Amenable, password=null, country=null]

  serialVersionUID๋ฅผ ์„ค์ •ํ•œ ํ›„ ํด๋ž˜์Šค๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์˜ˆ์ƒํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.(User [name=์ด๋ฐ”๋‹ค, id=Amenable, password=null, country=null])

  ๊ทธ๋Ÿฌ๋‚˜ static๋ณ€์ˆ˜๋‚˜ ์ƒ์ˆ˜ ๋˜๋Š” transient๊ฐ€ ๋ถ™์€ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๊ฐ€ ์ถ”๊ฐ€๋˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ง๋ ฌํ™”์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํด๋ž˜์Šค์˜ ๋ฒ„์ „์„ ๋‹ค๋ฅด๊ฒŒ ์ธ์‹ํ•˜๋„๋ก ํ•  ํ•„์š”๋Š” ์—†๋‹ค. 

 

ํ•ด๋‹น ๊ธ€์€ ๋‚จ๊ถ ์„ฑ๋‹˜์˜ Java์˜ ์ •์„์„ ์ฝ๊ณ  ์ž‘์„ฑํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.