Spring Data JPA에서 Entity에 기본 생성자가 필요한 이유가 무엇일까?
@NoArgsConstructor(accsee = AccseeLevel.PROTECTED)를 왜 붙이는거지? 궁금해졌다.
궁금하면 공부해야지~
▶Reflection이란?
그 전에 알아야하는것이 있다.
Reflection은 Java에서 제공하는 API이다.
Reflection은 구체적인 클래스 타입을 알지 못해도!
클래스의 메서드나, 타입, 변수들에 접근할 수 있도록 해주는 API이다.
아니? 이게 어떻게 가능하죠? 클래스나 메소드가 프라이빗하면요??
그걸 알아보기 전에! Reflection이 무슨 의미인지 아는가?
반사라는 의미이다.
즉 클래스 자체를 조작하는것이 아니라, 반사된 클래스를 조작해서 클래스에 영향을 주는것이다.
여기서 반사된 클래스란 무엇일까?
JVM을 보면 Runtime Data Area안에 Method Area라는 공간이 있다.
이 공간안에 우리가 작성한 클래스의 정보가 저장되어있다.
즉, Reflection은 이 Method Area안에 저장되어 있는 클래스 정보에 접근이 가능하기 때문에
new나 setter등의 과정 없이 객체에 데이터를 넣을 수 있다. (private 접근도 가능)
하지만! Reflection은 생성자의 인자 정보를 가져올 수는 없다.
기본 생성자를 통해 객체를 생성할 수 있고, 생성된 객체를 통해서 Reflection API는 필드 값 등을 넣어 줄 수 있는것이다.
그러므로 해당 클래스에 기본생성자가 꼭 있어야한다!
▶그래서 JPA의 Entity에는 기본 생성자가 필요하구나!
그렇다!
Reflection은 기본 생성자를 통해 객체를 생성하기 때문에!
그말은 즉! 해당 클래스에 기본생성자가 꼭 있어야한다는 말!
3줄정리
동적으로 객체 생성시에 Reflection API를 활용하는 JPA
-> Reflection은 기본생성자가 없으면 해당 객체를 생성할 수 없음
-> 따라서 JPA의 Entity에는 기본 생성자가 필요!
이렇게 볼 수 있다.
▶ @NoArgsConstructor(accsee = AccseeLevel.PROTECTED)을 사용하는 이유
좋아! 기본생성자가 왜 필요한지 알게되었다고!!
@Entity
@Getter
@Table(name = "order")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@Column
private int quantity;
@Column
private int price;
@Builder
public Order(Long id, String name, int quantity, int price) {
this.id = id;
this.name = name;
this.quantity = quantity;
this.price = price;
}
}
근데 기본생성자를 생성해주는 어노테이션인 @NoArgsConstructor 옆에 붙은 저 액세스 머시긴 뭐지?
저건 기본 생성자의 접근제한자를 protected 만들어준다!
protected Order() {}
이렇게! 이것의 의미 ->다른 패키지에 소속된 클래스는 접근 금지!이다.
오잉 왜 protected로 기본생성자를 만들지?
그건 바로 프록시와 관련이 있다.
JPA가 매핑한 Entity를 조회할 때 Hibernate가 생성한 proxy 객체를 사용하여서
연관된 데이터를 실제 사용하는 시점에 조회할 수 있다.
▷아니 근데 프록시 객체가 뭐임??
proxy객체가 뭐길래 자꾸 프록시 프록시하는걸까?
JPA는 매핑한 Entity를 조회할 때 2가지 전략이 있다.
1. 조회 시점에 함께 가져온다. (EAGER)
2. 매핑한 Entity를 사용할 때 조회한다. (LAZY)
이때 LAZY(지연로딩)을 사용할 경우 임시로 Hibernate가 생성한 proxy 객체를 생성하고 가리킨다.
여기서 proxy 객체는 직접 만든 객체 class를 상속하기 때문에!
public 또는 protected 기본생성자가 필요하다!
private으로 생성자를 만들게되면 파생된 클래스로부터
상속 형태가 접근 불가가되어버리기 때문에 오류가 발생한다.
즉, public, protected 기본 생성자가 없다면 proxy객체를 사용할 수 없다.
정리하면 public이나 protected기본 생성자가 없다면 JPA가 매핑한 Entity를 조회하여서 값을 불러올 수가 없는것이다!
*접근 제어자의 허용 범위*
private -> default -> protected -> public
▶요약
JPA는 동적으로 객체 생성시에 Reflection API를 활용한다.
-> Reflection은 기본생성자가 없으면 해당 객체를 생성할 수 없음
-> 따라서 JPA의 Entity에는 기본 생성자가 필요하다. 근데 public이나 protected이여야 한다.
-> 왜냐하면 JPA는 Proxy객체를 사용하여 연관된 데이터를 조회한다.
-> 근데 Proxy 객체에 직접 만든 클래스를 상속하기 위해서는 public이나 protected로 열려있어야한다.
-> 그러므로 JPA는 기본생성자가 필요한데, 그 기본 생성자는 public이나 protected이여야 한다!
추가로
@NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용하면 객체 생성 시 안전성을 어느 정도 보장받을 수 있다.
기본 생성자 접근을 protected으로 변경하면 외부에서 해당 생성자를 접근 할 수 없으므로
생성자를 통해서 값 변경 목적으로 오는 접근을 차단할 수 있다!
그러므로 @NoArgsConstructor(access = AccessLevel.PROTECTED) 을 사용하는 것이다...!!
+DTO에서는 @NoArgsConstructor(access = AccessLevel.PROTECTED) 이 아닌 @NoArgsConstructor을 사용한다.
DTO 클래스는 보통 외부에서 생성되어 사용되는데, 이런 경우 기본 생성자를 public으로 선언하는 것이 일반적이다.
DTO 클래스에 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용하는 것은 필요하지 않을 뿐만 아니라, 이러한 설정은 DTO 클래스의 사용 용도와 일반적인 관행에 맞지 않는다.
▣Reference.
https://bluemoon-clover.tistory.com/131
https://velog.io/@yyy96/JPA-%EA%B8%B0%EB%B3%B8%EC%83%9D%EC%84%B1%EC%9E%90
'BackEnd > Spring Boot🍃' 카테고리의 다른 글
[Spring] Identifying(식별관계)와 Non-Identifying(비식별관계) (0) | 2024.07.19 |
---|---|
[Spring] Entity에서 원시타입과 래퍼클래스를 사용하는 기준 (0) | 2024.04.16 |
[Spring] 클래스 선언부에 @Builder와 @NoArgsConstructor, @AllArgsConstructor을 같이 사용해야 하는 이유 (0) | 2024.04.13 |
[Spring] Builder 패턴과 Lombok의 @Builder (0) | 2024.04.13 |
[Spring] @AllArgsConstructor과 @RequiredArgsConstructor 사용을 "지양"하는 이유, 생성자에관한 어노테이션 알아보기 (0) | 2024.04.13 |