참조 사이트 1
참조 사이트 2
참조 사이트 3
스프링 406에러 참조사이트
개발 언어를 무엇을 선택하든, 사용하는 데이터의 메모리 구조는 크게 2가지로 나뉜다.
위 두 가지 데이터 중에서 디스크에 저장하거나 통신할 때는 값 형식 데이터만 사용할 수 있다. 참조 형식 데이터는 실제 데이터 값이 아닌 힙에 할당되어 있는 메모리 번지 주소를 가지고 있기 때문이다.
예를 들어, 객체 A를 만들고 주소 값이 0x00045523라고 가정하자. 그리고 이 값을 파일에 포함하여 저장했다고 해보자. 이후 프로그램을 종료하고 다시 실행해서 주소 값 0x00045523을 가져오더라도 기존 A 객체의 데이터를 가져올 수 없다. 프로그램이 종료되면 기존에 할당되었던 메모리(0x00045523)는 해제되고 없어지기 때문이다.
네트워크 통신 또한 마찬가지이다. 각 PC마다 사용하고 있는 메모리 공간 주소는 전혀 다르다. 그러므로 내가 다른 PC로 전송한 A 객체 데이터(0x00045523)는 무의미하다. 이 데이터를 받은 PC의 메모리 주소 0x00045523에는 전혀 다른 값이 존재하기 때문이다.
그러니까 그냥 참조 형식 데이터(메모리번지 주소)만 보내게 된다면 할당되어있는 데이터저장소가 사라지기 때문에 전혀 값이없거나 할당위치가 다르기 때문에 다른값이 나오게 되서 원하는 값이 도착하지 않는다.
직렬화가 된 데이터는 언어에 따라서 텍스트 또는 바이너리 등의 형태가 되는데, 이러한 형태가 되었을 때 저장하거나 통신할 때 파싱이 가능한 유의미한 데이터가 된다.
즉, 직렬화를 하는 이유는 사용하고 있는 데이터를 파일 저장 혹은 데이터 통신에서 파싱할 수 있는 유의미한 데이터를 만들기 위함이다.
--
public class A {
...
}
아래와같이 해주면된다. implements Serializable 추가해주거나
public class A implements Serializable {
...
}
아래와 같히 해주면 된다.
Member member = new Member("김배민", "deliverykim@baemin.com", 25);
byte[] serializedMember;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(member);
// serializedMember -> 직렬화된 member 객체
serializedMember = baos.toByteArray();
}
}
직렬화된 파일 등을 역으로 직렬화하여 다시 객체의 형태로 만드는 것을 의미한다. 저장된 파일을 읽거나 전송된 스트림 데이터를 읽어 원래 객체의 형태로 복원한다.
public class User implements Serializable{
private String name;
private transient String password;
private String email;
public int age;
public User(String name, String password, int age) {
this.name = name;
this.password = password;
this.age = age;
}
public String toString() {
return "(" + name + ", " + password + ", " + email + ", " + age + ")";
}
}
public class MainClass {
private static final String USERINFO_SER = "user.ser";
public static void main(String[] args) {
// TODO Auto-generated method stub
conductSerializing();
conductDeserializing();
}
public static void conductSerializing() {
try {
FileOutputStream fos = new FileOutputStream(USERINFO_SER);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream out = new ObjectOutputStream(bos);
User u1 = new User("이방원", "1234", "lby@abc.com", 30);
User u2 = new User("무휼", "8877", "mh1398@abc.com", 25);
ArrayList list = new ArrayList<>();
list.add(u1);
list.add(u2);
out.writeObject(u1);
out.writeObject(u2);
out.writeObject(list);
out.close();
System.out.println("직렬화 완료");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void conductDeserializing(){
try {
FileInputStream fis = new FileInputStream(USERINFO_SER);
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream in = new ObjectInputStream(bis);
User u1 = (User) in.readObject();
User u2 = (User) in.readObject();
ArrayList list = (ArrayList) in.readObject();
System.out.println(u1.toString());
System.out.println(u2.toString());
System.out.println("count :: " + list.size());
System.out.println(list.toString());
in.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
conductSerializing() 메서드에서 직렬화를 수행하여 user.ser 파일을 생성하고 이를 읽어서 바로 역직렬화 한다. 여기서 눈여겨 볼 것은 실행 결과이다.
역직렬화 결과를 보면 직렬화한 순서 그대로 출력됨을 알 수 있다. 즉, 직렬화와 역직렬화 할 때 순서가 매우 중요하다는 것이다!!
매번 순서를 고려해야하고 하나라도 맞지않으면 역직렬화에 실패한다. 같은 객체를 여러번 보내기보다 ArrayList와 같은 자료구조로 한번에 넣는 것이 더욱 효율적이다.
하지만 User가 계속 유지되면 좋겠지만 변수가 추가되거나 달라질수있다. 그럴때마다 다른곳들도 바꿔줘야하는문제점이 있는데 이러한 문제점을 해결하기 위해서 User에서 SerialVersionUID를 맨위쪽에 더해주는것이다.
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
1L은원하는 대로 변경해주면된다.
이렇게 선언한다면 추후에 User클래스에 변경이 생겨도 여전히 1이기 때문에 역직렬화를 성공적으로 할수있다.
JPA에서 양방향으로 연결된 엔티티를 JSON 형태로 직렬화하는 과정에서, 서로의 정보를 계속 순환하며 참조하여 StackOverflowError 를 발생시키는 현상
가장 추천하는 방식이다.
Entity 클래스는 데이터베이스와 맞닿는 핵심 클래스이다.
Entity 클래스를 기준으로 수많은 클래스나 비즈니스 로직들이 동작하고 있다.
Entity 클래스를 통해 여러 클래스들이 영향을 받을 수 있으므로 Entity 클래스를 Request/Response 클래스로 사용하는 것은 강력하게 추천하지 않는다.
컨트롤러에서 Response 값으로 여러 테이블을 조인해야하는 경우가 많으므로, DB Layer 와 View Layer 의 역할 분리를 철저하게 해주자.
역시나 객체지향 설계에 있어서 역할과 책임은 중요한 요소인 것 같다.
양방향 관계에서 직렬화 방향을 설정하여 순환 참조를 해결할 수 있도록 설계된 애노테이션
연관관계 주인 반대 Entity 에 선언
정상적으로 직렬화 수행
연관관계의 주인 Entity 에 선언
직렬화가 되지 않도록 수행
양방향 관계를 가지고 있는 두 엔티티 중 하나의 엔티티의 참조 필드에 직렬화를 제외시키는 방법 JSON 직렬화 과정에서 해당 애노테이션이 선언된 필드는 직렬화 대상에서 제외 해당 필드가 직렬화에 필요할 경우에는 적합하지 않은 방법