RPC vs REST vs GraphQL

오랫동안 API개발자들은 RESTful API가 최고라고 생각하고 모든 표준을 RESTful 에 맞춰서 개발을 했어요. 그런데 최근에 RPC나 GraphQL과 같은 다양한 프로토콜에 관심이 옮겨지면서 이제 더이상 RESTful만 고집하던 API세상은 갔어요.

RPC API도 아래와 같이 RESTful API와 비슷한 형태로 API결과를 반환하는게 가능합니다.

GET /listConversations
GET /listMessages?id=123

그리고 JSON형식의 body데이타를 넘겨주는것도 가능합니다.

POST /sendMessages?id=2
{
    "body": "value"
}

물론 http가 아닌 RPC프로토콜을 이용해서 통신을 할뿐이죠. 많은 큰 규모의 테크회사들이 RPC를 이용하는 이유는 PRC통신이 굉장히 쉽고 high performance를 제공하기 때문이에요. 특히 내부적으로 복잡한 마이크로 서비스를 가진 회사들은 마이크로 서비스간에 수억, 수조의 통신이 발생하기 때문에 이걸 전부 http를 거쳐서 RESTful API로 통신하는것보다 군더더기 없는 RPC의 메세지가 훨씬 작아 더 경제적이고 성능면에서도 좋기때문에 최근들어 대규모시스템을 가진 회사들이 특히 RPC를 선호하는 경향이 있습니다.

하지만 RPC라고 전부 좋은것만은 아니에요. 일단 어떤 서비스에서 제공하는 함수를 가지고 외부서비스가 API를 만들기때문에 시스템간에 연결관계가 너무 돈독해집니다. 그러면 독립적으로 움질일때보다 변경이나 삭제등이 필요할때 신속하게 움직일수 없겠죠. 그리고 함수명이 그대로 드러나는 형태라서 각 마이크로서비스의 내부 시스템 디자인과 구현방식이 그대로 노출된다는 단점이 있습니다.

그리고, RPC API는 함수만 일차원적으로 나열된 형태이기 때문에 해당서비스를 이미 잘 알고 있지 않은 경우에 어디서 부터 시작해야할지 막막할수가 있어요. 그리고 그 함수들을 만들기가 너무 쉽기때문에 함수가 매우 많아질 가능성이 크고, 그렇게 되면 이름만으로 함수를 관리하기가 힘들어져서 함수가 중복될수도 있고, 새로 조인한 개발자는 엄청난 양의 함수를 이해하기 위해 많은 노력을 들여야 하는 경우도 생깁니다.

그 반면 RESTful는 최대한 각 서비스들이 독립적으로 운영되도록 디자인하기에 좋은 프로토콜입니다. 표준을 세우고, 그룹을 나누고 룰에 따라서 개발을 한다면 독립성을 유지할수 있는 좋은 프로토콜이지요. 하지만 개발자들은 종종 유혹에 빠집니다. RESTful API에 함수개념의 endpoint를 추가함으로써 RESTful도 RPC도 아닌 그 중간 어딘가의 기준하는 Restful..ish API가 결과적으로 나오는거죠.

RESTful API를 만드는 가장 이상적인 방법은 바로 Hypertext As The Engine Of Application State (HATEOAS)를 구현하는 것입니다. 이것은 클라이언트와 서버를 분리시키는데 목적을 둡니다. 서버와 클라이언트간에 소통되는 모든 디테일이 한눈에 보여야합니다. 별도의 설명서 없이 API만으로도 모든 기능이 이해가 되고 사용이 가능해야하는거죠. 예를 들면 endpoint를 모르면 그냥 /루트 path만 쳐도 거기서 부터 시작해서 어디로 가면 되는지자체가 API를 통해 가이드가 되는거죠. 이걸 이렇게 잘 관리한다는거는 정말 어려운 일이에요. 아주 잘 만들어진 API도 이걸 전부 지키기는 정말 힘들기때문에 잘 못합니다. 하지만 할수만 있다면 정말 완벽한 API를 만드는데 아주 큰 일조를 하게 될 것입니다.

GraphQL은 RESTful과 RPC의 장점을 조합해서 쿼리를 통해 정확하게 내가 필요한 정보만 요청해서 받을수 있는 API타입입니다. 아래는 GraphQL의 Schema 정의의 예제입니다.

type Query {
    listConversations: [Conversation]
}
type Mutation {
    sendMessage(text: String): Message
}
type Conversation {
    id: Int
    title: String
    messages: [Message]
}
type Message {
    id: ID
    text: String
    author: User
}
type User {
    id: ID
    name: String
}

GraphQL을 구현하기 위해서는 API에서 반환하는 모든 데이타를 위와 같이 타입으로 만들어 놔야합니다. 그러면 GraphQL클라이언트는 정의된 데이타 타입 중 필요한 타입만 아래와 같이 쿼리를 할수가 있어요.

{
  listConversations {
    titie,
    messages {
      text
    }
  }
}

그러면 서버는 요청한 칼럼만 결과로 반환하게 됩니다.

{
  "data": {
    "listConversations": [
      {
        "title": "vim or emacs?",
        "messages": [
          {
            "text": "Real programmers use Notepad."
          }
        ]
      }
    ]
  }
}

GraphQL방식을 이용하면 RESTful이 사용하는 프로토콜을 이용하지만 마치 RPC에서 제공하는 함수를 쓰는것처럼 필요한 함수를 호출하고, 필요한 칼럼만 선택적으로 받기때문에 불필요한 데이타를 전송해야하는 네트워크 트래픽을 절약할수 있어요. 페이스북은 접속과 동시에 엄청나게 큰 쿼리를 단 한번 호출합니다. 그 안에 필요한 모든 데이타가 다 들어있어서 추가적인 요청을 안해도 되기때문에 사용자가 인터넷이 지속적으로 제공되지 않는 장소에 있을때 유용합니다.

아래의 표는 각 API방식이 주는 장단점을 나열한 표입니다. 개발하시는데 도움이 되시길 바랍니다.

Source: Nate Barbettini – API Throwdown: RPC vs REST vs GraphQL, Iterate 2018