Slf4j의 문제점
아래는 로그 레벨을 Debug로 설정해뒀을 때만 출력되는 로그 예제이다.
if (logger.isDebugEnabled()) {
logger.debug("{}", foo.veryExpensiveMethod());
}
이렇게 작성하는 이유는 다음과 같다:
- foo.veryExpensiveMethod()는 실행 비용이 크다.
- isDebugEnabled() 조건문을 사용해서 로그 레벨이 DEBUG가 아니라면 호출을 막아 비용을 절감한다.
하지만 매번 조건문을 작성해야 해서 보일러플레이트 코드가 발생한다.
람다(Supplier)를 이용한 개선 방식
Java 8부터 도입된 람다를 사용하여 개선할 수 있다. 람다는 함수의 마지막 파라미터를 함수 본문으로 옮길 수 있기 때문인데...
🔹 Supplier<Object>는 인터페이스다
@FunctionalInterface
public interface Supplier<T> {
T get(); // T 타입의 값을 반환한다.
}
즉, Supplier<T>는 아무런 입력도 받지 않고, T 타입의 결과만 반환하는 함수다.
즉, Supplier<Object>는 아무런 입력도 받지 않고, Object 타입의 결과만 반환하는 함수다.
Supplier<Object> supplier = () -> "Hello";
여기서 "Hello"는 String이지만, String은 Object의 하위 타입이기 때문에 OK.
supplier.get()을 호출하면 "Hello"라는 Object 타입 결과가 반환된다.
🔹 람다는 Supplier의 get() 메서드를 구현하는 방식
() -> "some string"
이건 자바에서 익명으로 Supplier의 get() 메서드를 구현한 것이다. (익명이므로, get() 메서드의 이름이 없는 것)
위의 람다는 다음과 동일하다:
new Supplier<Object>() {
@Override
public Object get() {
return "some string";
}
}
즉, 이 두 가지는 동일한 의미이다:
logger.debug(() -> "some string");
logger.debug(new Supplier<Object>() {
@Override
public Object get() {
return "some string";
}
});
자바에서는 Supplier<Object>처럼 함수형 인터페이스를 사용할 때,
- new Supplier<>() { ... } 대신,
- 간단하게 () -> ... 식으로 람다를 쓸 수 있다.
logger.debug(() -> foo.veryExpensiveMethod().toString());
logger.debug(() -> "Hello " + person + " " + person);
이 방식이 가능한 이유는 로그 라이브러리(SLF4J, Log4j 등)가 다음과 같은 메서드 오버로드(overload)를 제공하기 때문이다:
public void debug(Supplier<Object> messageSupplier);
🔹 오버로드란?
오버로드란 같은 이름의 메서드를 서로 다른 매개변수 형태로 여러 개 정의하는 것이다.
logger.debug(...)는 문자열, 포맷+파라미터, 그리고 Supplier<Object>를 받는 여러 버전이 존재할 수 있다.
✅ 왜 이 방식이 성능에 좋을까?
람다는 지연 실행(lazy execution)이다.
() -> foo.veryExpensiveMethod().toString()
이 표현식은 Supplier<Object>의 get() 메서드를 구현한 것이다.
이 람다는 단지 함수 자체를 전달하는 것이며, 실제로 실행되지 않는다.
logger.debug(Supplier<Object>)의 내부 구현은 보통 이렇게 되어 있다:
public void debug(Supplier<Object> msgSupplier) {
if (isDebugEnabled()) {
Object msg = msgSupplier.get(); // 이때서야 람다 실행
logToConsoleOrFile(msg.toString());
}
}
즉,
- 로그 레벨이 DEBUG일 때만 get()이 호출되어 람다가 실행된다.
- 로그 레벨이 INFO나 WARN이면 람다는 아예 실행되지 않는다.
- 결과적으로 foo.veryExpensiveMethod()도 호출되지 않으므로 불필요한 비용을 아낄 수 있다.
'【 개발 이야기 】' 카테고리의 다른 글
enum 클래스 (1) | 2025.06.24 |
---|---|
MDCContext (2) | 2025.06.20 |
[Spring Boot] WebFilter (1) | 2025.06.19 |
[coroutine] runBlocking (0) | 2025.06.18 |
e.stackTrace와 e.stackTraceToString (1) | 2025.06.18 |