Lombok은 자바 컴파일 시점에서 특정 어노테이션으로 해당 코드를 추가할 수 있는 라이브러리
@Data 는 @ToString
, @EqualsAndHashCode
, @Getter
, @Setter
, @RequiredArgsConstructor
을 모두 포함하는 강력한 어노테이션이다
발생 가능한 문제
Setter
남용Setter
는 그 의도가 분명하지 않고 객체를 언제든지 변경할 수 있는 상태가 되어서 객체의 안전성이 보장받기 힘들다.
불필요한 변경 포인트를 제공하지 않음으로써 안정성을 취할 수 있다.
@ToString
: 클래스 인스턴스의 데이터를 문자열로 반환하는 메서드
Member 와 Coupon 이 1:N 양방향으로 매핑되어 있는 상황을 가정할 수 있다.
이때, ToString을 호출하면 무한 순환 참조가 발생한다.
이러한 문제를 해결하기 위해서는 @ToString(exclude = "coupons") 처럼 어노테이션을 사용해서 특정 항목을 제외시키는 방법을 사용할 수 있다.
@EqualsAndHashCode
는 상당히 고품질의 euqals()와 hashCode() 메소드를 만들어준다. 따라서 잘 사용하면 좋지만, 남발하면 심각한 문제가 생긴다.
특히 문제가 되는 점은 Mutable
객체에 아무런 파라미터 없이 그냥 사용하는 경우이다.
@EqualsAndHashCode
public static class Order {
private Long orderId;
private long orderPrice;
private long cancelPrice;
public Order(Long orderId, long orderPrice, long cancelPrice) {
this.orderId = orderId;
this.orderPrice = orderPrice;
this.cancelPrice = cancelPrice;
}
}
Order order = new Order(1000L, 19800L, 0L);
Set<Order> orders = new HashSet<>();
orders.add(order); // Set에 객체 추가
System.out.println("변경전 : " + orders.contains(order)); // true
order.setCancelPrice(5000L); // cancelPrice 값 변경
System.out.println("변경후 : " + orders.contains(order)); // false
위와 같이 동일한 객체여도 필드 값을 변경시키면 hashCode가 변경되면서 찾을 수 없는 값이 되버린다.
핵심은, 어노테이션 자체의 문제라기 보다는 변경 가능한 필드에 이를 남발함으로써 생기는 문제이다.
Immutable
클래스를 제외하고는 아무 파라미터 없는 @EqualsAndHashCode
사용은 지양한다.@EqualsAndHashCode(of={“필드명시”})
형태로 동등성 비교에 필요한 필드를 명시하는 형태로 사용한다.JPA에서는 프록시를 생성을 위해서 기본 생성자를 반드시 하나를 생성해야한다. 이때 접근 권한이 protected
이면 된다. 굳이 외부에서 생성을 열어둘 필요가 없다.
public
으로 설정하면 안정성을 심각하게 떨어트린다private
으로 설정하면 JPA가 프록시를 만들 때 접근하지 못해 객체를 생성하지 못하게 된다@NoArgsConstructor(access = AccessLevel.PROTECTED)와 함께
@Builder
를 사용하면 반드시 필요한 값이 있어야 객체가 생성됨을 보장할 수 있어 안정성을 높일 수 있다. (참고 - @Builder란?, @NonNull?)
@AllArgsConstructor는 모든 필드 값을 파라미터로 받아야 하는 생성자를 만드는 어노테이션이다.
만약 User testUser = new User(이기태, 25)
과 같이 모든 필드 값을 쓰지 않고(생일을 쓰지 않는다던가) 생성을 한 경우 자동으로 Null Check를 해서 에러를 발생시킨다
매우 편리하게 생성자를 만들어주지만, 별 생각없이 작성한 코드가 치명적인 버그를 만들어낼 수도 있다.
@AllArgsConstructor
public static class Person {
private String firstName;
private String lastName;
}
// 성은 권, 이름은 현수
Person me = new Person("권", "현수");
만약 누군가가 실수로 아래와 같이 바꾼다면 lombok이 개발자도 인식하지 못하는 사이에 파라미터 순서를 바꿔놓는다.
@AllArgsConstructor
public static class Person {
private String lastName;
private String firstName;
}
@RequiredArgsConstructor는 final
이 붙거나 @NotNull
이 붙은 필드의 생성자를 자동 생성해주는 롬복 어노테이션이다.
@RequiredArgsConstructor
public class User{
@NonNull
private Long id;
private String name;
private String password;
@NonNull
private Integer age;
}
이런 Entity가 있을 경우 @RequiredArgsConstructor는
public User(Long id, Integer age){
this.id = id;
this.age = age;
}
이런 생성자를 만들어낸다.
@Service
public class BannerServiceImpl implements BannerService {
private BannerRepository bannerRepository;
private CommonFileUtils commonFileUtils;
@Autowired
public BannerServiceImpl(BannerRepository bannerRepository,
CommonFileUtils commonFileUtils) {
this.bannerRepository = bannerRepository;
this.commonFileUtils = commonFileUtils;
}
...
}
@Service
@RequiredArgsConstructor
public class BannerServiceImpl implements BannerService {
private final BannerRepository bannerRepository;
private final CommonFileUtils commonFileUtils;
...
}
참고
올바른 Lombok 사용법
AllArgsConstructor, NoArgsConstructor, RequiredArgsConstructor