오늘은 lombok 관련해서 포스팅하는 두 번째 시간이다.

저번시간에는 특히나 자주 사용하는 것에 대해 설명 하였는데 오늘은 자주 사용하지는 않을 거 같지만(필자 생각) 있는 기능이니 필요하다면 사용해도 괜찮을 것 같은 기능들을 살펴도록 할텐데 그 전에 어제 자주 사용하는 것 중에 설명하려고 했던 기능이 있는데 어제 깜빡하고 못해서 오늘은 자주 사용할 것 같은 기능 위주로 처음에 작성해보겠다. 흠..말이 이상한데.. 아무튼 그냥 설명하겠다. 참고로 IDEA 기준으로 설명한다. 다른 툴들은 각자가 인터넷에서 구글링 해보면 잘 나와있다.

그럼 시작해보자!

@Log

이 이노테이션 역시 자주 사용할 것 같은 기능에 속한다. 어노테이션 명 그대로 log를 출력할 수 있도록 도와준다. 이 어노테이션은 log라는 변수를 자동으로 만들어 주는 아주 편리한 기능이다.

@Log
public class LogObject {
  public void print() {
    log.info("hello world");
  }
}

위와 같이 log를 선언하지 않아도 자동으로 log라는 변수를 만들어 주어 개발자가 편리하게 사용할 수 있다.
하지만 로그 라이브러리는 아주 다양하다. 그 중에서 가장 많이 사용되는 jul, log4j, log4j2, logback, JBossLog(?) 이건 잘.. 기타 등등 그리고 로그 라이브러리는 아니지만 흠.. 파사드 라이브러리라 해야되나.. slf4j, jakarta(apache) common logging 등이 존재 한다. 위의 @Log 어노테이션은 jul(java util logger) 을 사용하는 어노테이션이다.
각각에 맞게 어노테이션이 존재하니 사용하고 싶은 라이브러리 추가 후 사용하며 된다.

log4j

@Log4j
public class LogObject {

  public void print() {
    log.info("hello world");
  }
}

위와 같이 @Log4j 어노테이션을 작성해주면 된다. 위의 코드는 아래의 코드와 동일하다.

public class LogObjectNot {
  private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogObjectNot.class);
}

참고로 log4j는 end of life 처리 되었으니 신규 프로젝트에서는 다른 더 좋은 라이브러리를 선택하자!

Log4j2

public class LogObject {

  public void print() {
    log.info("hello world");
  }
}

public class LogObjectNot {
  private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogObjectNot.class);
}

JBossLog

@JBossLog
public class LogObject {

  public void print() {
    log.info("hello world");
  }
}

public class LogObjectNot {
  private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogObjectNot.class);
}

위와 같이 요즘은 로그를 직접적으로 사용하지 않고 파샤드 라이브러리를 이용해서 로그를 출력하는 경우가 많을 것이라고 생각된다.

Slf4j

@Slf4j
public class LogObject {

  public void print() {
    log.info("hello world");
  }
}

public class LogObjectNot {
  private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogObjectNot.class);
}

jakarta(apache) common logging

@CommonsLog
public class LogObject {

  public void print() {
    log.info("hello world");
  }
}

class LogObjectNot {
  private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogObjectNot.class);
}

동일하게 파사드 라이브러리도 해당 어노테이션으로 사용가능하다. 레거시 라이브러리 말고 요즘 나오는 라이브러리들은 jakarta logging 보다는 slf4j를 더 많이 사용하고 있다고 생각하면 된다. 예전에 jakarta logging이 메모리 누수가 발생한다 했었던 기억이 어렴풋이 난다. 하지만 Spring의 메인 로그 파사드 라이브러리는 jakarta common logging 을 사용한다. 아마 후회한다고 했지…

다 좋지만 log라는 변수명이 맘에 들지 않을 수 도 있다. 변수명을 바꿀 수 없을까? lombok 만든 개발자분들이 다 될 수 있게 만들었다. IDEA 기준으로 root 디렉토리에 lombok.config 라는 파일을 만들어서 다음 같이 작성하자.

lombok.log.fieldName=logger

참고로 IDEA lombok plugin 어느 정도 최신 버전을 설치해야 된다. 아니면 컴파일은 되지만 해당 필드에 에러로 표시 되어 있다. 아무튼 위와 같이 설정하면 log 라는 변수명을 logger로 변경 할 수 있다.

@Slf4j
public class LogObject {

  public void print() {
    logger.info("hello world");
  }
}

만약 static도 맘에 들지 않는다면 다음과 같이 설정하면 된다.

lombok.log.fieldIsStatic=false

이 외의 다른 설정들은 문서를 참고하길 바란다. 딱히 없긴 할 듯 하다.

@FieldDefaults, @NonFinal

@FieldDefaults 어노테이션은 클래스 레벨에 사용가능하며 필드를 컨트롤 할 수 있다. 필드의 접근제한자를 설정할 수 있으며 final 설정도 가능하다.

@RequiredArgsConstructor
@FieldDefaults(makeFinal=true, level= AccessLevel.PRIVATE)
public class FieldDefaultsObject {
  @NonFinal Long id;
  String name;
}

속성은 makeFinallevel이 있으며 makeFinal 경우에는 final 여부를 묻는 것이고 level은 접근제한을 할 수 있는 속성이다.
만약 위와 같이 코드를 작성 하였다면 아래와 같은 코드로 변환이 될 것으로 예상된다.

public class FieldDefaultsObjectNot {
  private Long id;
  private final String name;

  public FieldDefaultsObjectNot(String name) {
    this.name = name;
  }
}

Spring 에서 여러 Object를 접근할 때 사용하면 적절할 듯 하다. 대략 이런식?

@RequiredArgsConstructor
@FieldDefaults(makeFinal=true, level= AccessLevel.PRIVATE)
class SomeService {
  SomeRepository someRepository;
  SomeRepository1 someRepository1;
  SomeRepository2 someRepository2;
  SomeRepository3 someRepository3;
  SomeRepository4 someRepository4;

  //something
}

한 두개 쯤이야 그냥 사용하는 것이 낫겠지만 많이 접근해야 한다면 위와 같은 방식도 나쁘지는 않다.

@NonNull

해당 어노테이션은 파라미터에 자주 사용될 듯 싶다. 메서드 레벨에도 가능하긴한데 무슨 의미 일까?
null 아니여야 한다는 의미를 갖고 있다. 파라미터에 해당 어노테이션을 작성하면 파라미터에 null이 들어 왔을 경우 NullPointerException을 던진다. 뭐 어차피 변수를 사용하면 NullPointerException이 떨어 지긴 할텐데 말이다..

public class NonNullObject {

  public void print(@NonNull String name) {
    System.out.println(name);
  }
}

위와 같이 파라미터에 @NonNull을 작성할 경우에는 다음과 같은 코드가 만들어 진다.

public class NonNullObjectNot {
  public void print(String name) {
    if(name == null){
      throw new NullPointerException("name");
    }
    System.out.println(name);
  }
}

하지만 나는 NullPointerException 싫다. IllegalArgumentException으로 던지고 싶다. 라고 한다면 lombok.config에 다음과 같이 에 설정을 해주면 된다.

lombok.nonNull.exceptionType=IllegalArgumentException

그럼 파라미터에 null이 들어 왔을 경우에는 IllegalArgumentException을 던지게 된다.

@SneakyThrows

checked exception 처리를 간단하게 처리 할 수 있다.

public class SneakyThrowsObject {

  @SneakyThrows(ClassNotFoundException.class)
  public void classFind() {
    Class.forName("me.wonwoo.test");
  }
}

@SneakyThrows 어노테이션을 사용해서 해당 exception을 처리 하면 된다. 해당 코드를 다시 작성하면 이런코드가 나온다고 한다.

class SneakyThrowsObjectNot {
  public void classFind() {
    try {
      Class.forName("me.wonwoo.test");
    } catch (ClassNotFoundException e) {
      throw Lombok.sneakyThrow(e);
    }
  }
}

@ExtensionMethod

메서드를 확장할 수 있는 기능으로 보인다. 안타까운건 intellij에서는 동작하지 않는다..(이클립스에선 동작할려나..) 아주 좋은 기능 같은데.. 지금 현재는 테스트를 할 수 없으니 문서에 있는 예제를 가져왔다. 딱 보면 대충 어떤 느낌인지 알 것이다. 코틀린에서도 이런 기능 메서드를 확장할 수 있는 기능이 존재 했던걸로 기억한다.

@ExtensionMethod({java.util.Arrays.class, Extensions.class})
public class ExtensionMethodObject {
  public String test() {
    int[] intArray = {5, 3, 8, 2};
    intArray.sort();

    String iAmNull = null;
    return iAmNull.or("hELlO, WORlD!".toTitleCase());
  }
}

class Extensions {
  public static <T> T or(T obj, T ifNull) {
    return obj != null ? obj : ifNull;
  }

  public static String toTitleCase(String in) {
    if (in.isEmpty()) return in;
    return "" + Character.toTitleCase(in.charAt(0)) +
      in.substring(1).toLowerCase();
  }
}

이 외에도 간단하게 자원을 해제하는 @Cleanup, 빌더(Builder) 패턴을 만들어주는 @Builder, 메서드를 동기화 시켜주는 @Synchronized 등 많은 기능을 지원 해준다. 만약 궁금하다면 한번씩 해보는 것도 나쁘지 않다!

오늘도 이렇게 lombok을 잘 사용하는 방법에 대해서 살펴봤다. 각자가 더 궁금한게 있다면 나머지는 해당 문서를 살펴보도록 하자!

하루 쉬었더만 일주일이 금방가구만..