Apidog

올인원 협업 API 개발 플랫폼

API 설계

API 문서

API 디버깅

API 모킹

자동화 테스트

RPC, REST, GraphQL 명확하게 설명

Rebecca Kovács

Rebecca Kovács

Updated on May 20, 2025

현대 소프트웨어 개발에서 애플리케이션은 거의 독립적으로 존재하지 않습니다. 애플리케이션들은 서로 통신하고 데이터를 교환하며 다른 애플리케이션에서 액션을 트리거하여 광범위하게 상호 연결된 생태계를 형성합니다. 이러한 통신은 서로 다른 소프트웨어 구성 요소가 상호 작용하는 방식에 대한 규칙과 프로토콜을 정의하는 애플리케이션 프로그래밍 인터페이스(API)에 의해 조율됩니다. 수십 년 동안 이러한 서비스 간 통신을 용이하게 하기 위해 여러 아키텍처 스타일과 프로토콜이 등장했습니다. 가장 대표적인 것들로는 원격 프로시저 호출(RPC), 표현 상태 전송(REST), 그리고 GraphQL이 있습니다.

분산 시스템을 설계하는 모든 개발자나 아키텍트에게 이 세 가지 패러다임을 이해하는 것은 매우 중요합니다. 각 패러다임은 고유의 철학, 강점, 약점, 그리고 이상적인 사용 사례를 가지고 있습니다. 이 글은 RPC, REST, GraphQL을 명확하게 설명하고, 핵심 개념, 작동 방식, 장점, 단점, 그리고 각 방식이 빛을 발하는 시나리오를 깊이 있게 다루는 것을 목표로 합니다.

💡
멋진 API 문서를 생성하는 훌륭한 API 테스트 도구를 원하십니까?

개발팀이 최대한의 생산성으로 함께 작업할 수 있는 통합된 올인원 플랫폼을 원하십니까?

Apidog는 당신의 모든 요구를 충족시키며, 훨씬 더 저렴한 가격으로 Postman을 대체합니다!
button

기본: 클라이언트-서버 통신

세부 사항을 살펴보기 전에, 이 모든 방식이 기반으로 하는 근본적인 모델인 클라이언트-서버 통신을 이해하는 것이 중요합니다. 이 모델에서 클라이언트(예: 웹 브라우저, 모바일 앱, 다른 서버)는 특정 데이터를 필요로 하거나 어떤 작업을 수행하고자 합니다. 동시에 서버(원격 머신 또는 프로세스)는 해당 데이터를 호스팅하거나 작업을 수행하는 로직을 가지고 있습니다. 클라이언트는 서버에 요청을 보내고, 서버는 응답을 다시 보냅니다. 우리가 논의할 메커니즘들, 즉 RPC, REST, GraphQL은 이러한 요청과 응답을 구성하는 서로 다른 방식입니다.

RPC: 네트워크를 통한 함수 호출

RPC란 무엇인가?

원격 프로시저 호출(RPC)은 프로세스 간 통신을 위한 가장 초기이자 가장 직접적인 패러다임 중 하나입니다. 근본적인 아이디어는 원격 서버에 대한 요청이 마치 로컬 함수나 프로시저 호출처럼 보이게 하고 작동하게 만드는 것입니다. 클라이언트 애플리케이션은 로컬 함수("프로시저")처럼 보이는 것을 호출하지만, 이 함수의 실행은 실제로는 원격 서버에서 발생합니다. 네트워크 통신의 복잡성은 신중하게 추상화되어, 분산 프로그래밍이 전통적인 단일 머신 프로그래밍과 유사한 단순함을 가지게 합니다.

RPC 작동 방식:

RPC 프로세스는 원격 실행을 투명하게 만들기 위해 조정된 일련의 단계를 통해 진행됩니다. 처음에는 클라이언트가 원격 프로시저에 대한 "스텁" 또는 "프록시"를 가지고 있습니다. 이 스텁은 실제 원격 프로시저의 시그니처를 반영합니다. 클라이언트 애플리케이션이 이 스텁을 호출할 때, 로직은 로컬에서 실행되지 않습니다. 대신 클라이언트 스텁은 함수에 전달된 매개변수를 가져와 "마샬링" 또는 "직렬화"합니다. 이 중요한 단계는 매개변수를 메모리 내 표현에서 바이너리, XML, JSON과 같이 네트워크 전송에 적합한 형식으로 변환합니다.

마샬링 후, 이 직렬화된 매개변수는 호출될 특정 프로시저에 대한 식별자와 함께 네트워크를 통해 서버로 전송됩니다. 서버 측에서는 "스켈레톤" 또는 서버 측 스텁이 수신 요청을 기다리고 받습니다. 이 서버 스켈레톤은 수신된 데이터를 "언마샬링" 또는 "역직렬화"하여 실제 서버 프로시저가 예상하는 매개변수로 다시 변환하는 작업을 수행합니다.

매개변수가 성공적으로 재구성되면, 서버 스켈레톤은 서버에서 지정된 프로시저를 호출하고 언마샬링된 매개변수를 전달합니다. 프로시저 실행이 완료되면, 반환 값과 발생한 모든 예외는 서버 스켈레톤에 의해 마샬링됩니다. 이 직렬화된 응답은 네트워크를 통해 클라이언트 스텁으로 다시 전송됩니다. 응답을 수신하면 클라이언트 스텁은 이를 언마샬링하여 클라이언트 애플리케이션이 쉽게 이해할 수 있는 반환 값으로 데이터를 다시 변환합니다. 마지막으로 클라이언트 스텁은 이 값을 원래 호출 코드로 반환하여 로컬 함수 호출이 수행된 것처럼 보이는 착각을 완성합니다.

RPC의 주요 특징:

RPC API는 일반적으로 액션 중심입니다. addUser(userDetails) 또는 calculatePrice(itemId, quantity)와 같은 동사 또는 명령어를 중심으로 설계됩니다. 주요 초점은 "수행할 수 있는 액션"에 있습니다.

전통적으로 RPC 시스템의 클라이언트와 서버는 강한 결합을 보입니다. 클라이언트는 서버에서 사용 가능한 특정 함수 이름과 정확한 매개변수 시그니처에 대한 명시적인 지식이 필요한 경우가 많습니다. 결과적으로 서버 측 수정은 클라이언트 측의 해당 변경을 자주 필요로 합니다.

RPC 프레임워크는 일반적으로 공유 인터페이스 정의 언어(IDL)에서 다양한 프로그래밍 언어로 클라이언트 스텁과 서버 스켈레톤을 생성하는 도구를 제공합니다. IDL의 예로는 CORBA IDL, Protocol Buffers(.proto 파일), Apache Thrift IDL이 있습니다. 이러한 코드 생성 기능은 상호 운용성을 촉진합니다.

효율성 측면에서, 많은 RPC 프로토콜, 특히 바이너리 형식을 사용하는 프로토콜은 데이터 크기와 처리 속도 면에서 최적의 성능을 위해 설계되었습니다.

진화: gRPC

XML-RPC 또는 Java RMI와 같은 초기 RPC 구현에는 특정 제약이 있었지만, gRPC(Google RPC)와 같은 현대적인 프레임워크의 등장으로 RPC 패러다임은 상당한 부활을 경험했습니다. gRPC는 상당한 개선을 도입했습니다. 주로 Protocol Buffers를 IDL 및 메시지 직렬화에 사용합니다. Protocol Buffers는 구조화된 데이터를 직렬화하기 위한 언어 독립적이고 플랫폼 중립적이며 확장 가능한 메커니즘을 제공하며, 종종 XML보다 더 작고 빠르며 간단한 대안으로 설명됩니다.

또한 gRPC는 HTTP/2 위에서 작동하며, 이는 멀티플렉싱(단일 연결을 통해 여러 요청 및 응답 허용), 서버 푸시 기능, 헤더 압축과 같은 고급 기능을 가능하게 합니다. 이러한 기능들은 종합적으로 성능 향상과 지연 시간 감소에 기여합니다.

gRPC의 주목할 만한 강점은 다양한 스트리밍 모드를 지원한다는 것입니다. 여기에는 단방향(단순 요청-응답 패턴), 서버 스트리밍(클라이언트가 요청을 보내고 서버가 메시지 스트림으로 응답), 클라이언트 스트리밍(클라이언트가 메시지 스트림을 보내고 서버가 단일 응답을 발행), 양방향 스트리밍(클라이언트와 서버 모두 독립적으로 메시지 스트림을 보낼 수 있음)이 포함됩니다.

마지막으로 gRPC는 코드 생성을 위한 강력한 도구를 제공하여 다양한 인기 프로그래밍 언어로 클라이언트 및 서버 코드를 자동으로 생성할 수 있게 합니다.

RPC의 장점 (특히 gRPC와 같은 최신 RPC):

  • 성능: 특히 Protocol Buffers와 같은 바이너리 프로토콜을 HTTP/2와 함께 사용할 때 매우 뛰어난 성능을 보일 수 있습니다. 낮은 지연 시간은 특징적인 장점입니다.
  • 단순성 (개발자에게): 원격 호출을 로컬 함수처럼 추상화하는 것은 개발 노력을 크게 단순화할 수 있으며, 특히 내부 마이크로서비스에 유용합니다.
  • 강력한 타입 계약: IDL은 클라이언트와 서버 간에 명확하고 모호하지 않은 계약을 강제하여 컴파일 타임에 통합 오류를 포착하는 데 도움이 됩니다.
  • 스트리밍 기능: 실시간 데이터 흐름이나 대규모 데이터 세트 전송이 필요한 시나리오에서 탁월합니다.
  • 코드 생성: 클라이언트 라이브러리 및 서버 스텁의 자동 생성은 개발자가 작성해야 하는 상용구 코드의 양을 줄입니다.

RPC의 단점:

  • 강한 결합: IDL을 사용하더라도 프로시저 시그니처 변경은 종종 클라이언트 및 서버 코드의 재생성과 재배포를 필요로 합니다.
  • 탐색성: REST와 달리, IDL 또는 관련 문서에 사전 접근하지 않고는 사용 가능한 프로시저나 그 구조를 발견하는 표준화된 방법이 없습니다.
  • 브라우저 친화성 부족 (과거): 전통적인 RPC 메커니즘은 REST에 비해 웹 브라우저와 직접 통합하기가 간단하지 않았습니다. gRPC-Web이 이 간극을 메우려 하지만, 일반적으로 프록시 계층이 필요합니다.
  • 방화벽 통과: HTTP 기반이 아닌 RPC 메커니즘은 주로 HTTP 트래픽을 허용하도록 구성된 방화벽에서 때때로 어려움을 겪을 수 있습니다. gRPC는 HTTP/2를 사용하여 이러한 우려를 크게 완화합니다.

RPC는 언제 사용해야 하는가:

성능과 낮은 지연 시간이 중요한 설계 목표인 내부 마이크로서비스 통신에 RPC를 고려하십시오. 복잡하고 고성능의 데이터 스트리밍이 필요한 애플리케이션에도 적합합니다. 서비스 간에 명확하게 정의되고 강력한 타입의 계약이 필요하다면 RPC는 상당한 이점을 제공합니다. 여러 언어에 대한 코드 생성이 개발을 간소화할 수 있는 다중 언어 환경에서도 RPC는 이점을 제공합니다. 마지막으로 메시지 크기의 효율성이 가장 중요한 네트워크 제약 환경에서 RPC, 특히 Protocol Buffers를 사용하는 gRPC는 강력한 후보입니다.

REST: 리소스와 하이퍼미디어

REST란 무엇인가?

REST, 즉 표현 상태 전송(Representational State Transfer)은 프로토콜이나 엄격한 표준이 아니라, 네트워크 애플리케이션 설계를 위한 아키텍처 스타일입니다. 이는 2000년 Roy Fielding의 박사 학위 논문에서 세심하게 정의되었습니다. REST는 HTTP의 기존 기능과 프로토콜을 능숙하게 활용하며, 무상태(stateless), 클라이언트-서버, 캐시 가능한 통신 모드에 중점을 둡니다. 핵심 개념은 URL로 고유하게 식별되는 리소스(데이터 엔티티)와 표준 HTTP 메서드를 사용하여 이러한 리소스와의 상호 작용이 수행된다는 것입니다.

REST의 핵심 원칙 (제약 조건):

REST 아키텍처 스타일은 몇 가지 지침 제약 조건에 의해 정의됩니다.

근본적인 원칙은 클라이언트-서버 아키텍처입니다. 이는 관심사의 명확한 분리를 요구합니다. 클라이언트는 사용자 인터페이스 및 사용자 경험 측면을 담당하고, 서버는 데이터 저장, 비즈니스 로직 및 API 자체 제공을 관리합니다.

또 다른 중요한 제약 조건은 무상태성(Statelessness)입니다. 클라이언트에서 서버로 전송되는 모든 요청은 서버가 해당 요청을 이해하고 처리하는 데 필요한 모든 정보를 포함해야 합니다. 서버는 개별 요청 간에 클라이언트 컨텍스트(세션 상태)를 유지하지 않습니다. 세션과 관련된 모든 상태는 클라이언트 측에서 유지됩니다.

캐시 가능성(Cacheability) 또한 핵심 원칙입니다. 서버가 생성하는 응답은 캐시 가능한지 또는 캐시 불가능한지를 명시적으로 정의해야 합니다. 이를 통해 클라이언트 및 중간 시스템(콘텐츠 전송 네트워크 또는 CDN 등)이 응답을 캐시할 수 있어 성능과 확장성을 크게 향상시킬 수 있습니다.

REST 시스템은 계층화된 시스템으로 설계됩니다. 이는 클라이언트가 통신 경로를 따라 최종 서버에 직접 연결되어 있는지 또는 중간 시스템(로드 밸런서 또는 프록시 등)에 연결되어 있는지 일반적으로 확인할 수 없음을 의미합니다. 중간 서버는 로드 밸런싱을 용이하게 하고 공유 캐시를 제공하여 시스템 확장성을 강화할 수 있습니다.

균일한 인터페이스(Uniform Interface) 제약 조건은 RPC를 가장 잘 구별하고 아키텍처를 단순화하며 분리하는 역할을 합니다. 이 제약 조건은 다시 여러 하위 제약 조건으로 나뉩니다.

첫째, 리소스 식별: 모든 개념적 리소스는 URI(Uniform Resource Identifiers), 일반적으로 URL을 사용하여 요청 내에서 식별됩니다. 예를 들어, /users/123은 특정 사용자 리소스를 고유하게 식별합니다.

둘째, 표현을 통한 리소스 조작: 클라이언트는 리소스에 대해 직접 메서드를 호출하는 방식이 아니라, 이러한 리소스의 표현을 교환함으로써 리소스와 상호 작용합니다. 표현은 JSON, XML, HTML과 같은 다양한 형식일 수 있습니다. 클라이언트는 Accept 헤더를 사용하여 선호하는 형식을 나타내고, 서버는 Content-Type 헤더를 사용하여 전송된 표현의 형식을 지정합니다.

셋째, 자체 설명 메시지: 교환되는 각 메시지는 어떻게 처리되어야 하는지를 설명하는 충분한 정보를 포함해야 합니다. 예를 들어, Content-Type 및 Content-Length와 같은 HTTP 헤더는 메시지 본문에 대한 메타데이터를 제공하며, 상태 코드는 클라이언트에게 요청의 결과를 알려줍니다.

넷째, 애플리케이션 상태 엔진으로서의 하이퍼미디어(HATEOAS): 이 원칙은 종종 REST의 가장 정교하고 때로는 가장 적게 구현되는 측면으로 간주되며, 서버 응답에 링크(하이퍼미디어)가 포함되어야 한다고 규정합니다. 이 링크는 클라이언트가 다음에 어떤 작업을 수행할 수 있는지 또는 어떤 관련 리소스에 접근할 수 있는지를 나타내어 클라이언트를 안내합니다. 이를 통해 클라이언트는 하드코딩된 URI에 의존하는 대신 API를 동적으로 탐색할 수 있습니다. 예를 들어, 사용자 리소스에 대한 응답에는 해당 사용자의 주문을 보거나 프로필 세부 정보를 업데이트하는 링크가 포함될 수 있습니다.

선택적 제약 조건은 온디맨드 코드(Code on Demand)입니다. 이를 통해 서버는 JavaScript 스니펫과 같은 실행 가능한 코드를 전송하여 클라이언트의 기능을 일시적으로 확장하거나 사용자 정의할 수 있습니다.

REST 작동 방식:

RESTful 시스템에서 모든 것은 리소스(예: 사용자, 제품, 주문)로 개념화됩니다. 각 리소스는 URI로 고유하게 식별됩니다. 예를 들어, GET /users는 사용자 목록을 검색하고, GET /users/123은 ID 123을 가진 특정 사용자를 검색할 수 있습니다.

이러한 리소스에 대한 작업을 수행하기 위해 표준 HTTP 메서드(동사)가 사용됩니다. GET은 리소스를 검색하는 데 사용됩니다. POST는 일반적으로 새로운 리소스를 생성하거나 프로세스를 트리거하는 데 사용되며, 새로운 리소스에 대한 데이터는 요청 본문에 전송됩니다. PUT은 기존 리소스를 업데이트하는 데 사용되며, 일반적으로 리소스 데이터의 전체 교체를 요구하고 멱등성(idempotent)을 가집니다 (여러 번 동일한 요청을 해도 한 번의 요청과 같은 효과). DELETE는 리소스를 제거합니다. PATCH는 기존 리소스에 대한 부분 업데이트를 허용합니다. HEAD는 리소스에 대한 메타데이터를 검색하며, GET과 유사하지만 응답 본문은 없습니다. OPTIONS는 대상 리소스에 대한 통신 옵션 정보를 얻는 데 사용됩니다.

HTTP 표준의 일부인 상태 코드(Status Codes)는 요청의 결과를 나타내는 데 사용됩니다. 예로는 200 OK, 성공적인 생성을 위한 201 Created, 클라이언트 오류를 위한 400 Bad Request, 리소스가 없을 때의 404 Not Found, 서버 측 문제를 위한 500 Internal Server Error가 있습니다.

데이터 형식(Data Formats) 측면에서 JSON(JavaScript Object Notation)은 가벼운 특성과 파싱 용이성으로 인해 REST API에서 데이터를 교환하는 가장 일반적인 형식이 되었습니다. 하지만 XML, HTML 또는 일반 텍스트도 활용될 수 있습니다.

REST의 장점:

  • 단순성과 친숙성: 잘 이해되는 HTTP 표준을 활용하여 학습, 구현 및 사용이 비교적 쉽습니다.
  • 무상태성: 서버가 요청 간에 클라이언트 세션 정보를 유지할 필요가 없으므로 서버 설계가 단순화되고 확장성이 향상됩니다.
  • 캐시 가능성: HTTP 캐싱 메커니즘을 직접적이고 효과적으로 활용하여 성능을 개선하고 서버 부하를 줄일 수 있습니다.
  • 분리: 클라이언트와 서버는 분리됩니다. 리소스 URI와 메서드 계약이 일관성을 유지하는 한, 양측의 기본 구현은 독립적으로 발전할 수 있습니다.
  • 탐색성 (HATEOAS 사용 시): HATEOAS가 제대로 구현되면 클라이언트는 사용 가능한 작업을 동적으로 발견하고 리소스를 탐색할 수 있어 API가 더 유연하고 발전 가능하게 됩니다.
  • 광범위한 채택 및 도구: RESTful API를 지원하는 방대한 도구, 라이브러리, 클라이언트 SDK 및 게이트웨이 생태계가 있습니다. 이는 본질적으로 브라우저 친화적입니다.
  • 사람이 읽을 수 있는 형식: URI는 종종 사람이 읽을 수 있도록 설계되며, JSON과 같은 일반적인 데이터 형식은 개발자가 검사하고 디버깅하기 쉽습니다.

REST의 단점:

  • 과다 가져오기(Over-fetching) 및 부족 가져오기(Under-fetching): 이는 흔한 문제입니다.
  • 과다 가져오기는 특정 작업에 대해 클라이언트가 실제로 필요한 데이터보다 더 많은 데이터를 엔드포인트가 반환할 때 발생합니다. 예를 들어, 사용자 이름 목록을 표시하기 위해 /users 엔드포인트는 모든 사용자에 대해 주소, 전화번호 및 기타 세부 정보를 포함한 전체 사용자 객체를 반환할 수 있으며, 이 중 대부분은 사용되지 않을 수 있습니다.
  • 부족 가져오기는 클라이언트가 전체 뷰를 위해 필요한 모든 데이터를 수집하기 위해 서로 다른 엔드포인트에 여러 요청을 해야 할 때 발생합니다. 예를 들어, 사용자의 세부 정보와 최근 게시물을 얻기 위해 클라이언트는 먼저 /users/{id}를 호출한 다음 /users/{id}/posts에 별도의 호출을 해야 할 수 있습니다.
  • 다중 왕복: 부족 가져오기 문제는 종종 여러 네트워크 왕복을 유발하여 지연 시간을 증가시키고 사용자 경험에 부정적인 영향을 미칠 수 있으며, 특히 모바일 또는 불안정한 네트워크에서 그렇습니다.
  • 버전 관리의 어려움: 기존 클라이언트를 손상시키지 않고 REST API를 발전시키는 것은 어려울 수 있습니다. 일반적인 전략으로는 URI 버전 관리(예: /v1/users), 버전 관리를 위한 사용자 지정 요청 헤더 사용, 또는 미디어 타입 버전 관리(Accept 헤더를 통해)가 있습니다. 각 접근 방식에는 고유한 복잡성과 절충점이 있습니다.
  • HATEOAS 종종 무시됨: REST의 핵심 원칙임에도 불구하고 HATEOAS는 종종 완전히 구현되지 않습니다. 이는 REST가 제공하려는 진정한 탐색성과 동적 발전 가능성을 제한합니다.
  • 기본적으로 강력한 타입 없음: IDL을 사용하는 RPC 시스템과 달리 REST는 API 계약 정의를 위해 규칙 및 외부 문서(예: OpenAPI/Swagger 사양)에 의존합니다. 이는 컴파일 타임에 항상 강제되지 않아 통합 문제가 발생할 수 있습니다.

REST는 언제 사용해야 하는가:

REST는 광범위한 채택, 통합 용이성 및 상호 운용성이 중요한 공개 API에 탁월한 선택입니다. 엔티티에 대한 표준 CRUD(생성, 읽기, 업데이트, 삭제) 작업이 주요 상호 작용 모드를 형성하는 리소스 중심 애플리케이션에 매우 적합합니다. 성능 및 확장성을 위해 HTTP 캐싱을 활용하는 것이 중요하다면, HTTP 표준과의 REST의 일치는 상당한 이점입니다. 무상태성 및 수평적 확장성이 요구되는 상황에서도 RESTful 아키텍처 스타일은 큰 이점을 제공합니다. 또한 클라이언트가 API를 동적으로 탐색할 수 있도록 하이퍼미디어(HATEOAS)를 통한 탐색성이 필요한 경우, REST는 이를 위한 프레임워크를 제공합니다.

GraphQL: API를 위한 쿼리 언어

GraphQL이란 무엇인가?

GraphQL은 API를 위해 특별히 설계된 쿼리 언어이며, 데이터에 대해 정의하는 타입 시스템을 사용하여 이러한 쿼리를 실행하기 위한 서버 측 런타임도 포함합니다. 원래 Facebook에서 개발되어 2015년에 오픈 소스로 공개된 GraphQL은 RESTful 아키텍처에서 관찰된 일부 내재된 한계, 특히 데이터 과다 가져오기 및 부족 가져오기라는 두 가지 문제를 직접 해결하기 위해 고안되었습니다. 클라이언트는 일반적으로 단일 요청-응답 주기 내에서 필요로 하는 데이터를 정확히 요청할 수 있으며, 더 많지도 적지도 않게 가져올 수 있습니다.

GraphQL의 핵심 개념:

GraphQL의 아키텍처는 몇 가지 근본적인 개념 위에 구축됩니다.

핵심 요소는 스키마 정의 언어(SDL)입니다. GraphQL API는 강력한 타입 시스템에 의해 엄격하게 정의됩니다. 서버는 클라이언트가 쿼리할 수 있는 모든 데이터 타입과 이러한 데이터 타입 간에 존재하는 복잡한 관계를 세심하게 설명하는 스키마를 게시합니다. 이 스키마는 클라이언트와 서버 간의 바인딩 계약 역할을 합니다. SDL 내에서 다양한 구성을 정의합니다.

  • 타입(Types)은 가져올 수 있는 객체와 해당 필드를 설명합니다 (예: type User { id: ID!, name: String, email: String, posts: [Post] }).
  • 쿼리(Queries)는 데이터 가져오기 작업의 진입점을 정의합니다 (예: type Query { user(id: ID!): User, posts: [Post] }).
  • 뮤테이션(Mutations)은 데이터 수정 작업(데이터 생성, 업데이트, 삭제)의 진입점을 정의합니다 (예: type Mutation { createUser(name: String!, email: String!): User }).
  • 구독(Subscriptions)은 서버에서 특정 데이터가 변경될 때 클라이언트가 실시간 업데이트를 받는 방법을 정의합니다 (예: type Subscription { newPost: Post }).

또 다른 특징은 일반적으로 단일 엔드포인트를 사용한다는 것입니다. 서로 다른 리소스와 작업을 나타내기 위해 여러 URL을 사용하는 REST와 달리, GraphQL API는 일반적으로 단 하나의 엔드포인트(예: /graphql)만 노출합니다. 데이터 가져오기를 위한 쿼리, 데이터 수정을 위한 뮤테이션, 실시간 업데이트를 위한 구독 등 모든 요청은 일반적으로 HTTP POST 요청을 통해 이 단일 엔드포인트로 전달됩니다.

GraphQL의 힘의 중심에는 클라이언트 지정 쿼리 개념이 있습니다. 클라이언트 애플리케이션은 필요한 데이터를 정확히 명시하는 쿼리 문자열을 구성합니다. 여기에는 기본 객체의 필드뿐만 아니라 관련 객체의 필드도 포함될 수 있으며, 복잡한 데이터 관계를 탐색합니다. 그러면 서버는 이 쿼리를 처리하고 클라이언트가 제출한 쿼리 구조를 정확히 반영하는 JSON 객체로 응답합니다.

GraphQL 작동 방식:

GraphQL 시스템에서의 상호 작용은 잘 정의된 흐름을 따릅니다. 스키마 정의로 시작하며, 서버는 GraphQL의 SDL을 사용하여 데이터 기능을 정의합니다.

이어서 클라이언트 쿼리가 구성됩니다. 클라이언트는 필요한 특정 데이터 필드를 상세히 설명하는 GraphQL 쿼리 문자열을 작성합니다. 예를 들어:GraphQL

query GetUserDetails {
  user(id: "123") {
    id
    name
    email
    posts { # Fetch related posts
      title
      content
    }
  }
}

이 쿼리는 일반적으로 HTTP POST 요청 내 JSON 페이로드 형태로 단일 GraphQL 엔드포인트를 대상으로 하는 서버 요청으로 전송됩니다.

요청을 수신하면 서버 처리가 시작됩니다. 여기에는 여러 하위 단계가 포함됩니다. 서버는 먼저 수신된 쿼리의 구문을 확인하고 정의된 스키마에 부합하는지 확인하여 파싱 및 유효성 검사를 수행합니다. 유효하면 쿼리는 실행 단계로 넘어갑니다. 서버는 "리졸버(resolver)" 함수를 호출하여 쿼리를 실행합니다. GraphQL 스키마에 정의된 각 필드는 해당 리졸버 함수에 의해 지원됩니다. 리졸버는 특정 필드에 대한 데이터를 가져오는 역할을 하는 코드 조각입니다. 이러한 리졸버는 데이터베이스, 다른 내부 또는 외부 API, 심지어 정적 데이터와 같은 다양한 소스에서 데이터를 검색할 수 있습니다.

마지막으로 서버는 클라이언트 응답을 구성하여 전송합니다. 이 응답은 원래 쿼리의 형태를 반영하는 JSON 객체이며, 명시적으로 요청된 데이터만 포함합니다. 위 예제 쿼리에 대한 응답은 다음과 같을 수 있습니다:JSON

{
  "data": {
    "user": {
      "id": "123",
      "name": "Alice Wonderland",
      "email": "alice@example.com",
      "posts": [
        {
          "title": "My First Post",
          "content": "Hello world!"
        },
        {
          "title": "GraphQL is Cool",
          "content": "Learning about GraphQL..."
        }
      ]
    }
  }
}

GraphQL의 장점:

  • 과다 가져오기 또는 부족 가져오기 없음: 클라이언트는 필요한 데이터를 정확히 요청하므로 데이터 전송이 매우 효율적이고 낭비되는 대역폭이 줄어듭니다.
  • 여러 리소스에 대한 단일 요청: 서로 다른 개념적 리소스에 걸쳐 있는 관련 데이터도 단일 쿼리로 가져올 수 있어 네트워크 왕복 횟수를 크게 줄입니다.
  • 강력한 타입 스키마: 스키마는 클라이언트와 서버 간의 명확하고 권위 있는 계약 역할을 합니다. 이 강력한 타입은 자동 완성, 정적 분석, 쿼리 유효성 검사와 같은 강력한 개발 도구를 가능하게 하며, 자체 문서화 역할도 합니다.
  • 발전 가능성: 버전 관리에 의존하지 않고 API를 발전시키기가 더 쉬워집니다. 스키마에 새로운 필드나 타입을 추가해도 기존 클라이언트는 명시적으로 요청한 데이터만 받으므로 손상되지 않습니다. 오래된 필드를 폐기하는 것도 더 간단합니다.
  • 구독을 통한 실시간 데이터: GraphQL에는 구독을 통한 실시간 업데이트 지원이 내장되어 있어 클라이언트가 서버의 특정 데이터 변경을 수신 대기할 수 있습니다.
  • 자체 성찰적: GraphQL API는 본질적으로 자체 성찰적입니다. 클라이언트는 스키마 자체를 쿼리하여 사용 가능한 타입, 필드, 쿼리, 뮤테이션 및 구독을 발견할 수 있어 탐색 및 동적 클라이언트 생성을 용이하게 합니다.

GraphQL의 단점:

  • 복잡성: 특히 리졸버 로직 구현, 스키마 설계 및 성능 최적화와 관련하여 간단한 REST API에 비해 GraphQL 서버를 설정하고 관리하는 것이 더 복잡할 수 있습니다.
  • 캐싱: HTTP 캐싱 메커니즘은 REST의 리소스 기반 캐싱에 비해 GraphQL에 직접 적용하기가 덜 간단합니다. 정교한 클라이언트 측 캐싱 솔루션(예: Apollo Client 또는 Relay)이 존재하지만, 서버 측 및 중간 캐싱에는 다른 전략이 필요하며 필드 수준 캐싱은 복잡할 수 있습니다.
  • 파일 업로드: GraphQL 사양은 파일 업로드를 기본적으로 처리하지 않습니다. 이는 일반적으로 파일 처리를 위한 별도의 REST 엔드포인트를 사용하거나 GraphQL과 함께 멀티파트 요청 처리 라이브러리를 구현하는 등의 해결 방법을 필요로 합니다.
  • 속도 제한: GraphQL 쿼리의 "비용" 또는 리소스 집약성은 단순히 쿼리를 보는 것만으로는 즉시 명확하지 않기 때문에 효과적인 속도 제한 구현이 더 복잡할 수 있습니다. 깊게 중첩되거나 복잡한 쿼리는 실행 비용이 매우 높을 수 있습니다. 이는 종종 쿼리 비용 분석 메커니즘을 필요로 합니다.
  • 학습 곡선: GraphQL 개념을 이해하고 스키마 설계 모범 사례를 마스터하며 쿼리 언어 자체에 능숙해지는 데에는 학습 곡선이 있습니다.
  • 성능 함정: 잘못 작성된 리졸버 또는 지나치게 복잡하고 깊게 중첩된 쿼리는 데이터 로더와 같은 기술로 신중하게 처리되지 않으면 N+1 문제(항목 목록과 그 자식을 가져올 때 목록에 대한 하나의 쿼리와 각 항목의 자식에 대한 N개의 추가 쿼리가 발생하는 문제)와 같은 성능 문제를 유발할 수 있습니다.

GraphQL은 언제 사용해야 하는가:

GraphQL은 웹 애플리케이션, 모바일 앱, IoT 장치와 같이 다양한 클라이언트를 가진 애플리케이션에 특히 적합하며, 이들은 모두 다양한 데이터 요구 사항을 가질 수 있습니다. 데이터 전송을 최소화하고 네트워크 왕복 횟수를 줄이는 것이 중요한 상황, 예를 들어 느리거나 불안정한 네트워크에서 작동하는 모바일 애플리케이션에서 빛을 발합니다. 클라이언트가 여러 기본 소스에서 데이터를 가져오거나 깊게 중첩된 리소스 관계를 탐색해야 하는 복잡한 시스템에 GraphQL은 우아한 솔루션을 제공합니다. 강력한 타입의 API 계약, 강력한 자체 문서화 기능 및 API 발전 가능성이 매우 중요하게 여겨진다면 GraphQL은 이러한 이점을 제공합니다. 구독을 통한 실시간 업데이트가 필요한 애플리케이션은 이 기능에 대한 GraphQL의 기본 지원을 활용할 수 있습니다. 궁극적으로 GraphQL은 클라이언트에게 데이터를 가져오는 방식에 더 많은 권한과 유연성을 부여하고, 요청을 클라이언트의 정확한 요구에 맞추고 싶을 때 훌륭한 선택입니다.

RPC vs. REST vs. GraphQL: 비교 개요

특징RPC (예: gRPC)REST