깃헙에 코드가 있습니다.
gRPC란?
gRPC는 구글에서 만든 원격 프로시저 호출(RPC) 기술입니다.
RPC는 원격 위치에 있는 서버에서 함수를 직접 실행하는 방식으로, 클라이언트가 서버의 함수를 마치 로컬 함수처럼 호출할 수 있게 해줍니다.
gRPC는 저지연성을 위해 HTTP/2 위에서 동작하며, 주로 클라이언트 - 서버 간의 요청-응답을 처리하는데 사용됩니다.
그러나 여기서 말하는 클라이언트-서버는 React와 Spring Boot처럼 웹 프론트엔드와 백엔드를 의미하는 것이 아니라, 마이크로서비스 아키텍처(MSA) 환경에서 분산된 시스템 간의 통신을 의미합니다.
gRPC는 데이터가 바이너리 형식으로 처리되기 때문에, 사람이 읽기 어렵고 개발자가 아닌 일반 사용자와의 상호작용에는 적합하지 않습니다.
요약하자면 서버 간의 초고속 데이터 전송에만 주로 쓰인다고 볼 수 있습니다.
Spring Boot 구현
우선 gradle에 gRPC와 관련된 의존성을 추가합니다.
implementation 'net.devh:grpc-server-spring-boot-starter:2.15.0.RELEASE'
implementation 'net.devh:grpc-client-spring-boot-starter:2.15.0.RELEASE'
implementation 'io.grpc:grpc-netty-shaded:1.58.0'
implementation 'io.grpc:grpc-protobuf:1.58.0'
implementation 'io.grpc:grpc-stub:1.58.0'
그리고 아래와 같이 gRPC Server Port를 연결 해주면 HTTP/2 위에서 동작하도록 설정이 가능합니다.
grpc:
server:
port: 9090
위와 같이 설정했다면 gRPC 통신에서 필요한 .proto 파일을 만들어야합니다.
일단 .proto 파일을 만들기 전에 Protocol Buffer가 무엇인지 알아보겠습니다.
gRPC의 Protocol Buffer
프로토콜 버퍼(Protocol Buffers, Protobuf)는 네트워크를 통해 전송하기 위해 데이터를 이진(Binary) 형식으로 직렬화하고 역직렬화하는 것(이하 Serde - Serializer/Deserialaizer로 표현함)을 의미합니다.
데이터를 바이너리 형식으로 변경 시 전송 및 저장 시 데이터 크기를 줄이고, 성능을 향상시킬 수 있습니다.
흔히 개발 할 때 HTTP에서 Content-Type: json으로 설정하면 자동으로 Spring에서 Jackson Library가 Serde를 해주는 개념을 gRPC에선 프로토콜 버퍼가 하는 것이라고 생각할 수 있습니다.
- HTTP는 주로 텍스트 형식(JSON, XML 등)으로 데이터를 전송합니다.
- TCP는 바이트 스트림 형식으로 데이터를 처리하며 전송 시에는 바이너리 형식의 원시 데이터를 사용합니다.
- gRPC는 처리, 전송 과정에서 모두 바이너리 형식으로 데이터를 전송하되, 프로토콜 버퍼를 사용해 데이터를 직렬화하고 역직렬화하여 더 효율적으로 통신합니다.
일반적인 HTTP나 TCP 통신에서도 프로토콜 버퍼를 사용할 수 있습니다.
예를 들어 HTTP 같은 경우 Content-Type: application/x-protobuf로 설정하게 되면 프로토콜 버퍼를 사용할 수 있게 됩니다.
하지만 데이터가 바이너리 형식이기 때문에 사용자 친화적이지 않아서 보통은 사용하지 않고 말 그대로 서버간의 통신을 위해서만 사용한다고 볼 수 있습니다.
gRPC 같은 경우는 프로토콜 버퍼를 이용하는 특수한 방식이라고 생각할 수 있습니다.
.proto 파일 만들기
일반적으로 .proto 파일은 개발자가 수동으로 작성하는 것이 가장 일반적인 방법입니다.
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.example.grpc.proto";
package com.example.grpc;
service UserService {
rpc GetUser (UserRequest) returns (UserResponse) {}
}
message UserRequest {
int32 id = 1;
}
message UserResponse {
int32 id = 1;
string name = 2;
string email = 3;
}
service 키워드는 gRPC에서 사용할 원격 호출(RPC) 메서드를 정의합니다.
위에선 UserService 클래스에 GetUser 메서드를 사용합니다.(아래에서 이것을 토대로 Java 파일을 만듭니다.)
message는 RPC 호출에서 주고받는 데이터의 구조를 정의합니다.
프로젝트를 빌드하면 Protocol Buffer 컴파일러가 .proto 파일을 .protoc 파일로 컴파일 하면서 .proto 안에 들어있던 Service와 Message를 java 파일로 만들어서 사용합니다.
message에서 각 필드에 붙는 숫자(예: 1, 2, 3)는 필드 번호(tag number)를 의미합니다.
이 번호는 Protocol Buffers 직렬화 및 역직렬화 과정에서 사용되는 중요한 요소입니다.
IntelliJ와 같은 IDE에서는 Protocol Buffer 플러그인을 제공하여, 프로토콜 버퍼 파일을 쉽게 작성하고 관리할 수 있습니다. 이 플러그인은 구문 강조, 의미론적 분석, 참조 탐색 등 다양한 기능을 제공합니다.
RPC가 사용할 서비스 클래스 생성
위에서 설명했던 대로 프로젝트를 한번 빌드하고 나면 .proto 파일에 따라 생성된 UserServiceGrpc.UserServiceImplBase를 상속해서 Java 파일을 만들어 줘야합니다.
아래와 같이 .proto에서 정의한 내용과 매칭되도록 UserService를 만들어줍니다.
@GrpcService
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {
@Override
public void getUser(UserRequest request, StreamObserver<UserResponse> responseObserver) {
// 샘플 사용자 데이터 생성
UserResponse response = UserResponse.newBuilder()
.setId(request.getId())
.setName("John Doe")
.setEmail("john.doe@example.com")
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
그리고 아래와 같이 gRPC 요청을 해보면 정상적으로 응답하는 것을 볼 수 있습니다.
추가적인 개념
gRPC의 비동기 통신
gRPC는 HTTP/2의 멀티플렉싱을 기반으로 한 비동기적인 클라이언트-서버 통신을 지원합니다.
마치 WebFlux처럼 클라이언트는 비동기적으로 데이터를 받을 수 있습니다.
gRPC는 WebFlux를 대체하는가?
반은 맞고 반은 틀립니다.
각각의 처리 영역을 알아보겠습니다.
WebFlux의 비동기 처리 영역
- 프론트엔드-백엔드 간 통신 비동기
- 백엔드 내부의 블로킹 작업(DB, 외부 API 등) 비동기 처리
- 전체 애플리케이션의 반응형 스트림 처리
gRPC의 비동기 통신 영역
- 주로 서버-서버 간 통신의 비동기 처리(Request/Response 비동기 통신 패턴 지원)
위의 내용을 정리해볼 때 gRPC가 대체할 수 있는 부분은 Request, Response를 주고 받을 때의 비동기 통신이라고 볼 수 있습니다.
'🍃 Spring' 카테고리의 다른 글
[Spring] RFC 7232 - Conditional Requests로 비용 및 부하 최적화 하기 (0) | 2025.01.21 |
---|---|
[Spring] Redis 연결 관리 및 성능 최적화 (0) | 2025.01.06 |
[Spring] 동시성(Concurrency) 이슈 - 그 외(3) (0) | 2024.11.05 |
[Spring] 동시성(Concurrency) 이슈 - Database(2) (3) | 2024.10.25 |
[Spring] 동시성(Concurrency) 이슈 - 변수(1) (2) | 2024.10.25 |