본문 바로가기

프론트엔드/ReactJS

React + Apollo Tutorial (1) - Getting Started

이 글은 다음 페이지를 번역한 글입니다.

https://www.howtographql.com/react-apollo/1-getting-started/

 

Getting Started with GraphQL, React and Apollo Tutorial

Start building a Hackernews clone. Create the frontend with create-react-app and the backend with Prisma.

www.howtographql.com

 

Getting Started

이것은 프론트엔드 튜토리얼이므로 백엔드 구현에 시간을 할애하지 않을 것입니다. 대신 Node 튜토리얼에서 구현한 서버를 사용할 것입니다.

 

React 애플리케이션을 만들고 나서 백엔드에 필요한 코드를 가져올 것입니다.

참고: 이 자습서의 최종 프로젝트는 GitHub에서 찾을 수 있습니다. 다음 장을 진행하는 동안 길을 잃을 때마다 항상 참고 자료로 사용할 수 있습니다. 또한 각 코드 블록에는 파일 이름이 주석으로 지정되어 있습니다. 이러한 주석은 GitHub의 해당 파일에 직접 연결되므로 코드를 어디에 넣을지와 최종 결과가 어떻게 표시되는지 명확하게 볼 수 있습니다.

Frontend

Creating the app

원본에서는 패키지 매니저로 yarn을 사용했지만, 저는 npm을 사용하면서 튜토리얼을 따라가겠습니다.

그리고 타입스크립트를 사용하겠습니다!.

 

첫 번째 단계는 React 프로젝트를 만드는 것입니다! 처음에 언급했듯이 우리는 이를 위해 create-react-app을 사용할 것입니다.

 

터미널에서 다음의 명령어를 실행하세요.

npx create-react-app hackernews-react-apollo --template typescript

이렇게 하면 모든 기본 구성 설정이 포함된 hackernews-react-apollo라는 새 디렉토리가 생성됩니다.

 

디렉토리로 이동하고 앱을 시작하여 모든 것이 작동하는지 확인하십시오.

cd hackernews-react-apollo
npm start

 

 

그러면 브라우저가 열리고 앱을 실행 중인 http://localhost:3000으로 이동합니다. 모든 것이 잘 되었다면 다음이 표시됩니다.

프로젝트 구조를 개선하려면 src 폴더 안에 두 개의 디렉토리를 만드십시오. 첫 번째는 component라고 하며 모든 React 컴포넌트를 보관합니다. styles라는 두 번째 디렉토리를 생성하여 프로젝트의 모든 CSS를 보관합니다.

App.tsx는 컴포넌트이므로 component 디렉토리로 옮기세요. App.test.tsx를 거기로 이동할 수도 있습니다(또는 여기에서 사용하지 않을 것이므로 삭제하셔도 됩니다). App.cssindex.css에는 스타일이 포함되어 있으므로 스타일로 이동하십시오. 또한 index.tsxApp.tsx 모두에서 이러한 파일에 대한 참조를 적절하게 변경해야 합니다.

 

// index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import './styles/index.css';
import App from './components/App';
import reportWebVitals from './reportWebVitals';
// App.tsx

import React, { Component } from 'react';
import logo from '../logo.svg';
import '../styles/App.css';

 

프로젝트의 구조는 다음과 같습니다.

 


Prepare Styling

이 튜토리얼은 GraphQL의 개념과 GraphQL을 React 애플리케이션 내에서 사용하는 방법에 관한 튜토리얼입니다. 그러므로 가능한 한 스타일링에 시간을 덜 쓰고 싶습니다. 이 프로젝트에서 CSS 사용을 줄이기 위해 여러 CSS 클래스를 제공하는 Tachyons 라이브러리를 사용할 것입니다.

 

public/index.html을 열고 두 개의 기존 link 태그 바로 아래에 Tachyons를 가져오는 link 태그를 추가합니다.
// index.html

<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<link
  rel="stylesheet"
  href="https://unpkg.com/tachyons@4.12.0/css/tachyons.min.css"
/>​

 

우리는 여전히 좀 더 커스터마이징 된 스타일링을 원하기 때문에, 프로젝트에 포함되어야 하는 몇 가지 스타일도 준비했습니다.

 

index.css를 열고 내용을 다음으로 바꿉니다.
// index.css

body {
  margin: 0;
  padding: 0;
  font-family: Verdana, Geneva, sans-serif;
}

input {
  max-width: 500px;
}

.gray {
  color: #828282;
}

.orange {
  background-color: #ff6600;
}

.background-gray {
  background-color: rgb(246, 246, 239);
}

.f11 {
  font-size: 11px;
}

.w85 {
  width: 85%;
}

.button {
  font-family: monospace;
  font-size: 10pt;
  color: black;
  background-color: buttonface;
  text-align: center;
  padding: 2px 6px 3px;
  border-width: 2px;
  border-style: outset;
  border-color: buttonface;
  cursor: pointer;
  max-width: 250px;
}​

Install Apollo Client

다음으로, 여러 패키지를 통해 제공되는 Apollo Client(및 React hooks)의 기능을 가져와야 합니다.(다음 명령어를 터미널에 입력)
npm install @apollo/client graphql

 

다음은 설치한 패키지에 대한 간단한 설명입니다.

  • @apollo/client에는 우리의 앱과 GraphQL 클라이언트를 연결하는 데 필요한 모든 부분이 포함되어 있습니다. 이를 통해 ApolloClient, React 앱을 감싸는 provider인 ApolloProvider , useQuery와 같은 커스텀훅 등을 사용할 수 있습니다.
  • graphql에는 Facebook의 GraphQL 참조 구현(Facebook's reference implementation of GraphQL)이 포함되어 있습니다. Apollo Client는 내부에서 일부 기능을 사용합니다.

이제 코드를 작성할 준비가 되었습니다! 🚀


ApolloClient 구성

Apollo는 모든 저수준의 네트워킹 로직을 추상화하고 GraphQL 서버에 멋진 인터페이스를 제공합니다. REST API로 작업하는 것과 달리 더 이상 자체 HTTP request를 구성할 필요가 없습니다. 대신 단순히 query와 mutation을 작성하고 ApolloClient 인스턴스를 사용하여 보낼 수 있습니다.

 

Apollo를 사용할 때 가장 먼저 해야 할 일은 ApolloClient 인스턴스를 구성하는 것입니다. 네트워크 연결을 처리할 수 있도록 GraphQL API의 엔드포인트를 알아야 합니다.

 

src/index.tsx를 열고 내용을 다음과 같이 바꿉니다.
import React from "react";
import ReactDOM from "react-dom";
import "./styles/index.css";
import App from "./components/App";
import reportWebVitals from "./reportWebVitals";

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

// 2
const httpLink = createHttpLink({
  uri: "http://localhost:4000"
});

// 3
const client = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache()
});

// 4
ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById("root")
);

ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

위의 코드 스니펫에서 무슨 일이 일어나고 있는지 살펴보겠습니다.

  1. @apollo/client에서 Apollo 클라이언트를 연결하는 데 필요한 모든 의존성을 가져옵니다.
  2. ApolloClient의 인스턴스를 GraphQL API와 연결할 httpLink를 생성합니다. GraphQL 서버는 http://localhost:4000에서 실행됩니다.
  3. httpLink 인스턴스와 InMemoryCache 인스턴스를 전달하여 ApolloClient의 인스턴스를 만듭니다.
  4. 마지막으로 React 앱의 루트 컴포넌트를 렌더링합니다. Appclient를 prop으로 전달하는 고차 컴포넌트 ApolloProvider로 래핑됩니다.

이제 앱에 데이터를 로드할 준비가 되었습니다! 😎


Backend

Downloading the Server Code

위에서 언급했듯이 이번 튜토리얼의 백엔드는 노드 튜토리얼의 최종 결과물을 사용하기만 하면 됩니다.

터미널에서 hackernews-react-apollo 디렉토리로 이동하여 다음 명령을 실행합니다.
curl https://codeload.github.com/howtographql/react-apollo/tar.gz/starter | tar -xz --strip=1 react-apollo-starter/server​

 

이제 백엔드에 필요한 모든 코드가 포함된 server 디렉토리가 있습니다.

 

서버를 시작하기 전에 주요 구성 요소를 빠르게 훑어보겠습니다:

  • prisma: 이 디렉토리에는 Prisma 설정과 관련된 모든 파일이 들어 있습니다. Prisma Client는 GraphQL 리졸버에서 데이터베이스에 액세스하는 데 사용됩니다.(ORM과 유사)
    • schema.prisma는 프로젝트의 데이터 모델을 정의합니다. Prisma Schema Language를 사용하여 데이터베이스 테이블의 모양과 테이블 간의 관계를 정의합니다.
    • dev.db는 이 튜토리얼의 데이터를 저장하고 검색하는 데 사용할 SQLite 데이터베이스입니다.
  • src: 이 디렉토리에는 GraphQL 서버의 소스 파일이 있습니다.
    • schema.graphql에는 애플리케이션 스키마가 포함되어 있습니다. 애플리케이션 스키마는 프론트엔드에서 보낼 수 있는 GraphQL 작업을 정의합니다. 잠시 후에 이 파일에 대해 자세히 살펴보겠습니다.
    • resolvers 디렉토리에는 애플리케이션 스키마에서 정의한 작업에 대한 resolver 함수들이 있습니다.
    • index.js는 GraphQL 서버의 진입점(entry point)입니다.

언급한 구성 요소 중 server/src/schema.graphql에 정의된, 애플리케이션 스키마만 프론트엔드 개발자와 관련이 있습니다. schema.graphql에는 프론트엔드에서 보낼 수 있는 모든 작업(query, mutation 및 subscription; 쿼리, 뮤테이션, 섭스크립션이라고 쓰겠습니다)을 정의하는 GraphQL 스키마가 포함되어 있습니다.

 

파일의 내용은 다음과 같습니다:

type Query {
  info: String!
  feed(filter: String, skip: Int, take: Int, orderBy: LinkOrderByInput): Feed!
}

type Feed {
  id: ID!
  links: [Link!]!
  count: Int!
}

type Mutation {
  post(url: String!, description: String!): Link!
  signup(email: String!, password: String!, name: String!): AuthPayload
  login(email: String!, password: String!): AuthPayload
  vote(linkId: ID!): Vote
}

type Subscription {
  newLink: Link
  newVote: Vote
}

type AuthPayload {
  token: String
  user: User
}

type User {
  id: ID!
  name: String!
  email: String!
  links: [Link!]!
}

type Link {
  id: ID!
  description: String!
  url: String!
  postedBy: User
  votes: [Vote!]!
  createdAt: DateTime!
}

type Vote {
  id: ID!
  link: Link!
  user: User!
}

input LinkOrderByInput {
  description: Sort
  url: Sort
  createdAt: Sort
}

enum Sort {
  asc
  desc
}

scalar DateTime

 

위 스키마는 다음 작업을 허용합니다.

  • Queries:
    • feed: 백엔드에서 모든 링크를 검색합니다. 이 쿼리는 필터, 정렬 및 페이지 매김 인자(pagination arguments)도 허용합니다.
  • Mutations:
    • post: 인증된 사용자가 새 링크를 만들 수 있습니다.
    • signup: 새 사용자를 위한 계정을 만듭니다.
    • login: 기존 사용자 로그인합니다.
    • vote: 인증된 사용자가 기존 링크에 투표할 수 있습니다.
  • Subscriptions:
    • newLink: 새 링크가 생성될 때 실시간으로 업데이트를 수신합니다.
    • newVote: 투표가 제출되면 실시간으로 업데이트를 수신합니다.

예를 들어 다음 feed 쿼리를 보내 서버에서 처음 10개의 링크를 검색할 수 있습니다:

{
  feed(skip: 0, take: 10) {
    links {
      description
      url
      postedBy {
        name
      }
    }
  }
}

또는 signup 뮤테이션을 보내 새로운 user를 생성할 수 있습니다:

mutation {
  signup(name: "Sarah", email: "sarah@prisma.io", password: "graphql") {
    token
    user {
      id
    }
  }
}

Creating a Database and Generating Prisma Client

서버를 시작하고 쿼리와 뮤테이션을 보내기 전에 해야 할 일이 하나 남았습니다. 실제로 데이터를 저장하고 검색하려면 데이터베이스와  Prisma Client가 필요합니다.

 

Prisma는 Postgres, MySQL, SQLite를 포함한 여러 관계형 데이터베이스를 지원합니다.

 

이 자습서에서는 작업을 단순하게 유지할 것이고, SQLite를 사용할 것입니다. SQLite는 시작하기가 매우 쉬운 파일 시스템 데이터베이스입니다. 그러나 SQLite는 프로덕션 목적에는 적합하지 않을 수 있습니다.

 

server/prisma 디렉토리에 dev.db라는 파일이 있습니다. 이 파일은 SQLite 데이터베이스입니다.

참고: 이 튜토리얼에서는, 원한다면 Postgres 또는 MySQL을 자유롭게 사용할 수 있습니다. 튜토리얼의 모든 부분은 Postgres 또는 MySQL을 사용해도 여전히 작동합니다.

다음으로 데이터베이스 마이그레이션을 실행하고 Prisma Client를 생성해 보겠습니다.

 

server 디렉토리에서 몇 가지 명령을 실행하여 Prisma Client를 생성해야 합니다.

 

Prisma Client를 생성하기 전에, 다른 dependency 들이 잘 설치되었는지 확인하겠습니다.

server 디렉토리는 튜토리얼 페이지에 나와있는 그대로 패키지 매니저를 사용했습니다.

cd server
yarn
npx prisma generate

Exploring the server

Prisma Client가 생성되면 이제 서버를 탐색할 수 있습니다.

server 디렉토리로 이동하고, 다음 명령을 실행하여 서버를 시작하십시오.
yarn dev​

저는 서버를 실행했을 때, 다음과 같은 경고가 출력됐습니다.

[DEP0111] DeprecationWarning: Access to process.binding('http_parser') is deprecated.

실행하면 [nodemon] app crashed - waiting for file changes before starting...
위와 같이 크래시가 났고, 다음 링크를 참고하여 수정했습니다.
https://github.com/howtographql/howtographql/issues/1370

 

yarn devpackage.json에 정의된 dev 스크립트를 실행합니다. 스크립트는 먼저 nodemon(이후 http://localhost:4000에서 실행)을 사용하여 서버를 시작한 다음 GraphQL Playground를 열어서 API를 탐색하고 작업할 것입니다.

플레이그라운드는 대화형 환경을 제공하는 "GraphQL IDE"입니다. 플레이그라운드를 통해 GraphQL API로 쿼리, 뮤테이션 및 섭스크립션을 보낼 수 있습니다. 만약 REST API로 작업해 본 경험이 있다면 알 수도 있는 Postman과 유사합니다. 하지만 추가적인 이점이 있습니다.

 

플레이그라운드에 대해 가장 먼저 주목해야 할 점은 GraphQL API에 대한 문서가 내장되어 있다는 것입니다. 이 문서는 GraphQL 스키마를 기반으로 생성되었으며 Playground의 오른쪽 가장자리에 있는 녹색 SCHEMA 버튼을 클릭하여 열 수 있습니다. 결과적으로 위의 애플리케이션 스키마에서 본 것과 동일한 정보를 보여줍니다.

Playground의 왼쪽 창은 쿼리, 뮤테이션 및 섭스크립션을 작성하는 데 사용할 수 있는 편집기입니다. 중간에 있는 재생 버튼을 클릭하면 요청(request)이 전송되고 서버의 응답(response)이 오른쪽 결과 창에 표시됩니다.

 

처음으로, 유저를 생성해봅시다(아직 생성하지 않은 경우). 다음과 같이 뮤테이션을 보내 새 유저를 생성할 수 있습니다:
mutation {
  signup(name: "Alice", email: "alice@prisma.io", password: "graphql") {
    token
    user {
      name
      id
    }
  }
}​
서버의 응답으로부터 인증 token을 복사하고 Playground에서 다른 탭을 엽니다. 새로운 탭에서, 왼쪽 하단 모서리에 있는 HTTP HEADERS 창을 열고 Authorization 헤더를 지정합니다. 다음 스니펫의 __TOKEN__ 자리를 복사된 토큰으로 바꿉니다.
{
  "Authorization":" Bearer __TOKEN__"
}​

 

이제 해당 탭에서 쿼리/뮤테이션을 보낼 때마다 인증 토큰이 전달됩니다. 이것은 로그인이 필요한 쿼리 및 뮤테이션을 수행하는 데 필수적입니다. 이제 이러한 뮤테이션 두 개를 실행합니다.

다음 두 뮤테이션을 편집기 창에 복사합니다.
mutation CreatePrismaLink {
  post(
    description: "Prisma gives you a powerful database toolkit 😎"
    url: "https://prisma.io"
  ) {
    id
  }
}

mutation CreateApolloLink {
  post(
    description: "The best GraphQL client for React"
    url: "https://www.apollographql.com/docs/react/"
  ) {
    id
  }
}​

 

편집기에 두 개의 뮤테이션을 한 번에 추가하기 때문에 뮤테이션에는 작업 이름이 있어야 합니다. 작업 이름은 CreatePrismaLinkCreateApolloLink라고 하겠습니다.

  • 두 창 중간에 있는 재생 버튼을 클릭하고 드롭다운에서 각 뮤테이션을 정확히 한 번 선택합니다.

이렇게 하면 데이터베이스에 두 개의 새 Link 레코드가 생성됩니다. 이미 열려 있는 플레이그라운드에서 다음 쿼리를 전송하여 뮤테이션이 실제로 작동했는지 확인할 수 있습니다:

{
  feed {
    links {
      id
      description
      url
    }
  }
}
참고: app 섹션의 default 플레이그라운드에서 feed 쿼리를 보낼 수도 있습니다.

 

모든 것이 잘 되었다면 쿼리는 다음 데이터를 반환할 것입니다(물론 id는 Prisma가 생성했으며 고유한 값이기 때문에 귀하의 경우에는 다를 것입니다.).

{
  "data": {
    "feed": {
      "links": [
        {
          "id": "cjcnfwjeif1rx012483nh6utk",
          "description": "The best GraphQL client",
          "url": "https://www.apollographql.com/docs/react/"
        },
        {
          "id": "cjcnfznzff1w601247iili50x",
          "description": "Prisma gives you a powerful database toolkit 😎",
          "url": "https://prisma.io"
        }
      ]
    }
  }
}

 

훌륭합니다. 우리 서버가 작동합니다! 👏