REST에서 gRPC로: 차세대 API 통신 방식 도입기
[kt cloud 플랫폼Innovation팀 강솔 님]
REST에서 gRPC로: 차세대 API 통신 방식 도입기
내부 서비스 간 효율적인 통신 체계 구측을 위하여 gRPC를 새롭게 도입하게 되었습니다.
이번 테크블로그를 통해서 gRPC 도입 과정을 소개하고, 우리(w/ 박현준(플랫폼Innovation팀)가 새롭게 학습한 내용들을 공유하여 유용한 인사이트를 제공하고자 합니다.
이번 포스팅에서는 gRPC에 대한 개요 수준으로 소개할 예정이며, 향후 구체적인 내용과 작성 샘플 등에 대해 공유할 예정입니다.
1. gRPC란 무엇일까?
API 설계에 사용되는 두 가지 방법 중 REST에 비해 gRPC는 다소 생소합니다. gRPC란 Google Remote Prodedure Call이라는 구글에서 개발한 RPC 프레임워크입니다.
RPC란 API 요청을 로컬해서 호출하듯 실행할 수 있게 하는 통신 프로토콜입니다. gRPC는 기존 RPC 프로토콜에 HTTP/2와 Protobuf를 적용하여 더 효율적인 통신을 지원합니다.
※ RPC vs gRPC
2. 왜 gRPC를 써야될까?
REST와 gRPC의 가장 큰 차이는 데이터 포맷과 프로토콜입니다. 그리고 이로 인해 API 전송 속도에서 큰 차이를 보입니다.
2.1. Json vs Protobuf 성능 비교
각 데이터 포맷의 성능 비교는 1) 직렬화/역직렬화 속도, 2) 전송 데이터 크기 두 가지를 기준으로 분석했습니다.
- 테스트 코드
import com.fasterxml.jackson.databind.ObjectMapper; import cohttp://m.example.protobuf.PersonProto.Person; public class PerformanceTest { private static final ObjectMapper objectMapper = new ObjectMapper(); public static void main(String[] args) throws Exception { // JSON과 프로토콜 버퍼 객체 생성 JsonPerson jsonPerson = new JsonPerson("Sam", "Doe", 30, "sam@gmail.com", true, 1000.2345, 123456789L, 500); Person protoPerson = Person.newBuilder().setFirstName("Sam").setLastName("Doe").setAge(30) .setEmail("sam@gmail.com").setEmployed(true).setSalary(1000.2345) .setBankAccountNumber(123456789L).setBalance(500).build(); // 테스트 실행 runTest("JSON", () -> { try { byte[] jsonBytes = objectMapper.writeValueAsBytes(jsonPerson); objectMapper.readValue(jsonBytes, JsonPerson.class); } catch (Exception e) { throw new RuntimeException(e); } }); runTest("Proto", () -> { try { byte[] protoBytes = protoPerson.toByteArray(); Person.parseFrom(protoBytes); } catch (Exception e) { throw new RuntimeException(e); } }); // 직렬화된 데이터 크기 비교 byte[] jsonBytes = objectMapper.writeValueAsBytes(jsonPerson); byte[] protoBytes = protoPerson.toByteArray(); System.out.println("JSON bytes length: " + jsonBytes.length); System.out.println("Proto bytes length: " + protoBytes.length); } private static void runTest(String testName, Runnable runnable) { long start = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { runnable.run(); } long end = System.currentTimeMillis(); System.out.println("Time taken for " + testName + ": " + (end - start) + " ms"); } } |
- 성능 비교
- Protobuf가 Json 포맷보다 (역)직렬화 속도가 4배 이상 빠름
- Protobuf의 데이터 크기 역시 Json 포맷에 비해 절반 이하
2.2. gRPC vs REST 통신 성능 비교
- 테스트 내용
- 주어진 숫자에 대한 제곱 값 반환(단순 계산)
- ex) 1000이 입력값으로 주어졌을 때, 1~1000까지 제곱 값 반환
- 주어진 숫자에 대한 제곱 값 반환(단순 계산)
- 성능 비교
- gRPC Unary(REST와 동일하게 1:1 요청/응답) 기준으로도 REST에 비해 2배 이상 빠름
- gRPC Unary(REST와 동일하게 1:1 요청/응답) 기준으로도 REST에 비해 2배 이상 빠름
결과적으로 속도 측면에서 gRPC가 REST에 비해 월등한 성능을 보입니다. 따라서 고성능을 요구하거나 많은 양의 데이터 로드가 필요한 시스템의 경우 gRPC를 채택하는 것이 유리합니다.
3. gRPC를 사용할 때 유의할 점
- 버전 관리
- REST의 경우 클라이언트 - 서버 간 연결이 느슨하지만, gRPC의 경우 클라이언트와 서버가 동일한 .proto 파일에 엑세스하기 때문에 동일한 버전을 사용하는지 주의해야 합니다.
- 브라우저 지원 문제
- HTTP/2 프로토콜을 기본으로 사용하고 있기 때문에 범용 브라우저를 지원하지 ㅇ낳습니다.
- 디버깅 & 로깅 이슈
- Protobuf 포맷의 경우 바이너리 형태이기 때문에 디버깅을 위해서는 JSON 포맷으로 변환하는 등의 추가 작업이 필요할 수 있습니다.
- API 게이트웨이 필요
- 외부 클라이언트와의 통신을 위해 REST API 제공을 위한 게이트웨이가 필요할 수 있습니다.
이 외에도 네트워크 및 보안 설정 그리고 .proto 파일에 대한 관리 등 추가로 고려할 사항들이 있습니다. 이러한 유의점으로 인하여 저희 프로젝트에서도 gRPC의 경우 내부 모듈 간 통신 시에만 적용하고 있으며, 외부 접점의 경우 REST API를 도입하여 두 가지를 혼합하여 사용하고 있습니다.
관련글