June 04, 2023
gRPC는 google에서 개발한 RPC(Remote Procedure Call) 프레임워크이다. RPC는 분산 시스템에서 프로시저 호출을 위한 프로토콜을 의미한다. gRPC는 Protocol Buffers라는 바이너리 직렬화 포맷을 사용해 효율적이고 경량화된 네트워크 통신을 지원하며, 대규모 분산 시스템에서 서비스 간의 통신을 구현할 때 사용된다.
이전에 주로 REST를 사용하다 gRPC를 사용하게 되며 알게 된 것들을 정리해보려 한다.
둘 다 웹 서비스를 구축하기 위한 프로토콜이지만 다음과 같은 차이가 있다.
@bufbuild/connect-web
@bufbuild/connect-web
에서 다음의 함수와 객체를 사용해 통신한다.
다음은 실존하지 않는 임의의 데이터를 gRPC로 호출하는 예시이다. https://grpc.foo.com url에서 호출할 수 있는 PaymentService에 속해있는 listPaymentItem를 호출한다. 멤버 토큰을 헤더에 넣어 호출하고, 토큰을 얻는 함수는 utils 폴더에 있다고 가정했다.
다음과 같이 통신을 위한 transport를 생성하고, Promise 기반의 클라이언트를 사용해 통신을 할 수 있다.
import { createGrpcWebTransport, createPromiseClient } from '@bufbuild/connect-web'
import type { CallOptions } from '@bufbuild/connect-web'
import { getMemberToken } from '@src/utils'
// ...
// header에 넣은 멤버 토큰을 가져오는 코드이다.
const memberToken = getMemberToken()
// 사용하는 grpc url를 넣어 gRPC 통신을 위한 transport를 생성한다.
const transport = createGrpcWebTransport({
baseUrl: "https://grpc.foo.com"
})
// 호출에 필요한 옵션을 공통으로 정의한다.
const options: CallOptions = {
header: {
'Content-Type': 'application/json',
Authorization: memberToken
}
}
// Promise 기반의 클라이언트를 생성한다.
const client = createPromiseClient(PaymentService, transport)
// PaymentItems에 있는 listPaymentItems에 ListPaymentRequest type의 파라미터를 전달해 호출한다.
const listPaymentItems = (param: ListPaymentRequest) =>
client.listPaymentItems(
{ param },
options
)
예시 작성을 위해 하나의 파일로 작성했지만 공통으로 사용되는 transport와 options 등은 common.ts
같은 이름의 파일에 분리해서 사용하곤 했다.
이전에 REST 사용 시에는 api 호출에 필요한 Request와 Response 타입을 직접 정의해서 사용하곤 했었는데, 타입 변경이 있을 경우 공유가 없었을 경우에는 에러 발생 시 이유를 알지 못해 헤매기도 했었다. 하지만 gRPC를 사용하니 buf 버전 업데이트만 하면 최신화된 proto를 알 수 있어 편했다. 그리고 이전에는 문서화를 직접 해서 문서를 항상 최신 상태로 유지하는 데 어려움이 있었는데, https://buf.build/ 에서 문서를 볼 수 있어 좋았다. 공통으로 사용하는 proto인 경우에는 연관되어 있는 buf를 업데이트하지 않으면 문서와 상이한 경우도 있었지만 아주 드문 경우여서 거의 최신의 상태를 확인할 수 있는 것이 장점이다.