@NoArgsConstructor, @Builder, @AllArgsConstructor 사용하는 이유

임동혁 Ldhbenecia·2024년 4월 3일

SpringBoot

목록 보기
1/28
post-thumbnail

개요

스프링부트로 개발을 시작하면서 자주 보이는 어노테이션들이 있다.
그것은 바로 @NoArgsConstructor, @Builder, @AllArgsConstructor 이 3 가지이다.

김영한 강사님의 강의를 들으면서 @Builder에 대해서는 아직 학습하지 못했으나, 깃허브에서 다른 프로젝트 코드들을 본 결과 많이 사용되는 어노테이션이라는 것을 알 수 있었다.

각각 무엇을 의미하고 왜 사용하는 지 알아보자.

@NoArgsConstructor

해석을 해보자면 아무 argument가 없는 생성자이다.
이 어노테이션의 경우 @NoArgsConstructor(access = AccessLevel.PROTECTED)와 같이 주로 사용한다.

언제 주로 사용하는 지 차근차근 알아보자.

@NoArgsConstructor(access = AccessLevel.PROTECTED)을 왜 사용하는 걸까?

@Entity
@Getter
@Setter
public class OrderItem {

    @Id @GeneratedValue
    @Column(name = "order_item_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "item_id")
    private Item item;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;

    private int orderPrice;
    private int count;

다음과 같은 코드가 존재한다.

// 주문상품 생성 방식 2가지
OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);

-------------------------------------------------------------------------------
OrderItem orderItem1 = new OrderItem();
orderItem1.setCount();

누군가는 위의 코드가 아닌 아래처럼 값을 Setter를 사용해서 전부 채우는 방식으로 개발할 수도 있다.

문제점은 유지보수 하기가 굉장히 어려워질 수가 있다는 점이다.
더군다나 Setter를 통해서 값을 채웠지만 몇 가지를 제외했다면 그 자리는 null을 반환할테니 해당 값을 가지지 않는 불완전한 객체가 될 것이다.

위의 처음의 방식 외의 다른 생성 방식은 전부 막아야 유지보수가 좋고 불완전한 객체 생성을 줄이는 코드가 될 것이다.

@Entity
@Getter
@Setter
public class OrderItem {

		...
		
		protected OrderItem() {
		}

다음과 같이 코드를 작성해주면 위의 방식이 막히게 된다.
이렇게 코드를 작성하면 위의 방식을 해결할 수 있다.

하지만 어노테이션이 왜 나왔을까?
이러한 귀찮은 반복적인 작업을 한 문장으로 줄이기 위해 나왔다.

@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {
}

Lombok을 사용하여 위와 같이 작성하면 한 줄로 해결할 수 있다.

이 어노테이션을 사용하면 접근 제한을 하여 기본 생성자의 Setter로 인한 무분별한 생성을 막아 의도하지 않은 엔티티를 만드는 것을 막을 수 있다.

@AllArgsConstructor

모든 Argument를 받는 생성자를 만든다.

@Getter
@AllArgsConstructor
public class UserSignUpDto {

    private String email;
    private String nickname;
    private String imageUrl;
}
@Getter
public class UserSignUpDto {

    private String email;
    private String nickname;
    private String imageUrl;
    
    public userSignUpDto(String email, String nickname, String imageUrl) {
	    this.email = email;
	    this.nickname = nickname;
	    this.imageurl = imageurl;
    }
}

위의 두 코드는 같은 것을 의미한다.
모든 필드에 대한 생성자를 만들어주는 어노테이션이다.

@Builder

빌더 어노테이션은 오늘 처음 사용해보았는데 값을 주입하는 과정에서 가독성을 향상시키기 위해 사용하였다.

왜 사용하는 지에 대해 초점을 두고 자세하게 설명은 하지 않겠다.
추후 학습을 원활히 마치면 정리를 해보려한다.

@Entity
@Builder
public class User {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long id;

    private String email;
    private String nickname;
    private String imageUrl;
    private int score;
}
User user = User.builder()
                .email(userSignUpDto.getEmail())
                .nickname(userSignUpDto.getNickname())
                .imageUrl(userSignUpDto.getImageUrl())
                .build();

@NoArgsConstructor(access = AccessLevel.PROTECTED)@Builder를 함께 사용하면 컴파일 에러가 발생한다.

해결 방법은 @AllArgsConstructor을 함께 사용하는 것이다.

어째서 3가지를 동시에 사용하는걸까?

💡 일반적으로 기본 생성자에 대한 접근 제어를 위해 @NoArgsConstructor(access=AccessLevel.PROTECTED)와 같이 사용한다.
무분별하게 생성되는 객체들을 한 번 더 체크해 줌으로써 의도하지 않은 엔티티 객체를 만드는 행위를 방지할 수 있기 때문이다.

하지만 @NoArgsConstructor(access = AccessLevel.PROTECTED)@Builder를 함께 사용하면 컴파일 에러가 발생한다.

  • 에러가 발생하는 이유?
    • 전체 생성자가 없기 때문이다. @Builder를 이용하면 원하는 파라미터만 가지고 객체를 생성할 수 있고, 전부 다 기입해서 객체를 생성할 수도 있다. 그러니 전체 생성자가 필요하다.
      @NoArgsConstructor는 전체 생성자를 만들지 않는다.
  • 그러면 @Builder만 사용해도 전체 생성자가 없는데 에러가 발생해야하지 않을까?
    • @Builder의 Description을 정리한 글을 찾아본 결과 @NoArgsConstructor이나 다른 생성자들이 존재하지 않는 경우 @Builder에서 자동으로 전체 생성자를 생성해준다고 한다.
    • 다른 생성자들이 존재할 경우 @AllArgsConstructor을 사용해서 직접 전체 생성자를 만들어줘야한다.

마치며

이 글을 정리하면서 또다른 궁금증이 파생되었다.
Entity에서 위와 같은 어노테이션을 사용하는데 Dto에서 사용되는 어노테이션은 또 왜 사용되는걸까?

참고 블로그

@NoArgsConstructor(access = AccessLevel.PROTECTED)를 이용하여 의미있는 생성자를 만들어 보자( + @Bulider)

[Spring/Java] @Builder 패턴 사용시 @AllArgsConstructor를 사용하는 이유

0개의 댓글