.NET에서 gRPC 문제 해결

작성자: James Newton-King

참고 항목

이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

Important

이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.

현재 릴리스는 이 문서의 .NET 8 버전을 참조 하세요.

이 문서에서는 .NET에서 gRPC 앱을 개발할 때 발생하는 일반적인 문제를 설명합니다.

클라이언트와 서비스의 SSL/TLS 구성이 일치하지 않음

gRPC 템플릿과 샘플은 기본적으로 TLS(전송 계층 보안)를 사용하여 gRPC 서비스를 보호합니다. gRPC 클라이언트는 보안 연결을 사용하여 안전한 gRPC 서비스를 호출해야 합니다.

앱 시작 시 작성된 로그에서 ASP.NET Core gRPC 서비스가 TLS를 사용하고 있는지 확인할 수 있습니다. 서비스는 HTTPS 엔드포인트에서 수신 대기합니다.

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

.NET Core 클라이언트는 서버 주소에 https를 사용하여 보안 연결을 통해 호출해야 합니다.

var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);

모든 gRPC 클라이언트 구현에서 TLS를 지원합니다. 다른 언어의 gRPC 클라이언트에는 일반적으로 SslCredentials로 구성된 채널이 필요합니다. SslCredentials는 클라이언트에서 사용할 인증서를 지정하며, 안전하지 않은 자격 증명 대신 이 인증서를 사용해야 합니다. TLS를 사용하도록 여러 gRPC 클라이언트 구현을 구성하는 예제는 gRPC 인증을 참조하세요.

신뢰할 수 없는/잘못된 인증서를 사용하여 gRPC 서비스 호출

.NET gRPC 클라이언트를 사용하려면 서비스에 신뢰할 수 있는 인증서가 있어야 합니다. 신뢰할 수 있는 인증서 없이 gRPC 서비스를 호출할 경우 다음 오류 메시지가 반환됩니다.

처리되지 않은 예외가 발생했습니다. System.Net.Http.HttpRequestException: SSL 연결을 설정할 수 없습니다. 내부 예외를 참조하세요. ---> System.Security.Authentication.AuthenticationException: 유효성 검사 절차에 따르면 원격 인증서가 잘못되었습니다.

로컬에서 앱을 테스트 중이며 ASP.NET Core HTTPS 개발 인증서를 신뢰할 수 없는 경우 이 오류가 표시될 수 있습니다. 이 문제의 해결 지침은 Windows 및 macOS에서 ASP.NET Core HTTPS 개발 인증서 신뢰를 참조하세요.

다른 머신에서 gRPC 서비스를 호출 중이며 인증서를 신뢰할 수 없는 경우 잘못된 인증서를 무시하도록 gRPC 클라이언트를 구성할 수 있습니다. 다음 코드는 신뢰할 수 있는 인증서 없이 호출을 허용하는 데 사용합니다 HttpClientHandler.ServerCertificateCustomValidationCallback .

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

gRPC 클라이언트 팩터리는 신뢰할 수 있는 인증서가 없는 호출을 허용합니다. 확장 메서드를 ConfigurePrimaryHttpMessageHandler 사용하여 클라이언트에서 처리기를 구성합니다.


var services = new ServiceCollection();

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ServerCertificateCustomValidationCallback =
            HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

        return handler;
    });

Warning

신뢰할 수 없는 인증서는 앱 개발 중에만 사용해야 합니다. 프로덕션 앱은 항상 유효한 인증서를 사용해야 합니다.

.NET Core 클라이언트를 사용하여 안전하지 않은 gRPC 서비스 호출

.NET gRPC 클라이언트는 서버 주소에서 http를 지정하여 안전하지 않은 gRPC 서비스를 호출할 수 있습니다. 예들 들어 GrpcChannel.ForAddress("http://localhost:5000")입니다.

앱이 사용하는 .NET 버전에 따라 안전하지 않은 gRPC 서비스를 호출하기 위한 몇 가지 추가 요구 사항이 있습니다.

  • .NET 5 이상에는 Grpc.Net.Client 버전 2.32.0 이상이 필요합니다.
  • .NET Core 3.x에는 추가 구성이 필요합니다. 앱은 스위치를 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport .로 true설정해야 합니다. 자세한 내용은 Asp.Net Core 3.x: .NET 클라이언트를 사용하여 안전하지 않은 gRPC 서비스를 호출합니다.

Important

안전하지 않은 gRPC 서비스는 HTTP/2 전용 포트에서 호스트되어야 합니다. 자세한 내용은 ASP.NET Core 프로토콜 협상을 참조하세요.

macOS에서 ASP.NET Core gRPC 앱을 시작할 수 없음

Kestrel 는 .NET 8 이전의 macOS에서 TLS를 사용하여 HTTP/2를 지원하지 않습니다. ASP.NET Core gRPC 템플릿과 샘플은 기본적으로 TLS를 사용합니다. gRPC 서버를 시작하려고 하면 다음과 같은 오류 메시지가 표시됩니다.

IPv4 루프백 인터페이스에 바인딩할 https://localhost:5001 수 없습니다. 'TLS를 통한 HTTP/2는 ALPN 지원이 누락되어 macOS에서 지원되지 않습니다.'.

.NET 7 이하에서 이 문제를 해결하려면 TLS 없이 HTTP/2를 사용하도록 구성하고 gRPC 클라이언트를 구성 Kestrel 합니다. 이 작업은 개발 중에만 수행해야 합니다. TLS를 사용하지 않으면 gRPC 메시지가 암호화되지 않고 전송됩니다. 자세한 내용은 Asp.Net Core 7.0: macOS에서 ASP.NET Core gRPC 앱을 시작할 수 없음을 참조하세요.

gRPC C# 자산은 .proto 파일에서 생성된 코드가 아닙니다.

구체적인 클라이언트와 서비스 기본 클래스의 gRPC 코드를 생성하려면 프로젝트에서 protobuf 파일 및 도구를 참조해야 합니다. 다음을 포함해야 합니다.

  • <Protobuf> 항목 그룹에서 사용하려는 .proto 파일. 가져온 .proto 파일을 프로젝트에서 참조해야 합니다.
  • gRPC 도구 패키지인 Grpc.Tools의 패키지 참조

gRPC C# 자산을 생성하는 방법에 대한 자세한 내용은 C#을 사용하는 gRPC 서비스를 참조하세요.

gRPC 서비스를 호스트하는 ASP.NET Core 웹앱에는 생성된 서비스 기본 클래스만 있으면 됩니다.

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

gRPC 호출을 수행하는 gRPC 클라이언트 앱에는 생성된 구체적인 클라이언트만 있으면 됩니다.

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

WPF 프로젝트가 .proto 파일에서 gRPC C# 자산을 생성할 수 없음

WPF 프로젝트에는 gRPC 코드 생성이 제대로 작동하지 않도록 하는 알려진 문제가 있습니다. Grpc.Tools.proto 파일을 참조하여 WPF 프로젝트에서 생성된 gRPC 형식을 사용하면 컴파일 오류가 발생합니다.

오류 CS0246: 형식 또는 네임스페이스 이름 'MyGrpcServices'를 찾을 수 없습니다(using 지시문 또는 어셈블리 참조가 누락되었나요?)

이 문제는 다음과 같은 방법으로 해결할 수 있습니다.

  1. 새 .NET Core 클래스 라이브러리 프로젝트를 만듭니다.
  2. 새 프로젝트에서 참조를 추가하여 파일에서 .proto C# 코드 생성을 사용하도록 설정합니다.
  3. WPF 애플리케이션에서 새 프로젝트의 참조를 추가합니다.

WPF 애플리케이션은 새 클래스 라이브러리 프로젝트의 gRPC 생성 형식을 사용할 수 있습니다.

하위 디렉터리에 호스트된 gRPC 서비스 호출

Warning

많은 타사 gRPC 도구는 하위 디렉터리에서 호스트되는 서비스를 지원하지 않습니다. gRPC를 루트 디렉터리로 호스트하는 방법을 찾는 것이 좋습니다.

gRPC 호출을 만들 때 gRPC 채널 주소의 경로 구성 요소가 무시됩니다. 예를 들어 GrpcChannel.ForAddress("https://localhost:5001/ignored_path")는 서비스에 대한 gRPC 호출을 라우팅하는 경우를 ignored_path를 사용하지 않습니다.

gRPC에 표준화된 규범적인 주소 구조체가 있으므로 주소 경로가 무시됩니다. gRPC 주소는 패키지, 서비스, 메서드 이름을 결합합니다(https://localhost:5001/PackageName.ServiceName/MethodName).

앱에 gRPC 호출을 포함하는 경로가 포함되어야 하는 시나리오가 있습니다. 예를 들어 ASP.NET Core gRPC 앱이 IIS 디렉터리에서 호스트되고 디렉터리를 요청에 포함해야 하는 경우가 있습니다. 경로가 필요한 경우 아래 지정된 사용자 지정 SubdirectoryHandler을 사용하여 gRPC 호출에 추가할 수 있습니다.

public class SubdirectoryHandler : DelegatingHandler
{
    private readonly string _subdirectory;

    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
        : base(innerHandler)
    {
        _subdirectory = subdirectory;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var old = request.RequestUri;

        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
        request.RequestUri = new Uri(url, UriKind.Absolute);

        return base.SendAsync(request, cancellationToken);
    }
}

SubdirectoryHandler은 gRPC 채널이 만들어질 때 사용합니다.

var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

var reply = await client.SayHelloAsync(
                  new HelloRequest { Name = "GreeterClient" });

앞의 코드가 하는 역할은 다음과 같습니다.

  • 경로 /MyApp을 사용하여 SubdirectoryHandler를 만듭니다.
  • SubdirectoryHandler을 사용하도록 채널을 구성합니다.
  • SayHelloAsync를 사용하여 gRPC 서비스를 호출합니다. gRPC 호출이 https://localhost:5001/MyApp/greet.Greeter/SayHello로 전송됩니다.

또는 AddHttpMessageHandler를 사용하여 클라이언트 팩터리를 SubdirectoryHandler로 구성할 수 있습니다.

HTTP/3을 사용하도록 gRPC 클라이언트 구성

.NET gRPC 클라이언트는 .NET 6 이상에서 HTTP/3을 지원합니다. 서버가 HTTP/3을 지원함을 나타내는 alt-svc 응답 헤더를 클라이언트에 보내면 클라이언트는 자동으로 해당 연결을 HTTP/3으로 업그레이드합니다. 자세한 내용은 ASP.NET Core Kestrel 웹 서버에서 HTTP/3 사용을 참조하세요.

DelegatingHandler를 사용하여 gRPC 클라이언트에서 강제로 HTTP/3을 사용하도록 할 수 있습니다. HTTP/3을 강제 적용하면 요청을 업그레이드하는 오버헤드가 방지됩니다. 다음과 비슷한 코드를 사용하여 HTTP/3을 강제로 적용합니다.

public class Http3Handler : DelegatingHandler
{
    public Http3Handler() { }
    public Http3Handler(HttpMessageHandler innerHandler) : base(innerHandler) { }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Version = HttpVersion.Version30;
        request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;

        return base.SendAsync(request, cancellationToken);
    }
}

Http3Handler은 gRPC 채널이 만들어질 때 사용합니다. 다음 코드는 Http3Handler를 사용하도록 구성된 채널을 만듭니다.

var handler = new Http3Handler(new HttpClientHandler());

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

var reply = await client.SayHelloAsync(
                  new HelloRequest { Name = "GreeterClient" });

또는 AddHttpMessageHandler를 사용하여 클라이언트 팩터리를 Http3Handler로 구성할 수 있습니다.

Alpine Linux에서 gRPC 빌드

패키지Grpc.Tools 라는 protoc번들 네이티브 이진 파일을 사용하여 파일에서 .proto .NET 형식을 생성합니다. Alpine Linux와 같이 네이티브 이진 파일에서 Grpc.Tools지원되지 않는 플랫폼에서 gRPC 앱을 빌드하려면 추가 단계가 필요합니다.

미리 코드 생성

한 가지 해결 방법은 코드를 미리 생성하는 것입니다.

  1. 파일 및 패키지 참조를 Grpc.Tools 새 프로젝트로 이동합니다.proto.
  2. 프로젝트를 NuGet 패키지로 게시하고 NuGet 피드에 업로드합니다.
  3. NuGet 패키지를 참조하도록 앱을 업데이트합니다.

이전 단계를 사용하면 코드가 미리 생성되므로 더 이상 앱을 빌드할 필요가 Grpc.Tools 없습니다.

네이티브 이 Grpc.Tools 진 파일 사용자 지정

Grpc.Tools 에서는 사용자 지정 네이티브 이진 파일을 사용할 수 있습니다. 이 기능을 사용하면 번들 네이티브 이진 파일이 지원하지 않는 환경에서 gRPC 도구를 실행할 수 있습니다.

네이티브 이진 파일을 빌드하거나 획득 protocgrpc_csharp_plugin 하고 사용하도록 구성 Grpc.Tools 합니다. 다음 환경 변수를 설정하여 네이티브 이진 파일을 구성합니다.

  • PROTOBUF_PROTOC - 프로토콜 버퍼 컴파일러의 전체 경로
  • GRPC_PROTOC_PLUGIN - grpc_csharp_plugin 전체 경로

Alpine Linux의 경우 프로토콜 버퍼 컴파일러 및 gRPC 플러그 인에 대한 커뮤니티 제공 패키지가 있습니다 https://pkgs.alpinelinux.org/.

# Build or install the binaries for your architecture.

# e.g. for Alpine Linux the grpc-plugins package can be used
#  See https://pkgs.alpinelinux.org/package/edge/community/x86_64/grpc-plugins
apk add grpc-plugins  # Alpine Linux specific package installer

# Set environment variables for the built/installed protoc
# and grpc_csharp_plugin binaries
export PROTOBUF_PROTOC=/usr/bin/protoc
export GRPC_PROTOC_PLUGIN=/usr/bin/grpc_csharp_plugin

# When dotnet build runs, the Grpc.Tools NuGet package
# uses the binaries pointed to by the environment variables.
dotnet build

지원되지 않는 아키텍처를 사용하는 Grpc.Tools 방법에 대한 자세한 내용은 gRPC 빌드 통합 설명서를 참조 하세요.

이 문서에서는 .NET에서 gRPC 앱을 개발할 때 발생하는 일반적인 문제를 설명합니다.

클라이언트와 서비스의 SSL/TLS 구성이 일치하지 않음

gRPC 템플릿과 샘플은 기본적으로 TLS(전송 계층 보안)를 사용하여 gRPC 서비스를 보호합니다. gRPC 클라이언트는 보안 연결을 사용하여 안전한 gRPC 서비스를 호출해야 합니다.

앱 시작 시 작성된 로그에서 ASP.NET Core gRPC 서비스가 TLS를 사용하고 있는지 확인할 수 있습니다. 서비스는 HTTPS 엔드포인트에서 수신 대기합니다.

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

.NET Core 클라이언트는 서버 주소에 https를 사용하여 보안 연결을 통해 호출해야 합니다.

static async Task Main(string[] args)
{
    // The port number(5001) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var client = new Greet.GreeterClient(channel);
}

모든 gRPC 클라이언트 구현에서 TLS를 지원합니다. 다른 언어의 gRPC 클라이언트에는 일반적으로 SslCredentials로 구성된 채널이 필요합니다. SslCredentials는 클라이언트에서 사용할 인증서를 지정하며, 안전하지 않은 자격 증명 대신 이 인증서를 사용해야 합니다. TLS를 사용하도록 여러 gRPC 클라이언트 구현을 구성하는 예제는 gRPC 인증을 참조하세요.

신뢰할 수 없는/잘못된 인증서를 사용하여 gRPC 서비스 호출

.NET gRPC 클라이언트를 사용하려면 서비스에 신뢰할 수 있는 인증서가 있어야 합니다. 신뢰할 수 있는 인증서 없이 gRPC 서비스를 호출할 경우 다음 오류 메시지가 반환됩니다.

처리되지 않은 예외가 발생했습니다. System.Net.Http.HttpRequestException: SSL 연결을 설정할 수 없습니다. 내부 예외를 참조하세요. ---> System.Security.Authentication.AuthenticationException: 유효성 검사 절차에 따르면 원격 인증서가 잘못되었습니다.

로컬에서 앱을 테스트 중이며 ASP.NET Core HTTPS 개발 인증서를 신뢰할 수 없는 경우 이 오류가 표시될 수 있습니다. 이 문제의 해결 지침은 Windows 및 macOS에서 ASP.NET Core HTTPS 개발 인증서 신뢰를 참조하세요.

다른 머신에서 gRPC 서비스를 호출 중이며 인증서를 신뢰할 수 없는 경우 잘못된 인증서를 무시하도록 gRPC 클라이언트를 구성할 수 있습니다. 다음 코드는 신뢰할 수 있는 인증서 없이 호출을 허용하는 데 사용합니다 HttpClientHandler.ServerCertificateCustomValidationCallback .

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

gRPC 클라이언트 팩터리는 신뢰할 수 있는 인증서가 없는 호출을 허용합니다. 확장 메서드를 ConfigurePrimaryHttpMessageHandler 사용하여 클라이언트에서 처리기를 구성합니다.

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ServerCertificateCustomValidationCallback = 
            HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

        return handler;
    });

Warning

신뢰할 수 없는 인증서는 앱 개발 중에만 사용해야 합니다. 프로덕션 앱은 항상 유효한 인증서를 사용해야 합니다.

.NET Core 클라이언트를 사용하여 안전하지 않은 gRPC 서비스 호출

.NET gRPC 클라이언트는 서버 주소에서 http를 지정하여 안전하지 않은 gRPC 서비스를 호출할 수 있습니다. 예들 들어 GrpcChannel.ForAddress("http://localhost:5000")입니다.

앱이 사용하는 .NET 버전에 따라 안전하지 않은 gRPC 서비스를 호출하기 위한 몇 가지 추가 요구 사항이 있습니다.

  • .NET 5 이상에는 Grpc.Net.Client 버전 2.32.0 이상이 필요합니다.

  • .NET Core 3.x에는 추가 구성이 필요합니다. 앱은 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 스위치를 true로 설정해야 합니다.

    // This switch must be set before creating the GrpcChannel/HttpClient.
    AppContext.SetSwitch(
        "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
    
    // The port number(5000) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("http://localhost:5000");
    var client = new Greet.GreeterClient(channel);
    

System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 스위치는 .NET Core 3.x에만 필요합니다. .NET 5에서는 아무것도 하지 않으며 필요하지도 않습니다.

Important

안전하지 않은 gRPC 서비스는 HTTP/2 전용 포트에서 호스트되어야 합니다. 자세한 내용은 ASP.NET Core 프로토콜 협상을 참조하세요.

macOS에서 ASP.NET Core gRPC 앱을 시작할 수 없음

Kestrel 는 .NET 8 이전의 macOS에서 TLS를 사용하여 HTTP/2를 지원하지 않습니다. ASP.NET Core gRPC 템플릿과 샘플은 기본적으로 TLS를 사용합니다. gRPC 서버를 시작하려고 하면 다음과 같은 오류 메시지가 표시됩니다.

IPv4 루프백 인터페이스에 바인딩할 https://localhost:5001 수 없습니다. 'TLS를 통한 HTTP/2는 ALPN 지원이 누락되어 macOS에서 지원되지 않습니다.'.

.NET 7 이하에서 이 문제를 해결하려면 TLS 없이 HTTP/2를 사용하도록 구성하고 gRPC 클라이언트를 구성 Kestrel 합니다. 이 작업은 개발 중에만 수행해야 합니다. TLS를 사용하지 않으면 gRPC 메시지가 암호화되지 않고 전송됩니다.

Kestrel은 Program.cs에서 TLS 없이 HTTP/2 엔드포인트를 구성해야 합니다.

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(options =>
{
    // Setup a HTTP/2 endpoint without TLS.
    options.ListenLocalhost(<5287>, o => o.Protocols =
        HttpProtocols.Http2);
});
  • 앞의 코드에서 localhost 포트 번호 5287을 gRPC 서비스 프로젝트 내에서 Properties/launchSettings.json에 지정된 HTTP(HTTPS 아님) 포트 번호로 바꿉니다.

TLS 없이 HTTP/2 엔드포인트를 구성한 경우 엔드포인트의 ListenOptions.ProtocolsHttpProtocols.Http2로 설정해야 합니다. HTTP/2를 협상하려면 TLS가 필요하기 때문에 HttpProtocols.Http1AndHttp2를 사용할 수 없습니다. TLS가 없을 경우 모든 엔드포인트 연결은 기본적으로 HTTP/1.1로 설정되며 gRPC 호출이 실패합니다.

또한 TLS를 사용하지 않도록 gRPC 클라이언트를 구성해야 합니다. 자세한 내용은 .NET Core 클라이언트를 사용하여 안전하지 않은 gRPC 서비스 호출을 참조하세요.

Warning

TLS가 없는 HTTP/2는 앱 개발 중에만 사용해야 합니다. 프로덕션 앱은 항상 전송 보안을 사용해야 합니다. 자세한 내용은 ASP.NET Core용 gRPC의 보안 고려 사항을 참조하세요.

gRPC C# 자산은 .proto 파일에서 생성된 코드가 아닙니다.

구체적인 클라이언트와 서비스 기본 클래스의 gRPC 코드를 생성하려면 프로젝트에서 protobuf 파일 및 도구를 참조해야 합니다. 다음을 포함해야 합니다.

  • <Protobuf> 항목 그룹에서 사용하려는 .proto 파일. 가져온 .proto 파일을 프로젝트에서 참조해야 합니다.
  • gRPC 도구 패키지인 Grpc.Tools의 패키지 참조

gRPC C# 자산을 생성하는 방법에 대한 자세한 내용은 C#을 사용하는 gRPC 서비스를 참조하세요.

gRPC 서비스를 호스트하는 ASP.NET Core 웹앱에는 생성된 서비스 기본 클래스만 있으면 됩니다.

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

gRPC 호출을 수행하는 gRPC 클라이언트 앱에는 생성된 구체적인 클라이언트만 있으면 됩니다.

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

WPF 프로젝트가 .proto 파일에서 gRPC C# 자산을 생성할 수 없음

WPF 프로젝트에는 gRPC 코드 생성이 제대로 작동하지 않도록 하는 알려진 문제가 있습니다. Grpc.Tools.proto 파일을 참조하여 WPF 프로젝트에서 생성된 gRPC 형식을 사용하면 컴파일 오류가 발생합니다.

오류 CS0246: 형식 또는 네임스페이스 이름 'MyGrpcServices'를 찾을 수 없습니다(using 지시문 또는 어셈블리 참조가 누락되었나요?)

이 문제는 다음과 같은 방법으로 해결할 수 있습니다.

  1. 새 .NET Core 클래스 라이브러리 프로젝트를 만듭니다.
  2. 새 프로젝트에서 참조를 추가하여 파일에서 .proto C# 코드 생성을 사용하도록 설정합니다.
  3. WPF 애플리케이션에서 새 프로젝트의 참조를 추가합니다.

WPF 애플리케이션은 새 클래스 라이브러리 프로젝트의 gRPC 생성 형식을 사용할 수 있습니다.

하위 디렉터리에 호스트된 gRPC 서비스 호출

Warning

많은 타사 gRPC 도구는 하위 디렉터리에서 호스트되는 서비스를 지원하지 않습니다. gRPC를 루트 디렉터리로 호스트하는 방법을 찾는 것이 좋습니다.

gRPC 호출을 만들 때 gRPC 채널 주소의 경로 구성 요소가 무시됩니다. 예를 들어 GrpcChannel.ForAddress("https://localhost:5001/ignored_path")는 서비스에 대한 gRPC 호출을 라우팅하는 경우를 ignored_path를 사용하지 않습니다.

gRPC에 표준화된 규범적인 주소 구조체가 있으므로 주소 경로가 무시됩니다. gRPC 주소는 패키지, 서비스, 메서드 이름을 결합합니다(https://localhost:5001/PackageName.ServiceName/MethodName).

앱에 gRPC 호출을 포함하는 경로가 포함되어야 하는 시나리오가 있습니다. 예를 들어 ASP.NET Core gRPC 앱이 IIS 디렉터리에서 호스트되고 디렉터리를 요청에 포함해야 하는 경우가 있습니다. 경로가 필요한 경우 아래 지정된 사용자 지정 SubdirectoryHandler을 사용하여 gRPC 호출에 추가할 수 있습니다.

/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
    private readonly string _subdirectory;

    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
        : base(innerHandler)
    {
        _subdirectory = subdirectory;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var old = request.RequestUri;

        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
        request.RequestUri = new Uri(url, UriKind.Absolute);

        return base.SendAsync(request, cancellationToken);
    }
}

SubdirectoryHandler은 gRPC 채널이 만들어질 때 사용합니다.

var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

앞의 코드가 하는 역할은 다음과 같습니다.

  • 경로 /MyApp을 사용하여 SubdirectoryHandler를 만듭니다.
  • SubdirectoryHandler을 사용하도록 채널을 구성합니다.
  • SayHelloAsync를 사용하여 gRPC 서비스를 호출합니다. gRPC 호출이 https://localhost:5001/MyApp/greet.Greeter/SayHello로 전송됩니다.

또는 AddHttpMessageHandler를 사용하여 클라이언트 팩터리를 SubdirectoryHandler로 구성할 수 있습니다.

HTTP/3을 사용하도록 gRPC 클라이언트 구성

.NET gRPC 클라이언트는 .NET 6 이상에서 HTTP/3을 지원합니다. 서버가 HTTP/3을 지원함을 나타내는 alt-svc 응답 헤더를 클라이언트에 보내면 클라이언트는 자동으로 해당 연결을 HTTP/3으로 업그레이드합니다. 서버에서 HTTP/3을 사용하도록 설정하는 방법에 대한 자세한 내용은 ASP.NET Core Kestrel 웹 서버에서 HTTP/3 사용을 참조하세요.

.NET 8의 HTTP/3 지원은 기본적으로 사용하도록 설정되어 있습니다. .NET 6 및 .NET 7의 HTTP/3 지원은 프로젝트 파일의 구성 플래그를 통해 사용하도록 설정해야 합니다.

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
</ItemGroup>

System.Net.SocketsHttpHandler.Http3SupportAppContext.SetSwitch를 사용하여 설정할 수도 있습니다.

DelegatingHandler를 사용하여 gRPC 클라이언트에서 강제로 HTTP/3을 사용하도록 할 수 있습니다. HTTP/3을 강제 적용하면 요청을 업그레이드하는 오버헤드가 방지됩니다. 다음과 비슷한 코드를 사용하여 HTTP/3을 강제로 적용합니다.

/// <summary>
/// A delegating handler that changes the request HTTP version to HTTP/3.
/// </summary>
public class Http3Handler : DelegatingHandler
{
    public Http3Handler() { }
    public Http3Handler(HttpMessageHandler innerHandler) : base(innerHandler) { }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Version = HttpVersion.Version30;
        request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;

        return base.SendAsync(request, cancellationToken);
    }
}

Http3Handler은 gRPC 채널이 만들어질 때 사용합니다. 다음 코드는 Http3Handler를 사용하도록 구성된 채널을 만듭니다.

var handler = new Http3Handler(new HttpClientHandler());

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

또는 AddHttpMessageHandler를 사용하여 클라이언트 팩터리를 Http3Handler로 구성할 수 있습니다.

Alpine Linux에서 gRPC 빌드

패키지Grpc.Tools 라는 protoc번들 네이티브 이진 파일을 사용하여 파일에서 .proto .NET 형식을 생성합니다. Alpine Linux와 같이 네이티브 이진 파일에서 Grpc.Tools지원되지 않는 플랫폼에서 gRPC 앱을 빌드하려면 추가 단계가 필요합니다.

미리 코드 생성

한 가지 해결 방법은 코드를 미리 생성하는 것입니다.

  1. 파일 및 패키지 참조를 Grpc.Tools 새 프로젝트로 이동합니다.proto.
  2. 프로젝트를 NuGet 패키지로 게시하고 NuGet 피드에 업로드합니다.
  3. NuGet 패키지를 참조하도록 앱을 업데이트합니다.

이전 단계를 사용하면 코드가 미리 생성되므로 더 이상 앱을 빌드할 필요가 Grpc.Tools 없습니다.

네이티브 이 Grpc.Tools 진 파일 사용자 지정

Grpc.Tools 에서는 사용자 지정 네이티브 이진 파일을 사용할 수 있습니다. 이 기능을 사용하면 번들 네이티브 이진 파일이 지원하지 않는 환경에서 gRPC 도구를 실행할 수 있습니다.

네이티브 이진 파일을 빌드하거나 획득 protocgrpc_csharp_plugin 하고 사용하도록 구성 Grpc.Tools 합니다. 다음 환경 변수를 설정하여 네이티브 이진 파일을 구성합니다.

  • PROTOBUF_PROTOC - 프로토콜 버퍼 컴파일러의 전체 경로
  • GRPC_PROTOC_PLUGIN - grpc_csharp_plugin 전체 경로

Alpine Linux의 경우 프로토콜 버퍼 컴파일러 및 gRPC 플러그 인에 대한 커뮤니티 제공 패키지가 있습니다 https://pkgs.alpinelinux.org/.

# Build or install the binaries for your architecture.

# e.g. for Alpine Linux the grpc-plugins package can be used
#  See https://pkgs.alpinelinux.org/package/edge/community/x86_64/grpc-plugins
apk add grpc-plugins  # Alpine Linux specific package installer

# Set environment variables for the built/installed protoc
# and grpc_csharp_plugin binaries
export PROTOBUF_PROTOC=/usr/bin/protoc
export GRPC_PROTOC_PLUGIN=/usr/bin/grpc_csharp_plugin

# When dotnet build runs, the Grpc.Tools NuGet package
# uses the binaries pointed to by the environment variables.
dotnet build

지원되지 않는 아키텍처를 사용하는 Grpc.Tools 방법에 대한 자세한 내용은 gRPC 빌드 통합 설명서를 참조 하세요.

이 문서에서는 .NET에서 gRPC 앱을 개발할 때 발생하는 일반적인 문제를 설명합니다.

클라이언트와 서비스의 SSL/TLS 구성이 일치하지 않음

gRPC 템플릿과 샘플은 기본적으로 TLS(전송 계층 보안)를 사용하여 gRPC 서비스를 보호합니다. gRPC 클라이언트는 보안 연결을 사용하여 안전한 gRPC 서비스를 호출해야 합니다.

앱 시작 시 작성된 로그에서 ASP.NET Core gRPC 서비스가 TLS를 사용하고 있는지 확인할 수 있습니다. 서비스는 HTTPS 엔드포인트에서 수신 대기합니다.

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

.NET Core 클라이언트는 서버 주소에 https를 사용하여 보안 연결을 통해 호출해야 합니다.

static async Task Main(string[] args)
{
    // The port number(5001) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var client = new Greet.GreeterClient(channel);
}

모든 gRPC 클라이언트 구현에서 TLS를 지원합니다. 다른 언어의 gRPC 클라이언트에는 일반적으로 SslCredentials로 구성된 채널이 필요합니다. SslCredentials는 클라이언트에서 사용할 인증서를 지정하며, 안전하지 않은 자격 증명 대신 이 인증서를 사용해야 합니다. TLS를 사용하도록 여러 gRPC 클라이언트 구현을 구성하는 예제는 gRPC 인증을 참조하세요.

신뢰할 수 없는/잘못된 인증서를 사용하여 gRPC 서비스 호출

.NET gRPC 클라이언트를 사용하려면 서비스에 신뢰할 수 있는 인증서가 있어야 합니다. 신뢰할 수 있는 인증서 없이 gRPC 서비스를 호출할 경우 다음 오류 메시지가 반환됩니다.

처리되지 않은 예외가 발생했습니다. System.Net.Http.HttpRequestException: SSL 연결을 설정할 수 없습니다. 내부 예외를 참조하세요. ---> System.Security.Authentication.AuthenticationException: 유효성 검사 절차에 따르면 원격 인증서가 잘못되었습니다.

로컬에서 앱을 테스트 중이며 ASP.NET Core HTTPS 개발 인증서를 신뢰할 수 없는 경우 이 오류가 표시될 수 있습니다. 이 문제의 해결 지침은 Windows 및 macOS에서 ASP.NET Core HTTPS 개발 인증서 신뢰를 참조하세요.

다른 머신에서 gRPC 서비스를 호출 중이며 인증서를 신뢰할 수 없는 경우 잘못된 인증서를 무시하도록 gRPC 클라이언트를 구성할 수 있습니다. 다음 코드는 신뢰할 수 있는 인증서 없이 호출을 허용하는 데 사용합니다 HttpClientHandler.ServerCertificateCustomValidationCallback .

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

gRPC 클라이언트 팩터리는 신뢰할 수 있는 인증서가 없는 호출을 허용합니다. 확장 메서드를 ConfigurePrimaryHttpMessageHandler 사용하여 클라이언트에서 처리기를 구성합니다.

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ServerCertificateCustomValidationCallback = 
            HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

        return handler;
    });

Warning

신뢰할 수 없는 인증서는 앱 개발 중에만 사용해야 합니다. 프로덕션 앱은 항상 유효한 인증서를 사용해야 합니다.

.NET Core 클라이언트를 사용하여 안전하지 않은 gRPC 서비스 호출

.NET gRPC 클라이언트는 서버 주소에서 http를 지정하여 안전하지 않은 gRPC 서비스를 호출할 수 있습니다. 예들 들어 GrpcChannel.ForAddress("http://localhost:5000")입니다.

앱이 사용하는 .NET 버전에 따라 안전하지 않은 gRPC 서비스를 호출하기 위한 몇 가지 추가 요구 사항이 있습니다.

  • .NET 5 이상에는 Grpc.Net.Client 버전 2.32.0 이상이 필요합니다.

  • .NET Core 3.x에는 추가 구성이 필요합니다. 앱은 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 스위치를 true로 설정해야 합니다.

    // This switch must be set before creating the GrpcChannel/HttpClient.
    AppContext.SetSwitch(
        "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
    
    // The port number(5000) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("http://localhost:5000");
    var client = new Greet.GreeterClient(channel);
    

System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 스위치는 .NET Core 3.x에만 필요합니다. .NET 5에서는 아무것도 하지 않으며 필요하지도 않습니다.

Important

안전하지 않은 gRPC 서비스는 HTTP/2 전용 포트에서 호스트되어야 합니다. 자세한 내용은 ASP.NET Core 프로토콜 협상을 참조하세요.

macOS에서 ASP.NET Core gRPC 앱을 시작할 수 없음

Kestrel 는 .NET 8 이전의 macOS에서 TLS를 사용하여 HTTP/2를 지원하지 않습니다. ASP.NET Core gRPC 템플릿과 샘플은 기본적으로 TLS를 사용합니다. gRPC 서버를 시작하려고 하면 다음과 같은 오류 메시지가 표시됩니다.

IPv4 루프백 인터페이스에 바인딩할 https://localhost:5001 수 없습니다. 'TLS를 통한 HTTP/2는 ALPN 지원이 누락되어 macOS에서 지원되지 않습니다.'.

.NET 7 이하에서 이 문제를 해결하려면 TLS 없이 HTTP/2를 사용하도록 구성하고 gRPC 클라이언트를 구성 Kestrel 합니다. 이 작업은 개발 중에만 수행해야 합니다. TLS를 사용하지 않으면 gRPC 메시지가 암호화되지 않고 전송됩니다.

Kestrel은 Program.cs에서 TLS 없이 HTTP/2 엔드포인트를 구성해야 합니다.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(options =>
            {
                // Setup a HTTP/2 endpoint without TLS.
                options.ListenLocalhost(5000, o => o.Protocols = 
                    HttpProtocols.Http2);
            });
            webBuilder.UseStartup<Startup>();
        });

TLS 없이 HTTP/2 엔드포인트를 구성한 경우 엔드포인트의 ListenOptions.ProtocolsHttpProtocols.Http2로 설정해야 합니다. HTTP/2를 협상하려면 TLS가 필요하기 때문에 HttpProtocols.Http1AndHttp2를 사용할 수 없습니다. TLS가 없을 경우 모든 엔드포인트 연결은 기본적으로 HTTP/1.1로 설정되며 gRPC 호출이 실패합니다.

또한 TLS를 사용하지 않도록 gRPC 클라이언트를 구성해야 합니다. 자세한 내용은 .NET Core 클라이언트를 사용하여 안전하지 않은 gRPC 서비스 호출을 참조하세요.

Warning

TLS가 없는 HTTP/2는 앱 개발 중에만 사용해야 합니다. 프로덕션 앱은 항상 전송 보안을 사용해야 합니다. 자세한 내용은 ASP.NET Core용 gRPC의 보안 고려 사항을 참조하세요.

gRPC C# 자산은 .proto 파일에서 생성된 코드가 아닙니다.

구체적인 클라이언트와 서비스 기본 클래스의 gRPC 코드를 생성하려면 프로젝트에서 protobuf 파일 및 도구를 참조해야 합니다. 다음을 포함해야 합니다.

  • <Protobuf> 항목 그룹에서 사용하려는 .proto 파일. 가져온 .proto 파일을 프로젝트에서 참조해야 합니다.
  • gRPC 도구 패키지인 Grpc.Tools의 패키지 참조

gRPC C# 자산을 생성하는 방법에 대한 자세한 내용은 C#을 사용하는 gRPC 서비스를 참조하세요.

gRPC 서비스를 호스트하는 ASP.NET Core 웹앱에는 생성된 서비스 기본 클래스만 있으면 됩니다.

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

gRPC 호출을 수행하는 gRPC 클라이언트 앱에는 생성된 구체적인 클라이언트만 있으면 됩니다.

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

WPF 프로젝트가 .proto 파일에서 gRPC C# 자산을 생성할 수 없음

WPF 프로젝트에는 gRPC 코드 생성이 제대로 작동하지 않도록 하는 알려진 문제가 있습니다. Grpc.Tools.proto 파일을 참조하여 WPF 프로젝트에서 생성된 gRPC 형식을 사용하면 컴파일 오류가 발생합니다.

오류 CS0246: 형식 또는 네임스페이스 이름 'MyGrpcServices'를 찾을 수 없습니다(using 지시문 또는 어셈블리 참조가 누락되었나요?)

이 문제는 다음과 같은 방법으로 해결할 수 있습니다.

  1. 새 .NET Core 클래스 라이브러리 프로젝트를 만듭니다.
  2. 새 프로젝트에서 참조를 추가하여 파일에서 .proto C# 코드 생성을 사용하도록 설정합니다.
  3. WPF 애플리케이션에서 새 프로젝트의 참조를 추가합니다.

WPF 애플리케이션은 새 클래스 라이브러리 프로젝트의 gRPC 생성 형식을 사용할 수 있습니다.

하위 디렉터리에 호스트된 gRPC 서비스 호출

Warning

많은 타사 gRPC 도구는 하위 디렉터리에서 호스트되는 서비스를 지원하지 않습니다. gRPC를 루트 디렉터리로 호스트하는 방법을 찾는 것이 좋습니다.

gRPC 호출을 만들 때 gRPC 채널 주소의 경로 구성 요소가 무시됩니다. 예를 들어 GrpcChannel.ForAddress("https://localhost:5001/ignored_path")는 서비스에 대한 gRPC 호출을 라우팅하는 경우를 ignored_path를 사용하지 않습니다.

gRPC에 표준화된 규범적인 주소 구조체가 있으므로 주소 경로가 무시됩니다. gRPC 주소는 패키지, 서비스, 메서드 이름을 결합합니다(https://localhost:5001/PackageName.ServiceName/MethodName).

앱에 gRPC 호출을 포함하는 경로가 포함되어야 하는 시나리오가 있습니다. 예를 들어 ASP.NET Core gRPC 앱이 IIS 디렉터리에서 호스트되고 디렉터리를 요청에 포함해야 하는 경우가 있습니다. 경로가 필요한 경우 아래 지정된 사용자 지정 SubdirectoryHandler을 사용하여 gRPC 호출에 추가할 수 있습니다.

/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
    private readonly string _subdirectory;

    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
        : base(innerHandler)
    {
        _subdirectory = subdirectory;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var old = request.RequestUri;

        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
        request.RequestUri = new Uri(url, UriKind.Absolute);

        return base.SendAsync(request, cancellationToken);
    }
}

SubdirectoryHandler은 gRPC 채널이 만들어질 때 사용합니다.

var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

앞의 코드가 하는 역할은 다음과 같습니다.

  • 경로 /MyApp을 사용하여 SubdirectoryHandler를 만듭니다.
  • SubdirectoryHandler을 사용하도록 채널을 구성합니다.
  • SayHelloAsync를 사용하여 gRPC 서비스를 호출합니다. gRPC 호출이 https://localhost:5001/MyApp/greet.Greeter/SayHello로 전송됩니다.

또는 AddHttpMessageHandler를 사용하여 클라이언트 팩터리를 SubdirectoryHandler로 구성할 수 있습니다.

이 문서에서는 .NET에서 gRPC 앱을 개발할 때 발생하는 일반적인 문제를 설명합니다.

클라이언트와 서비스의 SSL/TLS 구성이 일치하지 않음

gRPC 템플릿과 샘플은 기본적으로 TLS(전송 계층 보안)를 사용하여 gRPC 서비스를 보호합니다. gRPC 클라이언트는 보안 연결을 사용하여 안전한 gRPC 서비스를 호출해야 합니다.

앱 시작 시 작성된 로그에서 ASP.NET Core gRPC 서비스가 TLS를 사용하고 있는지 확인할 수 있습니다. 서비스는 HTTPS 엔드포인트에서 수신 대기합니다.

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

.NET Core 클라이언트는 서버 주소에 https를 사용하여 보안 연결을 통해 호출해야 합니다.

static async Task Main(string[] args)
{
    // The port number(5001) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var client = new Greet.GreeterClient(channel);
}

모든 gRPC 클라이언트 구현에서 TLS를 지원합니다. 다른 언어의 gRPC 클라이언트에는 일반적으로 SslCredentials로 구성된 채널이 필요합니다. SslCredentials는 클라이언트에서 사용할 인증서를 지정하며, 안전하지 않은 자격 증명 대신 이 인증서를 사용해야 합니다. TLS를 사용하도록 여러 gRPC 클라이언트 구현을 구성하는 예제는 gRPC 인증을 참조하세요.

신뢰할 수 없는/잘못된 인증서를 사용하여 gRPC 서비스 호출

.NET gRPC 클라이언트를 사용하려면 서비스에 신뢰할 수 있는 인증서가 있어야 합니다. 신뢰할 수 있는 인증서 없이 gRPC 서비스를 호출할 경우 다음 오류 메시지가 반환됩니다.

처리되지 않은 예외가 발생했습니다. System.Net.Http.HttpRequestException: SSL 연결을 설정할 수 없습니다. 내부 예외를 참조하세요. ---> System.Security.Authentication.AuthenticationException: 유효성 검사 절차에 따르면 원격 인증서가 잘못되었습니다.

로컬에서 앱을 테스트 중이며 ASP.NET Core HTTPS 개발 인증서를 신뢰할 수 없는 경우 이 오류가 표시될 수 있습니다. 이 문제의 해결 지침은 Windows 및 macOS에서 ASP.NET Core HTTPS 개발 인증서 신뢰를 참조하세요.

다른 머신에서 gRPC 서비스를 호출 중이며 인증서를 신뢰할 수 없는 경우 잘못된 인증서를 무시하도록 gRPC 클라이언트를 구성할 수 있습니다. 다음 코드는 신뢰할 수 있는 인증서 없이 호출을 허용하는 데 사용합니다 HttpClientHandler.ServerCertificateCustomValidationCallback .

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

gRPC 클라이언트 팩터리는 신뢰할 수 있는 인증서가 없는 호출을 허용합니다. 확장 메서드를 ConfigurePrimaryHttpMessageHandler 사용하여 클라이언트에서 처리기를 구성합니다.

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ServerCertificateCustomValidationCallback = 
            HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

        return handler;
    });

Warning

신뢰할 수 없는 인증서는 앱 개발 중에만 사용해야 합니다. 프로덕션 앱은 항상 유효한 인증서를 사용해야 합니다.

.NET Core 클라이언트를 사용하여 안전하지 않은 gRPC 서비스 호출

.NET gRPC 클라이언트는 서버 주소에서 http를 지정하여 안전하지 않은 gRPC 서비스를 호출할 수 있습니다. 예들 들어 GrpcChannel.ForAddress("http://localhost:5000")입니다.

앱이 사용하는 .NET 버전에 따라 안전하지 않은 gRPC 서비스를 호출하기 위한 몇 가지 추가 요구 사항이 있습니다.

  • .NET 5 이상에는 Grpc.Net.Client 버전 2.32.0 이상이 필요합니다.

  • .NET Core 3.x에는 추가 구성이 필요합니다. 앱은 System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 스위치를 true로 설정해야 합니다.

    // This switch must be set before creating the GrpcChannel/HttpClient.
    AppContext.SetSwitch(
        "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
    
    // The port number(5000) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("http://localhost:5000");
    var client = new Greet.GreeterClient(channel);
    

System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport 스위치는 .NET Core 3.x에만 필요합니다. .NET 5에서는 아무것도 하지 않으며 필요하지도 않습니다.

Important

안전하지 않은 gRPC 서비스는 HTTP/2 전용 포트에서 호스트되어야 합니다. 자세한 내용은 ASP.NET Core 프로토콜 협상을 참조하세요.

macOS에서 ASP.NET Core gRPC 앱을 시작할 수 없음

Kestrel 는 .NET 8 이전의 macOS에서 TLS를 사용하여 HTTP/2를 지원하지 않습니다. ASP.NET Core gRPC 템플릿과 샘플은 기본적으로 TLS를 사용합니다. gRPC 서버를 시작하려고 하면 다음과 같은 오류 메시지가 표시됩니다.

IPv4 루프백 인터페이스에 바인딩할 https://localhost:5001 수 없습니다. 'TLS를 통한 HTTP/2는 ALPN 지원이 누락되어 macOS에서 지원되지 않습니다.'.

.NET 7 이하에서 이 문제를 해결하려면 TLS 없이 HTTP/2를 사용하도록 구성하고 gRPC 클라이언트를 구성 Kestrel 합니다. 이 작업은 개발 중에만 수행해야 합니다. TLS를 사용하지 않으면 gRPC 메시지가 암호화되지 않고 전송됩니다.

Kestrel은 Program.cs에서 TLS 없이 HTTP/2 엔드포인트를 구성해야 합니다.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(options =>
            {
                // Setup a HTTP/2 endpoint without TLS.
                options.ListenLocalhost(5000, o => o.Protocols = 
                    HttpProtocols.Http2);
            });
            webBuilder.UseStartup<Startup>();
        });

TLS 없이 HTTP/2 엔드포인트를 구성한 경우 엔드포인트의 ListenOptions.ProtocolsHttpProtocols.Http2로 설정해야 합니다. HTTP/2를 협상하려면 TLS가 필요하기 때문에 HttpProtocols.Http1AndHttp2를 사용할 수 없습니다. TLS가 없을 경우 모든 엔드포인트 연결은 기본적으로 HTTP/1.1로 설정되며 gRPC 호출이 실패합니다.

또한 TLS를 사용하지 않도록 gRPC 클라이언트를 구성해야 합니다. 자세한 내용은 .NET Core 클라이언트를 사용하여 안전하지 않은 gRPC 서비스 호출을 참조하세요.

Warning

TLS가 없는 HTTP/2는 앱 개발 중에만 사용해야 합니다. 프로덕션 앱은 항상 전송 보안을 사용해야 합니다. 자세한 내용은 ASP.NET Core용 gRPC의 보안 고려 사항을 참조하세요.

gRPC C# 자산은 .proto 파일에서 생성된 코드가 아닙니다.

구체적인 클라이언트와 서비스 기본 클래스의 gRPC 코드를 생성하려면 프로젝트에서 protobuf 파일 및 도구를 참조해야 합니다. 다음을 포함해야 합니다.

  • <Protobuf> 항목 그룹에서 사용하려는 .proto 파일. 가져온 .proto 파일을 프로젝트에서 참조해야 합니다.
  • gRPC 도구 패키지인 Grpc.Tools의 패키지 참조

gRPC C# 자산을 생성하는 방법에 대한 자세한 내용은 C#을 사용하는 gRPC 서비스를 참조하세요.

gRPC 서비스를 호스트하는 ASP.NET Core 웹앱에는 생성된 서비스 기본 클래스만 있으면 됩니다.

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

gRPC 호출을 수행하는 gRPC 클라이언트 앱에는 생성된 구체적인 클라이언트만 있으면 됩니다.

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

WPF 프로젝트가 .proto 파일에서 gRPC C# 자산을 생성할 수 없음

WPF 프로젝트에는 gRPC 코드 생성이 제대로 작동하지 않도록 하는 알려진 문제가 있습니다. Grpc.Tools.proto 파일을 참조하여 WPF 프로젝트에서 생성된 gRPC 형식을 사용하면 컴파일 오류가 발생합니다.

오류 CS0246: 형식 또는 네임스페이스 이름 'MyGrpcServices'를 찾을 수 없습니다(using 지시문 또는 어셈블리 참조가 누락되었나요?)

이 문제는 다음과 같은 방법으로 해결할 수 있습니다.

  1. 새 .NET Core 클래스 라이브러리 프로젝트를 만듭니다.
  2. 새 프로젝트에서 참조를 추가하여 파일에서 .proto C# 코드 생성을 사용하도록 설정합니다.
  3. WPF 애플리케이션에서 새 프로젝트의 참조를 추가합니다.

WPF 애플리케이션은 새 클래스 라이브러리 프로젝트의 gRPC 생성 형식을 사용할 수 있습니다.

하위 디렉터리에 호스트된 gRPC 서비스 호출

Warning

많은 타사 gRPC 도구는 하위 디렉터리에서 호스트되는 서비스를 지원하지 않습니다. gRPC를 루트 디렉터리로 호스트하는 방법을 찾는 것이 좋습니다.

gRPC 호출을 만들 때 gRPC 채널 주소의 경로 구성 요소가 무시됩니다. 예를 들어 GrpcChannel.ForAddress("https://localhost:5001/ignored_path")는 서비스에 대한 gRPC 호출을 라우팅하는 경우를 ignored_path를 사용하지 않습니다.

gRPC에 표준화된 규범적인 주소 구조체가 있으므로 주소 경로가 무시됩니다. gRPC 주소는 패키지, 서비스, 메서드 이름을 결합합니다(https://localhost:5001/PackageName.ServiceName/MethodName).

앱에 gRPC 호출을 포함하는 경로가 포함되어야 하는 시나리오가 있습니다. 예를 들어 ASP.NET Core gRPC 앱이 IIS 디렉터리에서 호스트되고 디렉터리를 요청에 포함해야 하는 경우가 있습니다. 경로가 필요한 경우 아래 지정된 사용자 지정 SubdirectoryHandler을 사용하여 gRPC 호출에 추가할 수 있습니다.

/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
    private readonly string _subdirectory;

    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
        : base(innerHandler)
    {
        _subdirectory = subdirectory;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var old = request.RequestUri;

        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
        request.RequestUri = new Uri(url, UriKind.Absolute);

        return base.SendAsync(request, cancellationToken);
    }
}

SubdirectoryHandler은 gRPC 채널이 만들어질 때 사용합니다.

var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

앞의 코드가 하는 역할은 다음과 같습니다.

  • 경로 /MyApp을 사용하여 SubdirectoryHandler를 만듭니다.
  • SubdirectoryHandler을 사용하도록 채널을 구성합니다.
  • SayHelloAsync를 사용하여 gRPC 서비스를 호출합니다. gRPC 호출이 https://localhost:5001/MyApp/greet.Greeter/SayHello로 전송됩니다.

또는 AddHttpMessageHandler를 사용하여 클라이언트 팩터리를 SubdirectoryHandler로 구성할 수 있습니다.