# Table of Contents

# gRPC

gRPC(google Remote Procedure Call)을 사용하면 다른 서버의 메서드를 로컬 메서드처럼 직접 호출할 수 있다.

  • REST API는 HTTP 1.1을 사용하며, 메시지 포맷으로 JSON을 사용한다.
  • gRPC는 HTTP 2를 사용하며, 메시지 포맷으로 프로토콜 버퍼를 사용한다.
  • gRPC는 REST API에 비해 형식이 더욱 엄격하여 불필요한 논쟁을 줄인다.
  • gRPC의 프로토콜 버퍼는 REST API의 JSON 직렬화보다 최대 8배 빼를 수 있다고 한다.

# 프로젝트 구성

예제는 총 세 개의 프로젝트로 구성된다.

  • grpc-interface
  • grpc-server
  • grpc-client

# grpc-interface

grpc-interface는 클라이언트와 서버가 공통으로 사용하는 프로젝트다. 이 프로젝트에 다음과 같은 의존성을 추가하자.




 
 
 





 












 
 
 


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 





// build.gradle
buildscript {
    ext {
        protobufVersion = '3.14.0'
        protobufGradlePluginVersion = '0.8.14'
        grpcVersion = '1.35.0'
    }
}

plugins {
    id 'java'
    id 'com.google.protobuf' version "${protobufGradlePluginVersion}"
}

group 'com.yologger.grpc'
version '0.0.1'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation "io.grpc:grpc-protobuf:${grpcVersion}"
    implementation "io.grpc:grpc-stub:${grpcVersion}"
    compileOnly 'jakarta.annotation:jakarta.annotation-api:1.3.5'
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:${protobufVersion}"
    }
    generatedFilesBaseDir = "$projectDir/src/generated"
    clean {
        delete generatedFilesBaseDir
    }
    plugins {
        grpc {
            artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
        }
    }
    generateProtoTasks {
        all()*.plugins {
            grpc{}
        }
    }
}

test {
    useJUnitPlatform()
}

그 다음 main 폴더 안에 proto 폴더를 생성한다.







 





$ tree
.
├── ...
└── src
    ├── main
    │   ├── java
    │   ├── proto
    │   └── resources
    └── test
        ├── java
        └── resources

그 다음 .proto 확장자의 파일을 생성한다. 여기서는 MyService.proto라고 명명했다. (자세한 문법과 사용법은 이 곳 (opens new window)에서 확인할 수 있다.)

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.yologger.grpc.service";

service TestService {
  rpc doTest (TestRequest) returns (TestReply) {
  }
}

message TestRequest {
  string data = 1;
}

message TestReply {
  string message = 1;
}

마지막으로 프로젝트를 빌드한다.

$ ./gradlew clean build

빌드에서 성공하면 build/libs 경로에 JAR 파일이 생성된다.

# grpc-server

grpc-server는 RPC을 제공하는 프로젝트다.

먼저 프로젝트에 libs 디렉토리를 만들고 위에서 생성한 JAR 파일을 포함시킨다.

그 다음 위에서 추가한 JAR 파일과 gRPC Server을 의존성으로 추가한다.
















 
 








plugins {
    id 'org.springframework.boot' version '2.6.7'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.yologger.grpc'
version = '0.0.1'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'net.devh:grpc-server-spring-boot-starter:2.12.0.RELEASE'
    implementation files('libs/grpc-interface-0.0.1.jar')
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

이제 다음과 같이 서비스를 구현한다.

// GrpcServerService.java
import com.yologger.grpc.service.TestReply;
import com.yologger.grpc.service.TestRequest;
import com.yologger.grpc.service.TestServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

@GrpcService
public class GrpcServerService extends TestServiceGrpc.TestServiceImplBase {

    @Override
    public void doTest(TestRequest request, StreamObserver<TestReply> responseObserver) {

        TestReply reply = TestReply.newBuilder()
                .setMessage("reply: " + request.getData())
                .build();

        responseObserver.onNext(reply);
        responseObserver.onCompleted();
    }
}

마지막으로 gRPC 서버를 위한 포트를 설정한다.







 

# application.properties

# Spring MVC server
server.port=8002  

# Spring gRPC server
grpc.server.port=10000

어플리케이션을 실행하면 두 개의 서버가 구동되는 것을 확인할 수 있다.

# grpc-client

grpc-server는 RPC을 사용하는 프로젝트다.

이 프로젝트에도 libs 디렉토리를 만들고 위에서 생성한 JAR 파일을 포함시킨다.

그 다음 위에서 추가한 JAR 파일과 gRPC Client을 의존성으로 추가한다.
















 
 








plugins {
    id 'org.springframework.boot' version '2.6.7'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.yologger.grpc'
version = '0.0.1'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'net.devh:grpc-client-spring-boot-starter:2.12.0.RELEASE'
    implementation files('libs/grpc-interface-0.0.1.jar')
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

그리고 서비스를 구현한다.

import com.yologger.grpc.service.TestReply;
import com.yologger.grpc.service.TestRequest;
import com.yologger.grpc.service.TestServiceGrpc;
import io.grpc.StatusRuntimeException;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;

@Service
public class GrpcClientService {
    @GrpcClient("test")
    private TestServiceGrpc.TestServiceBlockingStub stub;

    public String doTest(String data) {
        try {
            TestRequest request = TestRequest.newBuilder().setData(data).build();
            TestReply reply = stub.doTest(request);
            return reply.getMessage();
        } catch (StatusRuntimeException e) {
            e.printStackTrace();
            return "FAILED with " + e.getStatus().getCode().name();
        }
    }
}

그 다음 컨트롤러를 구현한다.

@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired GrpcClientService service;

    @GetMapping("/test")
    String doTest() {
        return service.doTest("This is data from client");
    }
}

마지막으로 application.properties을 다음과 같이 작성한다.

server.port=8001
grpc.client.test.address=static://127.0.0.1:10000
grpc.client.test.negotiation-type=plaintext

grpc.client 다음의 test는 개발자가 자유롭게 설정할 수 있다. 다만 스텁을 만들 때 추가한 어노테이션의 값과 일치시켜야한다.




 





@Service
public class GrpcClientService {

    @GrpcClient("test")
    private TestServiceGrpc.TestServiceBlockingStub stub;

    // ...
}

이제 외부에서 클라이언트IP:8001/test/test로 접근해보자.