📌 개요
혹시 Spring boot Data JPA 를 사용하면서, @Entity 어노테이션을 활용하여 엔티티를 생성할 때,
기본생성자 직접 명시, 혹은 @NoArgsConstructor 를 명시하지 않은 경우,
다음과 같은 에러를 마주하신 적이 있으실 것 입니다.
그렇다면, JPA가 Entity 를 생성할 때 기본생성자를 명시하지 않으면 경고/에러가 발생하는지, 알아보겠습니다!
📌 JPA는 Entity를 생성 할 때 Reflection 기법을 사용한다.
해당 포스팅에선 Reflection에 대해선 자세하게 다루지 않고 간단하게 설명하고 넘어가겠습니다!
즉, 리플렉션은 해당 클래스에 대해서 타입을 알지 못하더라도, 해당 클래스의 메서드, 타입, 생성자, 변수들에 접근할 수 있도록 해주는 기법입니다.
컴파일이 아닌 실행시간(런타임)에 동적으로 해당 클래스의 정보를 추출하고 사용할 수 있는 기법이라고 볼 수 있습니다.
❓ 왜 JPA 는 Entity를 관리할 때 간단한 new 연산자를 사용하지 않고, 굳이 복잡한 Reflection을 사용할까?
만일, JPA 가 new 연산자를 사용하게 될 경우, 다음과 같은 한계가 있습니다.
❌동적 객체 생성이 어려움
JPA는 런타임에 어떤 엔티티 객체가 필요한지 미리 알 수 없습니다. 예를 들어, 쿼리 결과에 따라 여러 엔티티를 다룰 수 있어야 하는데, new 연산자로는 동적으로 어떤 객체를 생성해야 할지 알기 어렵습니다.
예를 들어, Member 엔티티를 데이터베이스에서 조회한다고 가정하겠습니다.
JPA는 SQL 쿼리를 통해 데이터를 받아오고, 이 데이터를 기반으로 Member 객체를 만들어야 합니다. 이때, 프로그램은 이 데이터를 받아올 때 어떤 엔티티 객체를 만들어야 할지 미리 알 수 없습니다. 즉, 조회하는 엔티티 클래스가 여러 개일 수 있고, 그에 맞게 동적으로 엔티티 객체를 생성해야 합니다. 따라서, 동적 객체 생성은 반드시 필요로 됩니다.
❌ 프록시 객체를 생성할 수 없음
JPA는 성능 최적화를 위해 Lazy Loading(지연 로딩) 같은 기법을 사용하는데, 이때 프록시(Proxy) 객체라는 것을 사용합니다.
프록시 객체는 진짜 엔티티 객체처럼 행동하지만, 실제 데이터를 데이터베이스에서 나중에 로드합니다. 이 프록시 객체는 런타임에 동적으로 생성되어야 하기 때문에, new 연산자로는 생성할 수 없습니다.
반대로 말하자면, Reflection 은 위의 단점을 보완할 수 있는 장점을 가질 수 있습니다.
✅동적 객체 생성
리플렉션을 통해 JPA는 런타임에 클래스 정보를 조회하고, 기본 생성자를 사용해 엔티티 객체를 동적으로 생성할 수 있습니다.
즉, 미리 어떤 엔티티를 생성할지 몰라도, 쿼리 결과에 따라 적절한 엔티티를 만들 수 있습니다.
✅프록시 객체 생성
리플렉션을 사용하면 프록시 객체와 같은 런타임에 동적으로 생성된 객체도 관리할 수 있습니다.
만일, 해당 reflection 내용이 이해가 잘 가지 않더라도, 이렇게 받아들이면 편할 것 같습니다.
"아~ 뭔지는 잘 모르겠지만, 객체를 생성할 때 우리는 계속 new 를 사용했었는데,
해당 방법으로는 JPA가 DB와 매핑하고 효율적으로 사용 하는것이 어렵다는거구나!
그래서 JPA 는 "new" 를 사용해서 객체를 생성하지 않고 "Reflection" 이라는 기법을 사용해서
객체를 만든다는 거구나!"
📌 JPA 가 실행중에 Entity를 Reflection을 통해 생성하고 관리한다. ⭐이때 기본생성자로 생성한다.
앞선 설명으로, JPA는 엔티티 객체를 생성할 때 리플렉션(Reflection) 을 사용한다고 말씀드렸습니다.
이때 리플렉션 기법으로 JPA가 리플렉션으로 엔티티 객체를 생성할 때, 매개변수가 있는 생성자는 호출할 수 없고 기본 생성자만 사용할 수 있기 때문에 기본 생성자가 필수입니다.
그렇다면 이제 아래의 에러가 이해가 될 것 같습니다.
따라서 기본생성자를 명시해주지 않으면 다음과 같은 빨간줄을 만나보게 되는 것 입니다!
앞으로 JPA 가 객체를 만들게 하기 위해서 기본생성자는 반드시 명시해줍시다!
✅ 조금 더 나아가서,
❓ 그래서 @AllArgsConstructor, @Builder 를 쓰면 빨간줄이 나오는거구나?
실제로 자바는 아무것도 생성자에 대해 명시하지 않았다면, 기본생성자를 자동으로 생성해줍니다.
그리고 JPA 또한 자동으로 생성된 기본생성자를 활용해서 어쩌면 문제 없이 돌아갈 것 입니다.
하지만 !
롬복의 기능으로 사용하는 @AllArgsConstructor , @Builder 를 사용하게 된다면, 해당 클래스는 매개변수를 받는 생성자를
만들게 되었다고 해석할 수 있습니다.
💥 매개변수가 있는 생성자를 만들었다.
더이상, 기본생성자는 자체적으로 생성되지 않음으로, 직접 명시를 해줘야한다.
따라서 아무런 생각없이, @AllArgsConstructor, @Builder 의 어노테이션을 사용 하였다면,
해당 에러를 마주하게 되는 것 입니다 !
이상, 해당 포스팅을 읽어주셔서 감사합니다 :-) 🙇♀️🙇♂️
'[Spring]' 카테고리의 다른 글
Spring - @Controller 와 @RestController 차이 (0) | 2025.03.01 |
---|---|
다양한 Lock 기법을 활용한 동시성 제어 방법 ( Synchronized , Database , Redis ) (1) | 2024.12.30 |
Bean을 등록하는 2가지 방법 (@Component / @Bean + @Configuration) (0) | 2024.08.04 |
Spring Security로 로그인 구현해보기 (0) | 2024.07.25 |
Lombok 롬복의 @Builder, @NoArgsConstructor 와의 충돌 이유 (0) | 2024.07.08 |