Lombok의 편리한 기능 세 가지(@UtilityClass, @Builder, @Slf4j, @Data)

Lombok에는 다양하고 편리한 어노테이션들이 있지만 그 중에서도 많이, 알차게 사용되는 기능 네 가지를 정리해보기로 했다.

1. @Data

이미지
@Data를 까보면 위와 같이 설명되어 있다. @Getter, @Setter… 등을 일일이 명시하지 않아도 @Data를 선언하는 것만으로 모두 사용 할 수 있다고 한다. 무척 편리한 기능이지만 주의해서 사용해야 할 때도 있다. 예를 들어 @Data에는 @ToString이 포함되어 있는데, 민감한 개인정보 등이 포함되어 있는 도메인에 자동으로 @ToString이 생성된다면 에러메시지 등에서 예기치 못하게 개인정보를 노출하게 될 수도 있다. 꼭 @Data의 모든 기능이 필요한 게 아니라면 사용 할 어노테이션만 골라서 명시하는 게 나을 수 있다.

1
2
3
4
5
6
7
8
@Data
public class DevDTO {
    @NonNull
    String name;
    Integer age;
    LocalDateTime startAt;
}

2. @Slf4j

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Slf4j //간편하게 logger 사용 가능
public class DevDTO {
    @NonNull
    String name;
    Integer age;
    LocalDateTime startAt;

    public void printLog(){
        log.info(getName());
    }
}

//test클래스  
class DevDTOTest {
    @Test
    void test(){
        devDTO.printLog();
    }

옛날에는 로그를 한 번 찍으려면 클래스마다 Logger를 생성해서 사용해야 했다. 정말 번거로운 일이다. @Slf4j 어노테이션을 명시하면 위의 예시처럼 바로 로그를 찍어 볼 수 있다. 이미지

3. @Builder

객체를 정의하고 생성할 때, 이런 식으로 생성자를 통해 만드는 경우가 많다.

1
2
3
4
5
6
7
        // ex1)
        DevDTO devDTO = new DevDTO();
        devDTO.setName("snow");
        devDTO.setAge(19);

        // ex2)
        DevDTO devDTO2 = new DevDTO("sum",20,LocalDateTime.now());

@Builder를 사용하면 아래와 같이 빌더 패턴을 사용 할 수 있다.

1
DevDTO devDTOBuilder = DevDTO.builder().name("eunju").age(12).build();  

@Builder 빌더 패턴을 사용하면 좋은 점 :

  • 지금 어떤 값을 주입하고 있는지 명확하게 볼 수 있다는 이점이 있다.
  • 생성자는 순서를 지켜서 입력해야 하는 반면, 빌더 패턴은 순서에 신경쓰지 않고 데이터 주입이 가능하다.
  • 그리고 비즈니스 로직 여기저기에서 setter로 데이터를 주입할 경우 응집도가 높아지고 코드 품질이 떨어지는데, 이러한 점도 예방 할 수 있다.

4. @UtilityClass

문자열 처리나, 수학 계산, 날짜 변환처럼 반복적으로 사용하는 유틸리티들을 만들 때 @UtilityClass를 유용하게 사용 할 수 있다. 먼저, @UtilityClass를 사용하지 않았을 때의 일반적인 코드를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MathUtils {

    // Private 생성자를 만들어 인스턴스화를 방지
    private MathUtils() {
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }

    // Static 메서드로 정의
    public static int add(int a, int b) {
        return a + b;
    }

    public static int subtract(int a, int b) {
        return a - b;
    }
}

이번에는 @UtilityClass를 사용했을 때의 코드다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import lombok.experimental.UtilityClass;

@UtilityClass
public class MathUtils {
    public int add(int a, int b) {
        return a + b;
    }

    public int subtract(int a, int b) {
        return a - b;
    }
} 

//사용
int result = MathUtils.add(10, 5);  

코드가 훨씬 간소화 되었다. @UtilityClass가 해주는 주요 역할은 다음과 같다.

  • 클래스가 final로 생성됨 : 다른 곳에서 상속 받을 수 없어짐
  • private 생성자 자동 생성 : 클래스가 인스턴스화 되는 것을 방지
  • 모든 메서드 static으로 설정