공부해봅시당
[Spring] 왜 Entity에 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용할까? 본문
[Spring] 왜 Entity에 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용할까?
tngus 2024. 2. 21. 07:30왜 Entity에 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용할까?
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
기본 생성자의 접근 제어를 Protected로 설정해놓게 되면 무분별한 객체 생성에 대해 한 번 더 체크할 수 있는 수단이 되기 때문
예를 들어
User라는 Class는 name, age, email 정보를 모두 가지고 있어야만 하는 상황일 경우
기본 생성자를 막는 것은 이를 도와주는 좋은 수단이 됨
if) 기본 생성자의 권한이 Public이라면?
/// User.java
@Getter
@Setter
@NoArgsConstructor
public class User {
private String name;
private Long age;
private String email;
}
/// Main.java
public static void main(String[] args) {
User user = new User();
user.setName("testname");
user.setEmail("test@test.com");
/// age가 설정되지 않았으므로 user는 완전하지 않은 객체
}
User의 멤버변수들을 설정할 방법이 없기 때문에 Setter를 만들어서 값을 설정하지만
실수로 setAge()를 누락할 경우 객체는 불완전한 상태가 됨
if) 기본 생성자의 권한이 Protected라면?
/// User.java
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
private String name;
private Long age;
private String email;
public User(Long age, String email) {
/// 파라미터가 두 개인 경우 name은 default 설정
this.name = "blank name";
this.age = age;
this.email = email;
}
}
/// Main.java
public static void main(String[] args) {
User user = new User(15, "test@a.com");
/// 기본 생성자가 없고 객체가 지정한 생성자를 사용해야하기 때문에
/// 무조건 완전한 상태의 객체가 생성되게 된다.
}
IDE 단계에서 누락을 방지할 수 있게 되어 훨씬 수월하게 작업할 수 있게 됨
@NoargsConstructor(AccessLevel.PROTECTED)와 @Builder를 함께 사용하면 좀 더 간단하지 않을까
보통 @Builder를 사용하게 되면 조금 더 자유롭게 객체를 생성할 수 있음
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
private String name;
private Long age;
private String email;
}
위처럼 @NoArgsConstructor(access = AccessLevel.PROTECTED)와 @Builder를 함께 사용해서 compile하면 바로 에러가 발생함
@Builder의 동작
@Builder는 Clss(Type)가 Target일 경우, 생성자 유무에 따라 아래와 같이 동작함
- 생성자가 없는 경우: 모든 멤버변수를 파라미터로 받는 기본 생성자 생성
- 생성자가 있을 경우: 따로 생성자를 생성하지 않음
위 과정 이후 모든 멤버 변수를 설정할 수 있는 Builder Class를 생성함
if) @NoArgsConstructor(access = AccessLevel.PROTECTED) 생성자와 @Builder를 함께 사용하면?
아래와 같이 컴파일 됨
public class User {
private String name;
private Long age;
private String email;
/// @NoArgsConstructor(access = AccessLevel.PROTECTED)로 생성된 생성자
protected User() {}
public static User.UserBuilder builder() {
return new User.UserBuilder();
}
public static class UserBuilder {
private String name;
private Long age;
private String email;
UserBuilder() {
}
public User.UserBuilder name(String name) {
this.name = name;
return this;
}
public User.UserBuilder age(Long age) {
this.age = age;
return this;
}
public User.UserBuilder email(String email) {
this.email = email;
return this;
}
public User build() {
/// 일치하는 생성자가 없다.
return new User(this.name, this.age, this.email);
}
}
}
User에는 기본 Protected 생성자가 이미 존재해서 따로 생성자를 만들지 않았지만
build()를 보면 모든 파라미터를 생성자로 받고 객체를 build 하려는 과정에서 알맞는 생성자를 찾을 수 없게됨
@NoargsConstructor(AccessLevel.PROTECTED)와 @Builder를 함께 사용할 수 없을까?
함께 사용할 수 있는 해결 방법은 아래와 같음
1. @AllArgsConstructor
@Builder
@AllArgsConstructor
public class User {
private String name;
private Long age;
private String email;
}
모든 멤버변수를 받는 생성자가 없는것이 이유이기 때문에 모든 멤버변수를 받는 생성자를 만들어주면 됨
위 어노테이션을 사용할 경우, 기본으로 Public 접근제한자로 Constuctor가 생성됨
따라서 필요하다면 @NoArgsConstructor와 같이 접근제한자를 설정할 수 있음
@AllArgsConstructor(access = Accesslevel.PROTECTED)
하지만 @AllArgsConstructor의 경우, 모든 필드의 값을 초기화해야 하기 때문에, 일부 필드 항목만 초기화해야 하는 경우에는 바람직하지 않음
2. 생성자에 설정하는 @Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
private String name;
private Long age;
private String email;
@Builder
public User(Long age, String email) {
this.name = "test_name";
this.age = age;
this.email = email;
}
@Builder
public User(String name, Long age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
}
생성자별로 설정되는 멤버변수 내용을 정의하고 생성자에 @Builder를 설정하게 되면
해당 생성자를 사용하는 Builder가 생성되어 의미있는 객체만 생성할 수 있게 됨
위 코드를 Java 코드만으로 변환하여 작성하면 아래와 같음
public class User {
private String name;
private Long age;
private String email;
public User(Long age, String email) {
this.name = "test_name";
this.age = age;
this.email = email;
}
public User(String name, Long age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public static User.UserBuilder builder() {
return new User.UserBuilder();
}
protected User() {
}
public static class UserBuilder {
private String name;
private Long age;
private String email;
UserBuilder() {
}
public User.UserBuilder age(Long age) {
this.age = age;
return this;
}
public User.UserBuilder email(String email) {
this.email = email;
return this;
}
public User.UserBuilder name(String name) {
this.name = name;
return this;
}
public User build() {
if (name == null) {
return new User(age, email); // name이 없는 생성자 호출
} else {
return new User(name, age, email); // name이 있는 생성자 호출
}
}
}
}
참조
https://cobbybb.tistory.com/14
@NoargsConstructor(AccessLevel.PROTECTED) 와 @Builder
@NoargsConstructor(AccessLevel.PROTECTED) 와 @Builder를 함께 사용할때 주의할 점에 대해서 서술합니다. "왜" 안되는지와 "왜" 이렇게 해결 할 수 있는지에 대해 집중하여 서술합니다. 1. 왜 NoargsConstructor(Access
cobbybb.tistory.com
'STUDY > Spring' 카테고리의 다른 글
[쉽게 배우자! Spring Triangle 0] POJO(Plain Old Java Object) (0) | 2024.02.22 |
---|---|
[Spring] @EnableJpaAuditing (0) | 2024.02.22 |
[스프링] 오브젝트와 의존관계 6 (2) | 2022.10.31 |
[스프링] 오브젝트와 의존관계 5 (0) | 2022.10.28 |
[스프링] 오브젝트와 의존관계 4 (0) | 2022.10.14 |