ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • lombok을 잘 써보자! (2)
    카테고리 없음 2023. 4. 22. 14:39
    오늘은 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을 잘 사용하는 방법에 대해서 살펴봤다. 각자가 더 궁금한게 있다면 나머지는 해당 문서를 살펴보도록 하자! 하루 쉬었더만 일주일이 금방가구만..

    댓글

Designed by Tistory.