ASP.NET Core MVC 필터를 활용한 요청 처리 확장

필터란 무엇인가?

ASP.NET Core에서 필터(Filter)는 컨트롤러의 액션 메서드 실행 전후에 특정 로직을 삽입할 수 있는 메커니즘입니다. 이를 통해 인증, 예외 처리, 로깅, 캐싱 등과 같은 공통 관심사(cross-cutting concerns)를 중복 코드 없이 일관되게 적용할 수 있습니다.

필터는 MVC 요청 처리 파이프라인 내에서 정해진 순서로 실행됩니다. 이 파이프라인은 라우팅 후 실행할 액션이 결정된 이후 시작되며, 다섯 가지 유형의 필터가 단계별로 작동합니다:

  • Authorization Filter: 요청 초기 단계에서 접근 권한을 검사합니다.
  • Resource Filter: 모델 바인딩 전후로 리소스 수준 제어를 수행합니다.
  • Action Filter: 액션 메서드 호출 전후에 실행됩니다.
  • Exception Filter: 처리되지 않은 예외를 캐치하고 사용자 정의 응답을 제공합니다.
  • Result Filter: 결과(Action Result) 실행 전후에 개입합니다.

모든 필터는 IFilterMetadata 인터페이스를 기반으로 하며, 동기 및 비동기 방식 모두를 지원합니다.

비동기 vs 동기 필터 구현

동기 필터는 각 단계별로 BeforeAfter 두 개의 메서드를 정의합니다. 반면 비동기 필터는 단일 메서드에서 await next() 호출을 기준으로 전후 로직을 분리합니다.

다음은 비동기 액션 필터의 예시입니다:

public class LoggingActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        Console.WriteLine("액션 시작 전: 로그 기록");

        var resultContext = await next();

        if (resultContext.Exception == null)
        {
            Console.WriteLine("액션 성공적으로 완료");
        }
        else
        {
            Console.WriteLine("액션 실행 중 예외 발생");
            resultContext.ExceptionHandled = true;
        }
    }
}

예외 필터로 글로벌 오류 처리

IExceptionFilter 또는 IAsyncExceptionFilter를 구현하여 애플리케이션 전역에서 예외를 중앙 집중식으로 처리할 수 있습니다. 특히 개발 환경에서는 예외 정보를 클라이언트에게 전달하는 것이 디버깅에 유용합니다.

다음은 개발 환경에서만 예외 스택 트레이스를 응답 본문에 포함하는 예외 필터입니다:

public class GlobalExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _env;

    public GlobalExceptionFilter(IHostEnvironment env) => _env = env;

    public void OnException(ExceptionContext context)
    {
        if (!_env.IsDevelopment()) return;

        context.Result = new ContentResult
        {
            StatusCode = 500,
            Content = context.Exception.ToString()
        };
        context.ExceptionHandled = true;
    }
}

이 필터를 전역으로 등록하려면 다음처럼 구성합니다:

builder.Services.Configure<MvcOptions>(options =>
{
    options.Filters.Add<GlobalExceptionFilter>();
});

주의할 점은 예외 필터보다 미들웨어가 더 일반적이고 유연한 오류 처리 수단이라는 것입니다. 예외 필터는 특정 액션에 특화된 에러 응답이 필요할 때 주로 사용됩니다.

액션 필터로 실행 흐름 제어

액션 필터는 액션 메서드 실행 전후에 개입하여 매개변수 조작, 실행 차단, 결과 수정 등을 수행할 수 있습니다. 비동기 방식을 사용하면 보다 효율적인 비동기 작업 통합이 가능합니다.

여러 액션 필터를 동시에 사용할 경우, 등록된 순서대로 "감싸는" 형태로 실행됩니다. 즉, 먼저 등록된 필터가 가장 바깥쪽 래퍼처럼 동작합니다.

다음은 두 개의 사용자 정의 필터를 등록하고 액션에 적용하는 예제입니다:

builder.Services.Configure<MvcOptions>(options =>
{
    options.Filters.Add<OuterActionFilter>();
    options.Filters.Add<InnerActionFilter>();
});

컨트롤러에서는 속성(Attribute) 기반으로도 필터를 적용할 수 있습니다:

[ApiController]
[Route("[controller]")]
[TypeFilter(typeof(OuterActionFilter))]
[TypeFilter(typeof(InnerActionFilter))]
public class TestController : ControllerBase
{
    [HttpGet]
    public IActionResult GetData()
    {
        Console.WriteLine("GetData 메서드 실행");
        return Ok("data");
    }
}

출력 예시는 다음과 같습니다:

OuterActionFilter: 시작
InnerActionFilter: 시작
GetData 메서드 실행
InnerActionFilter: 완료
OuterActionFilter: 완료

이처럼 필터는 실행 시점에 따라 중첩된 구조로 동작하며, next() 호출 여부에 따라 후속 단계를 건너뛸 수도 있습니다.

태그: ASP.NET Core MVC Filters Exception Handling Action Filter middleware

5월 26일 10:04에 게시됨