Caused by: org.hibernate.InstantiationException:
No default constructor for entity: : com.cos.security1.model.User
위와 같은 오류가 발생했습니다.
그 이유를 찾아보니 JPA의 엔티티는 기본 생성자가 있어야 한다고 합니다.
왜 있어야할까에 대한 의문이 생겼고
찾아보니 저와 같은 의문을 가지는 사람이 많아 이번에 정리해보려고 합니다.
매개 변수를 하나도 가지지 않고, 아무런 명령어도 포함되어 있지 않습니다.
자바 컴파일러는 컴파일 시 클래스에 생성자가 하나도 정의되어 있지 않으면 자동으로 다음과 같은 생성자를 추가합니다.
class Car{
private String modelName = "소나타";
private int modelYear = 2016;
private String color = "파란색";
public String getModel(){
return this.modelYear + "년식 " + this.color + " " + this.modelName;
}
}
public class Main{
public static void main(String[] args) {
Car myCar = new Car(); // 기본 생성자 호출
System.out.println(myCar.getModel()); // 2016식 파란색 소나타
}
}
출처 : https://miyakita.tistory.com/202
리플렉션에 대한 여러가지 설명
리플렉션이란
객체를 통해 클래스의 정보를 분석해 내는 프로그램 기법입니다. (투영, 반사)
리플렉션은 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API입니다.
컴파일된 바이트 코드를 통해 Runtime에 동적으로 특정 Class의 정보를 추출할 수 있는 프로그램 기법입니다.
Class class = Class.forName("클래스 이름")
스프링을 공부하다가 보면 BeanFactory라는 Spring Container 개념을 학습하게 됩니다.
이 BeanFactory는 어플리케이션이 실행한 후 객체가 호출될 당시 객체의 인스턴스를 생성하게 되는데 그 때 필요한 기술이 Reflection 입니다.
자바는 스크립트 언어가 아닌 컴파일 언어입니다. 원래 자바에선는 동적으로 객체를 생성하는 기술이 없었지만 동적으로 인스턴스를 생성하는 Reflection이 그 역할을 대신합니다.
그렇다면 동적과 정적의 차이는 무엇일까요?
먼저 바인딩의 개념을 알아봅시다.
바인딩이란 프로그램에 사용된 구성 요소의 실제 값 또는 프로퍼티를 결정짓는 행위
= 프로그램에서 사용되는 변수나 메서드 등 모든 것들이 결정되도록 연결해주는 것
이 바인딩을 결정 짓는 시기에 따라 정적 바인딩과 동적 바인딩으로 나뉘게 됩니다.
정적 바인딩 | 동적 바인딩 |
---|---|
- 컴파일 시간에 결정 | - 실행 시간(Runtime)에 결정 |
- 프로그램이 실행돼도 변하지 않는다. | - 늦은 바인딩이라고도 부른다. |
- 오버로딩 | - 오버라이딩 |
- private, final, static이 붙은 메서드 | - Java에서의 다형성, 상송이 가능한 이유 |
앞서 말했지만 Reflection은 동적 바인딩을 가능하게 합니다.
따라서 아래와 같은 경우에 활용될 수 있습니다.
@PostMapping(value = "/kakao")
public ResponseEntity<AuthResponse> kakaoAuthRequest(@RequestBody AuthRequest authRequest) {
return ApiResponse.success(kakaoAuthService.login(authRequest));
}
제가 프로젝트에서 사용하는 코드를 가져와 보았습니다. 앞서 저는 @RequestBody에 대한 이야기를 하였습니다.
@RequestBody로 들어오는 웹 요청을 그대로 객체로 바인딩하기 위해서는
바인딩될 타입에 기본 생성자가 존재해야 합니다.
@NoArgsConstructor
public class AuthRequest {
private String accessToken;
}
@NoArgsConstructor : 기본 생성자를 만들어주는 어노테이션
만약에 기본 생성자가 존재하지 않는다면 예외가 발생합니다. 이는 스프링의 @RequestBody 바인딩 방식이 기본 생성자를 통해 객체를 생성한 후 Java Reflection을 이용해서 필드 값을 집어넣어주는 방식이기 때문입니다.
Reflection은 클래스 이름만 알면 생성자, 필드, 메서드 등 클래스의 모든 정보에 접근이 가능합니다.
JPA 역시 데이터를 DB에서 조회해 온 뒤 객체를 생성할 때 Reflection을 사용합니다. 그래서 기본 생성자로 객체를 생성해야 합니다.
Reflection을 사용하는 이유는 우리가 엔티티로 어떤 타입을 생성할 지 JPA는 알수 없고 Reflection을 사용하지 않고 객체를 생성하려면 미리 객체의 타입을 알고 있어야 합니다.
하지만 프레임워크나 라이브러리는 사용자가 정의한 구체 클래스 정보를 알 수 없습니다. 어떤 타입으로 엔티티를 만들더라도 해당 엔티티를 생성하기 위해서는 Reflection을 사용해서 엔티티 인스턴스를 만들어 주는 것입니다.