Tech story/Cloud

gRPC로 시작하는 API 개발: 첫 번째 서버와 클라이언트 구현

kt cloud 테크블로그 2024. 10. 31. 13:50

[kt cloud 플랫폼Innovation팀 강솔 님]

 

gRPC로 시작하는 API 개발: 첫 번째 서버와 클라이언트 구현 

 

지금까지 gRPC의 핵심 동작 원리와 개념에 대해 학습했었습니다. 이번 포스팅에서는 gRPC 핵심 동작 원리를 바탕으로 Java와 Spring Boot를 사용해 간단한 은행 계좌 잔액 조회 서비스를 구현해보겠습니다.

 

이 프로젝트를 통해 서비스 스키마 정의부터 서버와 클라이언트 구현까지의 일련의 과정을 따라가며, gRPC 동작 방식을 학습합니다. 그리고 gRPC 관련 여러 가지 테스트 방법에 대해 소개하겠습니다.

 

 

1. gRPC 샘플 프로젝트 구현하기

 

1.1. Sample 시나리오 및 구성

    1. 서비스 정의

        a. Bank Service: 은행 계좌 서비스

        b. 특정 시점의 계좌 잔액 조회     

 

 

   2. Proto 파일 정의 - bank-service.proto

       a. 잔액 조회 서비스 정의

       b. Request, Response 정의

syntax = "proto3";
package cohttp://m.example.bank;
option java_package = "cohttp://m.example.bank";
option java_outer_classname = "BankServiceProto";
// 요청 메시지
message BalanceRequest {
    string account_number = 1;
}
// 응답 메시지
message BalanceResponse {
    uint64 balance = 1;
}
// 서비스 정의
service BankService {
    rpc GetBalance(BalanceRequest) returns (BalanceResponse);
}

 

   3. 프로젝트 구조

grpc-bank-service/
├── src/main/java/com/ktcloud/example/grpc/
│   ├── proto/
│   │   └── bank-service.proto           # Protobuf 파일
│   ├── server/
│   │   ├── BankServiceApplication.java  # 서버 구동 클래스
│   │   └── service/
│   │       └── BankServiceImpl.java     # Bank 서비스 구현
│   ├── client/
│   │   └── BankClient.java              # gRPC 클라이언트
│   └── config/
│       └── GrpcConfig.java              # gRPC 서버 설정 (필요 시)
└── build.gradle                         # Gradle 빌드 설정

 

1.2. gRPC 서버 구현하기

   1.Gradle 설정하기

       a. gRPC와 Protobuf 라이브러리를 추가하고, protobuf 코드 자동 생성을 위한 Gradle 설정을 추가합니다.

plugins {
    id 'java'
    id 'com.google.protobuf' version '0.9.4' // Protobuf 컴파일 관련 플러그인
    id 'org.springframework.boot' version '3.3.4'
    id 'io.spring.dependency-management' version '1.1.6'
}
group = 'cohttp://m.ktcloud.example.grpc'
version = '0.0.1-SNAPSHOT'
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}
repositories {
    mavenCentral()
}
ext {
    // grpc 및 protobuf 관련 라이브러리 버전정보 정의
    grpcSpringVersion = '3.1.0.RELEASE'
    grpcVersion = '1.58.0'
    protobufVersion = '4.28.0'
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    implementation "io.grpc:grpc-protobuf:${grpcVersion}" // protobuf 메세지 처리 라이브러리
    implementation "io.grpc:grpc-stub:${grpcVersion}" // protobuf 클라이언트/서버 stub 생성 라이브러리
    implementation "com.google.protobuf:protobuf-java:${protobufVersion}" // protobuf java 구현 라이브러리
    implementation "net.devh:grpc-server-spring-boot-starter:${grpcSpringVersion}" // grpc 서버와 springboot 통합 라이브러리
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
}
protobuf {
    protoc {
        // protobuf 컴파일러 지정 - protoc
        artifact = "com.google.protobuf:protoc:${protobufVersion}"
    }
    plugins {
        grpc {
            // grpc 코드 생성 관련 플러그인 설정
            artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
        }
    }
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                java {}
            }
            task.plugins {
                grpc {}
            }
        }
    }
}
tasks.named('test') {
    useJUnitPlatform()
}

 

   2.proto 파일 컴파일

       a.protoc를 활용하여 bank-service.proto 컴파일 시 아래와 같이 관련된 자바 코드가 자동 생성됩니다.

 

   3. 서버 구현하기

       a. @GrpcService 어노테이션을 사용하여 BankService 구현

           1) gRPC 스터터 사용: grpc-spring-boot-starter

import cohttp://m.example.bank.BankServiceGrpc.BankServiceImplBase;
import cohttp://m.example.bank.BankServiceProto;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
@GrpcService
public class BankServiceImpl extends BankServiceImplBase {
    @Override
    public void getBalance(BankServiceProto.BalanceRequest request, StreamObserver<BankServiceProto.BalanceResponse> responseObserver) {
        String accountNumber = request.getAccountNumber();
        // 계좌 번호에 따른 임의 잔액 설정
        Integer balance = switch (accountNumber) {
            case "12345" -> 100000;
            case "67890" -> 200000;
            default -> 0;
        };
        BankServiceProto.BalanceResponse response = BankServiceProto.BalanceResponse.newBuilder()
                .setBalance(balance)
                .build();
        System.out.println("고객계좌 : " + accountNumber + ", 잔고 : " + response);
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

 

       b. gRPC 서버 설정

          1) gRPC 서버 포트 설정

grpc:
  server:
    port: 9090  # gRPC 서버 포트 설정

         

         2)  gRPC 서버 자동 실행 설정

@SpringBootApplication
public class BankServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(BankServiceApplication.class, args);
        System.out.println("gRPC 서버가 9090 포트에서 시작되었습니다.");
    }
}

 

1.3. gRPC 클라이언트 구현하기

  1. 채널과 스텁을 사용하여 BankService의 원격 함수를 호출합니다.
  2. 동기 스텁(BlockingStub)을 사용합니다.
package cohttp://m.ktcloud.example.grpc.client;
import cohttp://m.example.bank.BankServiceGrpc;
import cohttp://m.example.bank.BankServiceProto;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
/**
 * (설명)
 * Created by xxx@kt.com
 * Date : 2024-10-24
 */
public class BankClient {
    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("127.0.0.1", 5665).usePlaintext().build();
        BankServiceGrpc.BankServiceBlockingStub blockingStub = BankServiceGrpc.newBlockingStub(channel);
        BankServiceProto.BalanceRequest request = BankServiceProto.BalanceRequest.newBuilder()
                        .setAccountNumber("12345")
                                .build();
        BankServiceProto.BalanceResponse response = blockingStub.getBalance(request);
        System.out.println("잔액: " + response.getBalance());
        channel.shutdown();
    }
}

 

   3. 테스트 실행

gRPC 서버가 9090 포트에서 시작되었습니다.
잔액: 10000

 

2. gRPC API 테스트하기

 

gRPC로 개발한 API를 테스트하기 위한 여러 가지 방법을 소개하겠습니다.

2.1. Postman

  1. gRPC 관련 세팅
    gRPC의 경우 HTTP/2를 사용하지만, postman의 경우 HTTP 프로토콜 기본 버전이 1.1로 설정되어있습니다. 따라서 해당 설정에 대한 변경이 필요합니다.

         a. HTTP 기본 설정 Auto로 변경

             1) Postman 우측 상단 “톱니바퀴(설정)” 아이콘 클릭

             2) Settings > General 탭 이동

             3) Request > HTTP version: Auto
                 -> 해당 설정이 보이지 않을 경우에는 Postman 업데이트 필요

 

   2. postman gRPC Request 추가

       a. 왼쪽 상단 “New” 버튼 클릭

       b. gRPC Request 선택

 

       c. Request 관련 설정

          1) Server URL 입력

          2) 서비스 선택

                1. 서비스가 정의되어 있는 .proto 파일 import

 

 

3. Request Message 정의

    a. 호출할 Service 선택

    b. Message 탭 이동

    c. “Use Example Message” 버튼 클릭

 

4. API 테스트 

 

    a. Server Log 확인

2.2. grpcurl

  1. grpcurl 설치 (mac 기준) 
brew install grpcurl

*참고: (Windows 설치)  https://godd.tistory.com/90

 

  2. 커멘드 구조 

grpcurl \
-plaintext \
-d "{\"account_number\":\"12345\"}" \
localhost:9090 \
cohttp://m.example.bank.BankService/GetBalance

 

    • grpcurl
      • gRPC 서비스 호출을 지원
    • -plaintext
      • TLS(암호화)를 사용하지 않고 평문으로 통신할 때 사용하는 옵션
    • -d "{\"account_number\":\"12345\"}"
      • 서버로 전송할 데이터(payload)
      • Json 형식으로 작성하여 전송 가능
    • localhost:9090
      • gRPC 서버의 주소와 포트
    • com.example.bank.BankService/GetBalance
      • com.example.bank: 서비스가 정의된 패키지
      • .BankService: gRPC 서비스 이름
      • /GetBalance: RPC 메서드

    3. Terminal 통해 커멘드 실행 및 결과 확인

 

2.3. IntelliJ Http Client

  1. HTTP Client 설정 및 요청 작성

      a. 프로젝트 내 .http을 작성합니다.

          1) src/test/resources/http 폴더(또는 원하는 위치)에 test.grpc.http라는 파일을 만듭니다.

          2) HTTP 요청 작성

### gRPC 요청 예제
GRPC localhost:9090/cohttp://m.example.bank.BankService/GetBalance
// 한 줄을 비운 후에 Request Data 작성
{
  "account_number": "12345"
}

 

       b. IntellJ Editor를 이용

           1) proto 파일에서 “HTTP Client 생성 버튼” 클릭합니다.

           2) HTTP 요청 작성 및 실행

 

    2. IntelliJ 테스트 실행

        a. 요청 결과 확인

 

  3.http 파일 공유

       a. src/test/resources/http 폴더에서 관리하고 git으로 공유할 수 있도록 합니다.

 

 

관련글