JPA 기본 생성자가 필요한 이유
@PersistenceContext
private EntityManager entityManager;
public User getUser(Long userId) {
User user = entityManager.find(User.class, userId);
return user; // JPA가 내부적으로 기본 생성자를 호출하여 객체를 생성하고, 리플렉션을 통해 필드 값을 설정
}
해당 과정을 살펴 보면 entityManager.find 즉, 조회 과정에서 User.class 타입을 인자로 받고 있다.
이 User.class 이 클래스 정보로 내부에 애너테이션이나 필드 생성자 등의 정보를 리플렉션을 통해 얻을 수 있게 된다.
그러면 이 정보를 가지고 DB에 데이터를 조회할 수 있게 된다. 이때, 돌아오는 데이터를 담아주기 위해 기본 생성자를 통해 객체를 생성해 값을 담아주게 된다. 그럼 이 기본 생성자는 어떻게 만들어주는가? 그건 class정보를 넘기면서 내부적으로 해당 클래스의 생성자의 정보 또한 알 수 있게 된다. 그럼 외부에서 해당 클래스의 기본 생성자를 통해 객체를 생성해서 값을 넣어주면 된다.
결론은, 리플렉션을 활용해 기본생성자를 가지고 클래스를 만들어 객체로 담아서 주기 때문에 필요함.
참고로 해당 기본 생성자는 public 또는 protected 접근 제어자를 가져야 한다.
이 또한,
Spring Data JPA 에서 기본 생성자가 필요한 이유
Spring Data JPA에서는 위의 코드와 같이 EntityManager를 직접 만들지 않고 자동으로 만들어줌.
즉, 위의 코드들을 자동으로 만들어서 제공을 해주기 때문에 동일하게 기본생성자가 필요하다.
참고로 JPA나 Spring Data JPA에서는 매 요청마다 영속성 컨텍스트와 EntityManager를 만들어서 제공을 해준다.
내가 왜 이 부분에 대해서 알게 되었나 보면
나는 프로젝트 당시 JPA를 활용하여 Entity클래스를 생성하고, 해당 클래스에 값을 넣어 DB에 insert를 해주는 로직을 짰었다. 이때, 나는 Entity에 필드가 많아 생성자를 통해 값을 넣어주기에는 데이터 하나하나의 대한 명확함이 필요하다고 판단이 되어 Builder 패턴을 활용하여 값을 넣어줘야겠다고 판단을 하게 되었다.
이후, Lombok의 @Builder 애너테이션을 활용하여, Builder 패턴을 쉽게 구현하여 값을 넣어주게 되었는데 이때 나는 의문이 들었다. 내가 아는 Builder패턴은 기본생성자를 기본적으로 private으로 만드는게 일반적인데, JPA에서는 기본적으로 기본생성자가 필요하면서 해당 기본 생성자는 public 접근 제어자 이어야 한다는 부분에서 모순을 느꼈다.
Builder 패턴에서 기본 생성자를 private으로 만들어야 하는 이유
애초에 builder패턴은 객체를 생성하는 과정에서 매개변수가 많은 경우 활용을 하게 되고, 그 과정에서 기본 생성자는 private으로 설정하지 않고 public으로 열어둔다면 객체를 생성하는 방법은 생성자 및 Builder 방식 총 2가지가 되게 된다. 이러면 객체 생성의 일관성과 Builder 패턴의 의도가 상실이 될 문제가 발생한다.
결국 JPA Entity 클래스에서 Builder패턴을 사용하려면 기본생성자를 넣어야한다.
그렇다면, 객체를 생성하는 방법은 Builder 및 생성자 두 가지 방법이 공존하게 되면서 일관성이 떨어지게 된다.
이 부분에서 얻을 수 있는 답은 JPA Entity 클래스에서는 public 기본 생성자를 활용해 객체를 생성하는 것이 좋을 것이다.
결론은 JPA Entity 클래스에서는 Builder 패턴을 사용하는 것이 썩 좋은 방법은 아닌 것 같다. 지향하는게 좋을듯