gRPC로 Node.js 서버 간 통신

strider·2023년 11월 17일
0

API 아키텍처에는 Soap(비누 아님), Graph QL, REST, gRPC 등이 있다
이 글에서는 Node.js 서버에서 gRPC로 서버간 데이터를 전달한 과정을 적었다
공식문서를 처음 읽어보고 관련 유튜브 영상을 보다가 한 인도 개발자의 영상을 보면서 코드를 작성해서 성공했습니다

gRPC 란?

구글에서 만든 확장가능하고 빠른 API를 만드는 데 사용되는 오픈소스 RPC(Remote Procedure Call) 프레임워크로 프로토콜 버퍼라는 데이터를 전달

프로토콜 버퍼란?

구글에서 개발하였으머, 구조화된 데이터를 원하는 개발언어에 맞게 효과적으로 직렬화할 목적으로 개발된 성능위주로 만들어진 구현체이다.

(Json보다 간결하고 살짝 더 빠르다고 함)

gRPC 사용하는 이유

REST API는 많은 응용 프로그램을 처리하기 위한 형식을 (GET, POST, PUT) 제공했
음에도 많은 양의 메타데이터를 생성해서 가벼운 RPC를 완전히 대체할 수 없었음

MSA 환경에서 서버 간 통신에 많이 사용된다 (서버가 다른 언어로 되어 있어도 즉 Java로 만든 서버와 Node.js로 만든 서버 간 통신 가능)

서버간 데이터 전달 방법

저는 일단 두 개의 Node.js 프로젝트를 npm init으로 만들었습니다.

1. Proto 파일 만들기

그 후 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 파일은 두 프로젝트에 똑같이 있어야합니다.

2. 모듈 설치하기

npm install @grpc/grpc-js

위 명령어로 grpc-js 모듈과

npm install @grpc/proto-loader

위 명령어로 gRPC Protobuf Loader 모듈을 설치합니다

3. 코드 작성


grpc-client 코드는 아래와 같습니다.
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
스마일게이트 윈터데브캠프 강의

profile
성장하고픈 개발자

0개의 댓글