gRPC proto 사용방법과 팁
gRPC를 도입할 때 가장 먼저 마주치는 것이 proto 파일 작성입니다.
이 포스트에서는 proto 파일을 작성하는 방법과 실전에서 유용한 팁을 초보자도 쉽게 이해할 수 있도록 정리합니다.
gRPC와 Protocol Buffers란?
gRPC는 구글에서 개발한 고성능, 범용 오픈소스 RPC(Remote Procedure Call) 프레임워크입니다.
Protocol Buffers(proto)는 gRPC에서 데이터 구조를 정의하는 직렬화 포맷입니다.
proto 파일은 서비스와 메시지의 구조를 선언적으로 정의하며, 다양한 언어로 코드 생성을 지원합니다.
proto 파일의 기본 구조
proto 파일은 크게 syntax
, package
, message
, service
로 구성됩니다.
syntax = "proto3";
package example;
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
syntax
: proto 버전을 지정합니다. 최신은 “proto3”입니다.package
: 네임스페이스 역할을 하며, 충돌 방지에 유용합니다.message
: 데이터 구조를 정의합니다.service
: 실제 RPC 메서드를 선언합니다.
proto 작성 실전 팁
1. 네이밍 컨벤션
- 패키지명은 소문자, 언더스코어 없이 작성합니다. (ex: myservice)
- 메시지명과 서비스명은 파스칼케이스로 작성합니다. (ex: UserRequest, UserService)
- 필드명은 스네이크케이스로 작성합니다. (ex: user_id)
2. 필드 번호 관리
- 각 필드는 고유한 번호를 가져야 하며, 한번 정한 번호는 변경하지 않는 것이 좋습니다.
- 필드 번호는 1~15는 내부적으로 더 빠르게 처리되므로 자주 쓰는 필드에 할당하세요.
3. 주석 적극 활용
//
를 사용해 메시지와 필드에 설명을 추가하세요.- 코드 자동 생성 시 일부 언어에서는 주석이 문서로 변환됩니다.
4. Enum 사용법
- 상태값, 타입 등 제한된 값은 enum으로 선언하세요.
- enum의 첫 값은 항상 0으로 시작해야 합니다.
enum Status {
STATUS_UNSPECIFIED = 0;
STATUS_ACTIVE = 1;
STATUS_INACTIVE = 2;
}
5. 반복 필드(repeated)와 맵(map)
- 배열은
repeated
키워드를 사용합니다. - 맵 구조가 필요할 때는
map<key_type, value_type>
을 사용하세요.
repeated string tags = 3;
map<string, int32> scores = 4;
6. Optional, Oneof 활용
- proto3에서는 모든 필드가 optional이지만, 명시적으로 optional을 쓸 수도 있습니다.
- 여러 타입 중 하나만 선택할 때는
oneof
을 사용하세요.
oneof contact {
string email = 1;
string phone = 2;
}
7. 서비스 버전 관리
- 패키지명 또는 서비스명에 버전을 명시하세요. (ex: package user.v1;)
- 기존 메시지와 서비스는 삭제하지 말고, 새 버전을 추가하는 방식으로 관리하세요.
8. 코드 생성 자동화
- proto 파일을 수정하면 반드시 코드 생성 스크립트를 실행해 최신 코드를 반영하세요.
- Kotlin에서는
protoc-gen-grpc-kotlin
플러그인을 활용할 수 있습니다.
9. 디렉토리 구조
- proto 파일은 서비스별, 도메인별로 디렉토리를 구분하여 관리하세요.
- 공통 메시지는 common.proto 등으로 분리하는 것이 좋습니다.
실전 예제: 간단한 User 서비스
syntax = "proto3";
package user.v1;
message User {
int64 id = 1;
string name = 2;
string email = 3;
}
message GetUserRequest {
int64 id = 1;
}
message GetUserResponse {
User user = 1;
}
service UserService {
rpc GetUser (GetUserRequest) returns (GetUserResponse);
}
Gradle 설정 및 코드 생성 예시 (Kotlin)
build.gradle.kts
예시:
dependencies {
implementation("io.grpc:grpc-kotlin-stub:1.3.0")
implementation("io.grpc:grpc-protobuf:1.44.1")
implementation("io.grpc:grpc-netty-shaded:1.44.1")
implementation("com.google.protobuf:protobuf-kotlin:3.19.4")
}
plugins {
id("com.google.protobuf") version "0.8.18"
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.19.4"
}
plugins {
id("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:1.44.1"
}
id("grpckt") {
artifact = "io.grpc:protoc-gen-grpc-kotlin:1.3.0:jdk7@jar"
}
}
generateProtoTasks {
all().forEach {
it.plugins {
id("grpc")
id("grpckt")
}
}
}
}
proto 파일 관리 실전 팁
- proto 파일은 git 등 버전관리 시스템에 반드시 포함시키세요.
- 서비스 간 공통 메시지는 별도의 파일로 분리해 재사용성을 높이세요.
- proto 파일 변경 시, 관련 코드 자동 생성 및 테스트를 반드시 진행하세요.
- 팀 내 proto 작성 규칙을 문서화하여 일관성을 유지하세요.
참고할 만한 레퍼런스
gRPC proto 파일을 잘 설계하면 서비스 확장성과 유지보수성이 크게 향상됩니다.
처음에는 어렵게 느껴질 수 있지만, 규칙을 지키며 작성하다 보면 자연스럽게 익숙해집니다.
꾸준히 공식 문서와 레퍼런스를 참고하며 실습해보세요! protoc { artifact = “com.google.protobuf:protoc:3.19.4” } plugins { id(“grpc”) { artifact = “io.grpc:protoc-gen-grpc-java:1.44.1” } id(“grpckt”) { artifact = “io.grpc:protoc-gen-grpc-kotlin:1.3.0:jdk7@jar” } } generateProtoTasks { all().forEach { it.plugins { id(“grpc”) id(“grpckt”) } } } } ```
코드 생성 명령:
./gradlew build
3. 서버 구현 (Kotlin)
생성된 코드를 기반으로 gRPC 서버를 구현합니다.
import example.GreeterGrpcKt
import example.HelloReply
import example.HelloRequest
import io.grpc.ServerBuilder
class GreeterService : GreeterGrpcKt.GreeterCoroutineImplBase() {
override suspend fun sayHello(request: HelloRequest): HelloReply {
return HelloReply.newBuilder().setMessage("Hello, ${'$'}{request.name}!").build()
}
}
fun main() {
val server = ServerBuilder.forPort(50051)
.addService(GreeterService())
.build()
server.start()
println("gRPC 서버가 50051 포트에서 시작되었습니다.")
server.awaitTermination()
}
4. 클라이언트 구현 (Kotlin)
import example.GreeterGrpcKt
import example.HelloRequest
import io.grpc.ManagedChannelBuilder
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
val channel = ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext().build()
val stub = GreeterGrpcKt.GreeterCoroutineStub(channel)
val response = stub.sayHello(HelloRequest.newBuilder().setName("World").build())
println(response.message)
channel.shutdown()
}
참고할 만한 레퍼런스
gRPC와 proto를 활용하면 다양한 환경에서 효율적이고 확장성 있는 서비스 통신이 가능합니다. Kotlin을 비롯한 여러 언어에서 쉽게 적용할 수 있으니, 단계별로 따라하며 익혀보세요.