GraphQL API는 어떻게 구축하나요?

GraphQL 커버란?

최근 몇 년 동안 GraphQL은 복잡한 API를 구축하는 데 널리 사용되고 있습니다. 이는 기존 REST API의 일부 한계를 없애고 API를 보다 유연하고 효율적으로 만들기 때문입니다.

이 글에서는 GraphQL과 그 장단점, 주요 GraphQL 용어에 대해 설명하고 GraphQL API를 REST API와 비교합니다. 또한 Back4app에서 자체 GraphQL API를 구축하고 Next.js 프런트엔드에서 연결하는 방법을 알려드립니다.

GraphQL 소개

GraphQL은 애플리케이션 프로그래밍 인터페이스(API)를 개발하기 위한 쿼리 언어이자 서버 측 런타임입니다. GraphQL을 사용하면 클라이언트는 고정된 데이터 집합을 제공하기 위해 백엔드에 의존하지 않고 API에서 필요한 데이터를 정확히 지정할 수 있습니다.

GraphQL은 빠르고 유연한 API를 구축하기 위한 현대적이고 효율적인 접근 방식입니다. 내장된 유형 시스템을 갖추고 있으며 API를 통해 사용할 수 있는 데이터를 정의하는 강력하게 유형화된 스키마를 기반으로 합니다. 실시간 업데이트 및 구독을 지원하며 개발자가 여러 데이터 소스에서 데이터를 가져올 수 있습니다. 또한 개발자가 기존 쿼리나 변경에 영향을 주지 않고 필드를 사용 중단할 수 있는 기능을 제공함으로써 API를 쉽게 발전시킬 수 있습니다.

GraphQL은 프로젝트 시작부터 사용하거나 기존 API에 통합할 수 있습니다. 일부 회사에서는 REST와 결합한 다음 단계적으로 마이그레이션하기도 합니다.

2012년 Facebook에서 내부적으로 개발한 후 2015년에 오픈소스로 공개되었습니다. GraphQL은 단일 페이지 애플리케이션(SPA)과 모바일 앱의 증가와 함께 인기를 얻었습니다. 출시 이후 GitHub, Airbnb, Pinterest, Shopify 등 많은 기술 대기업에서 채택하고 있습니다.

GraphQL과 REST

GraphQL과 REST는 웹 API를 구축하는 데 널리 사용되는 두 가지 접근 방식입니다.

REST(Representational State Transfer)는 웹의 구조를 설명하는 소프트웨어 아키텍처 스타일입니다. 2000년에 도입되어 10년 넘게 웹 API 구축의 사실상의 표준으로 사용되고 있습니다. 리소스를 조작하기 위해 GET, POST, PUT, PATCH, DELETE와 같은 HTTP 메서드를 사용합니다. 각 리소스는 엔드포인트에서 호스팅되며(아래 이미지 참조), 리소스를 요청할 때마다 전체 ‘데이터 세트’가 반환됩니다.

이 아키텍처에는 두 가지 문제가 발생했습니다. 첫 번째는 과소 가져오기(너무 적은 데이터 가져오기)와 과잉 가져오기(너무 많은 데이터 가져오기)입니다. 또한 REST API는 데이터 변경 사항을 구독할 수 없습니다.

이것이 바로 GraphQL이 필요한 이유입니다. GraphQL은 단일 엔드포인트를 가지고 있으며 한 번의 요청으로 여러 데이터 소스의 데이터를 쿼리할 수 있습니다. 이를 통해 필요한 데이터를 정확히 정의할 수 있습니다. 또한 GraphQL을 사용하면 서버를 폴링하지 않고도 데이터 변경 사항을 구독할 수 있습니다. 이를 통해 API를 더욱 예측 가능하고 자체 문서화할 수 있습니다.

결국 GraphQL과 REST 중 어떤 것을 선택할지는 프로젝트의 특정 요구사항에 따라 달라집니다. GraphQL은 훌륭하지만 단순한 프로젝트에는 너무 복잡할 수 있습니다.

REST API와 GraphQL API

일반적인 GraphQL 용어

GraphQL로 작업할 때 자주 접할 수 있는 몇 가지 일반적인 용어를 살펴보겠습니다.

유형

GraphQL에는 유형 시스템이 있으며 강력하게 유형화되어 있습니다. Int, Float, String, Boolean과 같은 일부 기본 제공 스칼라 유형이 제공되지만 사용자 정의 유형을 정의할 수도 있습니다.

사용자 지정 유형은 다음과 같이 정의할 수 있습니다:

type User {
    username: String!
    password: String!
    friends: [User!]
}

스키마

스키마는 GraphQL API를 통해 사용할 수 있는 데이터에 대한 설명입니다. 여기에는 객체 유형, 해당 필드 및 관련 데이터 유형, 관계, 쿼리, 변이 등이 포함됩니다. 스키마는 일반적으로 SDL(스키마 정의 언어)을 사용하여 정의됩니다.

스키마 예제

해결자

리졸버는 GraphQL 쿼리에서 특정 필드에 대한 데이터를 검색하는 함수입니다. 리졸버 함수는 데이터 소스에서 데이터를 가져와서 예상되는 형식으로 반환하는 역할을 합니다.

쿼리

쿼리는 GraphQL API의 데이터에 대한 읽기 전용 요청입니다. 쿼리는 REST API의 GET 요청으로 생각할 수 있습니다.

예시:

query GetUser {
    user(id: "Wfx8o2AZrE") {
        id
        username
        fullName
    }
}

이 쿼리는 모든 사용자 목록을 반환합니다.

돌연변이

변경은 GraphQL API에서 데이터를 조작하기 위한 요청입니다. 변형을 REST API의 POST/PUT/PATCH/DELETE 요청으로 생각할 수 있습니다. 또한 변이는 반환되는 데이터를 정의할 수도 있습니다.

예시:

mutation CreateAuthor {
  createAuthor(input: {fields: {firstName: "William", lastName: "Shakespeare"}}) {
    author {
      id
      createdAt
    }
  }
}

이 변경은 새 작성자를 생성하고 작성자의 아이디와 createdAt을 반환합니다.

구독

구독은 GraphQL API에서 실시간 업데이트를 요청하는 것입니다. 구독을 통해 클라이언트는 서버를 폴링하지 않고도 업데이트가 제공되는 즉시 업데이트를 받을 수 있습니다.

예시:

subscription OnPostCreate($postID: ID!) {
  userAdded(postID: $postID) {
    id
    user
    content
  }
}

이 구독은 새 글이 생성될 때 호출됩니다.

GraphQL은 어떻게 작동하나요?

GraphQL API를 구현하려면 다음 단계를 수행해야 합니다:

  1. 스키마를 사용하여 데이터 설명하기
  2. 리졸버를 데이터 원본에 연결
  3. 쿼리 및 변경 사항 작성
  4. 예측 가능한 결과 얻기

Back4app 또는 유사한 서비스를 사용하면 이 작업의 대부분이 추상화됩니다. 데이터베이스 모델을 만들면 Back4app에서 GraphQL 스키마와 문서를 자동으로 생성합니다. 이에 대한 자세한 내용은 튜토리얼의 실습 부분에서 확인할 수 있습니다.

GraphQL의 장점은 무엇인가요?

GraphQL은 배우기 쉽고, 사용자가 여러 소스의 데이터를 집계할 수 있으며, API를 위한 최신 언어입니다.

스키마

GraphQL API는 강력하게 유형화된 스키마를 기반으로 합니다. 따라서 런타임이 아닌 컴파일 타임에 유형 오류와 버그를 감지할 수 있습니다. 또한 GraphQL API는 내성적이기 때문에 외부 문서에 의존하지 않고도 자신에 대한 정보를 제공할 수 있습니다.

유연하고 효율적인 데이터 가져오기

GraphQL API는 클라이언트가 필요한 것을 정확히 지정할 수 있기 때문에 매우 유연합니다. 이를 통해 과소 가져오기 및 과잉 가져오기 문제를 해결하고 API 요청 수를 줄일 수 있습니다. API 요청이 적을수록 성능이 향상됩니다.

API 업데이트

GraphQL을 사용하면 현재 쿼리에 영향을 주지 않고 새로운 필드와 유형을 원활하게 통합할 수 있습니다. 더 이상 사용되지 않는 오래된 필드도 도구에서 숨길 수 있습니다. GraphQL API는 앱에 새로운 기능에 대한 지속적인 액세스를 제공하고 더 깔끔하고 지속 가능한 서버 코드 개발을 장려합니다.

진실의 단일 소스

GraphQL 스키마는 GraphQL 애플리케이션에서 단일 데이터 소스를 설정합니다. 이는 조직이 전체 API를 쉽게 관리할 수 있는 방법을 제공합니다.

GraphQL 확장

GraphQL은 방대한 GraphQL 개발자 커뮤니티의 지원을 받으며 다양한 오픈소스 확장 기능을 제공합니다. 확장 프로그램은 페이지 매김, 캐싱, 성능 모니터링 등과 같은 몇 가지 일반적인 API 작업을 간소화합니다.

GraphQL의 단점은 무엇인가요?

복잡성

GraphQL은 데이터 쿼리 작업의 대부분을 서버 측으로 이동시켜 백엔드를 더 복잡하게 만듭니다. 또한 쿼리 언어와 스키마 정의에 더 많은 사전 계획과 유지 관리가 필요할 수 있습니다.

학습 곡선

GraphQL은 REST보다 학습 곡선이 더 가파릅니다. 또한 GraphQL 개발자는 일반적으로 REST 개발자보다 더 비싸고 찾기도 더 어렵습니다.

표준화 부족

GraphQL에 대한 주요 비판 중 하나는 구현에 있어 표준화가 부족하다는 점입니다. REST API에는 잘 정립된 원칙과 모범 사례가 있는 반면 GraphQL 문서는 구현 방법에 대한 몇 가지 일반적인 팁만 제공합니다. 이로 인해 GraphQL API의 설계 및 사용 방식에 일관성이 없고 혼란을 초래할 수 있습니다.

보안

GraphQL의 장점은 클라이언트가 필요한 것을 정확하게 요청할 수 있다는 것이지만, 다른 한편으로는 잠재적인 보안 위험이 될 수도 있습니다. 악의적인 쿼리가 실행되지 않도록 사용자 입력을 적절히 검증하고 위생 처리하는 것이 중요합니다.

GraphQL API는 어떻게 구축하나요?

이 문서 섹션에서는 Back4app으로 GraphQL API를 만들고 Next.js 프런트엔드에서 연결하는 방법을 살펴봅니다.

전제 조건

  • JavaScript ES6 사용 경험
  • React 및 Next.js 사용 경험
  • GraphQL에 대한 기본 이해

Back4app이란 무엇인가요?

Back4app은 오픈 소스 소프트웨어를 기반으로 작동하는 탁월한 BaaS(서비스형 백엔드) 솔루션입니다. 이 플랫폼은 사용자가 웹 및 모바일 애플리케이션을 더 쉽고 빠르게 개발할 수 있는 다양한 기능을 제공합니다. 이를 통해 기업은 백엔드나 기본 인프라에 대해 걱정할 필요 없이 비즈니스 로직에만 집중할 수 있습니다.

Back4app에는 다양한 기능이 포함된 사용하기 쉬운 대시보드와 명령줄 인터페이스(CLI)가 함께 제공됩니다. 또한 Node.js, Flutter, React Native, Android, Angular, iOS 등 다양한 인기 도구에 대한 소프트웨어 개발 키트(SDK)를 제공합니다.

Back4app은 모든 앱의 요구 사항을 충족할 수 있는 간단한 가격 모델을 따릅니다. 또한 테스트 및 프로토타입 제작에 적합한 무료 요금제(신용카드 필요 없음)도 제공합니다.

Back4app에 대해 더 자세히 알고 싶으신가요? Back4app을 사용하는 이유를 확인하세요.

프로젝트 소개

간단한 웹 TODO 애플리케이션을 구축하겠습니다. 이 앱은 백엔드와 프론트엔드의 두 부분으로 구성될 것입니다. 백엔드는 GraphQL API로 구동되며 Back4app에 배포됩니다. 반면에 프론트엔드는 Next.js 프레임워크를 사용하여 TypeScript로 작성됩니다. 프론트엔드를 백엔드에 연결하기 위해 Apollo 클라이언트를 사용합니다.

최종 애플리케이션은 다음과 같이 표시됩니다:

back4app-graphql 프로젝트 미리보기

백엔드

Back4app 앱 만들기

다음 단계를 진행하려면 Back4app 계정이 있어야 합니다. 이미 계정이 있는 경우 로그인하세요. 그렇지 않은 경우 무료 계정을 만들어주세요.

Back4app을 사용하기 위한 첫 번째 단계는 앱을 만드는 것입니다. 대시보드에 로그인하면 현재 앱 목록을 볼 수 있습니다. 새 앱을 만들려면 “새 앱 만들기”를 클릭합니다.

Back4app 앱 생성

GraphQL API를 구성할 때 “백엔드 서비스” 옵션을 선택하고 앱의 고유한 이름을 지정합니다. 또한 데이터베이스 유형으로 “NoSQL”을 선택합니다.

데이터베이스, 애플리케이션 레이어, 확장, 백업 및 보안을 포함하여 앱에 필요한 모든 것을 구성할 때까지 Back4app이 인내심을 갖고 기다리세요.

설정이 완료되면 앱의 대시보드로 이동합니다.

Back4app 앱 생성

데이터베이스 모델 정의

간단한 TODO 앱을 구축하는 것이므로 데이터베이스 모델은 하나만 필요합니다.

시작하려면 Back4app 대시보드로 이동하여 사이드바에서 “데이터베이스” 옵션을 찾습니다. 거기에서 “클래스 만들기”를 선택하고 “작업”이라는 이름을 지정합니다. “공개 읽기 및 쓰기 활성화”를 선택한 다음 “클래스 만들기 및 열 추가”를 클릭합니다.

Back4app 모델 정의

후속 열을 포함합니다:

+-----------+------------------+---------------+----------+
| Data type | Name             | Default value | Required |
+-----------+------------------+---------------+----------+
| String    | name             | <leave blank> | yes      |
+-----------+------------------+---------------+----------+
| String    | description      | <leave blank> | no       |
+-----------+------------------+---------------+----------+
| Boolean   | isDone           | false         | no       |
+-----------+------------------+---------------+----------+

데이터베이스 모델을 만들면 4개의 데이터베이스 열이 자동으로 추가된 것을 확인할 수 있습니다:

  1. objectId는 객체의 기본 키입니다.
  2. 업데이트된 날짜는 마지막 업데이트의 타임스탬프입니다.
  3. createdAt는 생성 타임스탬프입니다.
  4. ACL은 “액세스 제어 목록”을 정의합니다.

이 튜토리얼의 뒷부분에서 GraphQL 쿼리 및 변이 작업을 할 때 필요하므로 기억해 두세요.

Back4app 대시보드에 익숙해지려면 예를 들어 두 가지 샘플 작업을 추가해 보세요:

+-----------------+----------------------------------------+--------+
| name            | description                            | isDone |
+-----------------+----------------------------------------+--------+
| GraphQL backend | Create a GraphQL backend via Back4app. | false  |
+-----------------+----------------------------------------+--------+
| Learn GraphQL   | Learn the basics of GraphQL.           | true   |
+-----------------+----------------------------------------+--------+

GraphQL API 콘솔

GraphQL API 콘솔을 사용하면 GraphQL 쿼리와 변형을 코드에서 구현하기 전에 테스트할 수 있습니다. 콘솔에 액세스하려면 사이드바에서 “API”를 선택한 다음 “콘솔 > GraphQL”을 선택합니다.

Back4app GraphQL 콘솔

콘솔은 사용하기 매우 쉽습니다. 왼쪽에서 사용자 지정 쿼리와 변형을 입력하면 오른쪽에 결과가 표시됩니다.

모든 것이 제대로 작동하는지 확인하려면 다음 쿼리를 실행하세요:

query CheckHealth {
    health
}

다음과 같은 JSON 응답이 표시됩니다:

{
  "data": {
    "health": true
  }
}

Back4app의 가장 큰 장점 중 하나는 Parse를 기반으로 한다는 점입니다. 데이터베이스 모델을 만들 때 Parse는 자동으로 GraphQL을 구성했습니다. 여기에는 GraphQL 스키마, 문서 생성 등이 포함됩니다.

데이터베이스에서 작업과 그 세부 정보를 나열해 보겠습니다:

query GetTasks {
  tasks {
    count
    edges {
      node {
        id
        name
        description
        isDone
      }
    }
  }
}

비슷한 답변을 받으실 수 있습니다:

{
  "data": {
    "tasks": {
      "count": 2,
      "edges": [
        {
          "node": {
            "id": "VGFzazpXQkJzSkRtV2xU",
            "name": "GraphQL backend",
            "description": "Create a GraphQL backend via Back4app.",
            "isDone": false
          }
        },
        {
          "node": {
            "id": "VGFzazpnM2lhNzdwUXBp",
            "name": "Learn GraphQL",
            "description": "Learn the basics of GraphQL.",
            "isDone": true
          }
        }
      ]
    }
  }
}

특정 객체를 가져오고, 객체를 만들고, 업데이트하는 등의 작업은 비슷하게 수행됩니다. 이러한 쿼리와 변이는 튜토리얼의 뒷부분에서 설명할 것이므로 여기서는 자세히 설명하지 않겠습니다. 또한 Back4app 문서에서 이러한 주제를 잘 다루고 있습니다:

  1. 객체 만들기
  2. 객체 가져오기
  3. 개체 찾기
  4. 개체 업데이트하기
  5. 개체 삭제하기
  6. 인증

백엔드 부분은 여기까지입니다. 다음 섹션에서는 프론트엔드에 대한 작업을 시작하겠습니다.

프론트엔드

다음 앱 만들기

Next.js 프로젝트를 부트스트랩하는 가장 쉬운 방법은 create-next-app 유틸리티를 사용하는 것입니다. 터미널을 열고 다음 명령을 실행합니다:

$ yarn create next-app

√ What is your project named? ... back4app-graphql
√ Would you like to use TypeScript with this project? ... No
√ Would you like to use ESLint with this project? ... Yes
√ Would you like to use the `src/` directory with this project? ... No
√ Would you like to use the experimental `app/` directory with this project? ... No
√ What import alias would you like configured? ... @/*

Successfully created a Next.js app.

다음으로 개발 서버를 실행합니다:

$ yarn dev

http://localhost:3000 으로 이동하면 기본 Next.js 랜딩 페이지가 표시됩니다.

다음JS 기본 랜딩 페이지

ChakraUI

UI/UX 빌드 프로세스를 가속화하고 간소화하기 위해 ChakraUI를 사용하겠습니다. Chakra UI는 React 앱을 빌드하는 데 필요한 모든 것을 제공하는 간단하고 모듈화된 접근 가능한 컴포넌트 라이브러리입니다.

문제가 발생하면 ChakraUI를 참조하세요 : Next.js 시작하기.

설치하려면 실행합니다:

yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion

그런 다음 _pages/app.tsx로 이동하여 다음과 같이 앱을 차크라프로바이더로 래핑합니다:

// pages/_app.tsx

import "../styles/globals.css";
import type {AppProps} from "next/app";
import {ChakraProvider} from "@chakra-ui/react";

export const theme = extendTheme({});

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ChakraProvider theme={theme}>
      <Component {...pageProps} />
    </ChakraProvider>
  );
}

export default MyApp;

차크라프로바이더를 가져오는 것을 잊지 마세요:

import {ChakraProvider} from "@chakra-ui/provider";

무작위로 색상이 깜박이는 것을 방지하려면 콘텐츠 전에 색상 모드 스크립트를 로드해야 합니다. 이를 위해 _document.js를 다음과 같이 활용하면 됩니다:

// pages/_document.tsx

import {ColorModeScript} from "@chakra-ui/react";
import { Html, Head, Main, NextScript } from "next/document";
import {theme} from "@/pages/_app";

export default function Document() {
  return (
    <Html lang='en'>
      <Head />
      <body>
        <ColorModeScript initialColorMode={theme.config.initialColorMode} />
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

좋아요, ChakraUI를 성공적으로 설치했습니다.

사용자 인터페이스

계속해서 사용자 인터페이스를 만들어 보겠습니다. 웹 앱은 다음 두 페이지로 구성됩니다:

  1. / 작업 목록을 표시합니다.
  2. 만들기/ 작업 생성을 위한 양식을 표시합니다.

먼저 pages/index.tsx를 다음 내용으로 바꾸세요:

// pages/index.tsx

import type {NextPage} from "next";
import {
    Button, Card, CardBody, Container,
    Heading, HStack, Stack, Text, VStack
} from "@chakra-ui/react";
import Link from "next/link";

let data = {
  "tasks": {
    "count": 2,
    "edges": [
      {
        "node": {
          "id": "VGFzazpXQkJzSkRtV2xU",
          "name": "GraphQL backend",
          "description": "Create a GraphQL backend via Back4app.",
          "isDone": false
        }
      },
      {
        "node": {
          "id": "VGFzazpnM2lhNzdwUXBp",
          "name": "Learn GraphQL",
          "description": "Learn the basics of GraphQL.",
          "isDone": true
        }
      }
    ]
  }
};

const ListPage: NextPage = () => {

  const handleMarkAsDone = async (id: string, isDone: boolean) => {
    console.log("TODO: mark task as done and refetch");
  };

  const handleDelete = async (id: string) => {
    console.log("TODO: delete task and refetch");
  };

  return (
    <>
      <Container maxWidth="container.lg">
        <HStack w="fill" justifyContent="space-between" mt={8} mb={4}>
          <Heading as="h1" size="lg">back4app-graphql</Heading>
          <Link href="/create">
            <Button size="sm" colorScheme="blue">
              Create task
            </Button>
          </Link>
        </HStack>
        <VStack spacing={4}>
          {data.tasks.edges.map((edge) => {
            let task = edge.node;
            return (
              <Card key={task.id} w="100%">
                <CardBody>
                  <Stack direction="column">
                    <Heading as="h2" size="md">
                      {task.isDone ? "✔️" : "❌"}{" "}
                      {task.name}
                    </Heading>
                    <Text>
                      {task.description}
                    </Text>
                    <Stack direction="row" pt={2}>
                      <Button size="sm" colorScheme="blue" onClick={() => handleMarkAsDone(task.id, task.isDone)}>
                        Toggle done
                      </Button>
                      <Button size="sm" colorScheme="red" onClick={() => handleDelete(task.id)}>
                        Delete
                      </Button>
                    </Stack>
                  </Stack>
                </CardBody>
              </Card>
            );
          })}
        </VStack>
      </Container>
    </>
  );
};

export default ListPage;
  1. 사용자 인터페이스를 디자인하기 위해 ChakraUI를 사용했습니다.
  2. 작업은 현재 데이터라는 정적 배열에서 로드됩니다.
  3. 나중에 handleMarkAsDone( ) 및 handleDelete() 를 활용하여 GraphQL API 요청을 보내겠습니다.

그런 다음 페이지 폴더에 다음 내용으로 create.tsx 파일을 만듭니다:

// pages/create.tsx

import type {NextPage} from "next";
import {
  Button, Card, CardBody, CardHeader, Container, FormControl,
  FormLabel, Heading, HStack, Input, Stack, Switch, Text
} from "@chakra-ui/react";
import Link from "next/link";
import {useState} from "react";
import {useRouter} from "next/router";

const CreatePage: NextPage = () => {
  const router = useRouter();

  const [name, setName] = useState("");
  const [description, setDescription] = useState("");
  const [isDone, setIsDone] = useState(false);

  const [formError, setFormError] = useState("");

  const handleSubmit = (event) => {
    event.preventDefault();

    if (!name || !description) {
      setFormError("Please enter the title and the description.");
      return;
    }

    console.log("TODO: call the GraphQL API and redirect");
  };

  return (
    <>
      <Container maxWidth="container.lg">
        <HStack w="fill" justifyContent="space-between" mt={8} mb={4}>
          <Heading as="h1" size="lg">back4app-graphql</Heading>
          <Link href="/">
            <Button size="sm" colorScheme="blue">
              View list
            </Button>
          </Link>
        </HStack>
        <Card>
          <CardHeader>
            <Stack direction="column">
              <Heading as="h2" size="md">Create task</Heading>
              <Text>
                Fill out the form and press &quot;Create&quot; to create a new task.
              </Text>
            </Stack>
          </CardHeader>
          <CardBody>
            <form onSubmit={handleSubmit}>
              <Stack direction="column">
                {formError && <Text color="red.500" fontWeight="bold">{formError}</Text>}
                <FormControl>
                  <FormLabel>Name</FormLabel>
                  <Input type="text" value={name} onChange={(event) => setName(event.target.value)}/>
                </FormControl>
                <FormControl>
                  <FormLabel>Description</FormLabel>
                  <Input type="text" value={description} onChange={(event) => setDescription(event.target.value)}/>
                </FormControl>
                <FormControl display="flex" alignItems="center">
                  <FormLabel mb="0">
                    Is done?
                  </FormLabel>
                  <Switch isChecked={isDone} onChange={() => setIsDone(!isDone)}/>
                </FormControl>
                <Button size="sm" colorScheme="blue" type="submit">Create task</Button>
              </Stack>
            </form>
          </CardBody>
        </Card>
      </Container>
    </>
  );
};

export default CreatePage;
  1. 다시 ChakraUI 컴포넌트를 사용하여 UI를 만들었습니다.
  2. 작업을 생성하기 위해 React로 제어되는 양식을 만들었습니다.
  3. 핸들 서브밋() 함수는 나중에 API 요청을 전송하는 데 사용됩니다.

웹 서버를 다시 시작하고 웹 앱( http://localhost:3000)을 방문합니다 . 하드코딩된 두 개의 작업이 보일 것입니다. 다음으로 ‘작업 만들기’를 클릭하여 작업 생성 양식을 확인합니다.

Back4app GraphQL 생성 작업

GraphQL 클라이언트

프런트엔드에서 GraphQL API에 연결하려면 먼저 GraphQL 클라이언트를 설치해야 합니다. 설정이 쉽고, 별다른 의견이 필요 없으며, 많은 상용구가 필요하지 않으므로 Apollo 클라이언트를 사용하는 것이 좋습니다.

먼저 @apollo/clientgraphql을 설치합니다:

$ yarn add @apollo/client graphql

클라이언트를 초기화하려면 Back4app API 자격 증명을 제공해야 합니다. 자격 증명을 얻는 가장 쉬운 방법은 GraphQL 콘솔로 이동하여 헤더를 메모하는 것입니다.

Back4app GraphQL 자격 증명

소스 코드에 API 키를 노출하고 싶지 않으므로 프로젝트 루트에 다음 내용으로 .env.local 파일을 생성합니다:

# .env.local

NEXT_PUBLIC_PARSE_APPLICATION_ID=<YOUR_PARSE_APP_ID>
NEXT_PUBLIC_PARSE_MASTER_KEY=<YOUR_PARSE_MASTER_KEY>
NEXT_PUBLIC_PARSE_CLIENT_KEY=<YOUR_PARSE_CLIENT_KEY>

그런 다음 pages/_app.tsx로 이동하여 Apollo 클라이언트를 초기화한 다음 다음과 같이 ApolloProvider로 애플리케이션을 래핑합니다:

// pages/_app.tsx

// ...
import {ApolloClient, ApolloProvider, InMemoryCache} from "@apollo/client";

const client = new ApolloClient({
  uri: "https://parseapi.back4app.com/graphql",
  headers: {
    "X-Parse-Application-Id": process.env.NEXT_PUBLIC_PARSE_APPLICATION_ID,
    "X-Parse-Master-Key": process.env.NEXT_PUBLIC_PARSE_MASTER_KEY,
    "X-Parse-Client-Key": process.env.NEXT_PUBLIC_PARSE_CLIENT_KEY,
  },
  cache: new InMemoryCache(),
});

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ChakraProvider theme={theme}>
      <ApolloProvider client={client}>
        <Component {...pageProps} />
      </ApolloProvider>
    </ChakraProvider>
  );
}

프론트엔드가 다시 컴파일될 때까지 기다린 다음 웹 앱으로 이동하여 콘솔에서 오류를 확인합니다. 오류가 없으면 연결에 성공했다는 뜻입니다.

GraphQL 쿼리 및 돌연변이

마지막으로 해야 할 일은 GraphQL 쿼리와 변형을 정의한 다음 React 코드에서 이를 호출하는 것입니다.

IDE에서 GraphQL 코드 지원을 사용하려면 Back4app GraphQL 콘솔로 이동하여 사이드바에서 “스키마”를 선택하고 SDL로 다운로드합니다. 그런 다음 프로젝트 루트에 schema.graphql이라는 새 파일을 만들고 SDL 콘텐츠에 붙여넣습니다.

먼저 pages/index.tsx로 이동하여 가져오기 뒤에 다음 쿼리를 추가합니다:

// pages/index.tsx

const GET_TASKS = gql`
  query GetTasks {
    tasks {
      count
      edges {
        node {
          id
          name
          description
          isDone
        }
      }
    }
  }
`;

const UPDATE_TASK = gql`
  mutation UpdateTask($id: ID!, $isDone: Boolean!) {
    updateTask(input: { id: $id, fields: { isDone: $isDone } }) {
      task {
        isDone
        updatedAt
      }
    }
  }
`;

const DELETE_TASK = gql`
  mutation DeleteTask($id: ID!) {
    deleteTask(input: { id: $id }) {
      task {
        id
      }
    }
  }
`;

이 세 가지 쿼리는 설명이 필요 없을 정도로 간단합니다. 첫 번째 쿼리는 모든 작업의 목록을 반환하고, 두 번째 쿼리는 특정 작업의 isDone 속성을 업데이트하며, 세 번째 쿼리는 특정 작업을 삭제합니다.

그런 다음 ListPage 상단을 다음과 같이 수정합니다:

// pages_index.tsx

// ...

const ListPage: NextPage = () => {

  const {loading, error, data, refetch} = useQuery(GET_TASKS);
  const [deleteTask] = useMutation(DELETE_TASK);
  const [updateTask] = useMutation(UPDATE_TASK);

const handleMarkAsDone = async (id: string, isDone: boolean) => {
    try {
      const updateTaskResponse = await updateTask({
        variables: {id: id, isDone: !isDone}
      });
      console.debug(updateTaskResponse);
      refetch();
    } catch (error) {
      console.error(error);
    }
  };

  const handleDelete = async (id: string) => {
    try {
      const deleteTaskResponse = await deleteTask({
        variables: {id: id},
      });
      console.debug(deleteTaskResponse);
      refetch();
    } catch (error) {
      console.error(error);
    }
  };

  if (error) return <p>Oops, something went wrong.</p>;
  if (loading) return <Spinner/>;

  // ...
};

export default ListPage;
  1. useQuery() 를 사용하여 GET_TASKS 쿼리를 실행하고 결과를 저장했습니다.
  2. GraphQL 돌연변이를 정의하기 위해 useMutation()을 사용했습니다.
  3. 변이 훅을 활용하도록 handleMarkAsDone()handleDelete() 를 업데이트했습니다.

계속해서 비슷한 방식으로 페이지/create.tsx를 수정합니다:

const CREATE_TASK = gql`
  mutation CreateTask($name: String!, $description: String, $isDone: Boolean!) {
    createTask(
      input: {
        fields: { name: $name, description: $description, isDone: $isDone }
      }
    ) {
      task {
        id
      }
    }
  }
`;

const CreatePage: NextPage = () => {

  // ...

  const [createTask] = useMutation(CREATE_TASK);

  const handleSubmit = (event) => {
    event.preventDefault();

    if (!name || !description) {
      setFormError("Please enter the title and the description.");
      return;
    }

    createTask({
      variables: {name: name, description: description, isDone: isDone}
    }).then(response => {
      router.push("/");
    });
  };

  // ...
};

export default CreatePage;

가져오기를 잊지 마세요:

import {gql, useMutation, useQuery} from "@apollo/client";

좋아요, 그거예요!

개발 서버를 다시 시작하고 웹 앱을 방문합니다. 데이터베이스에서 작업이 로드되었는지 확인하고 새 작업을 만든 다음 완료로 표시하고 삭제해 보세요.

결론

결론적으로 GraphQL은 유연한 쿼리 기능과 효율적인 데이터 검색 기능으로 인해 복잡한 API를 구축하는 데 적합한 선택입니다. 기존 REST API에 비해 많은 장점이 있지만 프로젝트를 시작할 때 고려해야 할 몇 가지 단점이 있습니다.

이 글에서는 GraphQL의 장단점을 살펴보고, REST API와 비교했으며, 주요 GraphQL 용어를 소개했습니다. 이제 Back4app에서 간단한 GraphQL API를 직접 구축하여 JavaScript 프런트엔드에서 연결할 수 있습니다.

최종 소스 코드는 GitHub 리포지토리에서 가져올 수 있습니다.

자주 묻는 질문

GraphQL이란 무엇인가요?

GraphQL은 강력하고 유연한 API를 개발하기 위한 쿼리 언어이자 서버 측 런타임입니다.

REST와 GraphQL의 주요 차이점은 무엇인가요?

REST는 웹 서비스를 구축하기 위한 표준화되고 간단한 접근 방식이며, GraphQL은 보다 유연한 쿼리 기능을 제공하고 데이터의 과소 수집 및 과다 수집 문제를 해결합니다.

GraphQL에서 자주 사용되는 용어는 무엇인가요?

– 스키마(Schema): GraphQL API를 통해 사용할 수 있는 데이터에 대한 설명입니다.
– 리졸버(Resolver): GraphQL 쿼리의 특정 필드에 대한 데이터를 가져오는 함수입니다.
– 쿼리(Query): GraphQL API에서 데이터를 읽기 전용으로 요청하는 것입니다.
– 뮤테이션(Mutation): GraphQL API에서 데이터를 수정하기 위한 요청입니다.
– 서브스크립션(Subscription): GraphQL API에서 실시간 업데이트를 요청하는 것입니다.

GraphQL API를 어떻게 구축하나요?

1. Back4app에서 무료 계정을 만드세요.
2. 데이터베이스 모델을 설계하세요.
3. Back4app GraphQL API 콘솔을 통해 쿼리와 뮤테이션을 작성하세요.
4. 프론트엔드 프로젝트에 GraphQL 클라이언트(Apollo 또는 Relay 등)를 설치하세요.
5. 클라이언트를 초기화하고 GraphQL API에 연결하세요.


Leave a reply

Your email address will not be published.