1P by GN⁺ | ★ favorite | 댓글 1개
  • API 설계의 출발점은 RPC와 REST의 차이이며, 세 방식은 모두 HTTP와 연결될 수 있지만 호출을 만들고 클라이언트를 구성하는 모델이 다름
  • REST는 서버가 제공한 URL을 따라가는 하이퍼텍스트 모델이라, 클라이언트가 URL 형식을 조합하는 OpenAPI식 API와 구분됨
  • OpenAPI는 URL 경로 템플릿과 HTTP 메서드로 작업을 정의해 실용적이고 널리 쓰이지만, REST보다는 HTTP에 매핑된 RPC 모델에 가까움
  • gRPC는 IDL, 코드 생성, 바이너리 페이로드, HTTP/2 연결 관리를 통해 RPC API를 직접 정의하고 HTTP 세부사항을 숨김
  • gRPC는 양쪽에 특수 소프트웨어가 필요하고 프록시 보강·동시 업데이트 방지·부분 업데이트에서 제약이 있어, 내부 API나 Cloud Endpoints 같은 변환 계층을 쓸 수 있을 때 특히 적합함

HTTP 기반 API를 보는 세 가지 모델

  • HTTP를 API 전송 계층으로 쓰는 대표 방식은 REST, OpenAPI, gRPC로 나뉨
  • 많은 공개 API와 사설 분산 API가 HTTP를 쓰는 이유 중 하나는 조직들이 80번과 443번 포트의 HTTP 트래픽 보안 문제에 익숙하기 때문임
  • 세 방식은 모두 HTTP와 연결될 수 있지만, 무엇을 주소로 삼고 클라이언트가 호출을 어떻게 만드는지가 다름

REST: URL을 조합하지 않고 따라가는 방식

  • REST에서 클라이언트는 서버가 전달한 URL을 그대로 사용하며, URL 형식을 API 명세의 일부로 이해하거나 조합하지 않음
  • 브라우저는 현재 페이지, 북마크, 사용자가 입력한 URL을 따라가고, URL에서 HTTP 요청 정보를 추출하거나 상대 URL을 절대 URL로 바꾸는 정도만 수행함
  • REST API의 핵심은 리소스 간 참조를 다른 리소스의 URL로 표현하는 하이퍼텍스트/하이퍼미디어 사용임
  • REST 방식에서는 모든 식별자를 URL로 주고받음
    • POST /accountsaccount_URL
    • POST /subscriptionsaccount_URL 전달 → subscription_URL
    • GET {account_URL} → 계정 데이터 트리 반환
  • REST의 장점은 웹 자체의 안정성, 일관성, 보편성에 가까우며, HTTP/REST의 엔티티 지향 모델이 API를 더 단순하고 규칙적으로 만들 수 있음

OpenAPI: HTTP에 직접 매핑한 RPC에 가까움

  • OpenAPI는 paths 아래에 URL 경로 템플릿을 정의하고, 경로와 HTTP 메서드의 조합을 작업(operation) 으로 부름
  • /pets/{petId} 같은 경로는 클라이언트가 {petId} 값을 알고 URL 템플릿에 끼워 넣어 HTTP 요청을 만들도록 요구함
  • 이 방식에서는 클라이언트가 URL 형식을 자세히 알아야 하므로 REST의 하이퍼텍스트 모델과 반대임
  • OpenAPI가 널리 쓰이는 이유는 명확함
    • 전통적인 RPC 모델과 비슷해 프로그래머에게 익숙함
    • RPC 개념을 HTTP 요청에 맞춰 직접 매핑할 수 있음
  • 공개 API에서는 표준 HTTP 기술만으로 거의 모든 언어와 환경에서 접근할 수 있다는 장점이 큼
  • 대신 URL 경로, HTTP 메서드, 파라미터 매핑을 설계해야 하므로 API 제공자와 소비자 모두가 배워야 할 세부사항이 늘어남

gRPC: HTTP/2를 숨긴 RPC 구현 기술

  • gRPC는 HTTP/2를 전송 계층으로 쓰지만, API 설계자와 클라이언트·서버 코드에는 HTTP 세부사항을 노출하지 않음
  • gRPC 클라이언트의 호출 흐름은 단순함
    • 호출할 프로시저를 결정함
    • 사용할 파라미터 값을 계산함
    • 코드 생성된 stub으로 파라미터를 전달해 호출함
  • gRPC는 interface description language 기반으로 원격 프로시저를 정의해, OpenAPI처럼 URL 경로·파라미터·HTTP 메서드 매핑을 함께 표현할 필요가 없음
  • 코드 생성, 프레임워크, 라이브러리를 통해 클라이언트 라이브러리와 서버 구현을 만들기 쉬울 수 있음
  • 바이너리 페이로드와 HTTP/2 연결 관리를 활용해 성능상 이점이 있으며, 같은 기술을 gRPC 없이 직접 쓸 수도 있지만 더 많은 기술 습득이 필요함

OpenAPI 대신 gRPC를 고려할 때

  • OpenAPI로 API를 설계할 때는 작업과 파라미터를 URL 경로와 HTTP 메서드 조합으로 표현해야 하며, 선택지가 많아 까다로울 수 있음
  • RPC식 모델을 계속 쓴다면 gRPC가 HTTP에 대한 사용자 정의 매핑을 직접 설계하는 부담을 줄여줌
  • gRPC와 OpenAPI는 기본 API 모델이 비슷하지만, HTTP 노출 방식이 다름
    • OpenAPI는 HTTP 전송 세부사항을 클라이언트에게 노출하고 설계자가 매핑을 제어함
    • gRPC는 미리 정의된 매핑과 생성 코드를 통해 HTTP 세부사항을 숨김
  • OpenAPI의 큰 장점은 클라이언트가 표준 HTTP 도구와 기술을 그대로 사용할 수 있다는 점이며, 많은 API 설계자에게 이 장점은 추가 설계 비용을 정당화함

RPC에 엔티티 지향 모델을 결합하기

  • gRPC나 OpenAPI를 쓰더라도 RPC 메서드를 엔티티 중심으로 제한하면 REST의 일부 장점을 얻을 수 있음
  • 절차 정의부터 시작하지 않고 리소스 타입을 먼저 정의한 뒤, 각 타입에 표준 엔티티 작업을 대응시키는 방식임
  • 기본 작업은 Create, Retrieve, Update, Delete, List이며 흔히 CRUD plus List로 볼 수 있음
  • 필요한 경우 추가 작업을 둘 수 있지만, 엔티티 지향 개념과 절차 지향 개념이 섞이면 일부 장점이 약해질 수 있음
  • 절차를 엔티티 타입별로 묶는 방식은 객체지향 언어의 핵심 아이디어 중 하나이기도 함

gRPC의 제약과 주의점

  • gRPC는 클라이언트와 서버 모두에 특수 소프트웨어가 필요하며, 생성 코드를 양쪽 빌드 프로세스에 통합해야 함
  • JavaScript나 Python처럼 개발 환경에서 빌드 프로세스가 거의 없을 수 있는 동적 언어 사용자에게는 이 요구가 부담이 될 수 있음
  • Google Cloud Endpoints는 gRPC API를 HTTP와 JSON으로 접근할 수 있게 해 클라이언트 선택지를 복원하지만, 모든 사용자가 이를 쓰거나 동등한 기능을 구축할 수 있는 것은 아님
  • REST API는 메타데이터 없이도 전체를 크롤링하는 봇을 만들기 쉽지만, gRPC나 OpenAPI 같은 RPC식 API는 엔티티 타입마다 다른 API와 메타데이터 또는 맞춤 소프트웨어가 필요함
  • HTTP API는 Apigee Edge 같은 API 관리 도구와 프록시를 통해 보안 기능, 입력 검증, 데이터 형식 매핑, 헤더·본문 수정 등을 추가하는 경우가 많지만, gRPC에서는 이런 프록시 보강이 훨씬 어려울 수 있음
  • gRPC는 동시 업데이트로 인한 데이터 손실을 막는 표준 메커니즘을 정의하지 않음
    • HTTP는 이를 위해 EtagIf-Match 헤더를 제공함
  • gRPC는 부분 업데이트 메커니즘도 정의하지 않음
    • HTTP에는 PATCH가 있고, JSON용 표준으로 JSON merge patchJSON patch가 있음
    • JSON merge patch는 더 단순하지만 배열 업데이트 같은 모든 경우를 처리하지 못함
    • JSON patch는 더 많은 경우를 처리하지만 사용이 더 복잡함

선택 기준

  • REST 하이퍼텍스트 모델을 이미 설계할 줄 알거나 배울 의지가 있다면, 안정성·일관성·보편성을 목표로 하는 좋은 선택지가 될 수 있음
  • OpenAPI는 표준 HTTP 기술만으로 접근 가능한 API를 만들 수 있지만, RPC 개념을 HTTP에 매핑하는 설계 선택지가 늘어나 설계·구현·학습이 어려워질 수 있음
  • OpenAPI를 고려하는 API라면 gRPC도 함께 검토할 만함. 두 방식의 기본 API 모델은 비교 가능하고, gRPC는 HTTP 매핑을 직접 만들 필요를 줄임
  • gRPC는 다음 조건에서 특히 매력적임
    • Cloud Endpoints 같은 제품으로 클라이언트가 gRPC 기술을 반드시 채택하지 않아도 되는 경우
    • 서버와 모든 클라이언트의 기술 선택을 통제할 수 있는 내부 API인 경우
  • gRPC를 OpenAPI나 REST 대신 채택할 때는 Apigee Edge 같은 API 관리 도구 기반 프록시로 API 동작을 보강하거나 보정할 기회가 훨씬 제한될 수 있음을 고려해야 함

댓글과 토론

Hacker News 의견들
  • 시간을 되돌릴 수 있다면 gRPC를 애초에 배우지 못하게 막고 싶을 정도임
    처음엔 그 비전에 빠졌지만, 몇 년 지나 보니 골칫거리가 너무 많았음. 내부를 숨겨준다는 말도 농담에 가깝고, 10번 중 1번 실패하는 요청 원인을 찾으려고 디버그 로그를 쏟아내며 이름도 애매한 타임아웃/재시도 설정 10~20개를 조정하게 됨
    Maven 플러그인, 이상한 deadline exceeded, HTTP/2를 싫어하는 로드밸런서, 방화벽 때문에 결국 표준 API를 써야 하는 상황, 빈약한 문서, 관측 가능성에서 쓸 만한 오류 메시지를 얻는 일까지 전부 시간을 잡아먹었음

    • gRPC의 문제는 프로토콜이나 protobuf 자체보다, 적어도 Java 쪽 도구 체인이 형편없다는 데 있음
      생성되는 코드의 개발자 경험이 나쁘고, 클라이언트 스텁은 final 구체 클래스라 테스트에서 모킹하기 어렵고, 서버 구현도 인터페이스가 아니라 구체 클래스를 상속해야 함
      서버 메서드는 비동기 시그니처라 @Transactional 같은 AOP 기반 동작을 망가뜨리고, 예외 지원도 없으며, 불변 값 클래스는 좋지만 전부 빌더로 만들어야 함
      결국 SOA에서 gRPC를 쓰려면 gRPC 잡음을 숨기고 테스트 가능한 깔끔한 코드를 얻기 위해 배관 코드를 많이 써야 하고, Thrift의 RPC 컴파일러도 비슷한 문제에 다른 문제까지 더해져 있음
    • 그 문제들은 gRPC/protobuf 명세 자체보다 특정 구현체의 문제에 더 가까워 보임
      최신 .NET과 C#에서 gRPC 경험은 꽤 좋아서, Microsoft도 WCF 같은 기존 RPC 기술을 종료하고 gRPC에 집중했음
    • 가장 크게 써본 프로젝트가 Java였는데, protoc가 생성한 바인딩 출력을 검증하는 일이 직접 직렬화하는 것보다 더 장황하고 오류가 나기 쉬웠음
      와이어 프로토콜은 타입 안전하지 않고, 타입 태그는 있지만 여러 데이터 타입에 같은 태그를 재사용함. zig-zag 정수 인코딩도 느림
      RPC 라이브러리로서는 형편없고, 직접 겪은 것 중 더 나빴던 건 FlatBuffers뿐이었음
    • Maven을 말한 걸 보면 Java를 쓰는 듯한데, 지난 8년 정도 Go로 프로그래밍해 온 입장에서는 gRPC 경험이 꽤 다름
      이 차이가 Java 때문인지 gRPC 기술 자체 때문인지가 궁금함
    • Go+Dart 스택에서 gRPC를 몇 년째 쓰고 있지만 이런 문제를 겪은 적이 없음
      Java+gRPC에 특화된 문제인지 궁금함
  • 오래 API를 만들어 왔고 gRPC와 HTTP/REST를 모두 써봤으며, OpenAPI 명세에서 Go 클라이언트와 서버를 생성하는 라이브러리 https://github.com/oapi-codegen/oapi-codegen도 공개했음
    글에서 OpenAPI와 REST를 구분하는 방식에는 동의하기 어려움. OpenAPI는 HTTP API의 동작을 문서화하는 방법이고, RESTful API든 완전히 임의의 API든 표현할 수 있음. 도구가 해석할 수 있게 API를 설명하는 스키마 언어라는 점에서 gRPC 프로토콜을 지정하는 Protocol Buffer 파일과 개념적으로 비슷함
    gRPC는 proto를 주고받는 RPC 메커니즘인데, Google이 protobuf를 공개할 때 내부 RPC 계층인 Stubby는 공개하지 않았고 gRPC는 Stubby만큼 훌륭하지는 않음. 그래도 전송 효율은 높고 확장도 비교적 쉬움
    다만 gRPC는 주류 HTTP 라이브러리만큼 견고한 생태계가 아니어서 로깅이나 인증 같은 미들웨어를 직접 구현해야 하는 일이 많고, 서로 다른 언어로 구현된 서비스 간 RPC에서는 특히 그렇다
    gRPC의 진짜 문제는 proto 파일이라고 봄. 모든 클라이언트가 서버와 호환되는 .proto 파일로 빌드되어야 하므로 발견 가능한 프로토콜이 아님. HTTP API는 OpenAPI 설명 없이도 curl이나 직접 작성한 코드로 호출할 수 있어 결합이 더 느슨하고, 그래서 작업과 디버깅이 쉬움

    • 이 블로그가 “OpenAPI”라고 부르는 것과 제대로 된 REST 사이에는 구분이 있음. 다만 실제로 진짜 REST API를 만드는 곳은 거의 없고, 대부분은 OpenAPI식 접근을 씀
      Roy Fielding의 2000년 박사 논문에서 정의한 REST는 루트 URL에 GET하면 200 OK 응답 안에 링크들이 있고, 그 링크를 따라가며 API가 제공하는 모든 리소스를 탐색할 수 있어야 했음. 계층 구조는 허용되지만 모든 것이 링크 트리 어딘가에서 접근 가능해야 했고, 이게 발견 가능성을 주려는 의도였음
      실제 지난 20년간 일해본 곳들은 거의 전부 POST resource_name/resource_id/sub_resource/sub_resource_id/mutation_type 또는 회사의 멱등성 처리 방식에 따라 PUT resource_name/resource_id/sub_resource/sub_resource_id를 썼고, 클라이언트가 Swagger/OpenAPI 같은 구조 지식을 바탕으로 마법 같은 URL을 조립했음
      그래서 엄밀한 사람들은 Fielding의 REST 정의를 구현하지 않는다는 뜻으로 실무 방식을 “REST”가 아니라 “RESTful”이라고 부르곤 함
    • OpenAPI와 REST를 구분한 부분이 나도 혼란스러웠음. 글에서 REST라고 부르는 건 HATEOAS에 더 가까워 보임
      내부/외부 클라이언트가 있는 꽤 큰 API들을 관리하는 입장에서, OpenAPI 명세에서 코드를 생성하는 흐름도 이해하기 어렵다. 생성된 스텁을 채운 뒤 API 명세를 반복 개선하면 도구는 새 스텁을 또 만들어주고, 수동 병합이 필요해지며 API가 커질수록 관련 변경을 찾기 어려워짐
      그래서 go/ast 등을 이용해 코드에서 OpenAPI 명세를 생성하는 괴물을 만들었음. 완벽하진 않지만 Echo와 Gin 모두에서 동작하는 95% 해법이고, 새 엔드포인트가 필요하면 요청/응답 구조체와 빈 핸들러를 만든 뒤 문서를 생성해 프런트엔드 개발자에게 보내는 식으로 빠르게 진행 가능함
      대부분의 개발자는 OpenAPI로 API를 어떻게 표현할지 고민할 필요가 없고, 문서도 코드와 항상 맞아떨어짐
    • 요즘은 발견을 위한 gRPC reflection이 있음: https://grpc.io/docs/guides/reflection/
    • OpenAPI 명세를 활용해 생성 타입과 함께 SQL 비슷한 질의 문법도 만들고 있음. 이렇게 하면 어떤 서드파티 API든 같은 느낌으로 다룰 수 있음
      Apipe.new(GitHun) |> from("search/repositories") |> eq(:language, "elixir") |> order_by(:updated) |> limit(1) |> execute()
      사용 가능한 gRPC 함수나 서드파티 API의 RESTful한 특이점을 몰라도 되고, 내장 문서화와 타입 접근도 유지할 수 있음
      https://github.com/cpursley/apipe
      JS/TS 프로젝트에 Supabase처럼 넣을 수 있도록 TypeScript 어댑터 계층도 고려 중임
      const { data, error } = await apipe.from('search/repositories').eq('language', 'elixir').order_by('updated').limit(1)
      이 호출은 Elixir 프록시를 거쳐 비동기 처리나 속도 제한 같은 무거운 일을 맡기게 할 수 있음
    • gRPC가 발견 가능한 프로토콜이 아니라는 말은 완전히 맞진 않음
      Protobuf의 JSON 직렬화를 바탕으로 OpenAPI 설명을 만들고 Swagger로 제공할 수 있고, gRPC 자체도 내장 reflection과 이를 쓰는 grpcurl 유틸리티를 제공함
  • FAANG 몇 곳에서 일해본 입장에서 Thrift/gRPC는 내부 서비스 라우팅에는 정말 유용함
    다만 그 복잡성의 상당 부분은 라이브러리, 서비스 발견 계층, 라우팅 등을 만드는 팀들이 관리해 주는 것임. RPC 프로토콜을 쓰면 일반적인 JSON/REST 서비스로는 하기 어려운 규모와 속도로 이런 일들이 가능해짐
    REST API가 동사를 새어나오게 하지 않는 경우도 본 적이 없고, 백엔드 서비스 메시를 만들거나 두 로컬 서비스를 네트워크 스트림으로 연결해야 한다면 항상 gRPC를 고를 것임
    하지만 고객 또는 웹에 노출되는 용도로는 gRPC를 절대 쓰지 않겠음. RPC는 많은 결정을 고정하고 “한 가지 방식”을 강제하기 때문에 강력함. 반대로 여러 기술 스택의 다양한 클라이언트가 서비스를 써야 한다면 REST가 훨씬 낫다

    • 공개 API에는 안 하겠지만, 비공개 API에서는 그냥 POST /api/doThingy 에 JSON 본문을 보내는 식으로 처리함
      가장 기본적인 HTTP 클라이언트만 있으면 누구나 참여할 수 있는 쉬운 RPC이고, 모든 OS와 브라우저에서 잘 동작함. URL 경로에 넣을지, 쿼리 매개변수에 넣을지, 본문에 넣을지로 씨름할 필요도 없음
      Buf나 Connect처럼 덜 불편하게 만들려는 서버 계열을 쓰면 gRPC에서도 HTTP를 통한 JSON을 기꺼이 받아줌
    • 웹이 아닌 클라이언트/서버 애플리케이션은 어떤지 궁금함
      온라인 게임이나 MMO처럼 REST보다 훨씬 더 실시간 통신이 필요한 경우를 떠올리는데, 요즘은 소켓 연결 위에 뭔가를 얹어 쓰는 건지 잘 모르겠음
    • 내부 서비스 라우팅에서 Thrift/gRPC가 축복이라면, 무엇과 비교해서 그런지 궁금함
      다른 무엇을 시도해 봤는지도 알고 싶음
    • “동사가 새어나온다”는 게 무슨 뜻인지 궁금함
  • 양방향 스트리밍을 하는 게 아니라면 gRPC는 대체로 시간 낭비라고 봄
    실행 시점 전이 의존성 지옥, 도구 체인 지옥이 있고, Google 내부에서 각 구현을 관리하는 팀들도 기본 기능이 어떻게 동작해야 하는지 철학적으로 합의하지 못하는 듯함
    내 언어를 쓰지 않는 팀, 특히 Go/Python/Java가 아니거나 그마저도 오래된 버전을 쓰는 팀에 gRPC API를 노출해 보거나, 상용 기성 제품과 통합해 보거나, 브라우저에 노출해 보면 전부 중간 계층이 필요해짐

    • 여러 회사와 팀에서 gRPC를 써봤고, 각 회사의 엔지니어 규모는 대략 100~500명 정도였는데 의존성과 도구 체인 문제를 겪지 않았음
      gRPC는 대체로 순조로웠음
    • 브라우저에 직접 노출하면 안 된다고 봄. 그게 gRPC가 잘하는 영역이 아니고, JSON으로 변환하는 맞춤 계층을 만드는 편이 낫다
      백엔드 서비스끼리 통신하는 데 REST를 쓰는 것도 성능 요구가 있다면 별 의미가 없고, 가끔 작은 데이터를 받는 호출이 아니라면 사람이 읽을 수 있는 프로토콜/API를 쓸 이유가 약함
    • 브라우저에 gRPC API를 노출하려다 “JSON다운” 인터페이스를 만들지 않았다고 추궁받은 기억이 있음
      oneof 하위 타입 중 현재 필드가 없는 타입을 반환하면 자동 변환 결과가 { "id": "id", "sub_type_two": { } }처럼 나왔음
      기능적으로는 동작하고, 나중에 필드가 생겨도 그 코드는 계속 동작함. 하지만 웹 세계에서 빈 객체로 응답 타입을 나타내는 건 이상하고, protobuf를 작성할 때는 이런 문제가 잘 안 보일 수 있음
    • 양방향 스트리밍은 “규모 있게” 운영하려는 대상에는 대체로 나쁜 생각임
    • Protobuf는 스트리밍에 적합하지 않음
      상상 가능한 거의 모든 이진 프로토콜과 비교해도 반스트리밍에 가깝다
  • 회사에서 gRPC를 써본 유일한 경험은 “성능이 필요하다”며 다른 시니어 개발자가 밀어붙인 프로젝트였음
    결국 프런트엔드가 소비할 수 있어야 해서 JSON API도 만들었고, 그 개발자 외에는 gRPC 경험자가 없었음. 정작 그 개발자도 gRPC Python 빠른 시작 가이드 이상으로 들어가지 않았고 버그 수정도 도와주지 않았음
    프로젝트는 수많은 이유로 엉망이었고, gRPC를 정당화할 만한 규모에도 전혀 도달하지 못함
    그래도 개인적으로 조금 써본 gRPC는 마음에 들었고, 훨씬 더 많은 작업과 고민이 필요하다는 느낌임. 그건 내가 JSON API를 훨씬 많이 해봤기 때문일 수도 있음

    • 그건 gRPC 자체보다는 채택 전에 브라우저와 호환되지 않는다는 점도 몰랐던 “시니어” 개발자에 대한 비판에 가까워 보임
  • ConnectRPC https://connectrpc.com/를 재미있게 쓰고 있음
    gRPC의 문제 있는 부분을 많이 고쳐주고, Safari가 WebTransport를 받아들이면 ConnectRPC가 더 나은 스트리밍을 개발할 수 있을 거라 기대함
    처음엔 https://buf.build가 과하다고 생각했지만, 서드파티 proto 파일을 하나씩 내려받지 않고 가져올 수 있는 기능이 결정적이었음
    deps:
    - buf.build/landeed/protopatch
    - buf.build/googleapis/googleapis
    자동 SDK 생성도 큼. 예전엔 약 9개 언어용 SDK를 자동 생성한다고 칭찬하려 했는데, 최근 하루이틀 사이 업데이트되어 이제 16개 언어에 OpenAPI와 다른 새 기능까지 보임
    나도 gRPC 스트리밍의 거짓 약속에 흔들렸고, 이 문서가 내 경험과 정확히 맞아떨어졌음: https://connectrpc.com/docs/go/streaming/

    • ReactNative에서 동작하게 하려고 ConnectRPC 스트리밍용 작은 WebSocket 기반 래퍼를 만들었음
      덕분에 브라우저에서도 양방향 스트리밍을 쓸 수 있음
    • 그래도 Protocol Buffers를 쓰고 있고, 내가 gRPC에서 겪는 많은 문제는 바로 거기서 나옴
    • Safari의 WebTransport 지원에 관한 최근 소식이 있는지 궁금함
  • Google이 업계 전체를 심리전이라도 건 것처럼 내부 서비스 통신에 gRPC를 쓰게 만든 느낌임
    gRPC의 개발자 경험은 REST보다 상당히 나쁨
    누군가에게 엔드포인트를 호출하는 간단한 명령 하나를 줄 수 없고, 표준화되지 않은 추가 도구가 필요함. 게다가 생성되는 클라이언트 쪽 코드는 어떤 언어에서든 보기 드문 추한 덩어리임

    • 백엔드 관점에서는 gRPC의 개발자 경험이 REST보다 나쁘다는 말에 강하게 동의하지 않음
      프로토콜 변경 하나로 어떤 하위 소비자를 업데이트하고 재배포해야 하는지 정적으로 알 수 있어, 몇 주짜리 작업이 한 시간짜리 변경으로 줄어들 수 있음
      받아들이고 내보내는 메시지가 즉시 검증된다는 것도 알고, 나중에 복원하기 위해 저렴하게 저장할 수도 있음
      proto로 매우 읽기 좋은 API 문서를 얻고, 코드나 비즈니스 로직에 흐려지지 않음. 버전 관리와 폐기 의미도 내장되어 있고, 맵을 제외하면 더 풍부한 자료 구조도 지원함
      비교하면 JSON은 백엔드에서는 부풀고 낡아 보임
    • gRPC는 중요한 면에서는 표준임
      데이터 타입과 함수 시그니처를 적어두면 실제 함수처럼 호출할 수 있는 것을 얻고, 직렬화/역직렬화 보일러플레이트 대신 비즈니스 로직에 집중할 수 있음
      Thrift도 마찬가지로 직접 다 만드는 방식보다 훨씬 낫고, GraphQL은 더 좋다고 봄
    • 여러 회사에서 강제로 써봤지만 99%는 필요 없는 기술 부채 투자였음
      Go에서도 재생성과 공유 proto 버전 관리를 파악하는 일이 번거롭고, 언어가 하나씩 추가될수록 더 나빠짐
      그래도 모든 스타트업은 마이크로서비스 100개와 gRPC가 필요하다고 생각하는 듯함
  • “API가 REST API라면 클라이언트는 URL 형식을 이해할 필요가 없고, 그 형식은 클라이언트에게 주는 API 명세의 일부가 아니다”라는 문장은 Roy Fielding의 REST 정의와 맞닿아 있음
    Fielding은 REST API가 초기 URI와 표준화된 미디어 타입 집합 외에는 사전 지식 없이 진입되어야 하며, 이후 모든 애플리케이션 상태 전이는 서버가 제공한 선택지를 클라이언트가 고르는 방식으로 이뤄져야 한다고 썼음
    https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
    이미 많이 다뤄진 얘기지만, 진짜 RESTful 시스템에서 클라이언트에게 주는 “API 명세”는 초기 진입점 URI/URL뿐이어야 한다는 점이 여전히 재미있음

    • 자기 서술적인 REST라는 생각은 이제 HATEOAS로 더 잘 알려져 있음
      개인적으로는 부풀어 있고 실제 문제를 해결하지 않는다고 봄
      https://en.m.wikipedia.org/wiki/HATEOAS
    • 초기 진입점만 공개하는 REST API에 대해 API 클라이언트를 어떻게 작성하는지 궁금함
      특히 클라이언트가 조작 가능한 리소스나 요청/응답 모델을 어떻게 발견해야 하는지 모르겠음
    • 진짜 RESTful 시스템에서 클라이언트에게 주는 API 명세가 초기 진입점 URI/URL뿐이어야 한다는 데 완전히 동의하진 않음
      설정은 그럴 수 있지만, API 명세는 URL보다 훨씬 많아야 하고 시스템이 쓰는 미디어 타입을 자세히 설명해야 함. 즉 HTTP 요청/응답 본문에 대한 설명이 대부분 필요함
      링크에도 “REST API는 리소스를 표현하고 애플리케이션 상태를 구동하는 미디어 타입 정의에 거의 모든 설명 노력을 써야 한다”고 되어 있음
      결국 application/json만 반환하는 게 아니라 특정 +json 같은 것을 반환하는 셈이고, 대부분은 일반 JSON이 아니라 애플리케이션이 이해해야 하는 비즈니스 데이터를 담고 있음
      대중적 논의에서는 초기 URL에만 초점이 맞춰지고, Fielding이 말한 “미디어 타입을 설명하라”는 대부분의 작업이 빠져 버림. 그래서 “URL 하나면 된다”는 말을 들은 사람이 “나머지 명세는 어디 있나?”라고 묻는 건 당연함
    • 둘은 같은 말에 동의하고 있음. 여기서 “URL 형식”은 말 그대로 URL의 형식이지 리소스 형식이 아님
      나도 또 REST를 이해 못한 블로거인가 싶어 글을 눌렀지만, 저자는 적어도 기본 개념은 알고 있는 듯함
      gRPC도 좋아하고 상업 프로젝트에서는 꽤 매력적이지만, 개인적이거나 이상주의적인 프로젝트라면 REST가 더 낫다고 봄
    • 좋은 아이디어가 유행한 뒤, 사람들이 그 아이디어를 오해한 채 계속 퍼뜨리는 전형적인 사례임
  • 데이터센터 내부에서 gRPC를 쓰는 걸 좋아하지 않음
    성능을 이유로 선택하지만 gRPC는 고성능이라고 보기 어렵고, 공개 클라이언트 품질도 특히 C++/Java 핵심 구현 밖에서는 매우 낮음. 예를 들면 Node.js 구현이 그렇다
    protobuf를 API 명세로 쓰는 데 반대하는 건 아니지만, TCP 위의 프레이밍 프로토콜과 함께 쓸 수 있어야 함. 다만 그런 방식의 RPC에서 뚜렷한 지배적 선택지가 없음
    웹 기반 API에서는 읽을 수 있는 페이로드를 더 선호하지만, 보통 JSON을 쓰면서 타입 구체성이 느슨해져 백엔드 언어 간 상호 운용 문제가 생김. 특히 Node.js에서는 JSON.parse가 스키마 매핑 구현처럼 쓰임
    이를 제대로 하려면 스키마에서 인코더와 디코더를 명시적으로 생성해야 하고, 그러면 JS 맥락에서 JSON을 쓰는 이점이 어느 정도 줄어듦

    • 성능을 걱정해서 gRPC를 고려할 정도라면, 동시에 Node.js를 스택 근처에 두는 건 왜 괜찮은지 궁금함
    • 동의하며, JS 스키마 문제에는 Zod가 큰 도움이 됨
      Microsoft의 TypeSpec 프로젝트도 지켜보고 있음: typespec.io
    • proto의 주된 장점은 여러 언어 사이의 상호 운용성
      단일 언어 기술 스택이라면 중요성이 줄어듦. 그리고 Google의 주요 언어 밖을 쓰면 경험이 그만큼 좋지 않을 가능성이 큼
    • 2023년에 데이터센터 RPC 용도의 비 TCP 기반 프로토콜인 Homa에 관한 발표가 있었음: https://youtu.be/xQQT8YUvWg8?si=g3u5TogBe0_QpPpj
  • gRPC는 Google 바깥의 나머지 사람들에게 불필요하게 접근성이 낮게 느껴졌음
    gRPC JS 클라이언트는 쓸데없이 무겁고 꽤 불투명함. 아이디어는 좋지만, REST의 “단순함”에 익숙한 사람들에 비하면 실행이 아쉬움

    • 프런트엔드/백엔드 경계에서 REST/JSON 진영과 RPC/protobuf/gRPC 진영이 충돌함
      RPC는 데이터 모델의 카디널리티나 관계를 하나의 규정적 패턴에 억지로 끼워 맞추지 않아도 되기 때문에 의미론적으로 더 유지보수하기 쉬움. API가 빠르게 변하는 세계에서 아름다운 RESTful 엔터티를 맞히기는 어렵고, 큰 팀과 변하는 요구사항/소유권에서는 서비스 중심 설계가 더 낫다
      프런트엔드 쪽은 백엔드 시스템을 유지보수하지 않음. 이해하기 쉬운 API를 원하고, REST로 추상화할 수 있는 엔터티를 원함. 그런 설계의 최종 수혜자임
      REST에 드는 노력은 API를 판매하고 서드파티 개발자가 주요 고객인 회사에서는 의미가 있음
      백엔드 개발에는 protobuf와 이진 와이어 인코딩이 더 쉽다. API를 정의해 정적 타입 방식으로 서비스 간 공유할 수 있고, 메시지 인코딩/디코딩 시간도 줄어듦. JSON은 의미론적이지도 타입화되어 있지도 않고 오버헤드도 큼
      반대로 프런트엔드는 텍스트와 JSON을 네이티브하게 다룸. protobuf 정의를 내려받거나 이진 데이터를 이등 시민처럼 다루고 싶어 하지 않고, 도구와도 깔끔하게 맞지 않음
      gRPC에는 라우팅, 재시도, 부가 채널, 스트리밍, 프로토콜 폐기 의미가 잘 들어 있지만, 프런트엔드에는 거의 드러나지 않음. 전부 백엔드 소비자를 위한 것임
      결국 100% 프런트엔드/백엔드 도구 격차이고, 인터페이스와 사용성의 불일치임
    • REST는 HTML과 비슷함
      기본적으로 소스를 볼 수 있고, 사람이 읽을 수 있으며, 검사하기 쉬움
      gRPC는 기계끼리 효율적으로 대화하기 위한 것이고, 코딩이든 요청/응답 검사든 사람이 끼어들면 약간 불편함
      맥락과 목표가 달랐기 때문에 이런 사용성 차이는 이해됨
    • 공식 gRPC JavaScript 구현은 좀 별로임
      내가 보기엔 buf.build 쪽 구현이 좋음
      https://buf.build/blog/protobuf-es-the-protocol-buffers-type...
    • gRPC는 좋은 아이디어지만 내가 갖고 있지 않은 Google식 문제에 대한 해법들로 무거워졌음
      계약이 있는 이진 RPC 같은 프로토콜이 필요해 보여 많은 것이 선택한 듯하지만, GoLang에서 멀어질수록 점점 나빠짐
    • gRPC가 빛나는 용도는 있음. 스트리밍이 그중 하나로, 하나의 “연결” 안에서 메시지 스트림을 투명하게 보낼 수 있음
      단순 CRUD 서비스라면 REST로도 충분함