-
Lombok 사용시 주의할 점Spring 2021. 12. 29. 09:36
스프링 프로젝트를 진행하면 lombok 라이브러리의 편리함 때문에 이를 사용하는 경우가 많지만,
롬복을 사용시에는 다음과 같은 주의점이 존재한다.
@AllArgsConstructor, @RequiredArgsConstructor 의 사용 금지
@AllArgsConstructor public static class Card { // private Card(long frontNumber, long backNumber) { // this.frontNumber = frontNumber; // this.backNumber = backNumber; // } private long frontNumber; private long backNumber; } @AllArgsConstructor public static class Card { // private Card(long backNumber, long frontNumber) { // this.backNumber = backNumber; // this.frontNumber = frontNumber; // } private long backNumber; private long frontNumber; }
@AllArgsConstructor의 경우 위와 같이 생성요소의 위치가 변하는 것 만으로 생성자의 파라미터 순서를 바꿔서 필드 선언에 맞춰서 변경시켜 버린다. 이 경우에는 IDE가 제공해주는 리팩토링이 작동하지 않고 동일한 타입의 파라미터로 오류가 발생하지 않아 개발자도 인식하기가 어려운 인자 값이 바뀌어 들어가게 되는 버그가 발생하게 된다. 이는 @RequiredArgsConstructor도 마찬가지로 버그가 발생할 수 있다.
위의 어노테이션은 편리하게 생성자를 생성해주는 어노테이션으로 프로젝트 중에서 많이 사용되는 롬복 어노테이션이지만 이것이 치명적인 버그를 생성할 수 있음에 주의해야 한다.
public static class Card { @Builder private Card(long frontNumber, long backNumber) { this.frontNumber = frontNumber; this.backNumber = backNumber; } private long frontNumber; private long backNumber; }
대신에 @Builder (파라미터를 순서가 아닌 인자 이름으로 인식)를 사용 해서 이를 방지하자.
@EqualsAndHashCode의 무분별한 사용 자제
@EqualsAndHashCode public static class Card { private int cardId; private int number; public Card(int cardId, int number) { this.cardId = cardId; this.number = number; } } Card card = new Card(2, 8); Set<Card> cards = new HashSet<>(); cards.add(card); // Set에 객체 추가 System.out.println("변경전 : " + cards.contains(card)); // true card.setNumber(10); // number 값 변경 System.out.println("변경후 : " + cards.contains(card)); // false
동일한 객체이지만 Set에 저장한 뒤에 필드 값을 변경하면 hashCode가 변경되어 찾을 수 없게 된다.
따라서, 불변 클래스를 제외하고는 아무 파라미터 없는 @EqualsAndHashCode의 사용은 금지하고, 일반적으로 비교에서 사용하지 않는 Data 성 객체는 equals & hashCode를 따로 구현하지 않아야 한다. (항상 @EqualsAndHashCode(of={"필드명시"})의 형태로 동등성 비교에 필요한 필드를 명시하는 형태로 사용해야 한다. )
온전한 Immutable 필드를 대상으로만 equals & hashCode를 만들기는 매우 어려우므로 최소한 꼭 필요하고 일반적으로 변하지 않는 필드에 대해서만 만들도록 해야 한다.
@Data 의 사용 금지
@Data public class Card { ... } @Getter @Setter @ToString public class Card { ... }
@Data는 파라미터 없는 @EqualsAndHashCode와 @RequiredArgsConstructor 등을 포함한 변경가능한 데이터 클래스를 만들어주는 조합형 어노테이션으로 이 때문에 사용을 아예 금지하고 위와 같이 필요한 부분을 명시하여 사용하는 것이 좋다.
또한 무분별한 @Data 어노테이션을 사용할 경우 양방향 연관관계에서의 순환참조 문제가 발생할 수 있다. @ToString 사용시에는 분명하게 @ToString사용임을 명시하고 @ToString(exclude="...")나 @ToString(of="...")처럼 어노테이션을 사용해서 불필요한 부분은 제외하도록 하자.
@Value 의 사용 금지
@Value public class Card { } @Getter @ToString public class Card { // private final 로 여러 필드 생성 // 생성자와 필요한 경우에만 equals, hashCode 직접 작성 }
@Value 어노테이션은 변경 불가능한 클래스를 만들어주는 조합 어노테이션이지만 이 또한 @EqualsAndHashCode ( 불변 클래스라 큰 문제는 되지 않는다), @AllArgsConstructor (문제 발생) 를 포함하여 사용을 금지시키고 필요한 부분을 명시하는 것이 좋다.
@Builder는 생성자나 static 객체 생성 메서드에 직접 바로 적용
@Builder public static class Card { private Card(long frontNumber, long backNumber) { this.frontNumber = frontNumber; this.backNumber = backNumber; } } public static class Card { @Builder private Card(long frontNumber, long backNumber) { this.frontNumber = frontNumber; this.backNumber = backNumber; } }
@Builder를 사용하면 객체 생성이 더 명확하고 쉬워지지만 이는 기본적으로 @AllArgsConstructor를 내포하고 있다.생성자를 package private으로 만들기 때문에 외부에서 생성자를 호출하는 일이 쉽게 발생하지 않아 이 자체로는 평상시에 문제가 되지 않지만 해당 클래스의 다른 메소드에서 이렇게 자동으로 생성된 생성자를 사용하거나 할 때 문제가 발생될 수 있다.
따라서 @Builder 어노테이션은 가급적 클래스 보다는 직접 만든 생성자 혹은 static 객체 생성 메서드에 붙이는 것을 권장한다.
@Log 의 사용은 @Slf4j로 가급적 사용
@Log 어노테이션을 이용해 각종 Logger를 자동생성할 수 있다. (기본적으로 private static final로 생성, static 필드 혹은 Logger 객체 이름을 변경하고자 한다면 lombok.config를 사용)
그런데 @Slf4j는 Logger와 같이 field 변수로 만들 필요가 없어 static final로 지정하여 별다른 처리없이 static method 에서도 호출이 가능하므로 가급적 @Slf4j를 사용하자.
Lombok
따라서 실무 프로젝트에서는 가급적 @Getter, @Setter, @ToString만 사용하도록 하고 그 외의 것들은 사용을 자제하거나 매뉴얼을 보고 보수적으로 사용하도록 하자. (@EqualsAndHashCode는 불변필드만 명시적으로 지정하면서 자제해서 사용하거나 가급적 IDE 코드 제너레이션 등을 통해 코드를 직접 만들어 사용)
(참고한 사이트)
https://kwonnam.pe.kr/wiki/java/lombok/pitfall
https://bkjeon1614.tistory.com/657
https://velog.io/@jsj3282/Lombok
'Spring' 카테고리의 다른 글
JPA getById() 와 findById()의 차이 (0) 2022.01.12 테스트 코드 작성할 때 Junit5 사용시 JUnit Vintage 오류 발생 (0) 2022.01.11 RESTful API (2) 2021.12.19 트랜잭션 (feat. @Transactional) (0) 2021.12.18 연관 관계의 엔티티 기준으로 order 해서 데이터를 가져오고 싶을 때 (0) 2021.11.06