
김영한 ⇒“기본적으로 JPA를 사용한다면 리플렉션이나 프록시 같은 기술을 사용해야 하는데 기본생성자가 없으면 리플랙션 혹은 프록시같은 기술을 사용할 수 없습니다. 기본생성자를 public 으로 설정하면 실수가 발생할수 있기 때문에 protected까지 허용해 줍니다.“
좋은 질문입니다. 이 부분은 Java의 접근 제어자(access modifier)와 JPA의 리플렉션(reflection)을 이해해야 합니다.
결론부터 말하면:
protected 기본 생성자는 일반 개발 코드에서는 접근을 제한하지만, JPA는 Java Reflection을 사용하기 때문에 객체 생성이 가능하다.
입니다.
1. protected의 기본 의미
Java 접근 제어자는 다음과 같은 순서로 접근 범위를 가집니다.
| public | O | O | O | O |
| protected | O | O | O | △ |
| default | O | O | X | X |
| private | O | X | X | X |
protected는:
- 같은 패키지에서는 접근 가능
- 상속 관계에서는 접근 가능
- 완전히 외부에서는 접근 불가능
입니다.
예를 들어:
package jpabook.jpashop.domain;
public class Address {
protected Address() {
}
}
다른 패키지:
package jpabook.jpashop.controller;
public class Test {
public void test() {
Address address = new Address(); // ❌ 불가능
}
}
컴파일 오류가 발생합니다.
왜냐하면 Test는:
- 같은 패키지도 아니고
- Address를 상속하지도 않았기 때문입니다.
2. 그런데 JPA는 어떻게 생성할까?
여기서 Reflection이 등장합니다.
일반적인 객체 생성:
Address address = new Address();
이 경우 Java 컴파일러가 접근 권한을 검사합니다.
즉:
개발자 코드
|
|
new Address()
|
Java 접근 검사
|
protected라서 안됨
입니다.
하지만 Reflection은 다릅니다.
예:
Constructor constructor =
Address.class.getDeclaredConstructor();
constructor.setAccessible(true);
Address address =
(Address) constructor.newInstance();
여기서:
constructor.setAccessible(true);
가 핵심입니다.
이 코드는:
"접근 제한자를 무시하고 이 생성자를 사용하겠다"
라는 의미입니다.
그래서:
private Address()
도 생성 가능합니다.
3. JPA가 실제로 하는 일
DB에서 데이터를 조회했다고 해봅시다.
테이블:
MEMBER
ID | CITY
---------
1 | Seoul
JPA는 내부적으로:
DB 데이터
|
↓
JPA
|
↓
Reflection으로 객체 생성
|
↓
Address 객체 생성
을 합니다.
즉 개발자가:
new Address()
를 호출하는 것이 아닙니다.
JPA 내부 코드가 Reflection으로 생성합니다.
4. 그렇다면 private 기본 생성자를 쓰면 안 되나?
기술적으로는 일부 Reflection에서는 가능합니다.
예:
private Address() {
}
하지만 JPA 스펙에서는:
엔티티와 임베디드 타입은 public 또는 protected 기본 생성자를 가져야 한다
라고 정의합니다.
그래서 권장 방식은:
@Embeddable
public class Address {
private String city;
protected Address() {
}
}
입니다.
5. 왜 public보다 protected가 좋은가?
비교해 보면:
public
public Address() {
}
누구나 가능:
Address address = new Address();
문제:
Address address = new Address();
System.out.println(address.getCity());
// null
초기화되지 않은 객체가 만들어질 수 있습니다.
protected
protected Address() {
}
개발자:
new Address();
불가능
JPA:
Reflection
|
↓
protected 생성자 호출 가능
가능
따라서:
JPA에게는 객체 생성 권한을 주고, 개발자가 잘못된 객체를 만드는 것은 막는다.
라는 장점이 있습니다.
6. 그래서 JPA 엔티티는 보통 이렇게 작성합니다.
@Entity
@Getter
public class Member {
@Id
private Long id;
private String name;
protected Member() {
}
public Member(String name) {
this.name = name;
}
}
사용자는:
Member member = new Member("Kim");
만 가능하고,
Member member = new Member();
는 막습니다.
하지만 JPA는:
Reflection
↓
protected Member()
↓
객체 생성
가능합니다.
정리하면:
protected 기본 생성자 = "JPA 같은 프레임워크는 접근 가능하지만, 일반 개발자가 실수로 빈 객체를 생성하는 것은 막는 장치"
라고 이해하면 됩니다.
'Spring&IntelliJ' 카테고리의 다른 글
| n+1문제에 대해서 (0) | 2026.06.16 |
|---|---|
| @Embeddable에 대해서 (0) | 2026.06.16 |
| @Inheritance에 대해서 (3가지 전략) (0) | 2026.06.16 |
| cascade = CascadeType.ALL은 어떨때 주로 쓰이고 왜 쓰여? (0) | 2026.06.16 |
| h2 파일 생성 실패시, Database not found 애러 해결방법 (0) | 2026.06.05 |