@Data 지양이유

배세훈·2022년 1월 27일
0

lombok

목록 보기
2/2

@Data

  • @ToString, @Getter, @Setter, @EqualsAndHashCode, @RequiredArgsConstructor을 한번에 사용하는 강력한 어노테이션

@Data 지양 이유

1. 무분별한 Setter 남용

  • Setter는 그 의도가 분명하지 않고 객체를 언제든지 변경할 수 있는 상태가 되어서 객체의 안전성이 보장받기 힘듭니다. 만약 user_id라는 필드의 변경 기능이 제공 되지 않는다고 가정하면 user_id의 setter도 제공되지 않아야 안전합니다. user_id의 변경 포인트를 제공하지 않음으로써 user_id의 변경 기능이 없다는 것을 표현한다고 봅니다.

2. ToString으로 인한 양방향 연관관계시 순환 참조 문제

ex)

@Entity
@Table(name = "member")
@Data
public class Member {
    ....
    @OneToMany
    @JoinColumn(name = "coupon_id")
    private List<Coupon> coupons = new ArrayList<>();
}

@Entity
@Table(name = "coupon")
@Data
public class Coupon {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @ManyToOne
    private Member member;

    public Coupon(Member member) {
        this.member = member;
    }
}

위 코드처럼 Member 객체와 Coupon 객체가 양방향 연관관계일 경우 ToString을 호출하면 무한 순환 참조가 발생합니다. JPA를 사용하다 보면 객체를 Json으로 직렬화 하는 가정에서 발생하는 문제와 동일한 이유입니다.

쉬운 해결방법으로는

@ToString(exclude = "coupons")
public class Member {...}

해당 어노테이션을 이용해서 ToString 항목에서 제외시키는 것입니다.

바람직한 Lombok 사용법

1. @NoArgsConstructor 접근 권한을 최소화 하자

  • JPA에서는 프록시를 생성을 위해 기본 생성자를 반드시 하나 생성해야합니다. 이때 접근 권한이 protected 이면 됩니다. 굳이 외부에서 생성을 열어둘 필요가 없습니다.

접근권한 public인 경우 예시

@Entity
@Table(name = "product")
@Getter
@NoArgsConstructor(access = AccessLevel.PUBLIC) // 테스트를 위해 임시로 Public, 의도한 코드는 PROTECTED
public class Product {

    @Id
    private String id;

    private String name;

    @Builder
    public Product(String name) {
        this.id = UUID.randomUUID().toString();
        this.name = name;
    }
}

Test

@Test
public void test(){
	Product product = new Product();
    assertThat(product.getId(), is(notNullValue()));
}

Id는 항상 null이 아니어야 하지만 public 생성자를 통해 객체를 생성하면 Id 값은 null이 되어 오류가 발생합니다. 이처럼 기본 생성자를 아무 이유 없이 열어두는 것은 객체 생성 시 안전성을 떨어뜨리게 됩니다.

2. Builder 사용시 매개변수를 최소화 하자

@Builder
public class Member {...}

클래스 위에 @Builder를 사용 시 @AllArgsNConstructor 어노테이션을 붙인 효과를 발생시켜 모든 멤버 필드에 대해서 매개변수를 받는 기본 생성자를 만듭니다.
하지만 다음과 같은 문제가 있습니다.

Member의 Id 생성전략은 DB의 auto_increment를 의존하고 있다고 가정하면 Id를 넘겨받지 않아야 합니다.
또한 createAt, updateAt 같은 경우는 @CreationTimestamp, @UpdateTimestamp 각각의 어노테이션이 해당 일을 담당하고 있습니다. 이처럼 객체 생성시 받지 않아야 할 데이터들이 클래스 상단 @Builder를 사용하게 되면 불필요한 값을 생성할 수 있게 됩니다.

public class Member{
	@Builder
    public Membeer(String email, String name){
    	this.email = email;
        this.name = name;
    }
}

이렇게 받으면 생성자를 필요조건에 따라 지정하고 그 위에 @Builder를 붙이는게 바람직하다고 생각합니다.

profile
성장형 인간

0개의 댓글