본문 바로가기
Spring/Spring Boot

@RequiredArgsConstructor와 @AllArgsConstructor 비교하기

by yoon_seon 2024. 6. 14.

문제 상황

프로젝트 진행 중에 Mapstruct 라이브러리를 사용하여 DTO를 자동 매핑하는 과정에서 문제를 겪었습니다.

기존에는 무조건 DTO 필드를 @AllArgsConstructor로 설정했었는데, DTO의 특성상 불변 객체로 만드는 것이 더 적합하다고 판단해서 @RequiredArgsConstructor로 변경했었습니다.

하지만 @RequiredArgsConstructor로 설정된 클래스의 필드가 누락되어 매핑되지 않는 오류가 발생했고 해결책을 찾기 위해 DTO 클래스를 디컴파일해 본 결과, 문제의 원인을 찾을 수 있었습니다.

 

Lombok 어노테이션 비교

Lombok의 @RequiredArgsConstructor@AllArgsConstructor는 각각 생성자를 자동으로 생성해주는 편리한 기능을 제공합니다. 하지만 두 어노테이션의 동작 방식의 차이가 존재하는데, 아래에서 각각의 차이를 살펴보겠습니다.

Lombok 공식 사이트에 의하면 두 어노테이션의 설명은 아래와 같습니다.

@RequiredArgsConstructor

@RequiredArgsConstructor generates a constructor with 1 parameter for each field that requires special handling. All non-initialized final fields get a parameter, as well as any fields that are marked as @NonNull that aren't initialized where they are declared. For those fields marked with @NonNull, an explicit null check is also generated. The constructor will throw a NullPointerException if any of the parameters intended for the fields marked with @NonNull contain null. The order of the parameters match the order in which the fields appear in your class.

 

@RequiredArgsConstructor는 다음과 같은 방식으로 생성자를 생성합니다.

  • 필드 선택:
    • 초기화되지 않은 final 필드
    • 선언 시 초기화되지 않은 @NonNull로 표시된 필드
  • 생성자 매개변수:
    • 위 조건에 해당하는 필드들만 매개변수로 가지는 생성자를 생성합니다.
  • null 체크:
    • @NonNull 로 표시된 필드에 대해 null 체크를 수행하며, null 값이 전달되면 NullPointerException을 발생시킵니다.
  • 매개변수 순서:
    • 생성자 매개변수의 순서는 클래스 내에서 필드가 선언된 순서를 따릅니다.

 

@AllArgsConstructor

@AllArgsConstructor generates a constructor with 1 parameter for each field in your class. Fields marked with 
@NonNull result in null checks on those parameters.

 

@AllArgsConstructor 는 다음과 같은 방식으로 생성자를 생성합니다.

  • 필드 선택:
    • 클래스의 모든 필드가 매개변수로 포함됩니다.
  • null 체크:
    • @NonNull로 표시된 필드에 대해 null 체크를 수행하며, null 값이 전달되면 NullPointerException을 발생시킵니다.

 

구현 및 디컴파일

구현 java 코드

import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

public class AnnotationTest {

    @AllArgsConstructor
    public static class AllArgsConstructorDto {
        private String field_1;
        @NonNull
        private String field_2;
        private final String field_3;
        private final String field_4;
    }

    @RequiredArgsConstructor
    public static class RequiredArgsConstructorDto {
        private String field_5;
        @NonNull
        private String field_6;
        private final String field_7;
        private final String field_8;
    }
}

 

디컴파일한 class 파일 코드

@AllArgsConstructor 결과

// AllArgsConstructorDto 디컴파일 소스
public static class AllArgsConstructorDto {
    private String field_1;
    private @NonNull String field_2;
    private final String field_3;
    private final String field_4;

    public AllArgsConstructorDto(final String field_1, final @NonNull String field_2,
    	final String field_3, final String field_4)
    {
        if (field_2 == null) {
            throw new NullPointerException("field_2 is marked non-null but is null");
        } else {
            this.field_1 = field_1;
            this.field_2 = field_2;
            this.field_3 = field_3;
            this.field_4 = field_4;
        }
    }
}

 

@AllArgsConstructor가 적용된 AllArgsConstructorDto 클래스는 모든 필드를 매개변수로 가지는 생성자를 생성합니다. final 키워드와 상관없이 모든 필드가 생성자에 포함되며, @NonNull 어노테이션이 붙은 field2에 대해서는 null 체크가 추가되는 것을 확인할 수 있습니다.

@RequiredArgsConstructor 결과

// RequiredArgsConstructorDto 디컴파일 소스
public static class RequiredArgsConstructorDto {
    private String field_5;
    private @NonNull String field_6;
    private final String field_7;
    private final String field_8;

    public RequiredArgsConstructorDto(final @NonNull String field_6, 
    	final String field_7, final String field_8)
    {
        if (field_6 == null) {
            throw new NullPointerException("field_6 is marked non-null but is null");
        } else {
            this.field_6 = field_6;
            this.field_7 = field_7;
            this.field_8 = field_8;
        }
    }
}

 

@RequiredArgsConstructor가 적용된 RequiredArgsConstructorDto 클래스는 final 키워드가 붙은 필드(field7 , field8)와 @NonNull 로 표시된 필드(field6 )만을 매개변수로 가지는 생성자를 생성합니다. final이 아닌 field5 는 생성자 매개변수에 포함되지 않는 것을 확인할 수 있습니다. 이와 같이 final 필드가 아니더라도 @NonNull 어노테이션이 붙어 있는 필드도 생성자의 매개변수로 추가됩니다.

 

결론

Lombok의 @RequiredArgsConstructor@AllArgsConstructor를 사용한다면 자동으로 생성자를 생성해줘서 편리하게 사용할 수 있지만, 각각의 어노테이션이 필드에 접근하는 방식이 다르기 때문에 어떤 어노테이션을 사용할지 선택할 때는 클래스의 요구사항에 맞게 신중하게 결정해야 합니다.

@RequiredArgsConstructor는 필드 중 특정 조건을 충족하는 필드만 생성자에 포함시키는 반면, @AllArgsConstructor는 클래스의 모든 필드를 생성자에 포함시킵니다.

이러한 차이를 이해하고 사용하면, 개발 중에 발생할 수 있는 매핑 오류나 다른 문제를 예방할 수 있습니다.

댓글