AOP란?
AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)는 애플리케이션의 핵심 로직과는 별개로 반복적으로 사용되는 횡단 관심사(Cross-cutting concerns)를 효과적으로 분리하는 프로그래밍 패러다임입니다. AOP를 활용하면 로깅, 보안, 트랜잭션 관리, 성능 모니터링과 같은 기능을 핵심 비즈니스 로직에서 분리하여 유지보수성과 재사용성을 높일 수 있습니다.
왜 AOP가 필요할까?
전통적인 OOP(Object-Oriented Programming, 객체 지향 프로그래밍)에서는 비즈니스 로직과 횡단 관심사 같은 클래스 내에서 섞이기 쉽습니다. 이러한 방식은 코드의 중복을 초래하고, 유지보수를 어렵게 만듭니다.
만약, 횡단 관심사에 대해 모르거나 알고 싶다면 아래를 클릭하세요.
2025.03.19 - [지식/IT] - 💢횡단 관심사 (Cross-cutting concerns)
.NET에서 AOP 구현 방법
1. 미들웨어 (Middleware)를 활용한 AOP 구현
ASP.NET Core에서는 미들웨어를 사용하여 HTTP 요청을 가로채고, 공통적인 로직(예: 로깅, 인증, 트랜잭션)을 적용할 수 있습니다.
[전체 요청을 처리하는 방식]
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
Console.WriteLine($"[LOG] Request: {context.Request.Method} {context.Request.Path}");
await _next(context);
Console.WriteLine($"[LOG] Response: {context.Response.StatusCode}");
}
}
Program.cs에서 미들웨어를 등록합니다.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseMiddleware<LoggingMiddleware>();
app.Run();
[특정 컨트롤러나 엔드포인트에만 적용하는 방식]
특정 컨트롤러에서 미들웨어를 적용하려면, UseWhen을 활용할 수 있습니다.
app.UseWhen(context => context.Request.Path.StartsWithSegments("/user"), appBuilder =>
{
appBuilder.UseMiddleware<LoggingMiddleware>();
});
위와 같이 하면 /user 경로에 대한 요청만 로깅이 적용됩니다.
2. 데코레이터 패턴 (Decorator Pattern)을 활용한 AOP 구현
데코레이터 패턴을 사용하면 특정 서비스의 메서드를 감싸서 공통적인 로직을 추가할 수 있습니다.
[전체 서비스에 적용하는 방식]
public class LoggingUserService : IUserService
{
private readonly IUserService _inner;
public LoggingUserService(IUserService inner)
{
_inner = inner;
}
public void RegisterUser(string username)
{
Console.WriteLine($"[LOG] 사용자 등록 시작: {username}");
_inner.RegisterUser(username);
Console.WriteLine($"[LOG] 사용자 등록 완료: {username}");
}
}
[특정 메서드에만 적용하는 방식]
public class ConditionalLoggingUserService : IUserService
{
private readonly IUserService _inner;
public ConditionalLoggingUserService(IUserService inner)
{
_inner = inner;
}
public void RegisterUser(string username)
{
if (username.StartsWith("admin"))
{
Console.WriteLine($"[LOG] 관리자 계정 등록 감지: {username}");
}
_inner.RegisterUser(username);
}
}
3. MediatR Pipeline Behavior를 활용한 AOP 구현
MediatR의 IPipelineBehavior를 활용하면 MediatR을 통해 실행되는 모든 요청(Request)에 대해 로깅, 성능 측정, 트랜잭션 관리 등의 기능을 손쉽게 추가할 수 있습니다.
[전체 요청에 적용하는 방식]
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
Console.WriteLine($"[LOG] {DateTime.Now}: Handling {typeof(TRequest).Name}");
var response = await next();
Console.WriteLine($"[LOG] {DateTime.Now}: Handled {typeof(TRequest).Name}");
return response;
}
}
[특정 핸들러에서만 적용하는 방식]
public class UserLoggingBehavior : IPipelineBehavior<RegisterUserCommand, Unit>
{
public async Task<Unit> Handle(RegisterUserCommand request, RequestHandlerDelegate<Unit> next, CancellationToken cancellationToken)
{
Console.WriteLine($"[LOG] 사용자 등록 시작: {request.Username}");
var response = await next();
Console.WriteLine($"[LOG] 사용자 등록 완료: {request.Username}");
return response;
}
}
4. Dynamic Proxy를 활용한 AOP 구현 (Castle Windsor)
[Dynamic Proxy를 활용한 인터셉터 적용]
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"[LOG] {invocation.Method.Name} 실행 시작");
invocation.Proceed();
Console.WriteLine($"[LOG] {invocation.Method.Name} 실행 완료");
}
}
[서비스 등록 및 프록시 생성]
var proxyGenerator = new ProxyGenerator();
var userService = proxyGenerator.CreateInterfaceProxyWithTarget<IUserService>(new UserService(), new LoggingInterceptor());
userService.RegisterUser("JohnDoe");
각 방식의 장단점 비교
방식 | 장점 | 단점 |
Middleware | 애플리케이션 전역에 적용 가능, 구현이 간단함 | 특정 클래스나 메서드에 개별 적용이 어려움 |
Decorator Pattern | 특정 서비스나 메서드에 유연하게 적용 가능 | 서비스 인터페이스가 필요하며, 모든 서비스마다 감싸야 함 |
MediatR Pipeline Behavior | MediatR을 활용한 CQRS 패턴과 잘 어울리며, 요청 단위로 AOP 적용 가능 | MediatR이 필요한 환경에서만 사용 가능하며, 요청 기반이 아닌 일반 메서드에는 적용 어려움 |
Dynamic Proxy | 런타임에서 동적으로 프록시를 생성하여 다양한 서비스에 적용 가능 | Castle Windsor 등의 라이브러리 의존성이 필요하며, 설정이 복잡할 수 있음 |
결론
AOP를 활용하면 핵심 비즈니스 로직과 횡단 관심사를 분리할 수 있어, 코드의 재사용성을 높이고 유지보수를 쉽게 만들 수 있습니다. .NET 환경에서는 미들웨어, 데코레이터 패턴, MediatR Pipeline Behavior, Dynamic Proxy 등을 활용하여 AOP를 구현할 수 있으며, 각 방법의 장단점을 고려하여 적절한 방식을 선택하는 것이 중요합니다.
'지식 > IT' 카테고리의 다른 글
💢횡단 관심사 (Cross-cutting concerns) (0) | 2025.03.19 |
---|
댓글