모든 내용은 아직 초보인 제가 스스로 학습하며 알게된 정보와 그에 대한 제 견해가 섞여있기 때문에 공식적이거나 정확한 정보가 아닙니다. 부족한 부분이 있다면 날카로운 지적들 주시면 감사하겠습니다 :)
처음 REST API 만들기 프로젝트를 Spring Boot 를 이용해 시작했을 때 많은 블로그 글들을 참고했다. 거의 다 복붙같은 내용이기에 표준인 줄 알고 그냥 따라쳤던 기억이 있다. (사실 아직도 그 수준에서 벗어나지 못했다 ㅎ)
대표적으로 JPA와 Lombok을 통해 쓸 수 있게 된 많은 어노테이션들이 다다다 붙어있는 것을 쉽게 볼 수 있었다. 이번에 프로젝트를 진행하면서는 질문을 던져 보았다.
이 많은 어노테이션들 꼭 다 붙여야 돼?
그렇다고 구글에다가 'Do I have to use all those annotations really..?' 를 칠 수는 없었기에 일단 하나씩 없애가며 에러가 뜨나 경고문이 뜨나 살펴봤다.
이런 방식으로 발견한 에러들을 읽어보며 손 댄 어노테이션들 자체에 대해 검색해보며 공부해봤다.
@AllArgsConstructor 는 없애도 문제가 없었지만, @NoArgsConstructor 는 없애니까 바로 에러가 떴다.
그리고 @Entity 도 없애보니 에러가 아래 에러가 사라졌다. @Entity 를 사용함으로써 @NoArgsConstructor 가 필요한가보다 라는 생각 정도만 들었다.
https://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/jpa_overview_pc.html#jpa_overview_pc_restrict
을 확인해보면 1.1 항에서 위 에러 발생의 이유를 찾을 수 있다.
The JPA specification requires that all persistent classes have a no-arg constructor. This constructor may be public or protected. Because the compiler automatically creates a default no-arg constructor when no other constructor is defined, only classes that define constructors must also include a no-arg constructor.
영속성을 가져야할 클래스를 컴파일하기 위해 JPA 가 기본 생성자를 반드시 요구하고 있기 때문에 @NoArgsConstructor 를 없애면 계속 달라고 하는 것이다.
블로그 글들에서 많이 본 내용이다. 그런데 아무리 찾아봐도 공식적인 내용은 없어서 '음...' 하고 있던 중에, chatGPT 에게 물어봤다..
The presence of the @Entity annotation does not automatically create a default constructor for the class.
If the class does not have a constructor, the JPA provider will create a default constructor for you.
However, if you have defined any constructors for the class, the JPA provider will not create a default constructor for you.
내 상황에 딱 맞는 답을 얻었다.
그래서 @NoArgsConstructor 를 없앴을 때 Class '~' should have [public, protected] no-arg constructor
메시지를 날린 것이다!
Client 로부터 받는 정보를 Entity 클래스로 변경하기 이전의 DTO 클래스에서 .toEntity()
메소드가 필요했고 여기에서 빌더 패턴을 사용했기 때문에 Entity 클래스에서 @Builder 어노테이션을 써야 했다. 하지만 클래스 레벨에 어노테이션을 붙이면 다음과 같은 에러가 뜬다.
Lombok @Builder needs a proper constructor for this class
@Builder 를 위해 모든 필드를 넘겨주는 생성자가 필요한데 현재 @NoArgsConstructor 만 있기 때문에 발생하고 있다.
이전과 같이 별 생각이 없었다면 바로 @AllArgsConstructor 도 추가해서 에러를 없앴을텐데 그러고 싶지 않았다. 실질적으로 @AllArgsConstructor 는 사용하지 않아도 될 녀석이었기 때문이다.
@Builder 를 통해서 모든 필드를 다 넘겨주려 하는 것이 아니라 Client 로부터 받을 Entity 의 '일부' 필드들만 넘겨주면 됐다. 그래서 클래스 레벨에 @Builder 를 붙이지 않았고 내가 필요한 생성자를 직접 선언해서 그 위에 @Builder 를 붙였다. 아래와 같이 말이다.
@Entity
@Table(name="todos")
@Getter
@NoArgsConstructor
public class TodoEntity extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
private Boolean isDone;
@Builder // 내가 필요한 생성자에 대해서만 어노테이션을 붙여줬다
public TodoEntity(String content, Boolean isDone){
this.content = content;
this.isDone = isDone;
}
...
}
사실 저렇게 작성한 것이 얼마나 큰 의미가 있는지는 잘 모르겠지만... 그래도 이 기회를 통해 아무 생각 없이 다 갖다 붙이던 것들에 대해 처음으로 찾아보고 공부하는 시간이 됐다.
공부하면서 느낀 것은... 내가 가진 궁금증들에 대해 이미 다들 스스로 질문을 던지고 정말 깊이 있게 공부하며 그것을 잘 정리해둔 분들을 인터넷상에서 많이 찾아볼 수 있다는 점이다. 그렇다면 인터넷에 올리지 않은 사람들은 얼마나 과연 더 많을까...
공부하면 할수록 부족함을 많이 느낀다. 그동안 공부한 내용들을 정리해보려고 블로그 글을 작성하다보니 막상 매우 겉핥기 식으로만 정리해두고 넘어간 내용들이 더 많아서 글 작성하다가 계속 공부하게 된다.
남에게 보일 것을 생각할 때에 더 신경 써서 확실하게 짚고 넘어가게 되는 것 같다. 일관성을 갖고 꾸준히 공부하자..