앞전 클라이언트 코드에서 Stub을 만들어준 적이 있다.

@Slf4j
@RequiredArgsConstructor
@Component
public class GrpcMemberClient {

    @GrpcClient("member-service")
    private Channel channel; // gRPC 채널 재사용

    // gRPC 클라이언트는 Feign처럼 자동 구성되는 것이 아니라 직접 호출 시점에 서킷 브레이커를 적용하는 방식으로 구성해야 합니다.
    private final CircuitBreakerRegistry circuitBreakerRegistry;
    private CircuitBreaker grpcCircuitBreaker;

    // Stub을 재사용하기 위해 필드로 선언
    private MemberServiceGrpc.MemberServiceBlockingStub stub;
    
       @PostConstruct
    public void init() {
        // "grpcCircuitBreaker" 이름으로 서킷 브레이커 생성 (설정은 기본값 또는 별도 구성한 값 사용)
        grpcCircuitBreaker = circuitBreakerRegistry.circuitBreaker("grpcCircuitBreaker");
        // 채널로부터 Stub을 한 번만 생성
        **stub = MemberServiceGrpc.newBlockingStub(channel);**
    }

그럼 Stub이 무엇일까?

간단하게 한 줄로 표현하자면

📦 Stub이 하는 일 (클라이언트 입장에서):

  1. 메서드 호출처럼 보이게 함

    → 실제로는 네트워크 요청인데, 그냥 stub.registerUser(request) 같은 코드로 사용 가능.

  2. 요청을 직렬화(Serialize)해서 서버로 전송

    → Java 객체 → protobuf 바이너리 형태로 변환해서 gRPC 서버로 보냄.

  3. 응답 수신 및 역직렬화(Deserialize)

    → 서버에서 온 응답을 protobuf → Java 객체로 바꿔서 돌려줌.

코드로 이해해보자

     @GrpcClient("member-service")
    private Channel channel; // gRPC 채널 재사용

    // Stub을 재사용하기 위해 필드로 선언
    private MemberServiceGrpc.MemberServiceBlockingStub stub;

    @PostConstruct
    public void init() {
        // "grpcCircuitBreaker" 이름으로 서킷 브레이커 생성 (설정은 기본값 또는 별도 구성한 값 사용)
        grpcCircuitBreaker = circuitBreakerRegistry.circuitBreaker("grpcCircuitBreaker");
        // 채널로부터 Stub을 한 번만 생성
        stub = MemberServiceGrpc.newBlockingStub(channel);
    }
    
    private MemberProto.MemberResponse performGrpcCall(Long memberId) {
        // gRPC 요청 객체 생성
        MemberProto.MemberIdRequest request = MemberProto.MemberIdRequest.newBuilder()
                .setId(memberId)
                .build();

        // 현재 인증 정보 로깅 (SecurityContext에서 가져옴)
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        log.trace("Current Authentication: {}", authentication);

        try {
            return stub.getMemberById(request); //실제 네트워크 호출!!
        } catch (StatusRuntimeException e) {
            log.error("gRPC 호출 실패 - 상태: {}, 설명: {}, 원인: {}",
                    e.getStatus(),
                    e.getStatus().getDescription(),
                    e.getCause());
            throw e;
        }
    }

프론트에서 요청을 보냄 → gRPC-Client-application의 컨트롤러 단으로 요청이 들어감

gRPC-Client-Application