공부해봅시당

[Spring] 왜 Entity에 @NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용할까? 본문

STUDY/Spring

[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