[java] 자바 프록시 패턴을 활용한 로깅 레벨 관리 기능 구현

로그는 소프트웨어 애플리케이션 디버깅과 성능 향상에 중요한 역할을 합니다. 특히, 로깅 레벨을 동적으로 변경할 수 있는 기능은 실시간 모니터링이나 디버깅 시에 매우 유용합니다. 이번 블로그에서는 자바의 프록시 패턴을 활용하여 로깅 레벨 관리 기능을 구현해보겠습니다.

프록시 패턴 소개

프록시 패턴은 실제 객체에 대한 접근을 제어하거나 추가 기능을 제공하기 위해 사용됩니다. 이 패턴을 사용하면 클라이언트가 객체에 직접 접근하지 않고, 대신 프록시 객체를 통해 간접적으로 접근할 수 있습니다. 이를 통해 객체의 동작을 제어하거나 보완할 수 있습니다.

로깅 레벨 관리 프록시 구현

먼저, 로깅 기능을 제공하는 Logger 인터페이스와 이를 구현한 ConsoleLogger 클래스를 작성합니다.

public interface Logger {
    void debug(String message);
    void info(String message);
    void error(String message);
}

public class ConsoleLogger implements Logger {
    @Override
    public void debug(String message) {
        System.out.println("DEBUG: " + message);
    }

    @Override
    public void info(String message) {
        System.out.println("INFO: " + message);
    }

    @Override
    public void error(String message) {
        System.err.println("ERROR: " + message);
    }
}

그 다음으로, 로깅 레벨을 동적으로 변경할 수 있는 프록시 클래스 LoggerProxy를 작성합니다.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class LoggerProxy implements InvocationHandler {
    private Logger target;
    private String logLevel = "info";

    public LoggerProxy(Logger target) {
        this.target = target;
    }

    public Logger getLoggerProxy() {
        return (Logger) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
    }

    public void setLogLevel(String logLevel) {
        this.logLevel = logLevel;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName().toLowerCase();
        if ("debug".equals(methodName) && "debug".equals(logLevel)) {
            return method.invoke(target, args);
        } else if ("info".equals(methodName) && ("info".equals(logLevel) || "debug".equals(logLevel))) {
            return method.invoke(target, args);
        } else if ("error".equals(methodName) && ("error".equals(logLevel) || "info".equals(logLevel) || "debug".equals(logLevel))) {
            return method.invoke(target, args);
        } else {
            return null;
        }
    }
}

이제, LoggerProxy 클래스를 사용하여 로깅 레벨을 동적으로 변경할 수 있는 예제를 살펴보겠습니다.

public class Main {
    public static void main(String[] args) {
        Logger consoleLogger = new ConsoleLogger();
        Logger loggerProxy = new LoggerProxy(consoleLogger).getLoggerProxy();

        loggerProxy.debug("Debug message");  // 출력되지 않음
        loggerProxy.info("Info message");    // 출력됨
        loggerProxy.error("Error message");  // 출력됨

        ((LoggerProxy) loggerProxy).setLogLevel("debug");

        loggerProxy.debug("Debug message");  // 출력됨
    }
}

위 예제에서는 ConsoleLogger를 이용하여 로그를 출력하고, LoggerProxy를 통해 로깅 레벨을 동적으로 변경하였습니다.

프록시 패턴을 활용하면 기존의 객체를 수정하지 않고도 새로운 기능을 추가하거나 제어할 수 있습니다. 로깅 레벨 관리 외에도 캐싱, 트랜잭션 관리 등 다양한 기능을 프록시 패턴을 활용하여 구현할 수 있습니다.

프록시 패턴 및 자바 리플렉션에 대한 더 자세한 내용은 Oracle Java Documentation를 참고하시기 바랍니다.