웹에서 서버와 통신하는 기본 방식은 HTTP다. 그리고 HTTP 위에서 API를 설계하는 가장 흔한 방식은 REST다. REST는 데이터를 자원(resource)으로 보고, URL과 HTTP 메서드를 조합해 다룬다. 사용자 1번을 지우는 일은 DELETE /users/1이 된다.
그런데 어떤 API는 자원보다 동작이 중심이다. “두 수를 빼라”, “이 작업을 취소하라"처럼 명령을 보내고 결과를 받는 일이 더 자연스러운 경우다. 이럴 때 자원 모델에 억지로 끼워 맞추는 대신, 원격 함수 호출이라는 다른 모델을 쓸 수 있다. JSON-RPC가 그 모델을 가장 단순하게 구현한 프로토콜이다.
RPC라는 개념
RPC는 Remote Procedure Call, 원격 프로시저 호출의 약자다. 네트워크 너머에 있는 함수를 마치 내 코드의 함수처럼 호출하는 방식을 말한다. 개발자가 보기에는 subtract(42, 23)라는 평범한 호출이지만, 실제로는 그 요청이 네트워크를 타고 다른 머신에서 실행되고 결과만 돌아온다.
REST가 “무엇을(자원)” 중심이라면 RPC는 “무엇을 하라(동작)” 중심이다. 같은 일을 두 모델로 표현하면 차이가 드러난다.
| 구분 | REST | RPC |
|---|---|---|
| 중심 개념 | 자원(resource) | 동작(method) |
| 표현 | URL + HTTP 메서드 | 메서드 이름 + 인자 |
| 사용자 삭제 | DELETE /users/1 | deleteUser(1) |
JSON-RPC가 정의하는 것
JSON-RPC가 정하는 것은 주고받는 메시지의 모양뿐이다. 어떤 길로 메시지를 실어 나를지는 정하지 않는다. 그래서 HTTP 위에서도, WebSocket 위에서도, 심지어 두 프로세스가 표준입출력(stdio)으로 대화할 때도 똑같이 쓸 수 있다. 전송 수단에 묶이지 않는다는 점이 이 프로토콜의 핵심 성격이다. 현재 표준은 JSON-RPC 2.0이다.
요청 메시지는 다음과 같이 생겼다.
{
"jsonrpc": "2.0",
"method": "subtract",
"params": [42, 23],
"id": 1
}
jsonrpc는 프로토콜 버전으로 항상 "2.0"이다. method는 호출할 함수 이름이고, params는 인자다. params는 위치 기반 배열([42, 23])로도, 이름 기반 객체({"minuend": 42, "subtrahend": 23})로도 보낼 수 있다. id는 이 요청을 식별하는 값으로, 응답을 요청과 짝지을 때 쓴다.
응답에는 결과 또는 오류 중 하나가 담긴다.
{ "jsonrpc": "2.0", "result": 19, "id": 1 }
{ "jsonrpc": "2.0", "error": { "code": -32601, "message": "Method not found" }, "id": 1 }
result와 error는 둘 중 정확히 하나만 들어간다. 둘 다 있거나 둘 다 없으면 규약 위반이다. 응답의 id는 요청의 id를 그대로 돌려준다. 여러 요청을 동시에 보내고 응답이 순서 없이 돌아올 때, 클라이언트는 이 id로 어떤 요청에 대한 답인지 맞춘다.
오류 코드 중 일부는 미리 정해져 있다. 파싱 실패는 -32700, 잘못된 요청 형식은 -32600, 없는 메서드는 -32601, 잘못된 인자는 -32602다. -32000부터 -32099까지는 서버 구현이 자유롭게 정의해 쓰는 구간이다.
응답이 없는 호출과 묶음 호출
요청에서 id를 빼면 알림(notification)이 된다. 서버는 처리만 하고 응답을 보내지 않는다. 결과가 필요 없는 단방향 통지에 쓴다.
여러 요청을 배열로 묶어 한 번에 보낼 수도 있다(batch). 응답도 배열로 오며, 이때 알림은 응답에서 빠진다. 왕복 횟수를 줄이고 싶을 때 유용하다.
어디에 쓰이나
JSON-RPC의 영향력은 단순함에서 나온다. 스펙이 짧아 어떤 언어로든 구현이 쉽고, 전송 수단을 가리지 않아 다양한 환경에 얹힌다. 이더리움 같은 블록체인 노드의 API가 대표적이고, 에디터와 언어 서버가 대화하는 Language Server Protocol(LSP), 그리고 AI 모델과 도구를 잇는 Model Context Protocol(MCP)도 JSON-RPC 2.0을 토대로 한다. 세 경우 모두 “자원"보다 “동작"을 주고받는 일이 본질이고, 전송 환경이 제각각이라는 공통점이 있다.
이점과 트레이드오프
이점은 단순함과 독립성이다. 배워야 할 규칙이 적고, 동작 중심 API를 자연스럽게 표현하며, 전송 수단을 바꿔도 메시지 구조는 그대로다.
감수하는 것도 있다. HTTP의 캐싱, 상태 코드, 표준 메서드 같은 기성 인프라의 이점을 그대로 누리기 어렵다. 단일 엔드포인트로 모든 호출이 들어오므로 URL만 보고 무슨 일이 일어나는지 파악하기 어렵고, 메서드와 인자 구조를 따로 문서화해야 한다. 자원을 CRUD로 다루는 API라면 REST가, 동작 명령을 전송 수단에 구애받지 않고 주고받아야 한다면 JSON-RPC가 더 자연스럽다.