이유는 Reflection + Proxy 때문이다
왜 JPA Entity에는 기본 생성자(Public, Protected)가 필요할까?
JPA는 Reflection이란 기술을 사용해
1. 런타임 시점에 Entity Class를 기본생성자로 new 하여 인스턴스를 생성 하고
2. 빈 클래스의 필드에 값을 채워넣는다.
= 기본 생성자가 필요한 이유
또한 JPA에서 지연로딩에 사용되는 Proxy 객체를 생성할때 Reflection이란 기술을 사용하는데
Proxy 객체는 엔티티 객체를 상속 받기 때문에 기본 생성자가 private이면 생성이 되지 않는다.
= 기본 생성자가 Public, Protected 이여야 하는 이유
Reflection은 뭔데? 그리고 필드가 Private인데 빈 클래스에 값을 어떻게 채워넣어?
Reflection이란?
구체적인 클래스 타입을 알지 못해도 그 클래스의 정보(메서드, 타입, 변수 등등)에 접근제어자 Private을 무시하고 접근할 수 있게 해주는 자바 API다.
※ JVM이 실행되면 작성된 자바 코드가 static 영역에 저장됨을 이용
하지만 이런 기술을 사용하려면 해당 클래스에 기본 생성자가 있어야 한다.
(예시)
이러한 Car 객체가 있고
이렇게 Object 객체를 만들고 obj.move()를 실행하면 컴파일 오류가 난다.
왜냐하면 new Car()는 런타임 시점에 만들어주기 때문에 컴파일러 입장에서 obj는 Car가 아닌 Object이다.
하지만 Reflection은 이를 가능하게 해준다.
클래스 이름 만으로 생성자, 필드, 메서드 등 클래스의 모든것을 가져올수 있다.
SetAccessible(true) 하면 Private에도 접근할수 있다.
근데 왜 Private 생성자이면 안됨?
JPA에서 매핑한 Entity를 조회할 때 지연 로딩으로 설정을 하면 hibernate가 생성한 proxy 객체를 사용하여 연관된 데이터를 실제 사용하는 시점에 조회한다.
이때 proxy 객체는 직접 만든 class 를 상속하기 때문에 public, protected 기본 생성자를 필요로 하게 된다. 결국 public, protected 생성자가 없다면 proxy 객체를 사용할 수 없게 되는 것이다.
@Entity도 public 기본 생성자를 자동으로 생성하지 않나?
맞다.
그런데 @Entity는 public 기본 생성자를 생성한다.
public 으로 기본 생성자를 열어두는 것은 무분별한 객체 생성을 야기할 수 있다.
그러므로 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용하자
사용자가 정의한 생성자가 있으면 @Entity는 사용자가 정의한 생성자를 따라간다.
+a DTO에 @Getter와 @AllArgsConstructor가 필요한 이유
DTO는 컨트롤러에서 프론트가 보낸 값을 받거나, 보낼때 사용한다.
이때 ObjectMapper라는 객체에서 Json으로, Json을 객체로 변환하는 친구가 있는데
객체에서 Json으로 : 직렬화
직렬화 과정에서 ObjectMapper가 Getter를 통해 객체를 Json으로 만든다.
Json을 객체로 : 역직렬화
역직렬화 과정에서 ObjectMapper가 Setter, 모든 객체가 들어간 생성자(@AllArgsConstructor)를 통해 Json을 객체로 변환한다.
참고
https://tecoble.techcourse.co.kr/post/2020-07-16-reflection-api/
'백앤드' 카테고리의 다른 글
유효성 검사는 DTO에서 하는게 좋을까 Entity에서 하는게 좋을까? (feat. @NotBlank, @Size) (0) | 2023.05.24 |
---|---|
@EntityListeners(AuditingEntityListener.class), @EnableJpaAuditing에 대해서 알아보자 (0) | 2023.05.21 |
[SQL] 관계형 DB 문법 정리 (0) | 2022.11.16 |