Javascript 기반으로 브라우저에서 gRPC를 사용하려고 하였지만, 적용하지 못한 내용에 대한 기록입니다.
gRPC에 대해서 간략히 설명하고 gRPC 라이브러리를 이용하여 node.js 기반의 Application을 만드는 과정,
그리고 브라우저와 back-end 사이에 grpc-web을 사용하여 직접 통신할 수 없는 이유를 작성하였습니다.
gRPC는 Google이 개발한 고성능 오픈소스 범용 RPC 프레임워크입니다. gRPC에서 클라이언트 애플리케이션은 다른 머신의 서버 애플리케이션에 있는 메소드를 로컬 객체처럼 직접 호출할 수 있으므로 분산 애플리케이션 및 서비스를 더 쉽게 만들 수 있습니다.
RPC는 네트워크 상 원격에 있는 서버의 서비스를 호출하는데 사용되는 프로토콜로 IDL(Interface Definition Language)로 인터페이스를 정의한 후 이에 해당하는 Skeleton 과 Stub 코드를 통해 프로그래밍 언어에서 호출해서 사용하는 방식입니다. 최근에는 HTTP를 활용한 RESTful이 많이 활용되어서 RPC는 거의 사용이 되지 않으나 요청/응답을 위한 규약이 명시적이지 않다는 단점으로 인해 다시 RPC의 방식을 채용한 프레임워크들이 나오기 시작했습니다.
gRPC는 자바, C/C++ 뿐만 아니라 Node.js, Python, Ruby, PHP, Go, Android 기반, Objective-C 등 다양한 언어들을 지원함으로 서버간 뿐만이 아니라 클라이언트 어플리케이션이나 모바일 앱에서도 사용이 가능합니다.
gRPC는 기존 RPC와 마찬가지로 IDL(Interface Definition Language)을 기반으로 원격으로 프로시저를 호출할 수 있는 방법을 제공합니다. 기본적으로 gRPC는 서비스 인터페이스와 페이로드 메시지의 구조를 표현하기 위해 Protocol Buffers를 IDL로 사용합니다.
JSON등 다른 직렬화 방식을 사용할 수도 있습니다.
Protocol Buffers는 구조화된 데이터를 직렬화하여 압축하기 때문에 JSON, XML에 비해 전송 속도가 빠릅니다
gRPC를 사용하면 아래 네 가지 서비스를 사용할 수 있습니다.
/* gRPC 구현을 위한 proto 파일 작성 예시 */
/* hello.proto */
//
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
rpc SayHello(HelloRequest) returns (HelloResponse){}
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){}
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {}
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){}
gRPC-WEB은 gRPC를 브라우저에서 사용가능하도록 브라우저 지원을 추가한 스펙입니다. Google 팀과 Improbable 팀이 2016년부터 독립적으로 구현을 진행하다가 새로운 프로토콜 스펙의 정의를 위해 함께 모여서 만들게 되었습니다.
gRPC-WEB 사양은 HTTP/1.1 과 HTTP/2를 모두 지원하지만, 현재 브라우저 API가 HTTP/2 엑세스를 지원하지 않기 때문에 gRPC-WEB을 사용하여 브라우저에서 서버로 직접 통신을 할 수는 없습니다. 하지만 응답 변환 Proxy 서버를 통해 요청 및 응답을 브라우저가 처리할 수 있도록 변환 할 수 있습니다.
Google 팀과 Improbable 팀은 독립적으로 각각의 저장소에 다른 방식으로 구현하였으며, 스펙도 완전히 준수하지는 않았습니다.
Improbable 팀은 Typescript 기반으로 @improbable-eng/grpc-web을 구현하였으며, Google팀은 Google의 클로저 라이브러리 베이스로 Javascript 기반의 grpc-web을 구현하였습니다.
Google의 grpc-web은 grpcwebtext 모두와 함께 사용하는 경우만 서버측 스트리밍을 지원합니다.
Improbable 팀의 양방향 스트리밍은 실험적인 기능이므로 상용 제품에 사용하기에는 문제가 있을 수 있습니다.
Google에서는 Envoy Proxy를 공식적인 기본 솔루션으로 홍보하고 있으며, Improbable 팀의 프록시는 실험적이라고 얘기하고 있습니다.
grpc 라이브러리를 사용하는 node.js 애플리케이션 작성 예제입니다. (grpc-web 동작 안함...😭)
protocol buffers를 사용을 위한 IDL을 작성합니다.
// helloworld.proto
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
$ protoc --proto_path=.\hello.proto --js_out=import_style=commonjs,binary:.\dist --plugin=protoc-gen-grpc-web=.
\plugin\protoc-gen-grpc-web.exe --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.\dist
/* Server */
/* : node server.js */
var messages = require('./dist/helloworld_pb');
var services = require('./dist/helloworld_grpc_pb');
var grpc = require('grpc');
function sayHello(call, callback) {
var reply = new messages.HelloReply();
reply.setMessage('Hello ' + call.request.getName());
callback(null, reply);
}
function main() {
var server = new grpc.Server();
console.log('Hello Server');
server.addService(services.GreeterService, { sayHello: sayHello });
server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure());
server.start();
}
main();
/* Client */
/* : node client.js */
var messages = require('./helloworld_pb');
var services = require('./helloworld_grpc_pb');
var grpc = require('grpc');
function main() {
var client = new services.GreeterClient('localhost:50051',
grpc.credentials.createInsecure());
var request = new messages.HelloRequest();
var user;
if (process.argv.length >= 3) {
user = process.argv[2];
} else {
user = 'world';
}
request.setName(user);
client.sayHello(request, function(err, response) {
console.log('Greeting:', response.getMessage());
});
}
main();
gRPC의 사용은 HTTP/2 기반에서 얻을 수 있는 헤더 압축, 단일 TCP Connection의 병렬 처리, 보안기능등으로 많은 성능상의 이점을 얻을 수 있습니다. 또한 Protocol Buffers를 사용하므로 데이터의 압축율을 크게 향상시킬 수 있습니다. 이러한 이점으로 gRPC는 아래와 같은 시나리오에서 적합하다고 합니다.
JavaScript Generated Code: https://developers.google.com/protocol-buffers/docs/reference/javascript-generated
protoc 컴파일러: https://github.com/protocolbuffers/protobuf/releases
protoc-gen-grpc-web 플러그인: https://github.com/grpc/grpc-web/releases
https://developers.google.com/web/fundamentals/performance/http2/?hl=ko
https://docs.microsoft.com/ko-kr/aspnet/core/grpc/comparison?view=aspnetcore-3.
https://medium.com/@goinhacker/microservices-with-grpc-d504133d191d
https://medium.com/@aravindhanjay/a-todo-app-using-grpc-web-and-vue-js-4e0c18461a3e
https://grpc.io/blog/grpc-web-ga/
https://grpc.io/blog/state-of-grpc-web/
https://github.com/grpc/grpc-web/issues/
https://github.com/grpc/grpc-web/issues/
https://grpc.github.io/grpc/node/grpc.Server.html
https://www.freecodecamp.org/news/how-to-use-grpc-web-with-react-1c93feb691b5/
https://arisu1000.tistory.com/
https://medium.com/@goinhacker/grpc-more-practical-story-87765af3b21d
https://morioh.com/p/ae48b33d10a
안녕하세요. 혹시 브라우저와 back-end 사이에 grpc-web을 사용하여 직접 통신할 수 없는 이유를 알 수 있을가요...?