GraphQL 입문서: 새로운 종류의 API가 필요한 이유(1부)

게시 됨: 2022-03-10
빠른 요약 ↬ 기능을 먼저 탐색하기보다 컨텍스트에 넣고 어떻게 존재하게 되었는지 이해하는 것이 도움이 됩니다. GraphQL에 대한 소개와 지난 60년 간의 API 개발에서 얻은 교훈.

이 시리즈에서는 GraphQL을 소개하고자 합니다. 결국에는 그것이 무엇인지 뿐만 아니라 그 기원, 단점 및 작업 방법에 대한 기본 사항을 이해해야 합니다. 이 첫 번째 기사에서는 구현에 뛰어들기보다는 RPC에서 현재까지 지난 60년 간의 API 개발에서 배운 교훈을 살펴봄으로써 GraphQL(및 유사한 도구)에 도달한 방법과 이유를 살펴보고자 합니다. 결국 마크 트웨인이 다채롭게 묘사했듯이 새로운 아이디어는 없습니다.

"새로운 아이디어라는 것은 없습니다. 불가능합니다. 우리는 단순히 많은 오래된 아이디어를 일종의 정신적 만화경에 집어넣습니다."

— 마크 트웨인의 "마크 트웨인의 자서전: 북미 리뷰의 장"

하지만 먼저 방에 있는 코끼리에게 말을 걸어야 합니다. 새로운 일은 언제나 설레지만 지칠 수도 있습니다. GraphQL에 대해 들어본 적이 있고 "왜..."라고 생각했을 수도 있습니다. 또는 "내가 새로운 API 디자인 트렌드에 관심을 갖는 이유는 무엇입니까?"와 같은 생각을 할 수도 있습니다. REST는… 괜찮습니다.” 이러한 질문은 정당한 질문이므로 이 질문에 주의를 기울여야 하는 이유를 설명하겠습니다.

소개

팀에 새로운 도구를 제공할 때의 이점과 비용을 비교해야 합니다. 측정할 것이 많습니다. 배우는 데 걸리는 시간이 있고 기능 개발에서 변환하는 데 걸리는 시간, 두 시스템을 유지 관리하는 오버헤드가 있습니다. 이러한 높은 비용으로 인해 새로운 기술은 엄청난 양으로 더 낫거나 빠르거나 생산성이 높아야 합니다. 점진적인 개선은 흥미롭긴 하지만 투자할 가치가 없습니다. 제가 이야기하고 싶은 API 유형, 특히 GraphQL은 제 생각에 엄청난 발전이며 비용을 정당화할 만큼 충분한 이점을 제공합니다.

기능을 먼저 탐색하기보다 컨텍스트에 넣고 기능이 어떻게 존재하게 되었는지 이해하는 것이 도움이 됩니다. 이를 위해 API의 역사에 대한 간략한 요약으로 시작하겠습니다.

점프 후 더! 아래에서 계속 읽기 ↓

RPC

RPC는 틀림없이 최초의 주요 API 패턴이었고 그 기원은 60년대 중반의 초기 컴퓨팅으로 거슬러 올라갑니다. 당시 컴퓨터는 여전히 너무 크고 비쌌기 때문에 우리가 생각하는 API 기반 애플리케이션 개발의 개념은 대부분 이론적인 것이었습니다. 대역폭/대기 시간, 연산 능력, 공유 연산 시간 및 물리적 근접성과 같은 제약으로 인해 엔지니어는 데이터를 노출하는 서비스가 아닌 분산 시스템의 관점에서 생각해야 했습니다 . 60년대의 ARPANET부터 90년대 중반까지 CORBA 및 Java의 RMI와 같은 것들에 이르기까지 대부분의 컴퓨터는 클라이언트가 프로시저를 유발하는 클라이언트-서버 상호 작용 모델인 RPC(원격 프로시저 호출)를 사용하여 서로 상호 작용했습니다. (또는 메서드) 원격 서버에서 실행할 수 있습니다.

RPC에는 좋은 점이 많이 있습니다. 주요 원칙은 개발자가 원격 환경에 있는 코드를 마치 로컬 환경에 있는 것처럼 처리할 수 있도록 하는 것입니다. 비록 훨씬 느리고 덜 안정적이기는 하지만 다른 방식으로 구별되고 이질적인 시스템에서 연속성을 생성합니다. ARPANET에서 나온 많은 것들과 마찬가지로 이러한 유형의 연속성은 DB 액세스 및 외부 서비스 호출과 같은 신뢰할 수 없는 비동기 작업을 수행할 때 여전히 노력하는 것이기 때문에 시대를 앞서갔습니다.

수십 년 동안 개발자가 이와 같은 비동기 동작을 일반적인 프로그램 흐름에 포함할 수 있도록 하는 방법에 대한 엄청난 양의 연구가 있었습니다. 당시에 Promises, Futures 및 ScheduledTasks와 같은 것이 사용 가능했다면 API 환경이 다르게 보일 수 있습니다.

RPC의 또 다른 장점은 데이터 구조의 제약이 없기 때문에 필요한 정보를 정확히 요청하고 검색하는 클라이언트를 위해 고도로 전문화된 메서드를 작성할 수 있어 네트워크 오버헤드를 최소화하고 페이로드를 줄일 수 있다는 것입니다.

그러나 RPC를 어렵게 만드는 요소가 있습니다. 첫째, 연속성은 컨텍스트 를 필요로 합니다. RPC는 의도적으로 로컬 시스템과 원격 시스템 사이에 많은 결합을 생성합니다. 로컬 코드와 원격 코드 사이의 경계를 잃게 됩니다. 일부 도메인의 경우 클라이언트 SDK와 같이 괜찮거나 선호되지만 클라이언트 코드가 잘 이해되지 않는 API의 경우 데이터 지향적인 것보다 훨씬 덜 유연할 수 있습니다.

그러나 더 중요한 것은 API 메소드의 확산 가능성입니다 . 이론적으로 RPC 서비스는 모든 작업을 처리할 수 있는 작은 API를 제공합니다. 실제로 많은 수의 외부 끝점이 많은 구조 없이 축적될 수 있습니다. 팀 구성원이 왔다 갔다 하고 프로젝트가 회전함에 따라 시간이 지남에 따라 겹치는 API와 중복을 방지하려면 엄청난 규율이 필요합니다.

내가 언급한 것과 같은 적절한 도구 및 문서 변경을 통해 관리할 수 있는 것은 사실이지만 소프트웨어를 작성하는 동안 자동 문서화 및 훈련된 서비스가 거의 없었기 때문에 나에게 이것은 약간의 문제입니다. 훈제 청어.

비누

다음으로 등장할 주요 API 유형은 90년대 후반 Microsoft Research에서 탄생한 SOAP입니다. SOAP ( Simple Object A access Protocol)는 응용 프로그램 간의 XML 기반 통신을 위한 야심찬 프로토콜 사양입니다. SOAP의 명시된 목표는 복잡한 웹 서비스를 위한 잘 구조화된 기반을 만들어 RPC, 특히 XML-RPC의 몇 가지 실용적인 단점을 해결하는 것이었습니다. 실제로 이것은 XML에 행동 유형 시스템을 추가하는 것을 의미했습니다. 슬프게도 오늘날 작성되는 새로운 SOAP 엔드포인트가 거의 없다는 사실에서 알 수 있듯이 해결된 것보다 더 많은 장애물을 만들었습니다.

"SOAP는 대부분의 사람들이 보통 정도의 성공이라고 생각하는 것입니다."

— 돈 상자

SOAP는 참을 수 없는 장황함과 끔찍한 이름에도 불구하고 몇 가지 좋은 점이 있었습니다. 클라이언트와 서버 간의 WSDL 및 WADL("wizdle" 및 "waddle"로 발음)의 시행 가능한 계약은 예측 가능하고 유형이 안전한 결과를 보장하며 WSDL을 사용하여 문서를 생성하거나 IDE 및 기타 도구와의 통합을 생성할 수 있습니다.

API 진화에 관한 SOAP의 큰 계시는 더 자원 지향적인 호출의 점진적이고 의도하지 않은 도입이었습니다. SOAP 끝점을 사용하면 데이터를 생성하는 데 필요한 방법을 생각하기보다 미리 결정된 구조로 데이터를 요청할 수 있습니다(이 방법으로 작성되었다고 가정).

SOAP의 가장 중요한 단점은 너무 장황하다는 것입니다. 많은 도구 없이 사용하는 것은 거의 불가능합니다 . 테스트를 작성하는 도구, 서버의 응답을 검사하는 도구, 모든 데이터를 구문 분석하는 도구가 필요합니다. 많은 구형 시스템은 여전히 ​​SOAP를 사용하지만 도구의 요구 사항으로 인해 대부분의 새 프로젝트에서 너무 번거롭고 XML 구조에 필요한 바이트 수가 모바일 장치 또는 수다스러운 분산 시스템을 제공하는 데 적합하지 않습니다.

더 많은 정보를 원하시면 SOAP 사양과 함께 초기 팀 구성원 중 한 명인 Don Box의 놀랍도록 흥미로운 SOAP의 역사를 읽어볼 가치가 있습니다.

쉬다

마지막으로 API 디자인 패턴인 REST에 도달했습니다. 2000년 Roy Fielding의 박사 학위 논문에서 소개된 REST는 완전히 다른 방향으로 진자를 휘둘렀습니다. REST는 여러 면에서 SOAP의 정반대이며 나란히 놓고 보면 그의 논문이 다소 화를 내며 그만둔 것 같은 느낌이 들게 합니다.

SOAP는 HTTP를 벙어리 전송으로 사용하고 요청 및 응답 본문에 구조를 구축합니다. 반면 REST는 클라이언트-서버 계약, 도구, XML 및 맞춤형 헤더를 폐기하고 대신 HTTP 동사를 사용하기 위해 선택하는 구조이므로 이를 HTTP 의미론으로 대체합니다. 데이터.

비누 쉬다
HTTP 동사 GET, PUT, POST, 패치, 삭제
데이터 형식 XML 당신이 원하는 무엇이든
클라이언트/서버 계약 하루 종일 '매일! 누가 그것들을 필요로 하는가
유형 시스템 JavaScript에 unsigned short가 있습니까?
URL 작업 설명 명명된 리소스

REST는 상호 ​​작용 모델링에서 도메인 데이터 모델링으로 API 디자인을 완전히 명시적으로 변경합니다. REST API로 작업할 때 완전히 리소스 지향적이므로 주어진 데이터 조각을 검색하는 데 필요한 것을 더 이상 알거나 신경 쓸 필요가 없습니다. 또한 백엔드 서비스 구현에 대해 알 필요도 없습니다.

단순함은 개발자들에게 도움이 되었을 뿐만 아니라 URL이 안정적인 정보를 나타내므로 쉽게 캐시할 수 있고 상태 비저장으로 인해 수평으로 쉽게 확장할 수 있으며 소비자의 요구를 예측하기보다 데이터를 모델링하므로 API의 표면적을 크게 줄일 수 있습니다. .

REST는 훌륭하고 편재되어 놀라운 성공을 거두었지만 이전의 모든 솔루션과 마찬가지로 REST에도 결함이 없는 것은 아닙니다. 몇 가지 단점에 대해 구체적으로 이야기하기 위해 기본 예를 살펴보겠습니다. 블로그 게시물 목록과 작성자 이름을 표시하는 블로그 방문 페이지를 만들어야 한다고 가정해 보겠습니다.

기본 블로그의 홈페이지
각 게시물의 제목과 작성자를 표시하는 기본 블로그의 홈페이지입니다. (큰 미리보기)

일반 REST API에서 홈페이지 데이터를 검색할 수 있는 코드를 작성해 보겠습니다. 리소스를 래핑하는 몇 가지 함수로 시작하겠습니다.

 const getPosts = () => fetch(`${API_ROOT}/posts`); const getPost = postId => fetch(`${API_ROOT}/posts/${postId}`); const getAuthor = authorId => fetch(`${API_ROOT}/authors/${authorId}`);

이제 오케스트레이션을 해보자!

 const getPostWithAuthor = postId => { return getPost(postId) .then(post => getAuthor(post.author)) .then(author => { return Object.assign({}, post, { author }) }) }; const getHomePageData = () => { return getPosts() .then(postIds => { const postDetails = postIds.map(getPostWithAuthor); return Promise.all(postDetails); }) };

따라서 코드는 다음을 수행합니다.

  • 모든 게시물을 가져옵니다.
  • 각 게시물에 대한 세부 정보를 가져옵니다.
  • 각 게시물에 대한 작성자 리소스를 가져옵니다.

좋은 점은 이것이 추론하기 매우 쉽고 잘 조직되어 있으며 각 리소스의 개념적 경계가 잘 그려져 있다는 것입니다. 여기서 안타까운 것은 우리가 방금 8개의 네트워크 요청을 만들었다는 것입니다.

 GET /posts GET /posts/234 GET /posts/456 GET /posts/17 GET /posts/156 GET /author/9 GET /author/4 GET /author/7 GET /author/2

예, API에 페이지가 매겨진 /posts 엔드포인트가 있을 수 있지만 이는 머리카락을 나누는 것이라고 제안하여 이 예를 비판할 수 있습니다. 완전한 애플리케이션이나 페이지를 렌더링하기 위해 서로 의존하게 만드는 API 호출 모음이 있는 경우가 많다는 사실이 남아 있습니다.

REST 클라이언트와 서버를 개발하는 것은 확실히 이전보다 낫거나 적어도 바보 같은 증거이지만 Fielding의 논문 이후 20년 동안 많은 것이 바뀌었습니다. 당시 모든 컴퓨터는 베이지색 플라스틱이었습니다. 이제 그들은 알루미늄입니다! 하지만 2000년은 개인용 컴퓨팅 폭발의 정점에 가까웠습니다. 매년 프로세서의 속도는 두 배로 증가하고 네트워크는 놀라운 속도로 빨라지고 있습니다. 인터넷의 시장 침투율은 약 45%였습니다.

그리고 2008년경에 모바일 컴퓨팅이 주류가 되었습니다. 모바일로 우리는 하룻밤 사이에 속도/성능 면에서 효과적으로 10년을 퇴보했습니다. 2017년에는 국내 스마트폰 보급률이 거의 80%, 전 세계 스마트폰 보급률이 50%를 넘었습니다. 이제 API 디자인에 대한 일부 가정을 재고해야 할 때입니다.

REST의 약점

다음은 클라이언트 애플리케이션 개발자, 특히 모바일에서 작업하는 개발자의 관점에서 REST를 비판적으로 살펴보는 것입니다. GraphQL 및 GraphQL 스타일 API는 새로운 것이 아니며 REST 개발자가 이해할 수 없는 문제를 해결하지 않습니다. GraphQL의 가장 중요한 기여는 이러한 문제를 다른 곳에서는 쉽게 사용할 수 없는 수준의 통합으로 체계적으로 해결할 수 있다는 것입니다. 즉, "배터리 포함" 솔루션입니다.

Fielding을 포함한 REST의 주요 저자는 2017년 말에 REST Architectural Style 및 "Principled Design of the Modern Web Architecture"에 대한 회고 논문을 발표했습니다. API 디자인에 관심이 있는 사람이라면 누구나 읽을 수 있는 짧고 절대적으로 가치가 있습니다.

일부 역사적 컨텍스트와 참조 앱을 사용하여 REST의 세 가지 주요 약점을 살펴보겠습니다.

REST는 수다스럽다

REST 서비스는 애플리케이션을 렌더링하기에 충분한 데이터를 얻기 위해 클라이언트와 서버 간에 여러 번 왕복해야 하기 때문에 적어도 다소 "수다스러운" 경향이 있습니다. 이러한 일련의 요청은 특히 모바일에서 성능에 치명적인 영향을 미칩니다. 블로그 예제로 돌아가서 4G 연결이 있는 안정적인 네트워크와 새 전화기를 사용하는 최상의 시나리오에서도 첫 번째 데이터 바이트가 다운로드되기 전에 대기 시간 오버헤드에 거의 0.5초를 소비했습니다.

55ms 4G 대기 시간 * 8 요청 = 440ms 오버헤드

앱 성능에 대한 사용자 응답을 보여주는 차트
다양한 수준의 앱 성능에 대한 사용자 응답을 설명하는 차트입니다. (이미지 크레디트: 고성능 브라우저 네트워킹) (큰 미리보기)

수다스러운 서비스의 또 다른 문제는 많은 경우에 많은 작은 요청보다 하나의 큰 요청을 다운로드하는 데 시간이 덜 걸린다는 것입니다. TCP 느린 시작, 헤더 압축 부족 및 gzip 효율성을 비롯한 여러 가지 이유로 인해 작은 요청의 성능이 저하된 것이 사실입니다. 이에 대해 궁금하시다면 Ilya Grigorik의 High-Performance Browser Networking을 읽는 것이 좋습니다. MaxCDN 블로그에도 훌륭한 개요가 있습니다.

이 문제는 기술적으로 REST가 아니라 HTTP, 특히 HTTP/1에서 발생합니다. HTTP/2는 API 스타일에 관계없이 채팅 문제를 모두 해결하며 브라우저 및 기본 SDK와 같은 클라이언트에서 광범위하게 지원됩니다. 불행히도 API 측에서 출시가 느렸습니다. 상위 10,000개 웹사이트 중 채택률은 2017년 말 기준으로 약 20%(상승 중)입니다. 놀랍게도 Node.js도 8.x 릴리스에서 HTTP/2 지원을 받았습니다. 능력이 있다면 인프라를 업데이트하십시오! 그 동안 이것은 방정식의 한 부분일 뿐이므로 머뭇거리지 말자.

HTTP는 제쳐두고 채팅이 중요한 이유의 마지막 부분은 모바일 장치, 특히 라디오가 작동하는 방식과 관련이 있습니다. 길고 짧은 것은 라디오 작동이 전화기에서 가장 배터리 집약적인 부분 중 하나이므로 OS가 기회가 있을 때마다 라디오를 끕니다. 라디오를 시작하면 배터리가 소모될 뿐만 아니라 각 요청에 더 많은 오버헤드가 추가됩니다.

TMI(오버페칭)

REST 스타일 서비스의 다음 문제는 필요한 것보다 더 많은 정보를 보낸다는 것입니다. 블로그 예제에서 필요한 것은 각 게시물의 제목과 반환된 것의 약 17%에 해당하는 작성자 이름뿐입니다. 이는 매우 단순한 페이로드에 대해 6배의 손실입니다. 실제 API에서 이러한 종류의 오버헤드는 엄청날 수 있습니다. 예를 들어 전자 상거래 사이트는 종종 단일 제품을 수천 줄의 JSON으로 나타냅니다. 채팅 문제와 마찬가지로 REST 서비스는 "희박한 필드 집합"을 사용하여 데이터의 일부를 조건부로 포함하거나 제외하여 이 시나리오를 처리할 수 있습니다. 불행히도, 이것에 대한 지원은 네트워크 캐싱에 대해 불규칙하거나 불완전하거나 문제가 있습니다.

도구 및 내성

REST API에 마지막으로 부족한 것은 내성을 위한 메커니즘입니다. 끝점의 반환 유형 또는 구조에 대한 정보가 포함된 계약이 없으면 문서를 안정적으로 생성하거나 도구를 만들거나 데이터와 상호 작용할 수 있는 방법이 없습니다. REST 내에서 작업하여 이 문제를 다양한 정도로 해결할 수 있습니다. OpenAPI, OData 또는 JSON API를 완전히 구현하는 프로젝트는 종종 깨끗하고 잘 지정되며 다양한 범위에서 잘 문서화되지만 이와 같은 백엔드는 드뭅니다. 상대적으로 성과가 낮은 하이퍼미디어(Hypermedia)조차도 수십 년 동안 컨퍼런스 강연에서 선전되었음에도 불구하고 여전히 잘 이루어지지 않는 경우가 많습니다.

결론

각 API 유형에는 결함이 있지만 모든 패턴에는 결함이 있습니다. 이 글은 소프트웨어의 거인들이 놓은 경이로운 토대에 대한 판단이 아니라 클라이언트 개발자의 관점에서 "순수한" 형태로 적용된 이러한 패턴 각각에 대한 냉정한 평가일 뿐입니다. REST나 RPC와 같은 패턴이 망가졌다는 생각에서 벗어나 각각이 어떻게 절충했는지 , 엔지니어링 조직이 자체 API를 개선하기 위해 노력을 집중할 수 있는 영역에 대해 생각하기를 바랍니다.

다음 기사에서는 GraphQL과 위에서 언급한 몇 가지 문제를 해결하는 것을 목표로 하는 GraphQL을 살펴보겠습니다. GraphQL 및 유사 도구의 혁신은 구현이 아니라 통합 수준에 있습니다. 귀하 또는 귀하의 팀이 "배터리 포함" API를 찾고 있지 않다면 오늘 보다 강력한 기반을 구축하는 데 도움이 될 수 있는 새로운 OpenAPI 사양과 같은 것을 고려하십시오!

이 기사가 마음에 들었다면(또는 싫어했다면) 저에게 피드백을 제공하고 싶다면 Twitter에서 @ebaerbaerbaer 또는 LinkedIn ericjbaer로 저를 찾으세요.