Spring에 net.devh 라이브러리를 활용한 gRPC 구현
개요
Spring에 Kotlin와 커뮤니티/오픈소스(3rd-party) 라이브러리인 net.devh를 활용한 간단한 gRPC 구현을 해보겠다.
목표
- Spring Boot 프로젝트에 커뮤니티/오픈소스(3rd-party) 라이브러리인
net.devh를 활용하여 gRPC 서버를 연동 .proto파일 작성 및 Stub 자동 생성- gRPC 서비스 구현
- gRPC 클라이언트 작성 및 호출
프로젝트 생성
프로젝트 생성은 Spirng ininitializr으로 생성할 수 있다.

화면 아래 부근에 “GENERATE” 버튼을 누르면, 설정한 프로젝트 파일을 다운로드를 받을 수 있다.
또는, 다음과 같이 curl 명령어를 사용하여 Spring Boot 초기 프로젝트를 생성할 수도 있다.
curl https://start.spring.io/starter.tgz \
-d bootVersion=3.5.7 \
-d baseDir=spring-grpc-1 \
-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
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()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
// 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")
implementation("com.google.protobuf:protobuf-kotlin:3.22.3")
// Spring gRPC Starter
implementation("net.devh:grpc-server-spring-boot-starter:3.1.0.RELEASE")
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict")
}
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.24.4"
}
plugins {
register("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:1.75.0"
}
}
generateProtoTasks {
all().forEach { task ->
task.plugins {
create("grpc")
}
}
}
}
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.HelloServiceGrpc
import net.devh.boot.grpc.server.service.GrpcService
import io.grpc.stub.StreamObserver
@GrpcService
class HelloServiceImpl : HelloServiceGrpc.HelloServiceImplBase() {
override fun sayHello(
request: HelloRequest,
responseObserver: StreamObserver<HelloResponse>
) {
val reply = "Hello, ${request.name}!"
val response = HelloResponse.newBuilder()
.setMessage(reply)
.build()
responseObserver.onNext(response)
responseObserver.onCompleted()
}
}
서버 설정
기본 gRPC 포트는 9090이다.
원하면 application.yml로 변경 가능하다.
src/main/kotli/resources/application.yml
grpc:
server:
port: 9090
gRPC 클라이언트 구현
같은 프로젝트에서 테스트를 하기 위해, 클라이언트 설정 Bean을 추가한다.
클라이언 설정
src/main/kotlin/com/devkuma/grpc/client/GrpcClientConfig.kt
package com.devkuma.grpc.client
import com.devkuma.grpc.HelloServiceGrpc
import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class GrpcClientConfig {
@Bean
fun helloChannel(): ManagedChannel =
ManagedChannelBuilder
.forAddress("localhost", 9090)
.usePlaintext()
.build()
@Bean
fun helloStub(channel: ManagedChannel): HelloServiceGrpc.HelloServiceBlockingStub =
HelloServiceGrpc.newBlockingStub(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.HelloServiceGrpc
import org.springframework.stereotype.Service
@Service
class HelloGrpcClientService(
private val helloStub: HelloServiceGrpc.HelloServiceBlockingStub,
) {
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.HelloGrpcClientService
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import kotlin.text.contains
@SpringBootTest
class GrpcIntegrationTest {
@Autowired
lateinit var client: HelloGrpcClientService
@Test
fun testSayHello() {
val result = client.sayHello("devkuma")
assert(result.contains("devkuma"))
println(result)
}
}
output:
Hello, devkuma!
참고
위에 예제 코드는 GitHub에서 확인해 볼 수 있다.