[이론] 유니티에서 gRPC 사용하기 1 - gRPC의 개념
gRPC란 무엇인가? 배경, 프로토콜 버퍼, 통신 패턴 요약
ASP.NET 과 gRPC를 기반으로 간단한 로그인 인증서버를 만들고 이를 유니티와 연동을 하는 작업을 진행중이다. 이때 유니티에서 gRPC를 사용하는 과정과 약간의 삽질(?) 같은 것을 포스팅하고자 한다. 우선 첫번째 포스팅은 gRPC가 무엇인지에 대해 소개를 하는 포스팅이다.
gRPC란?
현대의 분산 시스템 환경에서 gRPC(Google Remote Procedure Call)는 고성능 원격 프로시저 호출(RPC) 프레임워크로서 핵심적인 위치를 차지하고 있다.
gRPC 이전의 통신 환경
IPC 기술은 저수준의 소켓(Socket) 통신에서부터 시작해, 원격지의 함수를 로컬 함수처럼 호출할수 있도록 추상화한 RPC(Remote Procedural Call) 프레임워크로 발전했다. 또한 웹에서는 HTTP 프로토콜을 기반으로 하는 REST(Representational State Transfer)가 웹 API의 사실상 표준으로 자리잡았다. REST는 단순성, 유연성, 그리고 HTTP의 보편성을 무기로 웹 생태계를 장악했지만, 대규모 분산 시스템의 내부 통신에서는 그 한계를 드러내기 시작한다.
구글의 하이퍼스케일 당면 과제
2017년 기준으로 구글은 일주일에 20억 개의 컨테이너를 실행하고, 초당 100억 건에 달하는 원격 호출을 처리했다. 이러한 환경에서는 기존 통신 프레임워크의 사소한 비효율성조차 시스템 전체에 막대한 병목 현상을 유발했다.
예를들어 REST에서 주로 사용하는 JSON 페이로드는 직렬화.역직렬화 과정에서 상당한 CPU 자원을 소모했으며, HTTP/1.1의 연결 관리 방식은 수많은 마이크로서비스 간의 통신에서 심각한 지연 시간을 발생시켰다. 이러한 내부적 필요성이 기술 선택의 방향을 결정했다.
스터비(Stubby)
이러한 문제에 대한 구글의 해답이 바로 스터비(Stubby)였다. 스터비는 10년 이상 구글 내부에서 데이터센터 내 수많은 마이크로서비스를 연결하는데 사용된 범용 RPC 프레임워크였다. 수많은 내부 서비스를 통해 검증된 스터비는 구글의 하이퍼스케일 환경에서 요구되는 성능과 안정성을 완벽하게 만족시켰다. 하지만 스터비는 구글의 독자적인 내부 인프라와 매우 긴밀하게 결합되어있었기 때문에 외부 개발자들이 사용할 수 있도록 오픈소스로 공개되기에는 부적합했다.
gRPC의 탄생
구글은 스터비의 핵심 원리를 가져오면서도 전송 계층으로는 HTTP/2를 채택하고, 인터페이스 정의 언어(IDL)로는 프로토콜 버퍼(Protocol Buffers)를 표준으로 삼았다. 이 결정은 스터비가 가진 성능상의 이점을 유지하면서도, 기업 의존성을 제거하고 범용성을 확보하기위한 전략적 선택이였다. 2016년 8월, gRPC 1.0.0 버전이 공식 출시되면서, 고성능 오픈소스 RPC 프레임워크가 세상에 공개되었다.
proto 파일
gRPC 설계는 서비스정의에서 시작된다. 이는 .proto 확장자를 가진 파일을 통해 이뤄지며, 이 파일은 클라이언트와 서버 간의 엄격하고 언어에 구애받지 않는 계약 역할을 한다. gRPC는 기본적으로 프로토콜 버퍼를 인터페이서 정의 언어(IDL)로 사용하여, 호출 가능한 원격 메서드, 매개변수, 그리고 반환 타입을 명확하게 기술한다. 이처럼 계약을 먼저 정의하는 “Contract First” 접근 방식은 gRPC 개발 워크플로우의 근간을 이룬다.
스텁과 스켈레톤
.proto 파일에 정의된 계약은 프로토콜 버퍼 컴파일러(protoc)와 각 언어에 특화된 gRPC 플러그인을 통해 자동으로 코드로 변환된다. 이 과정에서 개발에 필요한 상용구 코드가 생성되어 개발자는 비즈니스 로직에만 집중할수 있다.
클라이언트 스텁(Client Stubs): 클라이언트 측에서는 원격 서비스의 메서드를 그대로 복제한 로컬 객체인 “스텁(stub)”이 생성된다. 개발자가 이 스텁의 메서드를 호출하면, 스텁은 요청 매개변수를 프로토콜 버퍼의 바이너리 형식으로 직렬화하고, 네트워크를 통해 서버로 전송하며, 서버로부터 받은 응답을 다시 역질렬화하여 애플리케이션에 반환하는 모든 과정을 추상화한다.
서버 스켈레톤(Server Skeletons): 서버 측에서는 “스켈레톤(skeleton)” 또는 서비스 기반 구현체라 불리는 추상 기본 클래스나 인터페이스가 생성된다. 개발자는 이 스켈레톤을 상속받아 각 서비스 메서드의 실제 비즈니스 로직을 구현한다. gRPC 서버 런타임은 네트워크로부터 들어온 바이너리 요청을 역직렬화하고, 이 스켈레톤을 통해 해당 요청을 개발자가 구현한 올바른 비즈니스 로직으로 라우팅을 하는 역할을 담당한다.
채널: 연결 통로
gRPC 채널(Channel)은 특정 호스트와 포트로 지정된 gRPC 서버 엔드포인트에 대한 논리적인 연결을 나타낸다. 채널은 재사용을 위해 설계된 핵심구성요소이다. 채널 하나는 내부적으로 하나 이상의 안정적인 HTTP/2 연결을 캡슐화하며, 이를 통해 여러개의 동시 RPC 호출이 효율적으로 다중화될수 있도록 지원한다.
프로토콜 버퍼(Protobuf)
- IDL 및 스키마:
.proto
파일에서syntax = "proto3";
,package
,option
으로 기본 구성을 정한다.- 데이터 구조는
message
, 열거형은enum
, 서비스는service
블록으로 정의한다. - 각 필드는 고유한 필드 번호(tag)를 갖고, 이 번호가 직렬화 포맷의 핵심이다.
- 스키마 진화: 기존 필드 번호 재사용 금지, 삭제 대신
reserved
처리, 선택 필드 추가는 호환된다.
- 바이너리 직렬화 매커니즘:
- 각 필드는 “필드 번호 + wire type” 헤더와 값으로 기록된다.
- 정수는 varint로, 음수는 zigzag 인코딩으로 효율적으로 저장된다.
- 길이 지정 타입(length‑delimited)은 문자열, 바이트, 중첩 메시지에 쓰인다.
- 알 수 없는 필드는 건너뛰거나 보존되어 상/하위 호환성이 유지된다.
- 성능 특성 바이너리 형식은 크기가 작을 뿐만아니라 파싱 속도도 월등히 빠르다. 스키마가 사전정의되어 있어서, 복잡한 파싱 과정없이 메모리에 직접 매핑하는 수준의 역직렬화가 가능하다.
- 네트워크 대역폭과 메모리 사용량이 작아지고, GC 압력도 낮다.
- JSON/XML과의 비교 프로토콜 버퍼는 기계 간 통신 효율을 최우선으로 설계됐다. JSON/XML은 사람이 읽고 디버깅하기 좋지만, 그만큼 페이로드가 크고 전송/파싱 비용이 높다. 프로토콜 버퍼는 엄격한 스키마와 타입 안정성, 스키마 진화 전략(reserved/optional)을 내장하고, 사람이 읽는 디버깅은 JSON 매핑이나 텍스트 포맷을 따로 사용하면 된다.
HTTP/2: 현대적인 전송 계층
HTTP/1.1은 요청마다 새로운 TCP 연결을 맺거나 파이프라이닝의 한계에 부딪히는 경우가 많았다. 반면, HTTP/2는 클라이언트와 서버 간에 단일 TCP 연결을 맺고 이를 오랫동안 유지한다.
- 단일 영구 연결: 하나의 TCP 연결을 오래 유지하면서 그 위에서 모든 요청/응답을 처리한다. 연결 생성/해제 오버헤드가 크게 줄어든다.
- 스트림과 다중화: gRPC가 적극 활용하는 핵심이다. 단일 연결 위에 여러 개의 독립적·양방향 스트림을 동시에 흘릴 수 있고, 각 RPC 호출은 하나의 스트림에 매핑된다. 덕분에 여러 호출이 병렬 처리되며, HTTP/1.1에서 자주 문제였던 Head-of-Line(HOL) 블로킹을 구조적으로 피한다.
- 바이너리 프레이밍: HTTP/2는 텍스트가 아닌 바이너리 프로토콜이다. 모든 데이터가 길이가 명시된 프레임으로 쪼개져 전송되며, 파싱 비용이 낮고 오류 가능성도 줄어든다.
- 헤더 압축(HPACK): 반복되는 헤더(:method, user-agent 등)를 효율적으로 압축한다. 마이크로서비스처럼 호출이 잦고 페이로드가 작은 환경에서 호출당 오버헤드를 크게 낮춘다.
호출 유형 요약
호출 유형 | 클라이언트 전송 | 서버 전송 |
---|---|---|
단항(Unary) | 메시지 1개 | 메시지 1개 |
서버 스트리밍 | 메시지 1개 | 메시지 스트림 |
클라이언트 스트리밍 | 메시지 스트림 | 메시지 1개 |
양방향 스트리밍 | 메시지 스트림 | 메시지 스트림 |
1.단항 RPC (Unary RPC)
정의: rpc MethodName(Request) returns (Response) {}
흐름: 클라이언트가 단일 요청 메시지를 보내면 서버가 단일 응답 메시지를 반환하는 가장 기본적인 형태다. 이는 일반적인 함수 호출과 매우 유사하다.
사용 사례: 단일 작업이 단일 결과를 생성하는 대부분의 표준 API 호출에 적합하다. 예를 들어, ID로 사용자 프로필 조회, 새로운 게시물 작성, 사용자 인증과 같은 작업이 여기에 해당한다.
해결하는 문제: 내부 서비스 간 통신에서 기존 REST의 GET, POST, PUT과 같은 요청-응답 패턴을 훨씬 높은 성능과 강력한 타입 시스템으로 대체한다.
2.서버 스트리밍 RPC (Server Streaming RPC)
정의: rpc MethodName(Request) returns (stream Response) {}
흐름: 클라이언트가 단일 요청을 보내면, 서버는 응답으로 메시지의 시퀀스를 스트림을 통해 지속적으로 전송한다. 클라이언트는 서버가 스트림을 닫을 때까지 메시지를 수신한다.
사용 사례: 서버가 클라이언트에게 여러 개의 업데이트를 푸시해야 하는 시나리오에 이상적이다. 예를 들어, 실시간 알림 구독, 주식 시세 데이터의 라이브 피드 수신, 또는 대용량 데이터셋을 메모리 부담 없이 여러 조각으로 나누어 전송하는 경우에 활용된다.
해결하는 문제: 클라이언트가 주기적으로 서버의 상태 변화를 확인하는 비효율적인 폴링(polling) 방식을 제거하고, 서버 주도의 효율적인 데이터 푸시를 가능하게 한다.
3.클라이언트 스트리밍 RPC (Client Streaming RPC)
정의: rpc MethodName(stream Request) returns (Response) {}
흐름: 클라이언트가 스트림을 통해 여러 개의 메시지를 순차적으로 서버에 전송한다. 클라이언트가 메시지 전송을 완료하면, 서버는 전체 스트림을 처리한 후 단일 응답을 반환한다.
사용 사례: 클라이언트에서 서버로 대량의 데이터를 효율적으로 전송할 때 사용된다. 대용량 파일 청크 업로드, IoT 센서 데이터의 스트리밍 전송, 클라이언트 측 로그를 일괄적으로 수집하는 데이터 수집 파이프라인 등이 대표적인 예다.
해결하는 문제: 잠재적으로 매우 큰 단일 요청을 서버 메모리에 한 번에 버퍼링해야 하는 부담을 없애고, 데이터가 도착하는 대로 처리할 수 있게 해준다.
4.양방향 스트리밍 RPC (Bidirectional Streaming RPC)
정의: rpc MethodName(stream Request) returns (stream Response) {}
흐름: 클라이언트와 서버 양측이 모두 스트림을 설정하고, 서로 독립적이고 비동기적으로 메시지를 주고받는다. 두 스트림은 완전한 전이중(full-duplex) 방식으로 작동한다.
사용 사례: gRPC의 가장 강력한 통신 패턴으로, 복잡하고 실시간적인 대화형 상호작용을 위해 설계되었다. 실시간 채팅 애플리케이션, 온라인 멀티플레이어 게임의 상태 동기화, 대화형 명령어를 처리하고 결과를 비동기적으로 반환하는 서비스 등에 사용된다.
해결하는 문제: 웹소켓(WebSocket)과 같은 별도의 프로토콜 없이도, 단일 연결 내에서 오버헤드가 적은 양방향 실시간 통신 세션을 구현할 수 있게 해준다.
gRPC 시각화
gRPC - REST 비교
특징 | gRPC | REST |
---|---|---|
패러다임 | RPC (서비스/행위 지향) | 자원 지향 |
전송 프로토콜 | HTTP/2 | 주로 HTTP/1.1 (HTTP/2 가능) |
페이로드 형식 | 프로토콜 버퍼 (바이너리) | 주로 JSON (텍스트) |
스키마/계약 | .proto 파일에 엄격히 정의 (IDL) | 느슨하게 정의 |
결합도 | 강한 결합 | 느슨한 결합 |
코드 생성 | 내장된 핵심 기능 | 서드파티 도구 필요 (Swagger / Codegen) |
스트리밍 | 단항, 서버, 클라이언트, 양방향 | 요청-응답 (단항) / 스트리밍 별도 구현 필요 |
성능 | 높은 처리량, 낮은 지연 시간 | 상대적으로 낮은 성능, 높은 지연 시간 |
브라우저 지원 | gRPC-Web 및 프록시 필요 | 네이티브, 범용 지원 |
가독성 | 없음 (바이너리 페이로드) | 높음 (JSON 페이로드) |
주요 사용 사례 | 내부 마이크로서비스 통신 | 공개 웹 API |
참고자료
- https://grpc.io/docs/what-is-grpc/
- https://livlikwav.github.io/study/grpc-and-its-history
- https://velog.io/@soshin_dev/gRPC-%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C
- https://jadhavsaurabh037.medium.com/grpc-deep-dive-introduction-to-grpc-5aa37243c132
- https://learn.microsoft.com/en-us/aspnet/core/grpc/performance?view=aspnetcore-9.0
- https://www.youtube.com/watch?v=VBtwIkE-W14
- https://narup.tistory.com/234#google_vignette
- https://appmaster.io/ko/blog/grpcneun-mueosibnigga
- https://www.youtube.com/watch?v=sImWl7JyK_Q