1. 정적 링킹 (Static Linking)
- 컴파일 시점에 필요한 라이브러리 코드를 실행 파일에 직접 포함
- 장점: 실행 시 외부 의존성이 없어 안정적
- 단점: 실행 파일 크기가 크고, 메모리 사용량이 많음
2. 동적 링킹 (Dynamic Linking)
- 실행 시점에 필요한 라이브러리를 메모리에 로드하여 공유
- 장점:
- 실행 파일 크기가 작음
- 메모리 효율성 (여러 프로그램이 같은 라이브러리 공유)
- 라이브러리 업데이트 시 재컴파일 불필요
- 단점: 필요한 동적 라이브러리가 없으면 실행 불가
3. 원격 함수 호출 (RPC)
- 네트워크를 통해 다른 컴퓨터의 함수를 호출
- 필요 요소:
- RPC 프로토콜 (통신 규약)
- Stub (로컬 함수 호출처럼 보이게 하는 인터페이스)
- IDL (Interface Definition Language) - 함수 시그니처 정의
이 3가지 방식은 각각 다른 상황에서 최적화된 선택이 됩니다:
- 정적 링킹: 안정성이 중요한 시스템 소프트웨어
- 동적 링킹: 일반적인 애플리케이션 개발
- RPC: 분산 시스템, 마이크로서비스 아키텍처
- IDL: "어떤 함수가 있고, 어떤 매개변수를 받는지" 정의
- Stub: IDL 정의를 바탕으로 실제 네트워크 통신을 처리하는 코드
전체적인 gRPC 동작 과정
1단계: 원격 호출 함수 작성
가장 먼저 실제로 서버에서 실행될 함수를 작성하는 단계다. 이 함수는 일반적인 Python 함수와 동일하며, 네트워크나 통신에 대한 코드는 전혀 포함되지 않는다. 예를 들어 숫자를 받아서 제곱값을 반환하는 간단한 계산 함수를 만드는 것이다. 이 단계에서는 순수한 비즈니스 로직만 구현하면 된다.
2단계: Protocol Buffer 파일 작성
이 단계가 gRPC의 핵심이다. proto 파일에서는 두 가지를 정의한다.
서비스 정의는 어떤 함수들이 원격으로 호출될 수 있는지 선언하는 부분이다. 각 함수의 이름, 입력 타입, 출력 타입을 명시한다. 이는 마치 인터페이스나 계약서 같은 역할을 한다.
메시지 정의는 함수 호출시 주고받을 데이터의 구조를 정의하는 부분이다. 어떤 필드가 있고, 각 필드의 타입은 무엇인지 명시한다. 이는 JSON 스키마와 비슷한 역할을 하지만, 더 엄격하고 효율적이다.
proto 파일의 중요한 특징은 언어 중립적이라는 것이다. 한 번 작성하면 Python, Java, Go, C++ 등 어떤 언어에서도 사용할 수 있다.
3단계: 자동 코드 생성
protoc 컴파일러가 proto 파일을 읽어서 실제 사용할 수 있는 코드를 자동으로 생성하는 단계다. 이 과정에서 두 종류의 파일이 만들어진다.
메시지 클래스 파일은 proto 파일에서 정의한 메시지 구조를 실제 Python 클래스로 변환한 것이다. 이 클래스는 데이터를 네트워크로 전송할 수 있는 바이너리 형태로 직렬화하고, 받은 데이터를 다시 객체로 역직렬화하는 기능을 포함한다.
서비스 클래스 파일은 서버용 베이스 클래스와 클라이언트용 스텁 클래스를 생성한 것이다. 서버용 클래스는 실제 구현해야 할 함수들의 틀을 제공하고, 클라이언트용 스텁은 원격 함수를 마치 로컬 함수처럼 호출할 수 있게 해준다.
4단계: 서버 구현
자동 생성된 서버용 베이스 클래스를 상속받아서 실제 서비스를 구현하는 단계다.
서비스 클래스 구현에서는 각 RPC 함수에 대해 실제 구현을 작성한다. 이때 중요한 것은 함수가 네트워크를 통해 들어온 메시지 객체를 받아서, 1단계에서 작성한 실제 비즈니스 로직을 호출하고, 결과를 다시 메시지 객체로 만들어서 반환한다는 점이다.
서버 설정에서는 gRPC 서버 객체를 생성하고, 구현한 서비스를 등록한다. 그리고 특정 포트에서 클라이언트의 연결을 기다리도록 설정한다. 서버는 멀티스레드로 동작하여 여러 클라이언트의 요청을 동시에 처리할 수 있다.
5단계: 클라이언트 구현
클라이언트는 서버에 연결해서 원격 함수를 호출하는 역할을 한다.
연결 설정에서는 서버의 주소와 포트를 지정해서 통신 채널을 생성한다. 이 채널을 통해 모든 통신이 이루어진다.
스텁 생성에서는 자동 생성된 클라이언트 스텁 클래스를 사용해서 스텁 객체를 만든다. 이 스텁이 실제로 네트워크 통신을 담당한다.
함수 호출에서는 전송할 데이터를 메시지 객체에 담고, 스텁을 통해 원격 함수를 호출한다. 클라이언트 입장에서는 마치 로컬 함수를 호출하는 것과 동일하게 보인다.
실제 동작 과정
클라이언트에서 stub.GetSquare(request)를 호출하면 다음과 같은 과정이 일어난다.
클라이언트 측에서는 요청 메시지 객체가 바이너리 데이터로 직렬화되어 네트워크를 통해 서버로 전송된다. 이때 HTTP/2 프로토콜이 사용되어 효율적인 통신이 가능하다.
서버 측에서는 받은 바이너리 데이터를 메시지 객체로 역직렬화하고, 구현된 서비스 함수를 호출한다. 함수 실행 결과를 다시 메시지 객체로 만들어서 바이너리로 직렬화한 후 클라이언트로 전송한다.
클라이언트 측에서는 서버의 응답을 받아서 메시지 객체로 역직렬화하고, 호출한 함수의 반환값으로 사용한다.
이 전체 과정에서 개발자는 직렬화, 네트워크 통신, 역직렬화 등의 복잡한 부분을 신경 쓸 필요가 없다. 모든 것이 자동으로 처리되기 때문이다.
gRPC의 4가지 통신 방식
gRPC는 클라이언트와 서버 간의 통신 패턴에 따라 4가지 방식을 제공한다.
1. Unary RPC
기본 개념
가장 기본적인 형태로, 일반적인 함수 호출과 동일하게 동작한다. 클라이언트가 하나의 요청을 보내면 서버가 하나의 응답을 반환하는 방식이다.
동작 방식
클라이언트는 요청 메시지를 서버에 전송하고, 서버의 응답을 기다린다. 서버는 요청을 처리한 후 하나의 응답 메시지를 클라이언트에게 전송한다. 이 과정은 완전히 동기적으로 이루어진다.
적용 사례
- 사용자 인증 (로그인/로그아웃)
- 단순한 계산 서비스 (덧셈, 제곱 등)
- 데이터베이스 조회 (특정 사용자 정보 요청)
- REST API와 유사한 기본적인 CRUD 작업
proto 정의
rpc GetUser(UserRequest) returns (UserResponse);
2. Server Streaming gRPC
기본 개념
클라이언트가 하나의 요청을 보내면, 서버가 여러 개의 응답을 스트림 형태로 연속적으로 전송하는 방식이다. 서버가 데이터를 준비되는 대로 순차적으로 보낼 수 있다.
동작 방식
클라이언트는 한 번의 요청을 보낸 후, 서버로부터 오는 응답 스트림을 계속해서 받는다. 서버는 처리 결과를 여러 번에 나누어 전송할 수 있으며, 모든 데이터 전송이 완료되면 스트림을 종료한다.
적용 사례
- 파일 다운로드 (큰 파일을 청크 단위로 전송)
- 실시간 주식 가격 정보 스트리밍
- 로그 파일 실시간 모니터링
- 대용량 데이터셋 조회 결과를 분할 전송
- 채팅방의 메시지 히스토리 전송
proto 정의
rpc GetStockPrices(StockRequest) returns (stream StockPrice);
장점
메모리 효율성이 뛰어나고, 클라이언트가 첫 번째 응답을 빨리 받을 수 있어 사용자 경험이 향상된다.
3. Client Streaming gRPC
기본 개념
클라이언트가 여러 개의 요청을 스트림 형태로 연속적으로 전송하고, 서버가 모든 요청을 받은 후 하나의 응답을 반환하는 방식이다.
동작 방식
클라이언트는 연결을 열고 여러 개의 메시지를 순차적으로 전송한다. 서버는 모든 클라이언트 메시지를 받고 처리한 후, 최종 결과를 하나의 응답으로 전송한다. 클라이언트가 스트림 전송을 완료했다는 신호를 보내면 서버가 최종 응답을 생성한다.
적용 사례
- 파일 업로드 (큰 파일을 청크 단위로 전송)
- 대량 데이터 입력 (배치 처리)
- IoT 센서 데이터 수집 (여러 센서 값들을 지속적으로 전송)
- 로그 데이터 수집 및 분석
- 실시간 위치 추적 데이터 전송
proto 정의
rpc UploadFile(stream FileChunk) returns (UploadResponse);
장점
네트워크 연결을 효율적으로 사용하고, 대용량 데이터를 메모리 효율적으로 처리할 수 있다.
4. Bidirectional Streaming gRPC
기본 개념
클라이언트와 서버가 동시에 스트림을 주고받는 방식이다. 양방향으로 독립적인 스트림이 동작하며, 각자 원하는 시점에 메시지를 전송할 수 있다.
동작 방식
클라이언트와 서버 모두 독립적으로 메시지를 읽고 쓸 수 있다. 한쪽에서 메시지를 보내는 순서와 다른 쪽에서 받는 순서가 반드시 일치하지 않아도 된다. 각각의 스트림은 독립적으로 종료될 수 있다.
적용 사례
- 실시간 채팅 서비스 (양방향 메시지 교환)
- 온라인 게임 (플레이어 간 실시간 상호작용)
- 실시간 협업 도구 (구글 독스 같은 동시 편집)
- 화상 회의 시스템 (음성/영상 스트림 교환)
- 실시간 모니터링 시스템 (명령 전송과 상태 수신)
proto 정의
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
장점
실시간 양방향 통신이 가능하고, 각 방향의 스트림이 독립적으로 동작하여 유연한 통신 패턴을 구현할 수 있다.
각 방식의 특징 비교
Unary RPC는 전통적인 함수 호출과 가장 유사하여 구현이 간단하고 이해하기 쉽다. 대부분의 기본적인 API 호출에 적합하다.
Server Streaming은 서버가 대용량 데이터를 효율적으로 전송해야 할 때 유용하다. 클라이언트는 전체 데이터를 기다리지 않고 부분적으로 처리를 시작할 수 있다.
Client Streaming은 클라이언트가 대용량 데이터를 서버에 전송해야 할 때 효과적이다. 네트워크 연결을 재사용하여 오버헤드를 줄일 수 있다.
Bidirectional Streaming은 가장 복잡하지만 실시간 상호작용이 필요한 애플리케이션에서는 필수적이다. WebSocket과 유사한 기능을 제공하지만 더 구조화된 방식으로 통신할 수 있다.
Protocol Buffers 개요
Protocol Buffers는 Google에서 개발한 데이터 직렬화 포맷이다. 2001년에 내부적으로 개발되어 2008년에 공개되었으며, 현재는 오픈소스로 제공된다. 구조화된 데이터를 직렬화하는 크로스 플랫폼 라이브러리로, 네트워크 통신이나 데이터 저장에 유용하다.
JSON vs Protocol Buffers 비교
위 이미지에서 볼 수 있듯이, 같은 데이터를 표현할 때 JSON과 Protocol Buffers의 차이점이 명확하다.
JSON 방식에서는 사람이 읽기 쉬운 텍스트 형태로 데이터를 표현한다. userName, favouriteNumber, interests 같은 필드명이 그대로 포함되어 있어 직관적이지만, 필드명이 반복되어 데이터 크기가 크다.
Protocol Buffers 방식에서는 .proto 파일에서 메시지 구조를 미리 정의한다. 각 필드에는 고유한 번호(태그)가 할당되고, 실제 데이터는 이 번호를 사용해 바이너리 형태로 인코딩된다.
Binary Encoding의 효율성
Protocol Buffers의 핵심 장점은 바이너리 인코딩에 있다. 이미지에서 보면 총 33바이트로 동일한 데이터를 표현하는데, 이는 JSON보다 훨씬 작은 크기다.
Field Tag와 Type이 각 필드를 구분한다. field tag = 1은 user_name, field tag = 2는 favourite_number를 의미한다. 각 필드는 타입 정보(string, varint 등)와 길이 정보를 포함한다.
Length Encoding을 통해 가변 길이 데이터를 효율적으로 처리한다. 문자열의 경우 길이 정보가 먼저 오고, 그 다음에 실제 데이터가 따라온다.
gRPC에서의 동작 과정
위 이미지는 gRPC의 전체 동작 과정을 보여준다.
상위 다이어그램에서는 로컬 함수 호출과 원격 프로시저 호출의 차이를 설명한다. 단일 프로세스 내에서 Order Management가 Payment를 호출하는 것과 달리, 분산 환경에서는 Server A의 Order Service가 RPC를 통해 Server B의 Payment Service를 호출한다.
하위 다이어그램에서는 gRPC 통신의 14단계 과정을 상세히 보여준다:
- 클라이언트 측: REST 호출이 시작된다
- RPC 호출: 애플리케이션 레벨에서 원격 함수를 호출한다
- Client Stub: 함수 호출을 네트워크 메시지로 변환한다
- Transport: HTTP/2를 통해 네트워크로 전송한다
- 서버 측: 메시지를 수신한다
- Server Stub: 네트워크 메시지를 함수 호출로 변환한다
- 실제 함수 실행: 비즈니스 로직이 실행된다
- 응답 과정: 결과가 역순으로 전달된다
Protocol Buffers의 핵심 기능
**IDL(Interface Description Language)**로서 데이터 구조를 언어 중립적으로 정의한다. 하나의 .proto 파일에서 여러 프로그래밍 언어용 코드를 자동 생성할 수 있다.
스키마 진화를 지원하여 하위 호환성을 유지한다. 새로운 필드를 추가하거나 기존 필드를 optional로 변경해도 기존 코드가 정상 동작한다.
효율적인 직렬화를 통해 JSON보다 작은 크기와 빠른 처리 속도를 제공한다. 바이너리 형태로 인코딩되어 네트워크 대역폭을 절약하고 파싱 속도가 빠르다.
타입 안전성을 보장하여 컴파일 시점에 데이터 타입 오류를 발견할 수 있다. 이는 런타임 오류를 줄이고 코드의 안정성을 높인다.
gRPC의 단점
1. 제한된 브라우저 지원 (Limited Browser Support)
근본적인 문제
현재 브라우저에서 gRPC 서비스를 직접 호출하는 것은 불가능하다. 이는 gRPC가 HTTP/2의 고급 기능들을 광범위하게 사용하는데, 브라우저가 gRPC 클라이언트를 지원하는 데 필요한 수준의 웹 요청 제어를 제공하지 않기 때문이다.
구체적으로 브라우저는 HTTP/2 사용을 강제할 수 없고, 하위 레벨의 HTTP/2 프레임에 접근할 수 없다. JavaScript의 fetch API나 XMLHttpRequest는 gRPC가 요구하는 세밀한 HTTP/2 제어를 지원하지 않는다.
해결 방안들
gRPC-Web이 주요 해결책으로 개발되고 있다. 이는 브라우저와 gRPC 서버 사이에 프록시를 두어 HTTP/1.1이나 HTTP/2를 gRPC 형태로 변환하는 방식이다. 하지만 이는 추가적인 인프라 복잡성을 야기한다.
HTTP 메타데이터 매핑을 통해 gRPC 서비스를 REST API로 노출하는 방법도 있다. Google Cloud의 HTTP Rule 같은 방식으로 .proto 파일에서 HTTP 엔드포인트를 자동 생성할 수 있다.
실무적 영향
이런 제약으로 인해 많은 조직에서 하이브리드 아키텍처를 채택한다. 앞서 본 마이크로서비스 다이어그램처럼 외부 클라이언트(브라우저, 모바일 앱)와는 HTTP/REST로 통신하고, 내부 서비스 간에는 gRPC를 사용하는 구조다.
2. 사람이 읽기 어려움 (Not Human Readable)
디버깅의 어려움
HTTP API 요청은 텍스트로 전송되어 사람이 읽고 직접 작성할 수 있다. curl 명령어나 Postman 같은 도구로 쉽게 테스트할 수 있고, 네트워크 패킷을 캡처해도 내용을 바로 확인할 수 있다.
반면 gRPC 메시지는 Protobuf로 인코딩되어 바이너리 형태다. Protobuf는 전송과 수신에는 효율적이지만, 바이너리 포맷이라 사람이 읽을 수 없다. 메시지를 제대로 역직렬화하려면 .proto 파일에 명시된 메시지 인터페이스 설명이 필요하다.
개발 도구의 필요성
추가적인 도구가 필요하다. 네트워크상의 Protobuf 페이로드를 분석하려면 전용 도구가 필요하고, 수동으로 요청을 작성하기도 복잡하다. Wireshark 같은 네트워크 분석 도구도 Protobuf 디코더가 있어야 내용을 이해할 수 있다.
gRPC용 도구들이 있긴 하다:
- grpcurl: gRPC 서비스를 커맨드라인에서 테스트
- BloomRPC: GUI 기반 gRPC 클라이언트
- grpcui: 웹 기반 gRPC 인터페이스
하지만 이런 도구들도 .proto 파일이 있어야 제대로 동작한다.
운영상의 복잡성
모니터링과 로깅이 복잡해진다. HTTP 로그는 바로 읽을 수 있지만, gRPC 로그는 별도의 파싱이 필요하다. 장애 상황에서 빠른 디버깅이 어려울 수 있다.
API 문서화도 추가 작업이 필요하다. REST API는 OpenAPI/Swagger로 쉽게 문서화되지만, gRPC는 .proto 파일에서 문서를 별도로 생성해야 한다.
'학교공부 > 풀스택 서비스 네트워킹' 카테고리의 다른 글
[풀스택 서비스 네트워킹] WebRTC (4) | 2025.06.09 |
---|---|
[풀스택 서비스 네트워킹] ZeroMQ (0) | 2025.04.01 |
[풀스택 서비스 네트워킹] Socket (1) | 2025.03.30 |
[풀스택 서비스 네트워킹] OSI Architecture L1,L2,L3 (1) | 2025.03.30 |
[풀스택 서비스 네트워킹] OSI Architecture Overall (0) | 2025.03.14 |