티스토리 뷰
작성일: 2025년 12월 18일
카테고리: Backend, Protocol, Microservices
키워드: gRPC, REST, Protocol Buffers, HTTP/2, Microservices, Streaming
요약
gRPC는 Google이 개발한 고성능 RPC 프레임워크로, Protocol Buffers와 HTTP/2를 기반으로 한다. REST와 비교하여 바이너리 직렬화로 60-70% 작은 메시지 크기, HTTP/2 멀티플렉싱으로 동시 요청 처리, 양방향 스트리밍 등의 장점이 있다. 이 글에서는 gRPC의 핵심 개념, REST와의 차이점, 4가지 통신 패턴, 실전 활용 가이드를 다룬다.
gRPC란?
gRPC는 Google이 개발한 고성능 원격 프로시저 호출(RPC) 프레임워크다.
flowchart LR
subgraph client["Client"]
C["Go/Python/Java"]
end
subgraph transport["Transport"]
H["HTTP/2"]
P["Protobuf"]
end
subgraph server["Server"]
S["Go/Python/Java"]
end
client -->|"함수 호출처럼"| transport -->|"네트워크 전송"| server
style transport stroke:#2563eb,stroke-width:2px
gRPC의 핵심 구성 요소
| 구성 요소 | 역할 |
|---|---|
| Protocol Buffers | 데이터 직렬화 형식 (IDL) |
| HTTP/2 | 전송 프로토콜 |
| Code Generator | 여러 언어용 클라이언트/서버 코드 생성 |
REST vs gRPC
비교 요약
| 항목 | REST | gRPC |
|---|---|---|
| 프로토콜 | HTTP/1.1 | HTTP/2 |
| 데이터 형식 | JSON (텍스트) | Protobuf (바이너리) |
| 계약 정의 | OpenAPI (선택적) | Proto 파일 (필수) |
| 스트리밍 | 제한적 (SSE, WebSocket) | 네이티브 지원 |
| 브라우저 지원 | 완벽 | 제한적 (gRPC-Web) |
| 사용 사례 | 외부 API, 웹 서비스 | 내부 마이크로서비스 |
성능 비교
flowchart TB
subgraph REST["REST/JSON"]
R1["큰 메시지 크기"]
R2["텍스트 파싱 오버헤드"]
R3["연결당 1 요청"]
end
subgraph gRPC["gRPC/Protobuf"]
G1["작은 메시지 크기"]
G2["바이너리 직렬화"]
G3["연결 멀티플렉싱"]
end
style REST stroke:#dc2626,stroke-width:2px
style gRPC stroke:#16a34a,stroke-width:2px
메시지 크기 비교
// REST JSON - 약 82 bytes
{
"userId": "12345",
"userName": "John Doe",
"email": "john@example.com",
"age": 30
}
// gRPC Protobuf - 약 30 bytes (바이너리)
// 필드 번호 + 타입 + 값으로 압축
gRPC는 동일한 데이터를 약 60-70% 더 작게 전송한다.
언제 무엇을 선택할까?
| 상황 | 추천 |
|---|---|
| 외부 개발자용 Public API | REST - 접근성, 도구 지원 |
| 브라우저 직접 호출 | REST - 네이티브 지원 |
| 내부 마이크로서비스 통신 | gRPC - 성능, 타입 안정성 |
| 실시간 양방향 통신 | gRPC - 스트리밍 지원 |
| 대용량 데이터 전송 | gRPC - 효율적 직렬화 |
Protocol Buffers
Protocol Buffers (Protobuf)는 gRPC의 기본 데이터 형식이다.
Proto 파일 구조
// user.proto
syntax = "proto3";
package user;
// 메시지 정의 (데이터 구조)
message User {
string id = 1; // 필드 번호 = 1
string name = 2; // 필드 번호 = 2
string email = 3; // 필드 번호 = 3
int32 age = 4; // 필드 번호 = 4
}
// 서비스 정의 (API 인터페이스)
service UserService {
rpc GetUser(GetUserRequest) returns (User);
rpc CreateUser(CreateUserRequest) returns (User);
rpc ListUsers(ListUsersRequest) returns (stream User);
}
message GetUserRequest {
string user_id = 1;
}
message CreateUserRequest {
string name = 1;
string email = 2;
int32 age = 3;
}
message ListUsersRequest {
int32 page_size = 1;
}
코드 생성 흐름
flowchart LR
P["Proto 파일"]
subgraph gen["protoc 컴파일러"]
C["코드 생성"]
end
subgraph output["생성된 코드"]
Go["Go"]
Py["Python"]
JS["TypeScript"]
end
P --> gen
gen --> Go & Py & JS
style gen stroke:#2563eb,stroke-width:2px
Proto 파일의 장점
| 장점 | 설명 |
|---|---|
| 계약 우선 (Contract First) | API 스펙이 코드보다 먼저 정의됨 |
| 타입 안정성 | 컴파일 타임에 타입 검증 |
| 다중 언어 지원 | 한 번 정의, 여러 언어로 생성 |
| 버전 호환성 | 필드 번호로 하위 호환성 유지 |
필드 번호의 중요성
message User {
string id = 1; // 절대 변경 금지!
string name = 2; // 절대 변경 금지!
// string email = 3; // 삭제됨 - 3번은 재사용 금지
int32 age = 4;
string phone = 5; // 새 필드는 새 번호로
}
필드 번호는 바이너리 인코딩에 사용된다. 한번 정해진 번호는 절대 변경하면 안 된다.
HTTP/2의 장점
gRPC는 HTTP/2를 사용하여 여러 성능 이점을 얻는다.
HTTP/1.1 vs HTTP/2
| 특성 | HTTP/1.1 | HTTP/2 |
|---|---|---|
| 연결당 요청 | 1개 (Head-of-Line Blocking) | 다중 스트림 |
| 헤더 | 매번 전송 (텍스트) | HPACK 압축 |
| 서버 → 클라이언트 | 응답만 가능 | 서버 푸시 가능 |
| 데이터 형식 | 텍스트 | 바이너리 프레임 |
멀티플렉싱 (Multiplexing)
flowchart TB
subgraph http1["HTTP/1.1"]
direction TB
C1["연결 1: 요청 A"]
C2["연결 2: 요청 B"]
C3["연결 3: 요청 C"]
end
subgraph http2["HTTP/2"]
direction TB
S["단일 연결"]
S1["스트림 1: 요청 A"]
S2["스트림 2: 요청 B"]
S3["스트림 3: 요청 C"]
S --> S1 & S2 & S3
end
style http1 stroke:#dc2626,stroke-width:2px
style http2 stroke:#16a34a,stroke-width:2px
HTTP/1.1: 요청마다 별도 TCP 연결 필요
HTTP/2: 하나의 연결에서 여러 요청을 동시에 처리
gRPC 통신 패턴
gRPC는 4가지 통신 패턴을 지원한다.
1. Unary RPC (단일 요청-응답)
가장 기본적인 패턴이다. REST API와 유사하다.
rpc GetUser(GetUserRequest) returns (User);
sequenceDiagram
participant C as Client
participant S as Server
C->>S: GetUserRequest
S-->>C: User
2. Server Streaming RPC
서버가 여러 응답을 스트림으로 전송한다.
rpc ListUsers(ListRequest) returns (stream User);
sequenceDiagram
participant C as Client
participant S as Server
C->>S: ListRequest
S-->>C: User 1
S-->>C: User 2
S-->>C: User 3
S-->>C: 완료
사용 사례: 대용량 데이터 조회, 실시간 피드
3. Client Streaming RPC
클라이언트가 여러 요청을 스트림으로 전송한다.
rpc UploadUsers(stream User) returns (UploadResult);
sequenceDiagram
participant C as Client
participant S as Server
C->>S: User 1
C->>S: User 2
C->>S: User 3
C->>S: 완료
S-->>C: UploadResult
사용 사례: 파일 업로드, 배치 데이터 전송
4. Bidirectional Streaming RPC
양방향 스트리밍: 클라이언트와 서버가 동시에 데이터를 주고받는다.
rpc Chat(stream Message) returns (stream Message);
sequenceDiagram
participant C as Client
participant S as Server
C->>S: Message A
S-->>C: Response A
C->>S: Message B
S-->>C: Response B
C->>S: Message C
S-->>C: Push Notification
사용 사례: 채팅, 실시간 게임, Agent 통신
패턴 요약
| 패턴 | 요청 | 응답 | 사용 사례 |
|---|---|---|---|
| Unary | 1 | 1 | 일반 API 호출 |
| Server Streaming | 1 | N | 대용량 데이터, 피드 |
| Client Streaming | N | 1 | 파일 업로드, 배치 |
| Bidirectional | N | N | 실시간 통신, 채팅 |
실전 활용
Go에서 gRPC 서버 구현
package main
import (
"context"
"log"
"net"
pb "example/proto"
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedUserServiceServer
}
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
// 사용자 조회 로직
return &pb.User{
Id: req.UserId,
Name: "John Doe",
Email: "john@example.com",
}, nil
}
func main() {
lis, _ := net.Listen("tcp", ":50051")
s := grpc.NewServer()
pb.RegisterUserServiceServer(s, &server{})
log.Printf("gRPC server listening on :50051")
s.Serve(lis)
}
Go에서 gRPC 클라이언트 구현
package main
import (
"context"
"log"
pb "example/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
conn, _ := grpc.NewClient("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()))
defer conn.Close()
client := pb.NewUserServiceClient(conn)
user, _ := client.GetUser(context.Background(), &pb.GetUserRequest{
UserId: "123",
})
log.Printf("User: %v", user)
}
양방향 스트리밍 예시
// 서버
func (s *server) Chat(stream pb.ChatService_ChatServer) error {
for {
msg, err := stream.Recv()
if err == io.EOF {
return nil
}
// 응답 전송
stream.Send(&pb.Message{
Content: "Echo: " + msg.Content,
})
}
}
// 클라이언트
func chat(client pb.ChatServiceClient) {
stream, _ := client.Chat(context.Background())
// 송신 고루틴
go func() {
for _, msg := range messages {
stream.Send(&pb.Message{Content: msg})
}
stream.CloseSend()
}()
// 수신
for {
reply, err := stream.Recv()
if err == io.EOF {
break
}
log.Printf("Received: %s", reply.Content)
}
}
gRPC Interceptor (미들웨어)
// 서버 인터셉터
func loggingInterceptor(
ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
start := time.Now()
// 핸들러 실행
resp, err := handler(ctx, req)
// 로깅
log.Printf("Method: %s, Duration: %v, Error: %v",
info.FullMethod, time.Since(start), err)
return resp, err
}
// 서버에 인터셉터 등록
s := grpc.NewServer(
grpc.UnaryInterceptor(loggingInterceptor),
)
결론
gRPC를 사용해야 할 때
- 내부 마이크로서비스 통신
- 실시간 양방향 통신이 필요할 때
- 높은 성능이 필요할 때
- 다중 언어 환경
REST를 사용해야 할 때
- Public API (외부 개발자용)
- 브라우저 직접 호출
- 단순한 CRUD 작업
- 디버깅 용이성이 중요할 때
하이브리드 접근
실제 시스템에서는 두 가지를 함께 사용하는 경우가 많다:
- 외부 API: REST (접근성, 호환성)
- 내부 통신: gRPC (성능, 타입 안정성)
flowchart LR
subgraph External["외부"]
Browser["브라우저"]
Mobile["모바일"]
Partner["파트너"]
end
subgraph Gateway["API Gateway"]
REST["REST API"]
end
subgraph Internal["내부 마이크로서비스"]
A["Service A"]
B["Service B"]
C["Service C"]
end
External --> REST
REST -->|gRPC| A
A -->|gRPC| B
B -->|gRPC| C
style Gateway stroke:#2563eb,stroke-width:2px
style Internal stroke:#16a34a,stroke-width:2px
참고 자료
공식 문서
도구
- grpcurl - gRPC용 curl
- grpc-gateway - REST ↔ gRPC 변환
- buf - Protobuf 관리 도구
학습 자료
'실제 경험과 인사이트를 AI와 함께 정리한 글' 카테고리의 다른 글
| Kubernetes Gateway API 설계 철학: Ingress를 넘어서 (0) | 2025.12.18 |
|---|---|
| Kubernetes Operator 패턴과 Reconciliation: 선언적 인프라의 핵심 (0) | 2025.12.18 |
| API Gateway 입문 가이드: 마이크로서비스의 관문 (0) | 2025.12.18 |
| Airflow vs Prefect vs Dagster vs Temporal: 빌링 SaaS를 위한 워크플로우 오케스트레이션 비교 (1) | 2025.12.14 |
| Google Stitch: 피그마를 몰라도 AI로 UI 디자인하기 (0) | 2025.12.11 |
- Total
- Today
- Yesterday
- architecture
- Rag
- knowledge graph
- security
- frontend
- ai 개발 도구
- Go
- AI agent
- SHACL
- AI Development
- LangChain
- api gateway
- Developer Tools
- Tax Analysis
- PYTHON
- AI
- authentication
- Tailwind CSS
- 개발 도구
- claude code
- LLM
- Ontology
- authorization
- backend
- react
- troubleshooting
- Claude
- Kubernetes
- Next.js
- workflow
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |