gRPC 인터셉터

gRPC 인터셉터 (Interceptors)

RPC 메서드를 하나씩 구현하다 보면, "이 로직은 이 메서드가 아니라 모든 호출에 적용되면 좋겠다" 싶은 것들이 늘어나요. 로깅, 인증, 메트릭 같은 것들이 대표적이죠. gRPC 인터셉터는 이렇게 특정 메서드에 얽매이지 않는 공통 로직을 깔끔하게 구현할 수 있게 해 주는 확장 지점입니다. '필터'나 '미들웨어'라는 이름으로 알고 있는 개념이라면 바로 그거예요.

출처: Interceptors

언제 쓸까

인터셉터는 단일 RPC 메서드에 국한되지 않는 로직을 구현하는 데 아주 잘 맞아요. 여러 클라이언트나 서버에서 쉽게 공유할 수도 있고, gRPC를 확장하는 중요한·자주 쓰는 방법이기도 합니다. 웬만하면 원하는 기능이 이미 gRPC 생태계에 인터셉터로 존재할 수도 있으니, 만들기 전에 먼저 찾아보는 게 좋아요.

주요 사용 사례는 다음과 같습니다.

  • 메타데이터 처리
  • 로깅(Logging)
  • 결함 주입(Fault injection)
  • 캐싱(Caching)
  • 메트릭(Metrics)
  • 정책 적용(Policy enforcement)
  • 서버 측 인증(Authentication)
  • 서버 측 인가(Authorization)

참고로 클라이언트 측 인증은 인터셉터로도 할 수 있지만, gRPC가 이에 더 잘 맞는 전용 "call credentials" API를 제공하니 Authentication 가이드를 참고하는 게 좋아요.

어떻게 쓸까

인터셉터는 gRPC 채널이나 서버를 만들 때 추가할 수 있어요. 한 번 추가하면 그 채널·서버의 모든 RPC에서 호출됩니다. 인터셉터 API는 클라이언트 측과 서버 측이 다르기 때문에, 인터셉터는 "클라이언트 인터셉터"이거나 "서버 인터셉터" 중 하나가 돼요.

인터셉터는 본질적으로 호출 단위(per-call)라서, TCP 연결 관리나 TCP 포트 설정, TLS 구성 같은 것에는 쓰지 못해요. 대부분의 커스터마이징에 적합한 도구지만, 모든 것을 다 해결해 주진 않는다는 점을 알아 두세요.

인터셉터 순서

인터셉터를 여러 개 쓸 때는 순서가 중요해요. 여러분이 쓰는 gRPC 구현이 어떤 순서로 실행하는지 반드시 이해해야 합니다. 인터셉터들이 애플리케이션과 네트워크 사이에 줄지어 있다고 상상해 보세요. 어떤 인터셉터는 네트워크에 가까워 보내는 것에 더 많은 제어를 갖고, 어떤 것은 애플리케이션에 가까워 애플리케이션 동작을 더 잘 들여다봅니다.

예를 들어 캐싱 인터셉터와 로깅 인터셉터, 두 개의 클라이언트 인터셉터가 있다고 해볼게요. 어떤 순서로 두는 게 좋을까요? 로깅 인터셉터를 네트워크에 더 가깝게 두면 애플리케이션의 통신을 더 잘 감시하고, 캐시에 걸린 RPC는 로그에서 제외할 수 있어요.

flowchart LR
  APP(Application) --> INT1
  INT1(Caching\nInterceptor) -->|Cache miss| INT2
  INT2(Logging\nInterceptor) --> NET
  NET(Network)

반대로 로깅 인터셉터를 애플리케이션에 더 가깝게 두면, 애플리케이션의 동작과 로드하는 정보를 더 잘 이해할 수 있어요.

flowchart LR
  APP(Application) --> INT2
  INT1(Caching\nInterceptor) -->|Cache miss| NET
  INT2(Logging\nInterceptor) --> INT1
  NET(Network)

인터셉터의 순서만 바꿔서 두 방식 사이에서 선택하면 됩니다.

언어별 지원

언어 예제
C++ C++ 예제
Go Go 예제
Java Java 예제
Python Python 예제

더 알아보기 (Learn more)