오늘은 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;
}
속성은
makeFinal
와
level
이 있으며 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을 잘 사용하는 방법에 대해서 살펴봤다. 각자가 더 궁금한게 있다면 나머지는 해당 문서를 살펴보도록 하자!
하루 쉬었더만 일주일이 금방가구만..