React + Apollo Tutorial (1) - Getting Started
이 글은 다음 페이지를 번역한 글입니다.
https://www.howtographql.com/react-apollo/1-getting-started/
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.css 및 index.css에는 스타일이 포함되어 있으므로 스타일로 이동하십시오. 또한 index.tsx와 App.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();
위의 코드 스니펫에서 무슨 일이 일어나고 있는지 살펴보겠습니다.
- @apollo/client에서 Apollo 클라이언트를 연결하는 데 필요한 모든 의존성을 가져옵니다.
- ApolloClient의 인스턴스를 GraphQL API와 연결할 httpLink를 생성합니다. GraphQL 서버는 http://localhost:4000에서 실행됩니다.
- httpLink 인스턴스와 InMemoryCache 인스턴스를 전달하여 ApolloClient의 인스턴스를 만듭니다.
- 마지막으로 React 앱의 루트 컴포넌트를 렌더링합니다. App은 client를 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 dev는 package.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 } }
편집기에 두 개의 뮤테이션을 한 번에 추가하기 때문에 뮤테이션에는 작업 이름이 있어야 합니다. 작업 이름은 CreatePrismaLink 및 CreateApolloLink라고 하겠습니다.
- 두 창 중간에 있는 재생 버튼을 클릭하고 드롭다운에서 각 뮤테이션을 정확히 한 번 선택합니다.
이렇게 하면 데이터베이스에 두 개의 새 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"
}
]
}
}
}
훌륭합니다. 우리 서버가 작동합니다! 👏