재우니의 블로그

 

 

아래는 MCP를 활용한 ASP.NET Core 애플리케이션을 설치부터 실행까지 단계별로 진행하는 시나리오와 실행 가능한 샘플 코드를 제공합니다.


1. 개발 환경 준비

  1. .NET 9 설치
  2. 프로젝트 생성
    • 원하는 작업 폴더에서 아래 명령어를 실행하여 새 ASP.NET Core Web API 프로젝트를 생성합니다.
      dotnet new web -n MCPExample
    • 생성된 폴더로 이동합니다.
      cd MCPExample

2. MCP를 활용한 코드 작성

프로젝트의 Program.cs 파일에 아래의 코드를 작성합니다. 이 코드는 MCP를 구현하는 핵심 클래스들과 미들웨어를 포함하며, 간단한 GET 엔드포인트에서 컨텍스트 정보를 출력합니다.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using System;
using System.Linq;
using System.Threading;

// 애플리케이션 시작: minimal hosting model (.NET 6 이상)
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// MCP 미들웨어 등록: 모든 요청에 대해 RequestContext를 자동 설정
app.UseMiddleware<RequestContextMiddleware>();

// 간단한 엔드포인트: 현재 RequestContext 정보를 반환
app.MapGet("/", (HttpContext context) =>
{
    var reqContext = RequestContextProvider.Current;
    return $"Hello, {reqContext.UserId} from tenant {reqContext.TenantId}! (CorrelationId: {reqContext.CorrelationId})";
});

// 애플리케이션 실행
app.Run();


// =============================
// MCP 구현 관련 클래스들
// =============================

/// <summary>
/// 요청과 관련된 정보를 담는 모델 (MCP 컨텍스트)
/// C# 11의 required 키워드를 사용하여 필수 초기화를 강제
/// </summary>
public class RequestContext
{
    public required string CorrelationId { get; set; } = Guid.NewGuid().ToString();
    public required string UserId { get; set; }
    public required string TenantId { get; set; }
}

/// <summary>
/// AsyncLocal을 사용하여 스레드 및 비동기 환경에서도 안전하게 RequestContext를 관리하는 제공자 클래스
/// </summary>
public static class RequestContextProvider
{
    private static readonly AsyncLocal<RequestContext?> _context = new();

    public static RequestContext Current => _context.Value ??= new RequestContext { UserId = "Anonymous", TenantId = "default" };

    public static void Set(RequestContext context) => _context.Value = context;
}

/// <summary>
/// HTTP 요청마다 RequestContext를 생성 및 설정해주는 미들웨어
/// </summary>
public class RequestContextMiddleware
{
    private readonly RequestDelegate _next;

    public RequestContextMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        // HttpContext의 User.Identity 정보나 헤더를 통해 사용자/테넌트 정보를 추출
        var userId = context.User.Identity?.Name ?? "Anonymous";
        var tenantId = context.Request.Headers["X-Tenant-ID"].FirstOrDefault() ?? "default";

        // 새로운 RequestContext 인스턴스 생성
        var requestContext = new RequestContext
        {
            UserId = userId,
            TenantId = tenantId
        };

        // RequestContextProvider에 설정하여 이후 어디서든 접근 가능하도록 함
        RequestContextProvider.Set(requestContext);

        // 다음 미들웨어로 요청 전달
        await _next(context);
    }
}

3. 실행 및 테스트

  1. 프로젝트 빌드 및 실행
    터미널에서 아래 명령어를 실행하여 애플리케이션을 실행합니다.
  2. dotnet run
  3. 웹 브라우저 테스트
    브라우저에서 http://localhost:5000 (또는 출력되는 URL)을 입력하면, 다음과 같은 결과를 확인할 수 있습니다.
  4. Hello, Anonymous from tenant default! (CorrelationId: {랜덤GUID})
  5. 헤더를 통한 정보 전달 테스트
    Postman이나 cURL을 사용하여 X-Tenant-ID 헤더를 포함한 요청을 보내면, 응답 메시지에 반영되는 것을 확인할 수 있습니다.
    예를 들어, cURL 명령어:실행 결과:
  6. Hello, Anonymous from tenant MyTenant! (CorrelationId: {랜덤GUID})
  7. curl -H "X-Tenant-ID: MyTenant" http://localhost:5000

4. 결론

위 예제는 MCP(Model Context Protocol)를 활용하여 비동기 환경에서도 안전하게 요청 컨텍스트를 관리하는 방법을 보여줍니다.

  • HttpContext와 달리, MCP는 AsyncLocal을 기반으로 하여 비동기 작업 및 백그라운드 작업에서도 올바르게 컨텍스트가 전파됩니다.
  • 이 샘플 코드를 통해 .NET 9에서 MCP를 어떻게 적용하고 활용할 수 있는지 이해할 수 있으며, 확장 가능한 마이크로서비스 및 클라우드 기반 애플리케이션 개발에 큰 도움이 될 것입니다.

MCP(Model Context Protocol)는 단순히 HttpContext 대체 용도로만 사용되는 것이 아니라, 다양한 비동기 및 분산 환경에서도 활용할 수 있습니다. 여기서는 다음과 같은 대표적인 활용 사례를 예제 코드와 함께 소개하겠습니다.

 

 


 

 

 

1. 백그라운드 작업에서의 컨텍스트 유지

백그라운드 작업을 수행하는 동안에도 RequestContext를 유지하여, 요청과 관련된 데이터를 안전하게 전달할 수 있습니다.

📌 예제: 백그라운드 작업에서 컨텍스트 유지

using System;
using System.Threading;
using System.Threading.Tasks;

public class BackgroundWorker
{
    public async Task ProcessAsync()
    {
        Console.WriteLine($"[Background Task] User: {RequestContextProvider.Current.UserId}, Tenant: {RequestContextProvider.Current.TenantId}");

        await Task.Delay(1000); // 비동기 작업 시뮬레이션
    }
}

// 실행 코드 예제
public class Program
{
    public static async Task Main()
    {
        // MCP 컨텍스트 설정
        var context = new RequestContext
        {
            UserId = "User123",
            TenantId = "TenantA"
        };
        RequestContextProvider.Set(context);

        Console.WriteLine($"[Main] User: {RequestContextProvider.Current.UserId}, Tenant: {RequestContextProvider.Current.TenantId}");

        // 백그라운드 작업 실행
        var worker = new BackgroundWorker();
        await worker.ProcessAsync();
    }
}

✅ 실행 결과

[Main] User: User123, Tenant: TenantA
[Background Task] User: User123, Tenant: TenantA
  • 일반적으로 ThreadLocal을 사용하면 백그라운드 작업에서 컨텍스트를 유지하기 어려운데, MCP를 사용하면 비동기 작업에서도 컨텍스트가 올바르게 유지됩니다.

2. 메시지 큐(Kafka, RabbitMQ)와 함께 사용

비동기 메시지 큐를 사용할 때, 메시지 컨텍스트(예: CorrelationId)를 유지하면서 안전한 로깅과 트레이싱이 가능합니다.

📌 예제: 메시지 큐에서 컨텍스트 유지

using System;
using System.Threading.Tasks;

public class MessageQueueProcessor
{
    public async Task ProcessMessageAsync(string message)
    {
        // 현재 컨텍스트 정보를 이용하여 메시지 처리
        Console.WriteLine($"[Processing] CorrelationId: {RequestContextProvider.Current.CorrelationId}, Message: {message}");
        await Task.Delay(500);
    }
}

// 실행 코드 예제
public class Program
{
    public static async Task Main()
    {
        var context = new RequestContext
        {
            UserId = "User123",
            TenantId = "TenantB",
            CorrelationId = Guid.NewGuid().ToString()
        };
        RequestContextProvider.Set(context);

        Console.WriteLine($"[Main] CorrelationId: {RequestContextProvider.Current.CorrelationId}");

        // 메시지 큐에서 메시지 수신 후 처리
        var processor = new MessageQueueProcessor();
        await processor.ProcessMessageAsync("Hello from queue");
    }
}

✅ 실행 결과

[Main] CorrelationId: 123e4567-e89b-12d3-a456-426614174000
[Processing] CorrelationId: 123e4567-e89b-12d3-a456-426614174000, Message: Hello from queue
  • 메시지 처리를 할 때, CorrelationId를 자동으로 유지하면서 로깅과 디버깅을 쉽게 할 수 있습니다.

3. 분산 트레이싱(OpenTelemetry)과 연계

MCP를 사용하면 분산 시스템에서 요청을 추적하는 CorrelationId를 자동으로 유지할 수 있습니다.

📌 예제: OpenTelemetry로 요청 추적

using System;
using System.Diagnostics;
using System.Threading.Tasks;

public class TracingExample
{
    private static readonly ActivitySource ActivitySource = new("MCPTracing");

    public async Task ProcessWithTracingAsync()
    {
        using var activity = ActivitySource.StartActivity("ProcessWithTracing");
        activity?.SetTag("UserId", RequestContextProvider.Current.UserId);
        activity?.SetTag("TenantId", RequestContextProvider.Current.TenantId);
        activity?.SetTag("CorrelationId", RequestContextProvider.Current.CorrelationId);

        Console.WriteLine($"[Tracing] CorrelationId: {RequestContextProvider.Current.CorrelationId}");
        await Task.Delay(500);
    }
}

// 실행 코드 예제
public class Program
{
    public static async Task Main()
    {
        var context = new RequestContext
        {
            UserId = "UserXYZ",
            TenantId = "TenantC",
            CorrelationId = Guid.NewGuid().ToString()
        };
        RequestContextProvider.Set(context);

        var tracer = new TracingExample();
        await tracer.ProcessWithTracingAsync();
    }
}

✅ 실행 결과

[Tracing] CorrelationId: 7f3b4a29-cc3d-4b5d-913c-9d531df23e99
  • OpenTelemetry와 함께 사용하면 서비스 간 요청 추적 및 모니터링이 쉬워집니다.

4. 다중 테넌트(Multi-Tenant) SaaS 환경에서 사용

MCP를 활용하면 다중 테넌트 SaaS 애플리케이션에서 각 테넌트의 데이터를 분리하여 관리할 수 있습니다.

📌 예제: 다중 테넌트 환경에서 데이터 분리

using System;

public class MultiTenantService
{
    public void ProcessTenantData()
    {
        Console.WriteLine($"[Multi-Tenant] Processing data for Tenant: {RequestContextProvider.Current.TenantId}");
    }
}

// 실행 코드 예제
public class Program
{
    public static void Main()
    {
        // 테넌트 A의 컨텍스트 설정
        var tenantAContext = new RequestContext { UserId = "UserA", TenantId = "TenantA" };
        RequestContextProvider.Set(tenantAContext);

        var serviceA = new MultiTenantService();
        serviceA.ProcessTenantData();

        // 테넌트 B의 컨텍스트 설정
        var tenantBContext = new RequestContext { UserId = "UserB", TenantId = "TenantB" };
        RequestContextProvider.Set(tenantBContext);

        var serviceB = new MultiTenantService();
        serviceB.ProcessTenantData();
    }
}

✅ 실행 결과

[Multi-Tenant] Processing data for Tenant: TenantA
[Multi-Tenant] Processing data for Tenant: TenantB
  • 각 요청에서 TenantId를 유지하여 데이터가 섞이지 않도록 안전하게 관리할 수 있습니다.

결론

MCP는 HttpContext를 대체하는 것뿐만 아니라, 다양한 비동기 환경에서 안정적인 컨텍스트 전파를 가능하게 합니다.

 

활용 사례 MCP 장점
백그라운드 작업 비동기 작업에서도 컨텍스트 유지
메시지 큐(Kafka, RabbitMQ) 메시지 처리 시 CorrelationId 자동 유지
분산 트레이싱(OpenTelemetry) CorrelationId를 유지하여 서비스 간 요청 추적 가능
다중 테넌트 SaaS TenantId를 유지하여 다중 테넌트 환경에서 데이터 분리 가능

MCP를 활용하면

  • AsyncLocal 기반으로 비동기 요청에서도 컨텍스트를 유지할 수 있으며,
  • 분산 시스템, 메시지 큐, OpenTelemetry, 다중 테넌트 환경에서 활용할 수 있습니다.

 

 

참고 사이트

 

https://medium.com/@jazabnajeeb/model-context-protocol-with-net-9-a-game-changer-for-scalable-applications-839d3c43c28d

 

Model Context Protocol with .NET 9: A Game Changer for Scalable Applications

.NET 9 brings several enhancements to the framework, but one of the most compelling additions for backend developers is the Model Context…

medium.com

 

Buy Me A Coffee