paint-brush
REST에 휴식을 주어야 할 때~에 의해@johnjvester
2,364 판독값
2,364 판독값

REST에 휴식을 주어야 할 때

~에 의해 John Vester13m2024/05/13
Read on Terminal Reader

너무 오래; 읽다

이 문서에서는 데이터 검색을 위한 GraphQL과 REST API를 비교하여 복잡한 시나리오에서 GraphQL의 유연성과 효율성이 어떻게 뛰어난지 보여줍니다. Heroku를 사용하여 Apollo Server를 배포하는 방법을 자세히 설명하고 GraphQL의 인증 및 실시간 데이터에 대한 향후 콘텐츠를 소개합니다.
featured image - REST에 휴식을 주어야 할 때
John Vester HackerNoon profile picture
0-item
1-item


수년간 서비스를 구축하는 동안 RESTful API는 제가 주로 사용하는 것이었습니다. 그러나 REST에 장점이 있더라도 이것이 모든 사용 사례에 가장 적합한 접근 방식이라는 의미는 아닙니다. 수년에 걸쳐 나는 때때로 특정 시나리오에 대해 더 나은 대안이 있을 수 있다는 것을 배웠습니다. 단지 열정이 있기 때문에 REST를 고수하는 것은 적합하지 않은데도 기술 부채와 제품 소유자와의 관계 긴장을 초래할 뿐입니다.


RESTful 접근 방식의 가장 큰 문제점 중 하나는 비즈니스 결정에 필요한 모든 정보를 검색하기 위해 여러 요청을 해야 한다는 것입니다.


예를 들어, 고객에 대한 360도 뷰를 원한다고 가정해 보겠습니다. 다음과 같은 요청을 해야 합니다.


  • GET /customers/{some_token} 기본 고객 정보를 제공합니다.
  • GET /addresses/{some_token} 필수 주소를 제공합니다.
  • GET /contacts/{some_token} 연락처 정보를 반환합니다.
  • GET /credit/{some_token} 주요 금융 정보를 반환합니다.


REST의 기본 목표는 각 리소스에 대한 응답을 집중적으로 유지하는 것임을 이해하지만 이 시나리오는 소비자 측에서 더 많은 작업을 수행합니다. 조직이 고객과의 향후 비즈니스와 관련된 결정을 내리는 데 도움이 되는 사용자 인터페이스를 채우기 위해 소비자는 여러 번 전화를 걸어야 합니다.


이 기사에서는 GraphQL이 RESTful API보다 선호되는 접근 방식인 이유를 설명하고 Apollo Server(및 Apollo Explorer)를 배포하여 GraphQL을 빠르게 시작하고 실행하는 방법을 보여줍니다.


Node.js로 솔루션을 구축하고 Heroku에 솔루션을 배포할 계획입니다.

REST 대신 GraphQL을 사용해야 하는 경우

GraphQL이 REST보다 더 나은 접근 방식인 몇 가지 일반적인 사용 사례가 있습니다.


  • 데이터 검색 방법에 유연성이 필요한 경우: 다양한 리소스에서 복잡한 데이터를 가져올 수 있지만 모두 단일 요청 으로 가져올 수 있습니다. (이 기사에서는 이 경로를 자세히 살펴보겠습니다.)
  • 프런트엔드 팀이 UI를 자주 개선 해야 하는 경우: 빠르게 변화하는 데이터 요구 사항으로 인해 백엔드에서 엔드포인트를 조정하고 차단을 유발할 필요가 없습니다.
  • 초과 가져오기부족 가져오기를 최소화하려는 경우: 때로는 REST에서 필요한 모든 데이터를 수집하기 위해 여러 끝점에 도달해야 하거나(언더 가져오기), 단일 끝점에 도달하면 실제로 필요한 것보다 훨씬 더 많은 데이터를 반환합니다(과다- 가져오기). 가져오기).
  • 복잡한 시스템 및 마이크로서비스로 작업하는 경우: 때로는 여러 소스가 해당 데이터에 대해 단일 API 계층에 도달해야 하는 경우가 있습니다. GraphQL은 단일 API 호출을 통해 이러한 유연성을 제공할 수 있습니다.
  • 실시간 데이터 푸시가 필요한 경우: GraphQL은 실시간 업데이트를 제공하는 구독 기능을 제공합니다. 이는 채팅 앱이나 실시간 데이터 피드의 경우에 유용합니다. (이 이점에 대해서는 후속 기사에서 더 자세히 다루겠습니다.)

Apollo 서버란 무엇입니까?

GraphQL 에 대한 내 기술이 아직 다듬어지지 않았기 때문에 이 기사에서는 Apollo Server를 사용하기로 결정했습니다.


Apollo Server는 모든 GraphQL 스키마와 작동하는 GraphQL 서버입니다. 목표는 GraphQL API 구축 프로세스를 단순화하는 것입니다. 기본 디자인은 Express 또는 Koa와 같은 프레임워크와 잘 통합됩니다. 다음 기사에서는 실시간 데이터에 대한 구독( graphql-ws 라이브러리를 통해)을 활용하는 기능을 살펴보겠습니다.


Apollo Server가 정말 빛나는 곳은 개발자가 GraphQL API를 탐색하고 테스트하는 데 사용할 수 있는 내장 웹 인터페이스인 Apollo Explorer입니다. 스튜디오는 쿼리를 쉽게 구성하고 API 스키마를 그래픽 형식으로 볼 수 있는 기능을 제공하므로 나에게 완벽하게 적합할 것입니다.

내 고객 360 사용 사례

이 예에서는 고객에 대한 360도 뷰를 제공하기 위해 다음 스키마가 필요하다고 가정해 보겠습니다.


 type Customer { token: String name: String sic_code: String } type Address { token: String customer_token: String address_line1: String address_line2: String city: String state: String postal_code: String } type Contact { token: String customer_token: String first_name: String last_name: String email: String phone: String } type Credit { token: String customer_token: String credit_limit: Float balance: Float credit_score: Int }


저는 다음 GraphQL 쿼리에 집중할 계획입니다.


 type Query { addresses: [Address] address(customer_token: String): Address contacts: [Contact] contact(customer_token: String): Contact customers: [Customer] customer(token: String): Customer credits: [Credit] credit(customer_token: String): Credit }


소비자는 보려는 고객에게 토큰을 제공합니다. 또한 적절한 주소, 연락처 및 신용 개체도 검색할 것으로 예상됩니다. 목표는 단일 API 호출이 아닌 이 모든 정보에 대해 4개의 서로 다른 API 호출을 피하는 것입니다.

Apollo 서버 시작하기

저는 로컬 워크스테이션에 graphql-server-customer 라는 새 폴더를 만드는 것부터 시작했습니다. 그런 다음 Apollo Server 설명서의 시작하기 섹션을 사용하여 Typescript 접근 방식을 사용하여 1단계와 2단계를 수행했습니다.


다음으로 스키마를 정의하고 테스트용 정적 데이터도 포함했습니다. 일반적으로 데이터베이스에 연결하지만 이 데모에서는 정적 데이터가 제대로 작동합니다.


다음은 업데이트된 index.ts 파일입니다.


 import { ApolloServer } from '@apollo/server'; import { startStandaloneServer } from '@apollo/server/standalone'; const typeDefs = `#graphql type Customer { token: String name: String sic_code: String } type Address { token: String customer_token: String address_line1: String address_line2: String city: String state: String postal_code: String } type Contact { token: String customer_token: String first_name: String last_name: String email: String phone: String } type Credit { token: String customer_token: String credit_limit: Float balance: Float credit_score: Int } type Query { addresses: [Address] address(customer_token: String): Address contacts: [Contact] contact(customer_token: String): Contact customers: [Customer] customer(token: String): Customer credits: [Credit] credit(customer_token: String): Credit } `; const resolvers = { Query: { addresses: () => addresses, address: (parent, args, context) => { const customer_token = args.customer_token; return addresses.find(address => address.customer_token === customer_token); }, contacts: () => contacts, contact: (parent, args, context) => { const customer_token = args.customer_token; return contacts.find(contact => contact.customer_token === customer_token); }, customers: () => customers, customer: (parent, args, context) => { const token = args.token; return customers.find(customer => customer.token === token); }, credits: () => credits, credit: (parent, args, context) => { const customer_token = args.customer_token; return credits.find(credit => credit.customer_token === customer_token); } }, }; const server = new ApolloServer({ typeDefs, resolvers, }); const { url } = await startStandaloneServer(server, { listen: { port: 4000 }, }); console.log(`Apollo Server ready at: ${url}`); const customers = [ { token: 'customer-token-1', name: 'Acme Inc.', sic_code: '1234' }, { token: 'customer-token-2', name: 'Widget Co.', sic_code: '5678' } ]; const addresses = [ { token: 'address-token-1', customer_token: 'customer-token-1', address_line1: '123 Main St.', address_line2: '', city: 'Anytown', state: 'CA', postal_code: '12345' }, { token: 'address-token-22', customer_token: 'customer-token-2', address_line1: '456 Elm St.', address_line2: '', city: 'Othertown', state: 'NY', postal_code: '67890' } ]; const contacts = [ { token: 'contact-token-1', customer_token: 'customer-token-1', first_name: 'John', last_name: 'Doe', email: '[email protected]', phone: '123-456-7890' } ]; const credits = [ { token: 'credit-token-1', customer_token: 'customer-token-1', credit_limit: 10000.00, balance: 2500.00, credit_score: 750 } ];


모든 것이 예상대로 구성되면 다음 명령을 실행하여 서버를 시작합니다.


 $ npm start


Apollo 서버가 포트 4000에서 실행되는 경우 http://localhost:4000/ URL을 사용하여 Apollo Explorer에 액세스했습니다. 그런 다음 다음 예제 쿼리를 설정했습니다.


 query ExampleQuery { addresses { token } contacts { token } customers { token } }


Apollo Explorer에서는 다음과 같이 표시됩니다.


예제 쿼리 버튼을 눌러 응답 페이로드가 index.ts 에 제공한 정적 데이터와 일치하는지 확인했습니다.


 { "data": { "addresses": [ { "token": "address-token-1" }, { "token": "address-token-22" } ], "contacts": [ { "token": "contact-token-1" } ], "customers": [ { "token": "customer-token-1" }, { "token": "customer-token-2" } ] } }


Customer 360 사용 사례를 더 자세히 다루기 전에 이 서비스를 클라우드에서 실행하고 싶었습니다.

Heroku에 Apollo 서버 배포

이 기사는 새로운 작업에 관한 것이므로 Apollo 서버를 Heroku에 배포하는 것이 얼마나 어려운지 알고 싶었습니다.


로컬로 실행하는 것과 클라우드 어딘가에서 실행하는 것 사이의 포트 번호 차이를 해결해야 한다는 것을 알았습니다. 아래와 같이 서버를 시작하기 위한 코드를 업데이트했습니다.


 const { url } = await startStandaloneServer(server, { listen: { port: Number.parseInt(process.env.PORT) || 4000 }, });


이번 업데이트에서는 환경 변수에 PORT 값이 지정되지 않은 한 포트 4000을 사용합니다.


Gitlab을 사용하여 이 파일에 대한 새 프로젝트를 생성하고 Heroku 명령줄 인터페이스(CLI)를 사용하여 Heroku 계정에 로그인했습니다.


 $ heroku login


CLI 또는 Heroku 대시보드 웹 UI를 사용하여 Heroku에서 새 앱을 만들 수 있습니다. 이 문서에서는 CLI를 사용합니다.


 $ heroku create jvc-graphql-server-customer


CLI 명령이 다음 응답을 반환했습니다.


 Creating ⬢ jvc-graphql-server-customer... done https://jvc-graphql-server-customer-b62b17a2c949.herokuapp.com/ | https://git.heroku.com/jvc-graphql-server-customer.git


이 명령은 또한 Heroku가 원격으로 사용하는 저장소를 자동으로 추가했습니다.


 $ git remote heroku origin


기본적으로 Apollo Server는 프로덕션 환경에서 Apollo Explorer를 비활성화합니다. 내 데모에서는 Heroku에서 실행되도록 두고 싶습니다. 이렇게 하려면 NODE_ENV 환경 변수를 개발로 설정해야 합니다. 다음 CLI 명령을 사용하여 이를 설정할 수 있습니다.


 $ heroku config:set NODE_ENV=development


CLI 명령이 다음 응답을 반환했습니다.


 Setting NODE_ENV and restarting ⬢ jvc-graphql-server-customer... done, v3 NODE_ENV: development


이제 우리는 코드를 Heroku에 배포할 수 있습니다.


 $ git commit --allow-empty -m 'Deploy to Heroku' $ git push heroku


Heroku 대시보드를 빠르게 보면 Apollo Server가 문제 없이 실행되고 있음을 알 수 있습니다.


Heroku를 처음 사용하는 경우 이 가이드에서는 새 계정을 만들고 Heroku CLI를 설치하는 방법을 보여줍니다.

수락 기준 충족: 내 Customer 360 예

GraphQL을 사용하면 다음 쿼리를 통해 Customer 360 사용 사례에 대한 승인 기준을 충족할 수 있습니다.


 query CustomerData($token: String) { customer(token: $token) { name sic_code token }, address(customer_token: $token) { token customer_token address_line1 address_line2 city state postal_code }, contact(customer_token: $token) { token, customer_token, first_name, last_name, email, phone }, credit(customer_token: $token) { token, customer_token, credit_limit, balance, credit_score } }


내가 해야 할 일은 customer-token-1 값을 가진 단일 Customer token 변수를 전달하는 것뿐입니다.


 { "token": "customer-token-1" }


단일 GraphQL API 호출을 사용하여 모든 데이터를 검색할 수 있습니다.


 { "data": { "customer": { "name": "Acme Inc.", "sic_code": "1234", "token": "customer-token-1" }, "address": { "token": "address-token-1", "customer_token": "customer-token-1", "address_line1": "123 Main St.", "address_line2": "", "city": "Anytown", "state": "CA", "postal_code": "12345" }, "contact": { "token": "contact-token-1", "customer_token": "customer-token-1", "first_name": "John", "last_name": "Doe", "email": "[email protected]", "phone": "123-456-7890" }, "credit": { "token": "credit-token-1", "customer_token": "customer-token-1", "credit_limit": 10000, "balance": 2500, "credit_score": 750 } } }


다음은 내 Heroku 앱에서 실행되는 Apollo Explorer의 스크린샷입니다.


결론

내 경력 초기에 Java와 C#이 개발자 채택을 위해 서로 경쟁하던 때를 기억합니다. 논쟁의 양쪽 옹호자들은 자신이 선택한 기술이 최선의 선택이었다는 것을 증명할 준비가 되어 있었습니다.


이 예에서는 다양한 방법으로 Customer 360 사용 사례를 충족할 수 있었습니다. 검증된 RESTful API를 사용했다면 효과가 있었지만 필요한 모든 데이터를 검색하려면 여러 API 호출이 필요했을 것입니다. Apollo Server와 GraphQL을 사용하면 단일 API 호출로 목표를 달성할 수 있었습니다.


또한 터미널에서 몇 가지 명령만으로 GraphQL 서버를 Heroku에 배포하는 것이 얼마나 쉬운지 정말 마음에 듭니다. 이를 통해 인프라 부담을 덜고 신뢰할 수 있는 타사 공급자에게 코드를 실행하는 등 구현에 집중할 수 있습니다. 가장 중요한 것은 이것이 내 개인적인 사명 선언문과 정확히 일치한다는 것입니다.


“지적 재산의 가치를 확장하는 특징/기능을 제공하는 데 시간을 집중하십시오. 다른 모든 것에 프레임워크, 제품, 서비스를 활용하세요.”

– J. 베스터


이 기사의 소스 코드에 관심이 있다면 GitLab 에서 확인할 수 있습니다.


하지만 잠깐만요... 더 있습니다!


후속 게시물에서는 구독을 통한 인증 및 실시간 데이터 검색을 구현하기 위해 GraphQL 서버를 추가로 구축할 것입니다.


정말 좋은 하루 보내세요!