왜 gRPC를 쓰려고 했나

내가 관리하는 서버에는 이커머스 플랫폼에서 발생한 데이터를 저장하고 관리하고 있다. 이 데이터는 여러 프로덕트들과 통신으로 주고받는다. 지금까지는 REST(흔히 쓰는 HTTP API 방식)로 이 데이터를 주고받았는데, gRPC라는 통신 방식으로 옮기면 다음과 같은 이득이 있을 거라 기대했다.

  1. 더 빠른 통신 → 리소스 부하와 비용 감소: 통신이 빨라지면 서버가 요청 하나를 처리하는 데 걸리는 시간이 줄고, 그만큼 같은 서버 자원으로 더 많은 요청을 처리할 수 있다.
  2. 내부 전용(private) 통신 구조로 인한 통신 비용 감소: gRPC는 외부에 노출되지 않는 private subnet을 이용한 VPC 내부망 통신으로 구현했기 때문에, 이 경로로 오가는 데이터에 대한 통신 비용이 줄어든다.
  3. 위성 서비스의 중복 리소스 관리 부담 감소: 1번의 효과로, 데이터를 가져다 쓰는 여러 위성 서비스(주문/리뷰/업셀/푸시 등)가 각자 캐싱이나 재시도 로직 같은 걸 두껍게 만들 필요가 줄어들 거라 예상했다.

기존 REST 통신들을 gRPC로 점차 옮겨가는 작업을 진행하면서, 정말 기대한 대로 빨라졌는지를 실제로 확인해보고 싶었다. 이 글은 그 확인 과정에서 벌어진 일을 정리한 것이다.

용어 먼저 정리: v1 · v2 · stub

이 글에 계속 나오는 세 단어를 먼저 정리한다.

용어한 줄 설명
v125년 5월에 최초로 만든 gRPC 방식. 모든 데이터 값을 문자열(“123”, “true”)로 주고받는다
v226년 7월에 v1을 개선한 새 버전. 숫자는 숫자(int), 참/거짓은 boolean으로 원래 타입 그대로 주고받는다
stub 직접호출v2에서 한 단계 더 나아가, 받은 데이터를 우리 코드가 쓰기 편한 형태(딕셔너리)로 변환하는 과정 자체를 생략하고 원본 그대로 쓰는 방식

1. HTTP(REST)와 gRPC(v1)를 비교해봤더니, 느렸다

기대와 확인해보고 싶은 마음을 가지고 실제로 REST와 gRPC(v1)의 속도를 비교했다. 결과는 예상과 정반대였다.

  • 데이터 110건처럼 적을 때는 gRPC가 REST보다 3070% 빨랐다. 여기까지는 기대한 대로였다.
  • 하지만 100건 이상 대량으로 조회하면 상황이 뒤집혔다. gRPC가 REST보다 최대 2.5~2.7배 느렸다.

예를 들어 상품 1,000건(실제 603건) 조회 기준:

방식걸린 시간(중간값)
REST159.1ms
gRPC v1338.2ms (REST의 2.1배)

“많은 데이터를 자주 오가는 이커머스 플랫폼에 유리할 것"이라는 도입 취지와, 실제로 대량 데이터일수록 더 느려진다는 결과가 정반대였다.


2. 왜 느렸을까 — 원인 분석

병목을 찾아보니 통신 자체가 아니라 데이터를 문자열로 바꾸고 다시 되돌리는 과정이 원인이었다. 모든 데이터를 문자열로 주고받아 이를 원래의 형식으로 바꿔줘야 했다. 예를 들어 가격이 데이터베이스에서는 숫자로 저장되어 있지만 gRPC 서버에서 이를 가져와 문자로 바꾼채 보내주고 있었다.

문제는 이 단순함이 대량 조회에서 비용으로 돌아왔다는 점이다.

  1. 서버는 응답을 만들 때 모든 필드를 문자열로 바꾸는 작업을 해야 했고,
  2. 클라이언트는 그 문자열 응답을 다시 JSON으로 감싸서 보내고 받는 쪽에서 파싱하고, 숫자·참거짓으로 되돌리는 작업을 매번 반복해야 했다.

데이터가 적을 땐 이 변환 비용이 미미했지만, 건수가 늘어날수록 변환해야 할 필드 수도 같이 늘어나 비용이 누적됐다. 즉, 도입을 단순하게 하기 위한 선택이 대량 조회 성능이라는 목표와 충돌한 것이다.


3. 그래서 만든 v2, 그리고 우연히 찾은 stub

원인이 타입 변환·문자열 왕복으로 좁혀지자, 이를 없애는 방향으로 새 버전(v2)을 만들었다.

  • 필드마다 원래 타입(숫자는 int, 참/거짓은 bool)을 정확히 정의해서, 서버가 문자열로 바꾸는 과정과 클라이언트가 되돌리는 과정을 없앴다.
  • 값이 없는 필드는 빈 문자열("") 대신 아예 키를 비워, 값이 없음인지 빈 값인지 구분할 수 있게 했다.
  • 기존 v1은 그대로 두고 v2를 병행 배포해서, 각 서비스가 준비되는 대로 점진적으로 옮겨갈 수 있게 했다.

v2를 만드는 과정에서 한 가지를 더 발견했다. v2는 원본 데이터를 받은 뒤 우리 코드가 쓰기 편한 딕셔너리로 한 번 더 변환하는데, 이 변환 과정 자체를 생략하고 원본(stub)을 그대로 쓰면 추가로 더 빨라진다는 것이었다. 대신 딕셔너리의 편리함(.get() 같은 접근)을 포기하고 원본 형식의 접근 방식에 맞춰야 하는 트레이드오프가 있다.

여기에 더해, 실험 중에 필요한 필드만 요청하면 어느 방식이든 훨씬 빨라진다는 것도 확인했다. 예를 들어 상품 조회 시 60여 개 필드 중 실제로 쓰는 6개만 요청하면 데이터량이 90% 이상 줄어든다.


4. 종합 실험 결과

앞서 나온 v1의 문제, v2의 개선, stub과 필드 축소까지 모두 넣어서 다시 측정했다. 상품 1,000건(603건) 조회 기준 핵심 결과:

방식걸린 시간REST 대비
REST (기존 방식)159.1ms기준
gRPC v1 (개선 전)338.2ms2.1배 느림
gRPC v2 (개선 후)194.6ms1.2배 느림
gRPC v2 + stub 직접호출145.0ms1.1배 빠름
gRPC v2 + stub + 필요한 필드만 요청126.7ms빠름

정리: v1은 REST보다 2배 이상 느렸지만, v2로 바꾸고 stub까지 쓰고 필요한 필드만 요청하면 REST보다 오히려 빠르다.

알아둘 예외

  • 필드를 줄여서 요청하면서 동시에 수백 건 이상 대량 조회하는 극단적인 경우엔 REST가 근소하게(수십 ms 차이) 더 빠르다.
  • 개발팀이 챙기기 가장 쉬운 습관은 stub 같은 고급 기법보다 필요한 필드만 요청하기다. 코드 변경이 적고 v1/v2/REST 어떤 방식을 쓰든 다 효과가 있다.

5. 결론

  • gRPC 도입 취지(대량 데이터를 빠르게, 내부 통신 비용을 줄이려고)는 합리적이었지만, 처음 설계(v1, 전부 문자열)는 그 취지와 반대로 대량 조회에서 REST보다 느린 결과를 냈다.
  • 원인을 좁혀서 v2(타입 복원)를 만들고, 그 과정에서 찾은 stub 직접호출과 필드 축소까지 더해 최종적으로 REST를 앞서는 결과를 만들었다.
  • 습관으로 남길 것 하나만 고르라면: 필요한 필드만 요청하기 — 가장 적은 코드 변경으로 어떤 방식을 쓰든 효과를 본다.

부록: 상세 비교표

측정 조건: Test 서버, shop_id=1 (주문 304건 / 상품 603건 / 고객 98건), 각 케이스 30회 반복.

A. v1 → v2 개선 폭 (gRPC 내부 비교, embed 옵션 꺼짐 기준, 단위 ms)

대상건수v1v2개선율
주문100건41.926.4-37%
주문1,000건(304)103.367.5-35%
상품100건59.638.6-35%
상품1,000건(603)332.5194.5-41%
고객100건28.421.2-25%

B. v2 vs REST

대상·건수v2REST승자
주문 1건7.4ms13.5msv2
주문 100건26.4ms30.8msv2
주문 1,000건(304)67.5ms64.8msREST
상품 100건38.6ms42.4msv2
상품 1,000건(603)194.5ms161.8msREST

→ 1회 100건 이하 조회는 v2가 REST보다 빠르거나 동급. 수백 건 이상 초대량에서만 REST가 근소하게 앞서는데, 이 구간을 stub + fields 조합으로 뒤집은 것이 이번 최적화의 핵심이다.

C. 필드 축소 효과 (전체 필드 → 필요한 필드만, p50 기준)

대상·건수방식전체 필드필요한 필드만시간 단축데이터량 감소
주문 1,000건v1102.7ms56.9ms-45%-86%
주문 1,000건v264.2ms50.7ms-21%-86%
주문 1,000건REST64.2ms43.8ms-32%-88%
상품 1,000건v1338.2ms150.9ms-55%-93%
상품 1,000건v2194.6ms130.8ms-33%-93%
상품 1,000건REST159.1ms93.7ms-41%-94%

주고받는 데이터량은 8694% 줄어드는데, 걸리는 시간은 1055%만 줄어든다. 필드를 줄여도 DB에서 데이터를 읽어오고 객체로 만드는 비용은 그대로이고, 줄어드는 건 전송·변환 비용뿐이기 때문이다.

D. stub 직접호출 효과 (v2 딕셔너리 변환 대비, 전체 필드 기준)

대상·건수v2 (변환 포함)v2 + stub (변환 생략)절감REST결과
주문 100건26.0ms23.1ms-11%29.6msstub 우위
주문 1,000건64.2ms54.1ms-16%64.2msstub 우위
상품 1,000건194.6ms145.0ms-25%159.1msstub 우위