gRPC 대 REST: 최고의 API 프로토콜 시작하기

게시 됨: 2022-07-22

오늘날의 기술 환경에서 대부분의 프로젝트는 API를 사용해야 합니다. API는 복잡한 단일 시스템을 나타낼 수 있지만 별도의 시스템에 상주하거나 호환되지 않는 여러 네트워크 또는 언어를 사용할 수도 있는 서비스 간의 통신을 연결합니다.

많은 표준 기술이 REST, SOAP, GraphQL 또는 gRPC와 같은 분산 시스템의 서비스 간 통신 요구 사항을 해결합니다. REST가 선호되는 접근 방식이지만 gRPC는 고성능, 형식화된 계약 및 우수한 도구를 제공하는 가치 있는 경쟁자입니다.

REST 개요

REST(Representational State Transfer)는 서비스 데이터를 검색하거나 조작하는 수단입니다. REST API는 일반적으로 리소스를 선택하기 위해 URI를 사용하고 원하는 작업을 선택하기 위해 HTTP 동사(예: GET, PUT, POST)를 사용하여 HTTP 프로토콜을 기반으로 구축됩니다. 요청 및 응답 본문에는 해당 작업과 관련된 데이터가 포함되어 있으며 해당 헤더는 메타데이터를 제공합니다. 설명을 위해 REST API를 통해 제품을 검색하는 간단한 예를 살펴보겠습니다.

여기에서 ID가 11 인 제품 리소스를 요청하고 API가 JSON 형식으로 응답하도록 지시합니다.

 GET /products/11 HTTP/1.1 Accept: application/json

이 요청이 주어지면 우리의 응답(관련 없는 헤더 생략)은 다음과 같을 수 있습니다.

 HTTP/1.1 200 OK Content-Type: application/json { id: 11, name: "Purple Bowtie", sku: "purbow", price: { amount: 100, currencyCode: "USD" } }

JSON은 사람이 읽을 수 있지만 서비스 간에 사용할 때는 최적이 아닙니다. 압축된 경우에도 속성 이름을 참조하는 반복적인 특성으로 인해 메시지가 부풀려질 수 있습니다. 이 문제를 해결할 대안을 살펴보겠습니다.

gRPC 개요

gRPC 원격 프로시저 호출(gRPC)은 일련의 기능을 외부 클라이언트에 노출하여 서비스 간 통신을 단순화하고 관리하는 오픈 소스 계약 기반 교차 플랫폼 통신 프로토콜입니다.

HTTP/2를 기반으로 구축된 gRPC는 양방향 스트리밍 및 기본 제공 TLS(전송 계층 보안)와 같은 기능을 활용합니다. gRPC는 직렬화된 바이너리 페이로드를 통해 보다 효율적인 통신을 가능하게 합니다. REST의 JSON 사용과 유사하게 구조화된 데이터 직렬화를 위한 메커니즘으로 기본적으로 프로토콜 버퍼를 사용합니다.

그러나 JSON과 달리 프로토콜 버퍼는 직렬화된 형식 이상입니다. 여기에는 세 가지 다른 주요 부분이 포함됩니다.

  • .proto 파일에 있는 계약 정의 언어(최신 프로토콜 버퍼 언어 사양인 proto3를 따릅니다.)
  • 생성된 접근자 기능 코드
  • 언어별 런타임 라이브러리

서비스에서 사용할 수 있는 원격 기능( .proto 파일에 정의됨)은 프로토콜 버퍼 파일의 서비스 노드 내부에 나열됩니다. 개발자로서 우리는 프로토콜 버퍼의 풍부한 유형 시스템을 사용하여 이러한 기능과 해당 매개변수를 정의하게 됩니다. 이 시스템은 입력 및 출력 메시지를 정의하기 위해 다양한 숫자 및 날짜 유형, 목록, 사전 및 nullable을 지원합니다.

이러한 서비스 정의는 서버와 클라이언트 모두에서 사용할 수 있어야 합니다. 불행히도 .proto 파일 자체에 대한 직접 액세스를 제공하는 것 외에 이러한 정의를 공유하는 기본 메커니즘은 없습니다.

이 예제 .proto 파일은 ID가 주어지면 제품 항목을 반환하는 함수를 정의합니다.

 syntax = "proto3"; package product; service ProductCatalog { rpc GetProductDetails (ProductDetailsRequest) returns (ProductDetailsReply); } message ProductDetailsRequest { int32 id = 1; } message ProductDetailsReply { int32 id = 1; string name = 2; string sku = 3; Price price = 4; } message Price { float amount = 1; string currencyCode = 2; }
스니펫 1: ProductCatalog 서비스 정의

proto3의 엄격한 입력 및 필드 순서 지정은 JSON 구문 분석보다 메시지 역직렬화를 훨씬 덜 부담스럽게 만듭니다.

REST와 gRPC 비교

요약하자면 REST와 gRPC를 비교할 때 가장 중요한 점은 다음과 같습니다.

쉬다 gRPC
크로스 플랫폼
메시지 형식 사용자 정의이지만 일반적으로 JSON 또는 XML 프로토콜 버퍼
메시지 페이로드 크기 중/대 작은
처리 복잡성 더 높음(텍스트 구문 분석) 하위(잘 정의된 이진 구조)
브라우저 지원 예(네이티브) 예(gRPC-Web을 통해)

덜 엄격한 계약과 페이로드에 대한 빈번한 추가가 예상되는 경우 JSON 및 REST가 적합합니다. 계약이 더 정적으로 유지되는 경향이 있고 속도가 가장 중요한 경우 일반적으로 gRPC가 유리합니다. 내가 작업한 대부분의 프로젝트에서 gRPC는 REST보다 가볍고 성능이 뛰어난 것으로 입증되었습니다.

gRPC 서비스 구현

간소화된 프로젝트를 구축하여 gRPC를 채택하는 것이 얼마나 간단한지 살펴보겠습니다.

API 프로젝트 생성

시작하려면 Visual Studio 2022 VS(커뮤니티 에디션)에서 .NET 6 프로젝트를 만듭니다. ASP.NET Core gRPC 서비스 템플릿을 선택하고 프로젝트( InventoryAPI 사용) 와 프로젝트 내 첫 번째 솔루션 ( Inventory ) 의 이름을 모두 지정합니다.

Visual Studio 2022 내의 "새 프로젝트 구성" 대화 상자. 이 화면에서 프로젝트 이름 필드에 "InventoryAPI"를 입력하고 위치 필드에서 "C:\MyInventoryService"를 선택하고 솔루션 이름 필드에 "Inventory"를 입력했습니다. . "같은 디렉터리에 솔루션 및 프로젝트 배치"를 선택하지 않은 상태로 둡니다.

이제 를 선택합니다. 프레임워크에 대한 NET 6.0(장기 지원) 옵션:

Visual Studio 2022 내의 추가 정보 대화 상자. 이 화면의 프레임워크 드롭다운에서 ".NET 6.0(장기 지원)"을 선택했습니다. "Enable Docker"를 선택하지 않은 상태로 두었습니다.

제품 서비스 정의

이제 프로젝트를 만들었으므로 VS는 Greeter 라는 샘플 gRPC 프로토타입 정의 서비스를 표시합니다. 우리는 Greeter 의 핵심 파일을 우리의 필요에 맞게 재활용할 것입니다.

  • 계약을 생성하기 위해 greet.proto 의 내용을 Snippet 1로 바꾸고 파일 이름을 product.proto 로 변경합니다.
  • 서비스를 생성하기 위해 GreeterService.cs 파일의 내용을 Snippet 2로 바꾸고 파일 이름을 ProductCatalogService.cs 로 바꿉니다.
 using Grpc.Core; using Product; namespace InventoryAPI.Services { public class ProductCatalogService : ProductCatalog.ProductCatalogBase { public override Task<ProductDetailsReply> GetProductDetails( ProductDetailsRequest request, ServerCallContext context) { return Task.FromResult(new ProductDetailsReply { Id = request.Id, Name = "Purple Bowtie", Sku = "purbow", Price = new Price { Amount = 100, CurrencyCode = "USD" } }); } } }
스니펫 2: ProductCatalogService

이제 서비스에서 하드코딩된 제품을 반환합니다. 서비스가 작동하도록 하려면 새 서비스 이름을 참조하도록 Program.cs 에서 서비스 등록만 변경하면 됩니다. 우리의 경우 app.MapGrpcService<GreeterService>(); app.MapGrpcService<ProductCatalogService>(); 새로운 API를 실행할 수 있도록 합니다.

공정한 경고: 표준 프로토콜 테스트가 아닙니다.

시도해 보고 싶은 마음이 들 수도 있지만 엔드포인트를 겨냥한 브라우저를 통해 gRPC 서비스를 테스트할 수는 없습니다. 이것을 시도하면 gRPC 끝점과의 통신은 gRPC 클라이언트를 통해 이루어져야 한다는 오류 메시지를 받게 됩니다.

클라이언트 생성

서비스를 테스트하기 위해 VS의 기본 콘솔 앱 템플릿을 사용하고 API를 호출하는 gRPC 클라이언트를 만들어 보겠습니다. 내 이름을 InventoryApp 으로 지정했습니다.

편의를 위해 계약을 공유할 상대 파일 경로를 참조하겠습니다. 참조를 .csproj 파일에 수동으로 추가합니다. 그런 다음 경로를 업데이트하고 Client 모드를 설정합니다. 참고: 상대 참조를 사용하기 전에 로컬 폴더 구조에 익숙해지고 확신을 갖는 것이 좋습니다.

서비스 및 클라이언트 프로젝트 파일 모두에 나타나는 .proto 참조는 다음과 같습니다.

서비스 프로젝트 파일
(클라이언트 프로젝트 파일에 복사할 코드)
클라이언트 프로젝트 파일
(붙여넣기 및 편집 후)
 <ItemGroup> <Content Update="Protos\product.proto" GrpcServices="Server" /> </ItemGroup>
 <ItemGroup> <Protobuf Include="..\InventoryAPI\Protos\product.proto" GrpcServices="Client" /> </ItemGroup>

이제 서비스를 호출하기 위해 Program.cs 의 내용을 교체합니다. 우리 코드는 다음과 같은 많은 목표를 달성할 것입니다.

  1. 서비스 끝점의 위치를 ​​나타내는 채널을 만듭니다(포트는 다를 수 있으므로 실제 값은 launchsettings.json 파일 참조).
  2. 클라이언트 개체를 만듭니다.
  3. 간단한 요청을 구성합니다.
  4. 요청을 보냅니다.
 using System.Text.Json; using Grpc.Net.Client; using Product; var channel = GrpcChannel.ForAddress("https://localhost:7200"); var client = new ProductCatalog.ProductCatalogClient(channel); var request = new ProductDetailsRequest { Id = 1 }; var response = await client.GetProductDetailsAsync(request); Console.WriteLine(JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = true })); Console.ReadKey();
스니펫 3: 새 Program.cs

출시 준비

코드를 테스트하기 위해 VS에서 솔루션을 마우스 오른쪽 버튼으로 클릭하고 시작 프로젝트 설정 을 선택합니다. 솔루션 속성 페이지 대화 상자에서 다음을 수행합니다.

  • 다중 시작 프로젝트 옆에 있는 라디오 버튼을 선택하고 작업 드롭다운 메뉴에서 두 프로젝트 ( InventoryAPIInventoryApp )를 시작 으로 설정합니다.
  • 확인 을 클릭합니다.

이제 VS 도구 모음에서 시작 을 클릭하거나 F5 키를 눌러 솔루션을 시작할 수 있습니다. 두 개의 새 콘솔 창이 표시됩니다. 하나는 서비스가 수신 중임을 알려주고 다른 하나는 검색된 제품의 세부 정보를 표시합니다.

gRPC 계약 공유

이제 다른 방법을 사용하여 gRPC 클라이언트를 서비스 정의에 연결해 보겠습니다. 클라이언트가 가장 쉽게 접근할 수 있는 계약 공유 솔루션은 URL을 통해 정의를 사용할 수 있도록 하는 것입니다. 다른 옵션은 매우 취약하거나(경로를 통해 공유되는 파일) 더 많은 노력이 필요합니다(기본 패키지를 통해 공유되는 계약). URL을 통한 공유(SOAP 및 Swagger/OpenAPI가 하는 것처럼)는 유연하고 더 적은 코드가 필요합니다.

시작하려면 .proto 파일을 정적 콘텐츠로 사용할 수 있도록 설정하세요. 빌드 작업의 UI가 "Protobuf Compiler"로 설정되어 있으므로 코드를 수동으로 업데이트합니다. 이 변경으로 인해 컴파일러는 .proto 파일을 복사하여 웹 주소에서 제공할 수 있습니다. VS UI를 통해 이 설정을 변경하면 빌드가 중단됩니다. 그런 다음 첫 번째 단계는 InventoryAPI.csproj 파일에 Snippet 4를 추가하는 것입니다.

 <ItemGroup> <Content Update="Protos\product.proto"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup> <ItemGroup> <Content Include="Protos\product.proto" CopyToPublishDirectory="PreserveNewest" /> </ItemGroup>
스니펫 4: InventoryAPI 서비스 프로젝트 파일에 추가할 코드

다음으로 ProductCatalogService.cs 파일 상단의 Snippet 5에 코드를 삽입하여 .proto 파일을 반환하도록 끝점을 설정합니다.

 using System.Net.Mime; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.FileProviders;
스니펫 5: Namespace 가져오기

이제 ProductCatalogService.cs 파일에서도 app.Run() 바로 앞에 Snippet 6을 추가합니다.

 var provider = new FileExtensionContentTypeProvider(); provider.Mappings.Clear(); provider.Mappings[".proto"] = MediaTypeNames.Text.Plain; app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(Path.Combine(app.Environment.ContentRootPath, "Protos")), RequestPath = "/proto", ContentTypeProvider = provider }); app.UseRouting();
스니펫 6: API를 통해 .proto 파일에 액세스할 수 있도록 하는 코드

Snippets 4-6이 추가되면 .proto 파일의 내용이 브라우저에 표시되어야 합니다.

새로운 테스트 클라이언트

이제 VS의 종속성 마법사를 사용하여 기존 서버에 연결할 새 콘솔 클라이언트를 만들고 싶습니다. 문제는 이 마법사가 HTTP/2를 사용하지 않는다는 것입니다. 따라서 HTTP/1을 통해 통신하고 서버를 시작하도록 서버를 조정해야 합니다. 이제 서버에서 .proto 파일을 사용할 수 있으므로 gRPC 마법사를 통해 서버에 연결하는 새 테스트 클라이언트를 빌드할 수 있습니다.

  1. HTTP/1을 통해 통신하도록 서버를 변경하려면 appsettings.json JSON 파일을 편집합니다.
    1. Https 를 읽도록 Protocol 필드( Kestrel.EndpointDefaults.Protocols 경로에 있음) 를 조정합니다.
    2. 파일을 저장합니다.
  2. 새 클라이언트가 이 proto 정보를 읽으려면 서버가 실행 중이어야 합니다. 원래 VS의 Set Startup Projects 대화 상자에서 이전 클라이언트와 서버를 모두 시작했습니다. 서버 프로젝트만 시작하도록 서버 솔루션을 조정한 다음 솔루션을 시작합니다. (이제 HTTP 버전을 수정했으므로 이전 클라이언트는 더 이상 서버와 통신할 수 없습니다.)
  3. 다음으로 새 테스트 클라이언트를 만듭니다. VS의 다른 인스턴스를 시작합니다. API 프로젝트 생성 섹션에 자세히 설명된 단계를 반복하지만 이번에는 콘솔 앱 템플릿을 선택하겠습니다. 프로젝트 및 솔루션의 이름을 InventoryAppConnected 로 지정하겠습니다.
  4. 클라이언트 섀시가 생성되면 gRPC 서버에 연결합니다. VS 솔루션 탐색기에서 새 프로젝트를 확장합니다.
    1. 종속성 을 마우스 오른쪽 버튼으로 클릭하고 상황에 맞는 메뉴에서 연결된 서비스 관리 를 선택합니다.
    2. 연결된 서비스 탭에서 서비스 참조 추가 를 클릭하고 gRPC 를 선택합니다.
    3. 서비스 참조 추가 대화 상자에서 URL 옵션을 선택하고 서비스 주소의 http 버전을 입력합니다( launchsettings.json 에서 임의로 생성된 포트 번호를 가져와야 함을 기억하십시오) .
    4. 마침 을 클릭하여 쉽게 유지 관리할 수 있는 서비스 참조를 추가합니다.

이 예제의 샘플 코드와 비교하여 작업을 자유롭게 확인하십시오. 내부적으로 VS는 첫 번째 테스트에서 사용한 것과 동일한 클라이언트를 생성했기 때문에 이전 서비스의 Program.cs 파일 내용을 그대로 재사용할 수 있습니다.

계약을 변경할 때 업데이트된 .proto 정의와 일치하도록 클라이언트 gRPC 정의를 수정해야 합니다. 그렇게 하려면 VS의 연결된 서비스에 액세스하고 관련 서비스 항목을 새로 고치기만 하면 됩니다. 이제 gRPC 프로젝트가 완료되었으며 서비스와 클라이언트를 쉽게 동기화할 수 있습니다.

차기 프로젝트 후보: gRPC

우리의 gRPC 구현은 gRPC 사용의 이점을 직접 엿볼 수 있도록 합니다. REST 및 gRPC는 각각 계약 유형에 따라 이상적인 사용 사례를 가지고 있습니다. 그러나 두 옵션이 모두 적합하다면 gRPC를 사용해 보시기 바랍니다. 그러면 API의 미래에 앞서 나갈 수 있습니다.