🚀 RPC(Remote Procedure Call): 분산 시스템의 핵심 통신 기법


RPC(Remote Procedure Call)는 네트워크에 연결된 다른 컴퓨터의 메서드를 마치 로컬에서 실행하듯이 호출할 수 있게 해주는 기술이다.

이 기술은 gRPC, Thrift, RMI 등의 프레임워크에서 기본적으로 사용되며, 마이크로서비스 아키텍처(MSA)분산 시스템의 핵심 기법 중 하나이다.

 

이번 글에서는 RPC의 작동 원리, 장단점, 구현 방법, 그리고 예제에 대해 다뤄보도록 한다.

 

🔍 RPC란 무엇인가?


클라이언트는 마치 로컬에서 함수를 호출하는 것처럼 원격 서버의 메서드를 호출하고, 서버는 그 결과를 반환한다.

 

예시 시나리오
  • 클라이언트 -> getUserData() 호출 -> 네트워크를 통해 서버에서 함수 실행 -> 결과 반환
  • 개발자는 함수가 로컬에 있는 것처럼 사용할 수 있으며, 네트워크 복잡성은 숨겨진다.

 

🏗 RPC의 구성 요소

RPC는 크게 클라이언트, 서버, 스텁(Stub) 세 가지 주요 구성 요소로 이루어진다.

구성 요소 설명

  • Client: 요청을 보내는 주체, 원격 메서드를 호출
  • Client Stub: 클라이언트와 서버 사이의 통신을 담당하는 프록시
  • Server Stub: 요청을 받아 서버의 메서드를 호출하고 응답을 반환
  • Network: 클라이언트와 서버 사이의 데이터 전송을 담당하는 네트워크
  • Service: 실제로 실행되는 서버 측의 메서드

 

예시 흐름
1. 클라이언트 → getUser() 메서드 호출
2. Client Stub → 호출 내용을 직렬화하여 전송
3. Server Stub → 데이터를 역직렬화하고, 서버의 함수를 호출
4. 결과 반환 → 응답 데이터를 직렬화하여 클라이언트로 전송
5. 클라이언트 → 결과 값 수신 및 출력

 

 


⚙️ RPC의 작동 원리


1️⃣ 클라이언트-서버 모델

RPC는 클라이언트-서버 아키텍처를 기반으로 동작한다.

  • 클라이언트: 요청을 보내는 주체 (메서드 호출)
  • 서버: 요청을 수신하고 해당 작업을 수행한 뒤 응답 반환

 

동작 순서

  1. 클라이언트가 원격 함수를 호출
  2. 서버에서 함수 실행
  3. 결과 반환

 

2️⃣ 프로시저 호출

  • 클라이언트 로컬 함수 호출 방식으로 원격 프로시저를 호출
  • 내부적으로는 네트워크를 통해 서버로 전달되고, 응답이 반환됨

 

예시 코드 (의사 코드)
// 클라이언트 측
String result = server.getUserData(1);

 

3️⃣ 스텁(Stub)

Stub은 클라이언트와 서버 간의 통신을 추상화해 주는 역할을 한다.

  • Client Stub -> 클라이언트가 메서드를 호출할 때 사용
  • Server Stub -> 서버 측에서 데이터를 수신하고, 메서드를 실행

 

스텁 동작 예시
public class UserServiceStub implements UserService {
    @Override
    public String getUserData(int userId) {
        // 네트워크 통신 대신 시뮬레이션
        return "User ID: " + userId + ", Name: Alice (From Server)";
    }
}

 

4️⃣ 마샬링과 언마샬링 (Marshalling & Unmarshalling)

  • 마샬링(Marshalling): 데이터나 객체를 네트워크 전송이 가능한 형태(바이너리, JSON 등)로 직렬화
  • 언마샬링(Unmarshalling): 네트워크로 전송된 데이터를 다시 원래의 형태로 역직렬화

 

예시 (Java에서 직렬화/역직렬화)
// 직렬화 (마샬링)
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(user);

// 역직렬화 (언마샬링)
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
User user = (User) inputStream.readObject();

 


👍 RPC의 장점


추상화

RPC는 복잡성을 숨기고 로컬 함수 호출처럼 원격 호출을 단순화해준다.

 

📌 추상화의 중요성

  • 복잡성(Complexity) 감소 -> 네트워크 통신을 추상화
  • 개발자 친화적 -> 로컬 메서드 호출처럼 직관적 사용 가능
  • 오류 감소 -> 직접 네트워크 요청을 구현하지 않아도 됨
추상화 구현 예시
public interface UserService {
    String getUserData(int userId);
}
  • 인터페이스 정의 → 클라이언트는 네트워크 구현을 몰라도 사용 가능

 

언어 독립성

RPC는 다양한 프로그래밍 언어에서 동일한 인터페이스로 통신할 수 있도록 지원한다.

 

📌 언어 독립성의 구현 방법

  • 표준화된 프로토콜 -> gRPC, Thrift 등
  • 데이터 직렬화 -> Protocol Buffers, JSON 등
  • 언어 독립적인 인터페이스 -> 모든 언어에서 공통적으로 사용할 수 있는 API 설계

 

분산 처리 용이성

분산 시스템 환경에서 다양한 서비스 간의 통신을 간편하고 효율적으로 처리할 수 있다.

 

📌 분산 처리 구현 예시

  • 서비스 지향 아키텍처(SOA) -> 서비스 단위로 모듈화
  • 마이크로서비스 아키텍처(MSA) -> 각 서비스가 독립적으로 배포되고 실행
  • 분산 컴퓨팅 -> 여러 서버에서 병렬 작업 실행

 


👎 RPC의 단점


네트워크 신뢰성 문제

네트워크에 의존하기 때문에 연결이 끊어지거나 지연되면 호출 실패 가능성이 존재한다.

 

📌 해결 방안

  • 타임아웃 및 재시도 -> 일정 시간 동안 응답이 없을 시 재시도
  • 로드 밸런싱 -> 서버 간 부하 분산
  • Failover -> 장애 발생 시 다른 서버로 요청 전환

 

디버깅의 어려움

원격 호출이기 때문에 문제 발생 시 원인을 찾기 어렵다.

 

📌 해결 전략

  • 로깅과 모니터링 -> 각 요청 및 응답 로그 기록
  • 분산 트레이싱 도구 사용 -> Jeager, Zipkin 등
  • 환경 일관성 유지 -> 로컬 및 프로덕션 환경에서 동일한 구성 사용

 


 

RPC와 gRPC의 차이점


기능 RPC gRPC
프로토콜 HTTP, TCP ✅ HTTP/2
직렬화 포맷 텍스트 기반 (XML, JSON) ✅ 바이너리 기반 (Protocol Buffers)
성능 일반적 ✅ 더 빠르고 효율적
스트리밍 지원 제한적 ✅ 양방향 스트리밍 지원
언어 지원 제한적 ✅ 다중 언어 지원 (Java, Go, Python 등)
보안 수동 설정 ✅ 기본 TLS 지원