π νλ‘μλ 무μμΈκ°?
νλ‘μ(Proxy)λ ν΄λΌμ΄μΈνΈκ° μλ²μ μ§μ μμ²νμ§ μκ³ , μ€κ°μ λ리 κ°μ²΄λ₯Ό ν΅ν΄ κ°μ μ μΌλ‘ μμ²μ μννλ ꡬ쑰μ΄λ€. μ΄λ μ€κ°μ λ리 κ°μ²΄κ° λ°λ‘ 'νλ‘μ'μ΄λ€. νλ‘μλ ν΄λΌμ΄μΈνΈμ μλ² μ¬μ΄μ μμΉνμ¬ μμ²μ κ°λ‘μ±κ±°λ, λΆκ°μ μΈ μμ μ μννκ±°λ, μμ²μ μ μ΄ν μ μλ€.
μΌλ°μ μΌλ‘ ν΄λΌμ΄μΈνΈλ μμ μ΄ μμ²νλ λμμ΄ μ€μ μλ²μΈμ§, νλ‘μμΈμ§λ₯Ό μμ§ λͺ»νλ€. λ°λΌμ νλ‘μλ μλ²μ λμΌν μΈν°νμ΄μ€λ₯Ό ꡬνν΄μΌ νλ€. μ΄λ‘ μΈν΄ ν΄λΌμ΄μΈνΈλ νλ‘μλ μλ²λ λμΌν λ°©μμΌλ‘ νΈμΆν μ μμΌλ©°, κ΅¬μ‘°κ° λ³κ²½λμ΄λ ν΄λΌμ΄μΈνΈ μ½λλ μ ν μν₯μ λ°μ§ μλλ€.
ποΈ νλ‘μ ν¨ν΄μ ꡬ쑰
νλ‘μ ν¨ν΄μ κΈ°λ³Έ ꡬ쑰λ λ€μκ³Ό κ°λ€.
- Client: μμ²μ 보λ΄λ 주체μ΄λ€.
- Proxy: μλ² λμ μμ²μ λ°μ μ²λ¦¬νκ±°λ, μλ²μκ² μ λ¬νλ μ€κ° κ°μ²΄μ΄λ€.
- RealSubject (Server): μ€μ λΉμ¦λμ€ λ‘μ§μ μ²λ¦¬νλ μλ² κ°μ²΄μ΄λ€.
μ΄ μΈ κ°μ²΄λ λ€μκ³Ό κ°μ μΈν°νμ΄μ€ ꡬ쑰λ₯Ό κ°λλ€.
public interface Subject {
String operation();
}
public class RealSubject implements Subject {
public String operation() {
return "μ€μ κ°μ²΄ νΈμΆ κ²°κ³Ό";
}
}
public class Proxy implements Subject {
private RealSubject realSubject;
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
public String operation() {
// λΆκ° κΈ°λ₯ μν
System.out.println("νλ‘μμμ μΆκ° μμ
μν");
return realSubject.operation();
}
}
μ΄λ κ² κ΅¬μ±λλ©΄ ν΄λΌμ΄μΈνΈλ μλμ²λΌ μ¬μ©ν μ μλ€.
public class Client {
private Subject subject;
public Client(Subject subject) {
this.subject = subject;
}
public void execute() {
String result = subject.operation();
System.out.println("κ²°κ³Ό: " + result);
}
}
μ΄ κ΅¬μ‘°μμ ν΄λΌμ΄μΈνΈλ Proxy
λ₯Ό μ£Όμ
λ°μλ, RealSubject
λ₯Ό μ£Όμ
λ°μλ μ ν μ μ μμΌλ©°, μ½λμ μμ μμ΄ λμΌνκ² λμνλ€.
νλ‘μ ν¨ν΄κ³Ό λ°μ½λ μ΄ν° ν¨ν΄
GOF λμμΈ ν¨ν΄μμλ νλ‘μ ν¨ν΄κ³Ό λ°μ½λ μ΄ν° ν¨ν΄μ ꡬλΆνλ€. λ λ€ κ΅¬μ‘°μ μΌλ‘λ μ μ¬νμ§λ§, μλ(intent)κ° λ€λ₯΄λ€.
1. νλ‘μ ν¨ν΄ - μ κ·Ό μ μ΄ λͺ©μ
νλ‘μ ν¨ν΄μ λ³΄ν΅ μ κ·Όμ μ μ΄νκΈ° μν΄ μ¬μ©λλ€.
- κΆνμ λ°λ₯Έ μ κ·Ό μ°¨λ¨
- κ²°κ³Όλ₯Ό μΊμ±ν΄μ μ±λ₯ ν₯μ
- μ§μ° λ‘λ©
μμ: μΊμ νλ‘μ
public class CacheProxy implements Subject {
private Subject target;
private String cache;
public CacheProxy(Subject target) {
this.target = target;
}
public String operation() {
if (cache == null) {
cache = target.operation();
}
return cache;
}
}
ν λ²λ§ μλ²μ μ κ·Όνκ³ , μ΄νμλ μΊμλ κ²°κ³Όλ₯Ό 리ν΄νλ€.
2. λ°μ½λ μ΄ν° ν¨ν΄ - κΈ°λ₯ νμ₯ λͺ©μ
λ°μ½λ μ΄ν° ν¨ν΄μ κΈ°μ‘΄ κΈ°λ₯μ μλ‘μ΄ κΈ°λ₯μ μΆκ°νλ λ° μ¬μ©λλ€. ꡬ쑰λ νλ‘μμ κ±°μ λμΌνμ§λ§, λͺ©μ μ΄ λ€λ₯΄λ€.
μμ: λ©μμ§ λ³κ²½ λ°μ½λ μ΄ν°
public class MessageDecorator implements Subject {
private Subject target;
public MessageDecorator(Subject target) {
this.target = target;
}
public String operation() {
String result = target.operation();
return "[λ°μ½λ μ΄μ
] " + result;
}
}
νλ‘μλ₯Ό μ°μμ μΌλ‘ μ°κ²°νλ μ²΄μΈ ννλ‘λ κ°λ₯νλ€.
Subject realSubject = new RealSubject();
Subject decorator1 = new MessageDecorator(realSubject);
Subject decorator2 = new TimeDecorator(decorator1);
Client client = new Client(decorator2);
client.execute();
μ΄μ²λΌ λ°μ½λ μ΄ν°λ 체μΈμ ꡬμ±ν΄μ μ μ κΈ°λ₯μ μΆκ°ν΄ λκ° μ μλ€. νλ‘μ μμ λ§μ°¬κ°μ§λ‘ νλ‘μ 체μΈμ ꡬμ±ν μ μλ€.
νλ‘μ ν¨ν΄μ ν΅μ¬ μ 리
- νλ‘μλ μλ²μ λμΌν μΈν°νμ΄μ€λ₯Ό ꡬνν΄μΌ νλ€.
- νλ‘μλ₯Ό λμ ν΄λ ν΄λΌμ΄μΈνΈ μ½λλ λ³κ²½ μμ΄ λμν΄μΌ νλ€.
- νλ‘μλ μ²΄μΈ ννλ‘ μ¬λ¬ κ° μ°κ²°ν μ μλ€.
- μ κ·Ό μ μ΄λ λΆκ° κΈ°λ₯ μΆκ° λ±μ μν μ μνν μ μλ€.
- νλ‘μλ μ€νλ§ AOPλ νλ‘μ κΈ°λ° DI ꡬνμμ λ§€μ° μ€μνλ€.
μ€νλ§μμμ μ€μ νλ‘μ ꡬν μμ
μλλ μ€νλ§μμ μΈν°νμ΄μ€ κΈ°λ°μΌλ‘ νλ‘μλ₯Ό ꡬνν μ½λμ΄λ€.
@RequiredArgsConstructor
public class OrderServiceInterfaceProxy implements OrderServiceV1 {
private final OrderServiceV1 target;
private final LogTrace logTrace;
public void orderItem(String itemId) {
TraceStatus status = null;
try {
status = logTrace.begin("OrderService.orderItem()");
target.orderItem(itemId);
logTrace.end(status);
} catch (Exception e) {
logTrace.exception(status, e);
throw e;
}
}
}
μ΄λ° λ°©μμΌλ‘ OrderController
, OrderRepository
λ νλ‘μ ꡬν체λ₯Ό λ§λ€μ΄, κΈ°μ‘΄ ꡬν체λ₯Ό κ°μΈλ λ°©μμΌλ‘ κΈ°λ₯μ νμ₯ν μ μλ€.
π JDK λμ νλ‘μ κΈ°μ
νλ‘μ κΈ°μ μ νμ©νμ¬ κΈ°μ‘΄ μ½λλ₯Ό μμ νμ§ μκ³ λΆκ° κΈ°λ₯μ μΆκ°ν μ μμμ§λ§, λκ° ν΄κ²°ν΄μΌν λΆλΆμ΄ λ¨μμλ κ² μ²λΌ 보μΈλ€.
κ°μ νΉμ μμ£Ό μ μ¬ν κΈ°λ₯μ μννλ νλ‘μλΌλ νλ‘μλ₯Ό μ μ©νκΈ° μν΄ μ μ© λμμ μ«μ λ§νΌ λ§μ νλ‘μ ν΄λμ€λ₯Ό μμ°ν΄μΌ νλ€λ μ μ΄λ€.
οΉ‘ μ μ© λμμ΄ 1μ΅κ°μ ν΄λμ€λΌλ©΄ νλ‘μ ν΄λμ€λ 1μ΅κ°λ₯Ό μμν΄μΌ νλ€.
λμ νλ‘μλ₯Ό μ΄ν΄νλ €λ©΄ μλ° λ¦¬νλ μ κΈ°μ μ λν μ΄ν΄κ° μ νλμ΄μΌ νλ€.
리νλ μ κΈ°μ μ μ¬μ©νλ©΄ ν΄λμ€λ λ©μλμ λ©νμ 보λ₯Ό λ°νμμ λμ μΌλ‘ νλνκ³ , μ½λλ λμ μΌλ‘ νΈμΆν μ μλ€.
λ°νμμμ ν΄λμ€λ λ©μλμ λ©νμ 보λ₯Ό μ¬μ©ν΄μ λμ μΌλ‘ νΈμΆνλ λ©μλλ₯Ό λ³κ²½ν μ μλ€.
π μλ° λ¦¬νλ μ μμ μ½λ
@Test
void reflection1() throws Exception {
//ν΄λμ€ μ 보
Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");
Hello target = new Hello();
//callA λ©μλ μ 보
Method methodCallA = classHello.getMethod("callA");
Object result1 = methodCallA.invoke(target);
log.info("result1={}", result1);
//callB λ©μλ μ 보
Method methodCallB = classHello.getMethod("callB");
Object result2 = methodCallB.invoke(target);
log.info("result2={}", result2);
}
@Slf4j
static class Hello {
public String callA() {
log.info("callA");
return "A";
}
public String callB() {
log.info("callB");
return "B";
}
}
JDK λμ νλ‘μ κΈ°μ μ μ΄ λ¦¬νλ μ κΈ°μ μ μ΄μ©νμ¬ κ°λ°μκ° μ§μ νλ‘μλ₯Ό λ§λ€μ§ μμλ λλλ‘ ν΄μ£Όλλ°, λ°νμμ νλ‘μ κ°μ²΄λ₯Ό λμ μΌλ‘ λ§λ€μ΄ μ£ΌκΈ° λλ¬Έμ΄λ€.
π μμ λ₯Ό ν΅ν΄ μμ보μ
AInterface
package hello.proxy.jdkdynamic.code;
public interface AInterface {
String call();
}
AInterface ꡬν체
package hello.proxy.jdkdynamic.code;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AImpl implements AInterface {
@Override
public String call() {
log.info("A νΈμΆ");
return "a";
}
}
BInterface
package hello.proxy.jdkdynamic.code;
public interface BInterface {
String call();
}
BInterface ꡬν체
package hello.proxy.jdkdynamic.code;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class BImpl implements BInterface {
@Override
public String call() {
log.info("B νΈμΆ");
return "b";
}
}
JDK λμ νλ‘μκ° μ 곡νλ InvocationHandler (InvocationHandlerμ ꡬνν΄μ Proxyμ λ§€κ°λ³μλ‘ λ£μ΄μ€μΌ νλ€.)
μ΄κ² JDK λμ νλ‘μμ μ μ©ν κ³΅ν΅ λ‘μ§μ΄λ€.
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
package hello.proxy.jdkdynamic.code;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@Slf4j
public class TimeInvocationHandler implements InvocationHandler {
private final Object target;
public TimeInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("TimeProxy μ€ν");
long startTime = System.currentTimeMillis();
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeProxy μ’
λ£ resultTime={}", resultTime);
return result;
}
}
- `TimeInvocationHandler` μ `InvocationHandler` μΈν°νμ΄μ€λ₯Ό ꡬννλ€. μ΄λ κ²ν΄μ JDK λμ νλ‘
μμ μ μ©ν κ³΅ν΅ λ‘μ§μ κ°λ°ν μ μλ€.
- `Object target` : λμ νλ‘μκ° νΈμΆν λμ
- `method.invoke(target, args)` : 리νλ μ
μ μ¬μ©ν΄μ `target` μΈμ€ν΄μ€μ λ©μλλ₯Ό μ€ννλ€. `args`
λ λ©μλ νΈμΆμ λκ²¨μ€ μΈμμ΄λ€.
package hello.proxy.jdkdynamic;
import hello.proxy.jdkdynamic.code.*;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Proxy;
@Slf4j
public class JdkDynamicProxyTest {
@Test
void dynamicA() {
AInterface target = new AImpl();
TimeInvocationHandler handler = new TimeInvocationHandler(target);
AInterface proxy = (AInterface) Proxy.newProxyInstance(AInterface.class.getClassLoader(), new Class[] {AInterface.class}, handler);
proxy.call();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
}
@Test
void dynamicB() {
BInterface target = new BImpl();
TimeInvocationHandler handler = new TimeInvocationHandler(target);
BInterface proxy = (BInterface) Proxy.newProxyInstance(BInterface.class.getClassLoader(), new Class[] {BInterface.class}, handler);
proxy.call();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
}
}
μ§μ νλ‘μμ JDK λμ νλ‘μ λΉκ΅
νΉμ ν¨ν΄μ URlλ§ λμ νλ‘μλ₯Ό μ μ©νκ³ μΆμ κ²½μ°
package hello.proxy.config.v2_dynamicproxy.handler;
import hello.proxy.trace.TraceStatus;
import hello.proxy.trace.logtrace.LogTrace;
import org.springframework.util.PatternMatchUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LogTraceFilterHandler implements InvocationHandler {
private final Object target;
private final LogTrace logTrace;
private final String[] patterns;
public LogTraceFilterHandler(Object target, LogTrace logTrace, String[] patterns) {
this.target = target;
this.logTrace = logTrace;
this.patterns = patterns;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//λ©μλ μ΄λ¦ νν°
String methodName = method.getName();
if (!PatternMatchUtils.simpleMatch(patterns, methodName)) {
return method.invoke(target, args);
}
TraceStatus status = null;
try {
String message = method.getDeclaringClass().getSimpleName() + "."
+ method.getName() + "()";
status = logTrace.begin(message);
//λ‘μ§ νΈμΆ
Object result = method.invoke(target, args);
logTrace.end(status);
return result;
} catch (Exception e) {
logTrace.exception(status, e);
throw e;
}
}
}
package hello.proxy.config.v2_dynamicproxy;
import hello.proxy.app.v1.*;
import hello.proxy.config.v2_dynamicproxy.handler.LogTraceFilterHandler;
import hello.proxy.trace.logtrace.LogTrace;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Proxy;
@Configuration
public class DynamicProxyFilterConfig {
private static final String[] PATTERNS = {"request*", "order*", "save*"};
@Bean
public OrderControllerV1 orderControllerV1(LogTrace logTrace) {
OrderControllerV1 orderController = new OrderControllerV1Impl(orderServiceV1(logTrace));
OrderControllerV1 proxy = (OrderControllerV1)
Proxy.newProxyInstance(OrderControllerV1.class.getClassLoader(),
new Class[]{OrderControllerV1.class},
new LogTraceFilterHandler(orderController, logTrace, PATTERNS)
);
return proxy;
}
@Bean
public OrderServiceV1 orderServiceV1(LogTrace logTrace) {
OrderServiceV1 orderService = new OrderServiceV1Impl(orderRepositoryV1(logTrace));
OrderServiceV1 proxy = (OrderServiceV1)
Proxy.newProxyInstance(OrderServiceV1.class.getClassLoader(),
new Class[]{OrderServiceV1.class},
new LogTraceFilterHandler(orderService, logTrace, PATTERNS)
);
return proxy;
}
@Bean
public OrderRepositoryV1 orderRepositoryV1(LogTrace logTrace) {
OrderRepositoryV1 orderRepository = new OrderRepositoryV1Impl();
OrderRepositoryV1 proxy = (OrderRepositoryV1)
Proxy.newProxyInstance(OrderRepositoryV1.class.getClassLoader(),
new Class[]{OrderRepositoryV1.class},
new LogTraceFilterHandler(orderRepository, logTrace, PATTERNS)
);
return proxy;
}
}
'Design Pattern' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
μ λ΅ ν¨ν΄(Strategy Pattern) μ 리 - ν νλ¦Ώ λ©μλ ν¨ν΄κ³Όμ μ°¨μ΄μ (0) | 2025.03.27 |
---|---|
[λμμΈ ν¨ν΄] ν νλ¦Ώ λ©μλ ν¨ν΄ (0) | 2025.03.24 |
[λμμΈ ν¨ν΄] Builder ν¨ν΄ (0) | 2025.03.23 |
λκΈ