API '프로토콜' 일곱 가지: 사실은 세 개의 다른 층위
MQTT 표준 문서를 열어보면 이런 문장이 나옵니다. "작은 코드 풋프린트가 필요하거나 네트워크 대역폭이 부족한, 제약된 환경에서의 사용에 이상적이다(OASIS MQTT 5.0)." 센서 하나가 배터리로 몇 달을 버텨야 하는 사물인터넷 기기 이야기입니다. 그런데 이 프로토콜이 REST·GraphQL·gRPC와 나란히 "웹 API를 배우려면 알아야 할 통신 방식"으로 묶여 소개되는 경우를 종종 봅니다.
저는 이 나열이 이상하다고 봅니다. 일곱 개를 하나씩 원문 사양까지 열어보면, 같은 종류의 물건이 하나도 없기 때문입니다.
일곱 개가 사실은 세 층위로 갈린다
REST는 건축 양식이고, GraphQL은 질의 언어이고, gRPC는 그 위에 얹는 RPC 프레임워크입니다. WebSocket과 MQTT만 실제로 IETF·OASIS가 정한 통신 프로토콜이고, SSE는 그냥 평범한 HTTP 위에 브라우저가 얹은 관행이며, 웹훅은 사양 문서 자체가 없습니다. "프로토콜을 고른다"는 말 한마디로 이 일곱 개를 같은 질문의 답으로 취급하면, 정작 결정을 가르는 성질(연결이 오래 유지되는가, 방향이 한쪽인가 양쪽인가, 기기가 얼마나 제약됐는가)이 가려집니다.
REST는 프로토콜이 아니라 지켜야 할 제약의 목록이다
REST(Representational State Transfer)는 로이 필딩이 2000년 박사 학위 논문에서 정의한 건축 양식입니다(필딩 논문 5장). 클라이언트-서버 분리, 무상태성, 캐시 가능성, 계층화된 시스템, 균일한 인터페이스 같은 제약 조건의 집합이지, 특정 전송 방식을 지정하지 않습니다. 이 논문에는 "HTTP"라는 단어조차 REST의 정의로 등장하지 않습니다. HTTP 위에서 구현하는 것이 압도적인 관행이 됐을 뿐, REST 자체가 HTTP를 요구한 적은 없습니다.
이 구분이 실무에 미치는 영향은 분명합니다. "REST API를 만든다"는 말은 흔히 "GET·POST·PUT·DELETE를 자원 경로에 붙인 HTTP API를 만든다"는 뜻으로 쓰이는데, 이건 REST의 제약 중 하나(균일한 인터페이스)를 HTTP라는 한 가지 전송 수단으로 구현한 결과물입니다. 나머지 제약(캐시 가능성, 계층화)을 지키지 않은 채 URL 구조만 자원 지향으로 짜도 여전히 "REST API"라고 부르는 경우가 많은데, 그건 정확히는 필딩이 정의한 REST가 아니라 그 일부만 흉내 낸 것입니다.
GraphQL은 통신 방식이 아니라 질의 언어다
GraphQL은 공식 사양에서 스스로를 "API를 위한 언어"로 소개합니다(graphql.org). 질의 언어와 타입 시스템이 본체이고, 그 질의를 어떤 전송 수단으로 실어 나를지는 사양이 정하지 않습니다. 실무에서는 거의 항상 HTTP POST 한 요청에 질의문을 실어 보내는 방식을 쓰지만, 이건 GraphQL이 강제한 것이 아니라 생태계가 정착시킨 관행입니다.
이 차이가 만드는 실무 문제가 하나 있습니다. REST는 자원 하나(엔드포인트 하나)마다 응답을 캐시할 수 있는 URL이 있지만, GraphQL은 질의문 하나가 곧 요청 하나라 URL 기반 캐시가 거의 무력화됩니다. 클라이언트가 원하는 필드만 골라 받을 수 있다는 유연성의 대가로, 서버가 흔히 겪는 문제도 따로 있습니다. 중첩된 질의 하나가 내부적으로 데이터베이스 질의를 N+1번 실행시키는 경우가 그것으로, GraphQL 서버 구현체 대부분이 이 문제를 완화하는 배치 로딩 계층을 따로 둡니다. 질의 언어의 유연함과 그 유연함을 감당하는 서버 쪽 설계는 별개의 숙제입니다.
gRPC는 HTTP/2 위에 얹은 RPC 프레임워크다
gRPC는 원격 메서드를 로컬 함수처럼 호출하게 해주는 RPC 프레임워크입니다(grpc.io). 기본 직렬화 방식은 구글의 Protocol Buffers이고, 전송 계층은 HTTP/2입니다. HTTP/2가 지원하는 하나의 연결 위 다중 스트림(멀티플렉싱)을 그대로 활용해, 여러 요청이 헤드 오브 라인 블로킹 없이 동시에 오갈 수 있습니다.
이 설계가 곧 gRPC의 경계이기도 합니다. 브라우저는 HTTP/2 프레임을 자바스크립트로 직접 다루게 해주지 않기 때문에, 웹 프런트엔드에서 gRPC를 쓰려면 gRPC-Web이라는 별도 계층과 프록시를 거쳐야 합니다. 그래서 gRPC는 마이크로서비스 사이의 내부 통신에는 자주 쓰이지만, 브라우저와 직접 마주하는 공개 API에는 REST나 GraphQL보다 드뭅니다. 프레임워크의 성능이 아니라 브라우저 환경의 제약이 이 선택을 가릅니다.
WebSocket과 MQTT는 진짜 프로토콜이지만 태생이 다르다
일곱 개 중 IETF나 OASIS 같은 표준화 기구가 문서 하나로 정의한 진짜 통신 프로토콜은 이 둘뿐입니다. WebSocket은 RFC 6455로 정의되어 있고(IETF), HTTP 요청 하나로 연결을 업그레이드한 뒤 그 위에서 양방향으로 메시지를 주고받는 지속 연결입니다. 채팅, 실시간 공동 편집처럼 클라이언트도 서버만큼 자주 말을 걸어야 하는 상황을 위해 태어났습니다.
MQTT는 완전히 다른 문제에서 출발합니다. 클라이언트-서버 발행/구독 메시징 프로토콜로, 앞서 인용한 사양 그대로 제약된 환경과 낮은 대역폭을 겨냥합니다. 브로커 하나에 여러 기기가 주제(topic)별로 구독하는 구조라 일대다 배포에 최적화돼 있고, 전송 오버헤드를 줄이는 데 사양 대부분을 씁니다. 웹 브라우저의 실시간 통신 목록에 이 프로토콜이 낄 자리는 원래 없습니다. 다만 산업 현장의 설비 데이터를 웹 대시보드로 끌어올 때는 MQTT 브로커와 웹 사이를 잇는 다리 역할로 여전히 자주 등장합니다.
SSE는 프로토콜이 아니라 그냥 HTTP다
SSE(Server-Sent Events)는 별도의 프로토콜이 아니라 WHATWG HTML 표준의 한 절로 정의된 브라우저 기능입니다(WHATWG). 서버가 text/event-stream이라는 미디어 타입으로 응답을 열어 두고 그 위에 계속 텍스트를 흘려보내면, 브라우저의 EventSource 객체가 이를 받아 이벤트로 재구성합니다. 특별한 핸드셰이크도, 새 포트도 없이 평범한 HTTP 연결 하나를 길게 열어 두는 것이 전부입니다.
WebSocket과 가장 자주 비교되는데, 방향이 다릅니다. SSE는 서버에서 클라이언트로만 흐르는 단방향입니다. 클라이언트가 서버에 뭔가 보내야 한다면 별도의 일반 HTTP 요청을 또 보내야 합니다. 대신 연결이 끊기면 브라우저가 자동으로 재연결을 시도하고 마지막으로 받은 이벤트 ID부터 이어받는 재개 기능이 사양에 내장돼 있어서, 알림 피드나 진행 상황 스트리밍처럼 서버가 일방적으로 밀어주기만 하면 되는 상황에는 WebSocket보다 구현이 단순합니다.
웹훅은 프로토콜조차 아니다
일곱 개 중 유일하게 표준화 기구의 사양 문서가 아예 없는 것이 웹훅입니다. 이 단어는 2007년 제프 린제이가 프로그래밍의 "훅(hook)"이라는 표현에서 따와 만들었습니다(위키백과). 정의도 "사용자가 지정한 HTTP 콜백"이 전부입니다. 어떤 사건이 벌어지면 그 사건의 주체가 미리 등록된 URL로 평범한 HTTP 요청을 보내는 것, 그게 웹훅의 기술적 실체 전부입니다.
새 기술이 아니라 방향을 뒤집은 관행이라는 점이 중요합니다. REST API를 부를 때는 내 서버가 상대 서버에 요청을 겁니다. 웹훅은 그 반대로, 상대 서버가 내 서버에 요청을 겁니다. 그래서 웹훅을 받는 쪽은 REST API를 만들 때와 똑같은 고민(그 요청이 정말 보낸다고 주장하는 곳에서 온 게 맞는지 서명으로 검증하는 일, 중복으로 두 번 올 수 있다는 전제하에 멱등하게 처리하는 일)을 그대로 떠안습니다. 프로토콜이 새로 생긴 게 아니라 HTTP 클라이언트와 서버의 역할이 바뀌었을 뿐이라, 새로 배울 프로토콜은 없고 대신 놓치기 쉬운 책임만 하나 늘어납니다.
이름이 아니라 성질로 고른다
일곱 개를 나란히 놓고 "이 중 뭘 배워야 하나"라고 물으면 답이 안 나오는 이유가 이제 보입니다. 층위가 다른 것끼리 비교하고 있기 때문입니다. 저는 실제로 골라야 할 질문은 이름이 아니라 아래 네 가지 성질이라고 봅니다.
| 성질 | 해당하면 | 대표 선택지 |
|---|---|---|
| 연결을 계속 열어 둬야 하는가 | 그렇다 | WebSocket, SSE, MQTT |
| 방향이 서버에서 클라이언트 한쪽뿐인가 | 그렇다 | SSE, 웹훅 |
| 클라이언트가 원하는 필드를 골라야 하는가 | 그렇다 | GraphQL |
| 기기가 배터리·대역폭 제약을 받는가 | 그렇다 | MQTT |
이 넷 중 어느 것에도 해당하지 않는, 자원을 만들고 읽고 고치고 지우는 평범한 CRUD라면 REST가 기본값입니다. 캐시가 되고, 도구 생태계가 가장 두껍고, 새로 배울 게 가장 적습니다. gRPC는 이 표에 없는데, 성능 요구가 아니라 "브라우저와 직접 마주하지 않는 내부 서비스 간 호출인가"라는 별도의 질문에 답하는 물건이라 같은 축에 놓기 어렵습니다.
한 문장으로 정리하면, 이 목록은 "일곱 가지 API 프로토콜"이 아니라 "API를 이루는 세 가지 서로 다른 층위(설계 양식, 질의 언어, 실제 프로토콜)에서 각자 다른 문제를 풀던 것들의 모음"입니다. 이름을 먼저 고르지 말고 위 네 가지 질문에 먼저 답해야, 그 이름이 실제로 무엇을 약속하는지도 같이 보입니다.
함께 읽기
- 서버 시간대와 업무 날짜: UTC 기준 오늘이 하루 밀리는 문제제조업 ERP 데모에서 날짜 칸의 기본값을 만드는 코드는 한 줄이었습니다.
- Go 백엔드의 역사와 현재: 성능이 아니라 빌드 시간에서 시작한 언어컨테이너를 실행하는 Docker, 그 컨테이너를 배치하는 Kubernetes, 지표를 모으는 Prometheus, 인프라를 코드로 적는 Terraform, 시크릿을 보관하는 Vault. 클라우드 운영에 쓰는 도구를 늘어놓으면 대부분이 Go로 짜여 있습니다.
- 시나리오형 FAQ 챗봇: 오래된 워드프레스 홈페이지에 붙이는 방식과 비용홈페이지에 챗봇을 붙이고 싶다는 문의가 왔습니다. 조건은 셋이었습니다. 관리자가 직접 질문과 답을 등록하는 시나리오형일 것, 어떤 질문이 많이 눌리는지 통계가 있을 것, 그리고 홈페이지는 워드프레스라는 것.
- 멱등성: 메서드가 아니라 구현이 지키는 성질같은 요청을 두 번 보냈습니다.
- Vercel의 역사: ZEIT와 Now에서 Fluid compute까지Vercel은 흔히 "Next.js를 배포하는 곳"으로 알려져 있습니다. 현재의 결합을 보면 자연스러운 설명이지만, 제품의 출발점은 프레임워크 호스팅보다 단순했습니다. 명령어 하나로 애플리케이션을 인터넷에 올리고, 배포마다 고유한 주소를 부여하는 것이 첫 문제였습니다.