728x90
반응형
파일 구조
build.gradle
buildscript {
ext {
protobufVersion = '3.25.1'
protobufPluginVersion = '0.8.14'
grpcVersion = '1.58.1'
}
}
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.4'
id 'io.spring.dependency-management' version '1.1.6'
id 'com.google.protobuf' version '0.9.4'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// gRPC 관련 의존성
implementation "com.google.protobuf:protobuf-java-util:3.25.1"
implementation 'com.google.protobuf:protobuf-java:3.25.1'
// grpc-client 프로젝트에서는 아래 server을 client로 수정
implementation 'net.devh:grpc-server-spring-boot-starter:2.15.0.RELEASE'
runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
implementation "io.grpc:grpc-stub:${grpcVersion}"
compileOnly 'org.apache.tomcat:annotations-api:6.0.53'
}
//tasks.named('test') {
// useJUnitPlatform()
//}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
clean {
delete generatedFilesBaseDir
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all()*.plugins {
grpc{}
}
}
}
Proto
- .proto 파일: gRPC 서비스의 계약을 정의하는 파일
- 클라이언트-서버: 어떤 서비스와 메시지가 사용되는지에 대한 명세 제공
test.proto
syntax = "proto3";
// .proto 파일의 패키지 네임스페이스 정의, 패키지 ~> 메시지와 서비스 구분
package pb.svc.test;
// Java 코드 생성 시, .proto 파일에서 생성된 클래스들이 패키지에 속하도록 설정
option java_package="com.example.pb.svc.test";
// message 및 service가 별도의 Java 파일로 생성되도록 함
option java_multiple_files=true;
// 외부의 .proto 파일(msg.proto) 가져와 사용할 수 있게 함
import "pb/unit/common/msg.proto";
// 클라이언트로부터 GreetingReq 메시지 받아 GreetingRes 메시지로 응답
service Test {
rpc Greeting(GreetingReq) returns (GreetingRes);
}
// 클라리언트 -> 서버: 요청 메시지
message GreetingReq {
string some = 1;
}
// 서버 -> 클라이언트: 응답 메시지
message GreetingRes {
unit.common.ReturnMsg result = 1;
}
msg.proto
syntax = "proto3";
// .proto 파일의 패키지 정의, 다른 .proto 파일들에서 ReturnMsg 메시지 가져다 사용할 수 있음
package unit.common;
option java_package="com.example.pb.unit.common";
option java_multiple_files=true;
message ReturnMsg {
string message = 1; // 응답시 사용하는 메시지
int32 code = 2; // 응답의 상태 코드(200: 성공, 400: 실패)
}
Client
동기 방식의 gRPC 호출을 통해 서버로부터 데이터를 수신한 후, 결과를 반환하는 방식
- `@GrpcClient` ~> 서버와 연결, 클라이언트 스텁(`TestBlockingStub`) → 서버로 요청 전송
- `GreetingReq` 메시지를 생성하고 서버로 보내면, `GreetingRes` 메시지 ~> 서버 응답 받음
// application.yml
spring:
application:
name: grpc-client
server:
port: 8080
grpc:
client:
test:
address: ${TEST_ADDR:localhost:9090}
enable-keep-alive: true
keep-alive-without-calls: true
negotiation-type: plaintext
TestController
@RestController
@RequiredArgsConstructor
public class TestController {
private final TestService testService;
@GetMapping("/greet")
public String greet(@RequestParam String name) {
return testService.sendGreeting(name);
}
}
TestService
@Service
@RequiredArgsConstructor
public class TestService {
@GrpcClient("test")
private TestGrpc.TestBlockingStub testStub;
public String sendGreeting(String message) {
GreetingReq request = GreetingReq.newBuilder()
.setSome(message)
.build();
GreetingRes response = testStub.greeting(request);
return response.getResult().getMessage();
}
}
- `TestBlockingStub`은 =동기식 호출을 지원하는 클라이언트 스텁
- 서버로부터 응답을 받을 때까지 대기하는 방식
Server
- 클라이언트로부터 요청 받고, 응답 메시지 생성해 반환하는 역할 수행
- `@GrpcService` 어노테이션 ~> gRPC 서버로서 동작
// application.yml
spring:
application:
name: grpc-server
main:
web-application-type: none
grpc:
server:
port: 9090
TestService
@GrpcService
public class TestService extends TestGrpc.TestImplBase{
@Override
public void greeting(GreetingReq request, StreamObserver<GreetingRes> responseObserver) {
String received = request.getSome();
System.out.println("Received: " + received);
// 응답 메시지 생성
ReturnMsg returnMsg = ReturnMsg.newBuilder()
.setMessage("Hello, " + received)
.setCode(200)
.build();
GreetingRes response = GreetingRes.newBuilder()
.setResult(returnMsg)
.build();
// 클라이언트로 응답 전송
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
728x90
반응형
'💻' 카테고리의 다른 글
Saga & 2PC (0) | 2025.02.23 |
---|---|
gRPC (0) | 2025.02.23 |
MyBatis VS. JPA (0) | 2025.02.23 |