@Entity란?
JPA를 사용해 테이블과 매핑할 클래스에 붙이는 어노테이션이다.
@Entity가 붙은 클래스는 JPA가 관리하는 객체가 된다.
🧐 무분별한 어노테이션 사용
@Setter // 문제 1. 객체가 무분별하게 변경될 가능성 있음
@Getter
@NoArgsConstructor // 문제 2. 기본 생성자의 접근 제어자가 불명확함
@AllArgsConstructor // 문제 3. 객체 내부의 인스턴스 멤버들을 모두 가지고 있는 생성자를 생성
@Builder // 문제 4. 모든 매개변수가 생성자 param으로 들어가 객체 생성 시 받지 않아야 할 매개변수도 빌더에 노출
@Data // 문제 5. 사용하지 않는 어노테이션을 따로 exclude 할 수 없기 때문에 개별 어노테이션 사용
@Entity
public class User
문제 1. @Setter
@Setter를 사용하면 객체를 언제든 변경할 수 있는 상태이기 때문에 객체의 안정성이 보장받기 어렵다.
특히 Entity에서 @Setter 사용 시 변경 사항이 어디서 발생했는지 추적하기 힘들다.
값의 수정이 필요한 경우 명시적인 메서드 생성을 통해 하는 것이 좋다.
@Getter
@Entity
public class User {
private String email;
private String password;
private String nickname;
public void updateNickname(String nickname) {
this.nickname = nickname;
}
}
해결법:❗️@Setter 삭제 후 명시적인 메서드 생성❗️
문제 2. @NoArgsConstructor
기본 생성자(NoArgsConstructor)의 접근 제어를 PROTECTED로 설정해 놓으면 무분별한 객체 생성에 대해 한번 더 체크할 수 있다.
예를 들어 User 클래스에서 email, password, nickname 정보를 모두 갖고 있어야 한다면
기본 생성자 생성을 하지 않아 완전하지 않은 객체 생성을 막는다.
기본 생성자의 권한을 따로 설정하지 않으면 access level이 public이 되는데 그 경우 아래 상황이 발생한다.
// User.java
@Setter
@Getter
@NoArgsConstructor
public class User {
private String email;
private String password;
private String nickname;
}
// Main.java
public static void main(String[] args) {
User user = new User();
user.setEmail("test@test.com");
user.setPassword("testpassword1!!");
// nickname 이 설정되지 않았기 때문에 user는 완전하지 않은 객체
}
User 객체를 만들어 Setter로 값을 설정할 경우 실수로 필수값을 설정하지 않아도 객체가 생성이 된다.
하지만 아래와 같이 변경 시 IDE 단계에서 필수값 누락을 막을 수 있다.
// User.java
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
private String email;
private String password;
private String nickname;
public User(String email, String password) {
this.email = email;
this.password = password;
// 파라미터가 두 개인 경우 default 값 설정
this.nickname = "닉네임을 설정하세요";
}
}
// Main.java
public static void main(String[] args) {
User user = new User(15, "test@a.com");
// 기본 생성자가 없고 객체가 지정한 생성자를 사용해야하기 때문에
// 무조건 완전한 상태의 객체가 생성되게 된다.
}
해결법:❗️@NoArgsConstructor(access = AccessLevel.PROTECTED)로 변경❗️
문제 3. @AllConstructor
@AllConstructor는 클래스에 존재하는 모든 필드에 대한 생성자를 자동으로 생성하는데, 인스턴스 멤버 변수의 순서를 바꾸면 입력 값 순서도 바뀌어 검출되지 않는 치명적인 오류를 발생시킬 수 있기 때문에 쓰지 않는 것이 좋다.
해결법:❗️@AllConstructor 삭제❗️
문제 4. @Builder
클래스 레벨에서 @Builder를 선언할 때 @NoArgsConstructor와 @AllArgsConstructor을 함께 사용하지 않으면 컴파일 에러가 발생한다.
JPA Entity를 사용할 경우 JPA는 엔티티 객체를 인스턴스화할 때 매개변수가 없는 기본 생성자를 요구한다. 만약 클래스에 @Builder만 사용하고 기본 생성자(@NoArgsConstructor)가 없다면, JPA는 객체를 생성할 수 없어 오류가 발생하게 된다.
또, @Builder는 객체를 생성할 때 내부적으로 모든 필드를 초기화할 수 있는 생성자가 필요하다. @AllArgsConstructor는 이러한 생성자를 제공하여 @Builder가 제대로 동작할 수 있도록 지원해 준다.
하지만 위 문제 3에서 언급한 것처럼 AllArgsConstructor는 사용하지 않는 것이 좋다.
따라서 생성자에 @Builder를 선언하면 객체 생성 시 받지 않아야 할 매개변수들도 빌더에 노출이 되는 문제점을 해결할 수 있다.
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
private String email;
private String password;
private String nickname;
@Builder
public User(String email, String password) {
this.email = email;
this.password = password;
this.nickname = "닉네임을 설정하세요";
}
@Builder
public User(String email, String password, String nickname) {
this.email = email;
this.password = password;
this.nickname = nickname;
}
}
해결법:❗️클래스에서 @Builder 삭제 후 생성자에 @Builder 붙이기❗️
문제 5. @Data
@Data는 @Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode을 한꺼번에 설정해 주는 어노테이션이다.
그러나 @Data와 함께 포함되어 있는 lombok의 설정들(ex. callSuper, includeFieldNames, exclude)을 지정할 수 없으며 사용하지 않는 어노테이션까지 한 번에 선언이 된다.
따라서 @Data 어노테이션을 사용하지 않고 필요한 개별 어노테이션을 추가하는 것이 좋다.
해결법:❗️필요한 개별 어노테이션 각각 추가❗️
🤩 올바른 사용
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String password;
private String nickname;
public void updateNickname(String nickname) {
this.nickname = nickname;
}
@Builder
public User(String email, String password) {
this.email = email;
this.password = password;
this.nickname = "닉네임을 설정하세요";
}
@Builder
public User(String email, String password, String nickname) {
this.email = email;
this.password = password;
this.nickname = nickname;
}
}
https://velog.io/@cieroyou/Builder-%EC%82%AC%EC%9A%A9%EB%B2%95
'Back-end' 카테고리의 다른 글
[Spring Boot] @Transactional(rollbackFor=Exception.class) (0) | 2023.03.21 |
---|---|
[git] git stash drop 복구 (0) | 2023.03.13 |
[Gradle] Spring boot 3.0.0 이상 Querydsl build.gradle + 멀티 모듈 프로젝트 (0) | 2023.02.27 |
[Spring Boot] 멀티모듈 java.lang.ClassNotFoundException, Gradle 의존성 옵션 (0) | 2023.02.20 |
[Spring Boot] DB Schema 및 Data 초기화 schema.sql data.sql (0) | 2023.02.19 |