롬복 @NoArgsConstructor에 대한 공식 문서 정리 + AccessLevel을 PROTECTED로 설정해야 하는 이유

이동영·2024년 3월 8일

자바 개념정리

목록 보기
7/21

@NoArgsConstructor는 파라미터가 없는 생성자를 제공해준다. 이것이 불가능한경우(예 final fields가 있다면) 컴파일 에러가 발생하게 된다.이를 해결하기 위해 @NoArgsConstructor(force = true)를 사용하면 된다. 이렇게 하면 모든 final필드 포함 모든 필드가 0,false 또는 null로 초기화 된다. 이것은 final필드가 있는 클래스에서도 매개변수가 없는 생성자를 생성하는데 도움이 된다.

정리 : 기본 생성자를 생성해주지면 클래스에 final키워드가 붙은 필드가 있다면 컴파일 오류가 발생하게 된다. 해결하기 위해 @NoArgsConstructor(force = true)선언하면 모든 필드가 0,false null로 초기화 된다.

각 fields의 제약이 @NonNull 이라면 개발자가 null체크를 할 필요가 없다.
**정리 : @NoArgsConstructor(force = true)를 사용할 때 @NonNull을 사용하면 개발자가 Null처리를 해줘야 한다.

@NonNull이 붙은 필드는 이 NonNull제약 조건을 잘 지켜줘야 한다.
즉 @NonNull이 붙은 필드는 객체가 생성되기 전까지 Null이 허용되지만 객체가 생성이 된 이후에는 Null이 허용이 되지 않기 때문에 이 field를 사용할 때는 이 점을 유의해야 한다.
정리 : @NonNull이 붙은 field는 객체 생성 이후 Null허용이 안되니 잘 유의하고 사용해야 한다.

몆몆 java프레임워크나 라이브러리에서 매개변수가 없는 생성자를 필요로 한다.주로 Hibernate나 Service Provider Interface와 같은 라이브러리에서 사용되는데 이러한 프레임워크 혹은 라이브러리를 사용할 때는 클래스에 매개변수가 없는 생성자를 제공해야 한다. 이런 외부 라이브러리 규칙을 따라줘야 라이브러리를 사용하는데 문제가 없다.

정리 : 몆몆 프레임워크 라이브러리에는 매개변수가 없는 생성자가 필요한 규칙이 있는데 이것을 지켜야 사용하는데 문제가 없다.

@NoArgsConstructor는 주로 @Data 또는 다른 어노테이션 과 함께 결합하여 사용할 때 유용하다.

예를들어 @Data는 getter setter equals hashcode toString메소드를 자동으로 생성해주는데 클래스에 매개변수가 없는 생성자가 필요할 수도 있다. 이러한 생성자를 자동으로 생성하기 위해 @NoArgsConstructor을 사용하면 매개변수가 없는 생성자를 생성할 수 있따.

AccessLevel

생성자의 접근 수준을 설정하는 것을 의미한다. 생성자는 이 AccessLevel을 기준으로 접근 제어자를 설정하여 생성자를 생성하는데 기본적으로 PUBLIC 접근제어자로 설정한다.

PUBLIC : public접근 수준을 갖으며 어디서든 접근 할 수 있는 객체를 생성한다.
PROTECTED : 동일한 패키지 혹은 해당 클래스를 상속 받은 클래스에서만 이 클래스에 접근할 수 있다.
PACKAGE : default 접근 수준을 갖는다. 동일한 패키지 내부에서만 접근이 가능한 객체를 생성한다.
PRIVATE : 외부에서는 절대로 접근할 수 없는 객체를 생성한다.

AccessLevel PROTECTED로 설정하는 이유

  • AccessLevel을 PROTECTED로 설정하게 되면 클래스의 접근 제어 수준을 보호하면서 프록시 객체 생성을 허용하는데 사용된다.
  • 지연 로딩시 실제 객체를 상속한 프록시 객체를 생성한다. 프록시 객체는 실제 객체의 참조변수를 갖고 있어야 하기 때문에 super()를 호출하게 된다. 하지만 실제 객체의 기본 생성자가 private라면 super를 호출할 수 없다. 즉 프록시 객체를 생성하기 위해서는 기본생성자의 접근 제한이 최초 최대 protected여야만 한다.

프록시 객체란?

실제 객체를 대신하여 동작하는 객체라고 한다. 실제 객체는 프록시 객체 뒤에 숨겨져 있으며 프록시 객체는 클라이언트에게 실제 객체인척 하며 상호 작용하는 객체이다.

예시 : 웹 페이지에는 수 많은 이미지 파일이 있다. 이 많은 이미지를 한번에 로딩하게 되면 페이지의 로딩 속도가 느려지게 된다. 하지만 프록시 객체를 사용하여 이미지 로딩을 최적화 할 수 있다.
프록시 객체 : 이미지를 로딩하는 객체
실제 객체 : 이미지 파일

작동 방식
1. 사용자가 웹 페이지에 방문한다.
2. 프록시 객체는 화면에 보이는 이미지만 로딩한다.
3. 사용자가 스크롤 하여 다른 이미지가 화면에 보이면, 프록시 객체는 해당 이미지를 로딩한다.

프록시 객체의 역활

  • 접근 제어 : 실제 객체에 대한 접근을 제어하고 특정 조건을 충족하는 경우에만 실제 객체에 접근하도록 한다.
  • 캐싱 : 실제 객체의 값을 캐싱하여 성능을 향상 시킨다.(캐싱 : 데이터를 임시적으로 저장하여 이후에 바르게 접근할 수 있도록 하는 기술)
  • 보안 : 실제 객체의 정보를 숨기고 보안을 강화한다.
  • 지연 로딩 : 사용하지 않는 객체는 로딩하지 않고 사용할 때만 로딩하여 메모리를 아끼는 기술이다. 예를들면 웹 페이지를 스크롤 할 때 보이는 부분만 로딩하고 나머지는 스크롤 하기 전까지는 로딩하지 않는다.
  • 네트워크 투명성 : 웹 서버에 직접 연결하지 않고 프록시 서버를 통해 연결되는데 클라이언트는 이를 프록시 서버가 웹 서버임을 알지 못한다. 즉 클라이언트가 원격 객체에 직접 연결하는것 처럼 보이도록 프록시 객체를 사용하여 원격 객체에 대한 접근을 투며하게 처리하는 기술이다.

프록시 서버

  • 클라이언트와 서버 사이에 위치하여 중개자 역활을 한다. 클라이언트는 서버에 직접 요청하지 않고 프록시 서버에 요청하면 프록시 서버는 클라이언트의 요청을 서버에 전달하며 서버의 응답을 클라이언트에게 전달하는 중개자 역활이다.

다시 본론으로 돌아와서 PROCTECTED사용이유에 대해 정리하겠다.

1. 접근 제어 수준 보호

  • 기본 생성자는 default로 pulic수준의 생성자를 제공하여 외부에서 자유롭게 호출이 될 수 있다.
  • @NoArgsConstructor(access = AccessLevel.PROTECTED)로 설정하여 기본 생성자의 접근 제어 수준을 protected로 변경하여 클래스 내부 혹은 상속 클래스에서만 호출할 수 있게 된다.

2. 프록시 객체 생성 허용

  • JPA와 같은 프레임워크는 엔티티 객체를 프록시 객체로 감싸서 사용한다. 해당 프록시 객체는 앤티티 대신에 연산을 수행한다.
  • 프록시 객체 생성 과정에서는 기본 생성자가 필요한데 protected의 기본 생성자가 필요하기에 @NoArgsConstructor(access = AccessLevel.PROTECTED)로 설정한다.
  • 프록시 객체는 예를들면 비밀 통로에 비유할 수 있다. 누구나 비밀통로를 만들 수 있다면 비밀이 유지될 수 없다. 따라서 비밀 통로는 특정한 사람만 만들어야 한다.
  • 즉 누구나 프록시 객체를 만들 수 있다면 실제 객체가 손상이 될 수 있다.
    정리 : 실제 객체를 보호하기 위해 프록시 객체의 생성자를 protected로 설정한다.

3. 무분별한 객체생성 방지

  • @NoArgsConstructor(access = AccessLevel.PROTECTED)로 설정하지 않으면 생성자는 public으로 생성되어 클래스 외부에서 자유롭게 호출이 되어 의도와 다른 객체가 생성이 될 수 있따.
  • 기본 생성자를 @NoArgsConstructor(access = AccessLevel.PROTECTED)로 설정하여 의도적인 객체만 생성하도록 접근제어자 레벨을 설정할 수 있다.

4. @Builder와 함께 사용

  1. 의도적인 객체 생성 : 기본 생성자를 protected로 만들어 부분별한 객체 생성을 방지할 수 있으며 @Builder는 필수 필드만 설정하여 객체를 생성할 수 있도록 한다.
  2. 불변 객체 생성 : @Builder와 함께 @Setter(AccessLevel.PRIVATE)을 사용하면 객체 생성후 필드값 방지를 위한 불변 객체를 생성할 수 있었다.
  3. @Builder는 생성자 코드를 간단하게 만들 수 있다.

정리 : 지연로딩시 프록시 객체를 사용하는데 실제 객체를 상속받아 사용하게 된다. 하지만 실제 객체의 접근제한자가 private이라면 프록시 객체는 실제 객체에 접근을 할 수 없다. 하지만 AccessLevel PROTECTED로 설정하게 되면 프록시 객체는 실제 객체에 대한 접근이 가능하여 프록시 객체의 역활에 충실할 수 있다. 또한 protected는 같은 패키지 내 접근이 가능하며 상속 받은 객체에 대해서만 접근을 허용하기에 외부에서 무분별한 객체생성을 방지할 수 있기때문에 기본적으로 AccessLevel PROTECTED로 설정하는것이다.

force

true값인 경우 최종 필드를 0/null/false로 초기화한다.

onConstructor

생성된 기본 생성자에 특정 어노테이션을 추가하는 용도로 사용하는 속성이다.
예를들어 이 예제에 AnyAnnotation[]속성을 붙인다면 이와 같은 코드로 구현할 수 있다.

import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.AccessLevel;

@NoArgsConstructor(access = AccessLevel.PROTECTED, onConstructor = @__(@Autowired))
public class Example {
    // 클래스의 내용
}

컴파일시에 생성자에 @Autowired가 추가되어 생성자를 생성해준다

staticName

staticName이 설정된 경우 생성자는 private 생성자가 되며 객체를 직접적으로 생성할 수 없게 된다. 대신에 클래스에 추가된 static메소드를 사용하여 객체를 생성할 수 있다. 이 static메소드는 생성자와 동일한 매개변수를 갖고 있으며 내부적으로 실제 생성자를 호출하여 객체를 생성한다. 이 static메소드를 사용하면 편리하게 객체를 사용할 수 있다.

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;

@Data
@NoArgsConstructor(staticName = "create")
public class Person {
    @NonNull private String name;
    private int age;
}

예를들어 @NoArgsConstructor(staticName = "create")와 같은 방식으로 선언하면 객체를 생성할 때 다음 예제와 같이 static메소드를 활용하여 객체를 생성해야 한다.

Person person = Person.create("John", 30);
profile
가치를 제공하는 개발자

0개의 댓글