Spring 공식 프로젝트 라이브러리를 활용한 gRPC 구현
개요
Spring 공식 프로젝트(Spring Team이 개발) gRPC 라이브러리를 활용한 gRPC 구현을 해보겠다.
목표
- Spring Boot 프로젝트에 Spring 공식 프로젝트 gRPC 라이브러리를 활용한 gRPC 서버를 연동
.proto파일 작성 및 Stub 자동 생성- gRPC 서비스 구현
- gRPC 클라이언트 작성 및 호출
프로젝트 생성
프로젝트 생성은 Spirng ininitializr으로 생성할 수 있다.

화면 아래 부근에 “GENERATE” 버튼을 누르면, 설정한 프로젝트 파일을 다운로드를 받을 수 있다.
또는, 다음과 같이 curl 명령어를 사용하여 Spring Boot 초기 프로젝트를 생성할 수도 있다.
curl https://start.spring.io/starter.tgz \
-d bootVersion=3.5.7 \
-d dependencies=spring-grpc \
-d baseDir=spring-grpc-2 \
-d groupId=com.devkuma \
-d artifactId=spring-grpc \
-d packageName=com.devkuma.grpc \
-d applicationName=GrpcApplication \
-d packaging=jar \
-d language=kotlin \
-d javaVersion=21 \
-d description="Demo project for Spring gRPC" \
-d type=gradle-project-kotlin | tar -xzvf -
빌드 스크립트 설정
빌드 스크립트에 gRPC 관련 라이브러리를 추가하고, protobuf 설정을 작성한다.
build.gradle
import com.google.protobuf.gradle.id
plugins {
kotlin("jvm") version "1.9.25"
kotlin("plugin.spring") version "1.9.25"
id("org.springframework.boot") version "3.5.7"
id("io.spring.dependency-management") version "1.1.7"
// gRPC
id("com.google.protobuf") version "0.9.5"
}
group = "com.devkuma"
version = "0.0.1-SNAPSHOT"
description = "Demo project for Spring gRPC"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
extra["springGrpcVersion"] = "0.12.0"
dependencies {
implementation("io.grpc:grpc-services")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.springframework.grpc:spring-grpc-spring-boot-starter") // Spring gRPC Starter
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("org.springframework.grpc:spring-grpc-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
// Kotlin coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
// gRPC + protobuf
implementation("io.grpc:grpc-kotlin-stub:1.4.3")
implementation("io.grpc:grpc-protobuf:1.75.0")
implementation("io.grpc:grpc-stub:1.75.0")
implementation("io.grpc:grpc-netty-shaded:1.75.0")
}
dependencyManagement {
imports {
mavenBom("org.springframework.grpc:spring-grpc-dependencies:${property("springGrpcVersion")}")
}
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc"
}
plugins {
id("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java"
}
id("grpckt") {
artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.3:jdk8@jar"
}
}
generateProtoTasks {
all().forEach {
it.plugins {
id("grpc") {
option("@generated=omit")
}
id("grpckt") {
option("@generated=omit")
}
}
}
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
Protobuf 파일 작성
src/main/proto 디렉토리에 gRPC 서비스와 메시지를 정의한 .proto 파일을 작성한다.
**src/main/proto/hello.proto
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.devkuma.grpc";
option java_outer_classname = "HelloProto";
service HelloService {
rpc SayHello(HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
gRPC 서버 구현
Service 객체 생성
src/main/kotlin/com/devkuma/grpc/service/HelloServiceImpl.kt
package com.devkuma.grpc.service
import com.devkuma.grpc.HelloRequest
import com.devkuma.grpc.HelloResponse
import com.devkuma.grpc.HelloServiceGrpcKt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.springframework.stereotype.Service
@Service
class HelloServiceImpl : HelloServiceGrpcKt.HelloServiceCoroutineImplBase() {
override suspend fun sayHello(request: HelloRequest): HelloResponse =
withContext(Dispatchers.Default) {
val message = "Hello, ${request.name}! (from Spring gRPC Kotlin)"
HelloResponse.newBuilder()
.setMessage(message)
.build()
}
}
서버 설정
기본 gRPC 포트는 9090이다.
원하면 application.yml로 변경 가능하다.
src/main/kotli/resources/application.yml
spring:
grpc:
server:
port: 9090
gRPC 클라이언트 구현
같은 프로젝트에서 테스트를 하기 위해, 클라이언트 설정 Bean을 추가한다.
클라이언 설정
src/main/kotlin/com/devkuma/grpc/client/GrpcClientConfig.kt
package com.devkuma.grpc.client
import com.devkuma.grpc.HelloServiceGrpcKt
import io.grpc.ManagedChannel
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.grpc.client.GrpcChannelFactory
@Configuration
class GrpcClientConfig {
@Bean
fun helloStub(channelFactory: GrpcChannelFactory): HelloServiceGrpcKt.HelloServiceCoroutineStub {
val channel: ManagedChannel = channelFactory.createChannel("local")
return HelloServiceGrpcKt.HelloServiceCoroutineStub(channel)
}
}
gRPC Stub을 호출하는 Service 생성
단순히 stub을 호출하는 Service를 생성한다.
src/main/kotlin/com/devkuma/grpc/client/HelloGrpcClientService.kt
package com.devkuma.grpc.client
import com.devkuma.grpc.HelloRequest
import com.devkuma.grpc.HelloServiceGrpcKt
import org.springframework.stereotype.Service
@Service
class MyGrpcClientService(
private val helloStub: HelloServiceGrpcKt.HelloServiceCoroutineStub
) {
suspend fun sayHello(name: String): String {
val request = HelloRequest.newBuilder()
.setName(name)
.build()
val response = helloStub.sayHello(request)
return response.message
}
}
테스트
서버와 클라이언트를 하나의 프로젝트에 넣었기에, 테스트 파일 1개로 테스트가 가능하다.
package com.devkuma.grpc
import com.devkuma.grpc.client.MyGrpcClientService
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
class GrpcIntegrationTest {
@Autowired
lateinit var client: MyGrpcClientService
@Test
fun testSayHello() = runBlocking {
val result = client.sayHello("devkuma")
assert(result.contains("devkuma"))
println(result)
}
}
output:
Hello, devkuma!
참고
위에 예제 코드는 GitHub에서 확인해 볼 수 있다.