API 아키텍처에는 Soap(비누 아님), Graph QL, REST, gRPC 등이 있다
이 글에서는 Node.js 서버에서 gRPC로 서버간 데이터를 전달한 과정을 적었다
공식문서를 처음 읽어보고 관련 유튜브 영상을 보다가 한 인도 개발자의 영상을 보면서 코드를 작성해서 성공했습니다
구글에서 만든 확장가능하고 빠른 API를 만드는 데 사용되는 오픈소스 RPC(Remote Procedure Call) 프레임워크로 프로토콜 버퍼라는 데이터를 전달
구글에서 개발하였으머, 구조화된 데이터를 원하는 개발언어에 맞게 효과적으로 직렬화할 목적으로 개발된 성능위주로 만들어진 구현체이다.
(Json보다 간결하고 살짝 더 빠르다고 함)
REST API는 많은 응용 프로그램을 처리하기 위한 형식을 (GET, POST, PUT) 제공했
음에도 많은 양의 메타데이터를 생성해서 가벼운 RPC를 완전히 대체할 수 없었음
MSA 환경에서 서버 간 통신에 많이 사용된다 (서버가 다른 언어로 되어 있어도 즉 Java로 만든 서버와 Node.js로 만든 서버 간 통신 가능)
저는 일단 두 개의 Node.js 프로젝트를 npm init으로 만들었습니다.
그 후 example.proto 라는 이름의 파일을 만든 후 아래와 같이 작성했습니다.
package는 안적어도 되지만, 뒤에 ; 안 붙이면 에러납니다.
syntax = "proto3";
package oepackage;
service UserService {
rpc addUser (User) returns (User);
rpc getUsers (Empty) returns (Users);
}
message Empty {
}
message User {
string name = 1;
string email = 2;
int32 age = 3;
}
message Users {
repeated User users = 1;
}
참고로 protofile 에서 syntax = "proto3" 은 버전인데, proto2 는 C++, Java, Python, Go를 지원하고 proto3은 2버전에서 지원하는 언어와 함께 Ruby, Objective-C, C#, JavaScript, Dart, PHP를 지원합니다.
또한 string name = 1; 이렇게 = 뒤에 나오는 숫자는 일종의 식별자로써의 역할을 합니다. (구분해주는 역할 같음)
package 아래에 service는 클라이언트에게 제공할 함수의 형태를 작성하는 부분입니다. rpc 함수의 이름 (보내는 데이터 형태) returns (받을 데이터 형태) 와 같이 작성합니다.
그리고 위의 코드 중
message Users {
repeated user users = 1;
}
에서 repeated는 임의의 반복가능한 필드라는 의미이고, repeated 대신에 required (필수인 필드), optional (해당 필드를 가지지 않아도 되거나 한 개만 가짐) 을 적을 수 있습니다.
proto 파일은 두 프로젝트에 똑같이 있어야합니다.
npm install @grpc/grpc-js
위 명령어로 grpc-js 모듈과
npm install @grpc/proto-loader
위 명령어로 gRPC Protobuf Loader 모듈을 설치합니다
const PROTO_PATH = './proto/example.proto'
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{keepCase: true,
longs: String,
enums: String,
arrays: true,
});
const UserService = grpc.loadPackageDefinition(packageDefinition).oepackage.UserService;
export const client = new UserService(
"localhost:3000",
grpc.credentials.createInsecure()
);
client.getUsers({},(err: any,users:any)=>{
if(err) throw err;
console.log(users)
})
타입스크립트로 작성했는데 tsc 해서 실행할 때는 자바스크립트 파일을 실행했습니다.
아래는 grpc-server 파일 코드입니다.
const PROTO_PATH = './proto/example.proto'
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const packageDefinition = protoLoader.loadSync(
PROTO_PATH,
{keepCase: true,
longs: String,
enums: String,
arrays: true
});
const userProto = grpc.loadPackageDefinition(packageDefinition).oepackage;
const server = new grpc.Server()
const users = [{
name: "Jay",
email : "test@gmail.com",
age: 12
}]
server.addService(userProto.UserService.service ,{
getUsers : (_, callback) => {
callback(null, {users})
},
addUser : (call, callback)=> {
const user = call.request;
users.push(user)
callback(null,user)
},
})
server.bindAsync("localhost:3000", grpc.ServerCredentials.createInsecure(),()=>{
console.log("server started on localhost:3000")
server.start()
})
저는 일단 테스트할 때 loacalhost:3000에서 테스트 해서 위와 같이 적었는데, 배포할 때는 보통 0.0.0.0:3000과 같이 포트 번호를 적어줍니다. (0.0.0.0 은 모든 IP 주소에서 오는 것을 허용하겠다는 뜻)
TypeScript를 사용하는 tRPC라는 것도 있는데, tRPC 는 RPC가 아닌 procedure라는 일종의 함수를 사용해서 하는 RPC를 흉내낸 것이었습니다...!
https://grpc.io/docs/languages/node/quickstart/
https://blog.naver.com/jhc9639/222642712063
스마일게이트 윈터데브캠프 강의