분류 Reactjs

슬래시 GraphQL 백엔드로 Pokédex React 앱을 빌드 하는 방법

컨텐츠 정보

  • 조회 597 (작성일 )

본문

프런트 엔드 개발자는 웹 애플리케이션의 백엔드와 상호 작용하는 것이 최대한 힘들지 않기를 원합니다. 

데이터베이스에서 데이터를 요청하거나 데이터베이스에 저장된 레코드를 업데이트하는 것은 프런트 엔드 개발자가 자신이 가장 잘하는 일, 즉 아름답고 직관적 인 사용자 인터페이스를 만드는 데 집중할 수 있도록 간단해야 합니다.


https://dev.to/thawkin3/how-to-build-a-pokedex-react-app-with-a-slash-graphql-backend-501n


GraphQL은 데이터베이스 작업을 쉽게 합니다. 데이터베이스를 쿼리 할 때 미리 선택된 데이터 필드를 반환하는 특정 API 엔드 포인트를 만들기 위해 백엔드 개발자에게 의존하는 대신, 프런트 엔드 개발자는 백엔드에 간단한 요청을 하고 필요한 정확한 데이터를 검색 할 수 있습니다. 

이러한 수준의 유연성이 GraphQL이 매력적인 이유 중 하나입니다.


더 좋은 점은 호스팅 된 GraphQL 백엔드 인 Slash GraphQL (Dgraph 제공)을 사용할 수 있다는 것입니다. 이 서비스는 완전히 새로운 서비스이며 2020 년 9 월 10 일에 공개적으로 출시되었습니다. Slash GraphQL을 사용하면 새로운 백엔드 엔드 포인트를 생성하고 그래프 데이터베이스에 원하는 스키마를 지정할 수 있습니다. 짜잔! 단계.


호스팅 된 백엔드의 장점은 자체 백엔드 인프라를 관리하거나 자체 데이터베이스를 생성 및 관리하거나 API 엔드 포인트를 생성 할 필요가 없다는 것입니다. 그 모든 것이 당신을 위해 처리됩니다.


이 기사에서는 Slash GraphQL의 기본 설정 중 일부를 살펴본 다음 몇 시간 만에 React 및 Slash GraphQL로 Pokémon Pokédex 앱을 구축 한 방법을 살펴볼 것입니다!


여기 GitHub에서 모든 코드를 볼 수 있습니다.


데모 앱 개요 


Pokémon Pokédex app 


어떤 90 년대 어린이 (또는 성인)가 150 개의 오리지널 포켓몬을 모두 잡는 것을 꿈꾸지 않았습니까? 데모 앱은 Pokémon 마스터가 되는 과정을 추적하는 데 도움이 됩니다.


앱을 빌드 할 때 API 작업을 위한 모든 CRUD 작업 (생성, 읽기, 업데이트 및 삭제)을 다룰 것입니다.


Slash GraphQL의 API Explorer에서 온라인으로 데이터베이스에 모든 포켓몬을 추가하는 것으로 시작합니다. 그런 다음 Pokédex 앱 UI에 데이터베이스에서 쿼리 된 151 개의 포켓몬을 모두 표시합니다. (Hey, Mew를 생략 할 수 없었습니다. 그래도 될까요?) 화면 상단에는 표시된 결과를 Pokémon 유형 및 Pokémon이 있는지 여부에 따라 필터링 할 수 있는 두 개의 드롭 다운 메뉴가 표시됩니다. 캡처. 또한 각 포켓몬 옆에 토글 스위치가 있어 포켓몬을 캡처 여부를 표시 할 수 있습니다. 앱의 UI를 통해 데이터베이스에서 Pokémon을 삭제하지는 않지만 일부 데이터를 정리해야 하는 경우이를 수행 할 수 있는 방법을 안내하겠습니다.


여행을 시작할 준비가 되셨습니까?


Slash GraphQL 시작하기 


새 백엔드 생성 


Slash GraphQL 계정을 만든 후에는 GraphQL을 몇 단계만으로 백업하고 실행할 수 있습니다.


  1. "백엔드 만들기"버튼을 클릭합니다.
  2. 이름을 지어주세요. (예를 들어 "pokedex"를 선택했습니다.)
  3. 선택적으로 API 엔드 포인트 URL에 하위 도메인 이름을 제공합니다. (다시 "pokedex"를 선택했습니다.)
  4. 선택적으로 공급자와 영역을 선택합니다. (기본적으로 미국 서부 2 리전에서 AWS를 사용합니다.)
  5. "Create New Backend"버튼을 클릭하여 선택을 확인하십시오.
  6. 백엔드 엔드 포인트를 가져옵니다. (내는 다음과 같습니다 : https://pokedex.us-west-2.aws.cloud.dgraph.io/graphql.)
  7. "스키마 만들기"버튼을 클릭합니다.

그게 다야! 새 백엔드를 생성 한 후에는 라이브 GraphQL 데이터베이스와 API 엔드 포인트를 사용할 수 있습니다.


Creating a new backend 

스키마 생성 


이제 백업 및 실행이 완료되었으므로 데이터베이스에 포함 할 데이터 유형에 대한 스키마를 만들어야 합니다. Pokédex 앱의 경우 Pokémon 유형과 PokémonType 열거 형이 있습니다.


enum PokemonType {
  Bug
  Dark
  Dragon
  Electric
  Fairy
  Fighting
  Fire
  Flying
  Ghost
  Grass
  Ground
  Ice
  Normal
  Poison
  Psychic
  Rock
  Steel
  Water
}

type Pokemon {
  id: Int! @search
  name: String! @search(by: [fulltext])
  captured: Boolean! @search
  imgUrl: String!
  pokemonTypes: [PokemonType!]! @search
}

적은 양의 코드로 풀어야 할 것이 많습니다! PokémonType 열거 형은 매우 간단합니다. Fire, Water, Grass 및 Electric을 포함한 모든 Pokémon 유형의 집합입니다. Pokémon 유형은 각 Pokémon에 대해 보유하게 될 데이터의 형태를 설명합니다. 각 포켓몬은 ID, 이름, 포켓몬의 사진을 표시하기위한 이미지 URL, 포켓몬 유형 및 포켓몬 캡처 여부를 나타내는 상태를 갖습니다. 


각 필드에 연관된 데이터 유형이 있음을 알 수 있습니다. 예를 들어 id는 Int (정수)이고 name 및 imgUrl은 String 유형이며 captured는 Boolean입니다. 느낌표가 있습니다! 필드가 필수임을 의미합니다. 마지막으로 @search 키워드를 추가하면 쿼리 및 변형에서 필드를 검색 할 수 있습니다.


데이터베이스 및 새로 생성 된 스키마 작업을 테스트하기 위해 Slash GraphQL 웹 콘솔 내에서 바로 데이터베이스에 대해 쿼리 및 변형을 실행할 수 있는 깔끔한 기능인 API Explorer를 사용할 수 있습니다.


데이터베이스 채우기 


API Explorer를 사용하여 모든 Pokémon을 Pokédex 데이터베이스에 삽입 해 보겠습니다. 다음과 같은 변형을 사용합니다.


mutation AddPokemon {
  addPokemon(input: [
    { id: 1, name: "Bulbasaur", captured: false, imgUrl: "http://img.pokemondb.net/artwork/bulbasaur.jpg", pokemonTypes: [Grass, Poison] },
    { id: 2, name: "Ivysaur", captured: false, imgUrl: "http://img.pokemondb.net/artwork/ivysaur.jpg", pokemonTypes: [Grass, Poison] },
    { id: 3, name: "Venusaur", captured: false, imgUrl: "http://img.pokemondb.net/artwork/venusaur.jpg", pokemonTypes: [Grass, Poison] },
    { id: 4, name: "Charmander", captured: false, imgUrl: "http://img.pokemondb.net/artwork/charmander.jpg", pokemonTypes: [Fire] },
    { id: 5, name: "Charmeleon", captured: false, imgUrl: "http://img.pokemondb.net/artwork/charmeleon.jpg", pokemonTypes: [Fire] },
    { id: 6, name: "Charizard", captured: false, imgUrl: "http://img.pokemondb.net/artwork/charizard.jpg", pokemonTypes: [Fire, Flying] },
    { id: 7, name: "Squirtle", captured: false, imgUrl: "http://img.pokemondb.net/artwork/squirtle.jpg", pokemonTypes: [Water] },
    { id: 8, name: "Wartortle", captured: false, imgUrl: "http://img.pokemondb.net/artwork/wartortle.jpg", pokemonTypes: [Water] },
    { id: 9, name: "Blastoise", captured: false, imgUrl: "http://img.pokemondb.net/artwork/blastoise.jpg", pokemonTypes: [Water] },
  ]) {
    pokemon {
      id
      name
      captured
      imgUrl
      pokemonTypes
    }
  }
}

간결함을 위해 위의 스니펫에서 처음 9 개의 포켓몬 만 표시했습니다. 모든 포켓몬을 추가하기 위한 전체 코드 스니펫을 확인하십시오.


Adding all the Pokémon via the API Explorer 


이제 빠른 온 전성 검사를 위해 데이터베이스를 쿼리하여 모든 포켓몬이 올바르게 추가되었는지 확인할 수 있습니다. 다음과 같이 모든 포켓몬에 대한 데이터를 요청합니다.


query GetAllPokemon {
  queryPokemon {
    id
    name
    captured
    imgUrl
    pokemonTypes
  }
}

Querying for all Pokémon in the API Explorer 


필요한 데이터가 전부인 경우에만 포켓몬 이름을 반환하는 유사한 쿼리를 작성할 수도 있습니다. GraphQL의 아름다움을 보라!


query GetAllPokemonNames {
  queryPokemon {
    name
  }
}

Querying for all Pokémon names in the API Explorer 


앱에서 데이터 가져 오기 


이제 포켓몬을 Pokédex에 추가하고 데이터가 실제로 있는지 확인 했으므로 앱에 표시되도록하겠습니다. 우리 앱은 프런트 엔드 용 ReactMaterial UI로 빌드 되었으며 create-react-app을 사용하여 부트 스트랩 되었습니다. 앱을 빌드하는 단계별 방법은 다루지 않겠지 만 핵심 부분 중 일부를 강조하겠습니다. 다시 말하지만 리포지토리를 복제하거나 살펴보고 싶다면 GitHub에서 전체 코드를 사용할 수 있습니다.


프런트 엔드 코드에서 Slash GraphQL을 사용할 때 기본적으로 백엔드를 생성 할 때 제공된 단일 API 엔드 포인트에 POST 요청을합니다. 요청 본문에서 GraphQL 코드를 쿼리로 제공하고 쿼리 또는 변형에 대한 설명 이름을 operationName으로 작성한 다음 선택적으로 GraphQL 코드에서 참조하는 변수의 객체를 제공합니다.


다음은 앱에서 포켓몬을 가져 오기 위해 이 패턴을 따르는 방법의 단순화 된 버전입니다.


// Main generic GraphQL request
async function fetchGraphQL(operationsDoc, operationName, variables) {
  const result = await fetch(
    'https://pokedex.us-west-2.aws.cloud.dgraph.io/graphql',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        query: operationsDoc,
        operationName,
        variables,
      }),
    }
  )

  return await result.json()
}

// Fetch all Pokemon - GraphQL
const fetchAllPokemonOperationsDoc = `
  query fetchAllPokemon {
    queryPokemon {
      id
      name
      captured
      imgUrl
      pokemonTypes
    }
  }
`

// Fetch all Pokemon - Function
function fetchAllPokemon() {
  return fetchGraphQL(fetchAllPokemonOperationsDoc, 'fetchAllPokemon', {})
}

// The rest of the following code is called in the main App component:

const { errors, data } = await fetchAllPokemon()

if (errors) {
  console.error(errors)
}

const result = data.queryPokemon.sort(
  (pokemonA, pokemonB) => pokemonA.id - pokemonB.id
)

// setPokedexData is a setter method from using the `useState` React hook, not shown in this gist
setPokedexData(result)

그런 다음 해당 데이터를 가져와 배열 맵 도우미 기능을 사용하여 반복하여 UI에 각 포켓몬을 표시합니다.


페이지 상단의 필터는 API에도 연결되어 있습니다. 필터 값이 변경되면 새 API 요청이 시작되지만 이번에는 검색 결과 집합이 더 좁아집니다. 예를 들어, 다음은 우리가 캡처 한 모든 불 유형 포켓몬입니다.


Captured Fire type Pokémon 


유형 및 캡처 된 상태별로 필터링 된 Pokémon에 대한 API 요청을 만드는 JavaScript는 다음과 같습니다.


const fetchPokemonOfCertainTypeAndByCapturedStatusOperationsDoc = ({
  pokemonType,
  isCaptured,
}) => `
  query fetchPokemonOfCertainTypeAndByCapturedStatus {
    queryPokemon(filter: { captured: ${isCaptured}, pokemonTypes: { eq: [${pokemonType}] } }) {
      id
      name
      captured
      imgUrl
      pokemonTypes
    }
  }
`

function fetchPokemonOfCertainTypeAndByCapturedStatus({
  pokemonType,
  isCaptured,
}) {
  return fetchGraphQL(
    fetchPokemonOfCertainTypeAndByCapturedStatusOperationsDoc({
      pokemonType,
      isCaptured,
    }),
    'fetchPokemonOfCertainTypeAndByCapturedStatus',
    {}
  )
}

앱에서 데이터 업데이트 


이 시점에서 API Explorer에서 Pokémon을 만들고 JavaScript를 통해 Pokédex 앱 내에서 Pokémon을 가져 오는 방법을 충분히 다루었습니다. 하지만 포켓몬 업데이트는 어떻습니까? 각 포켓몬에는 포켓몬의 캡처 상태를 제어하는 ​​토글 스위치가 있습니다. 토글을 클릭하면 데이터베이스에서 Pokémon의 캡처 된 상태가 업데이트 되고 그에 따라 UI가 업데이트 됩니다.


다음은 포켓몬을 업데이트하는 JavaScript입니다.


// Update the Pokemon Captured Status - GraphQL
const updatePokemonCapturedStatusOperationsDoc = (
  pokemonId,
  newIsCapturedValue
) => `
  mutation updatePokemonCapturedStatus {
    updatePokemon(input: {filter: {id: {eq: ${pokemonId}}}, set: {captured: ${newIsCapturedValue}}}) {
      pokemon {
        id
        name
        captured
        imgUrl
        pokemonTypes
      }
    }
  }
`

// Update the Pokemon Captured Status - Function
export function updatePokemonCapturedStatus(pokemonId, newIsCapturedValue) {
  return fetchGraphQL(
    updatePokemonCapturedStatusOperationsDoc(pokemonId, newIsCapturedValue),
    'updatePokemonCapturedStatus',
    {}
  )
}

그런 다음 토글 값이 변경되면 updatePokemonCapturedStatus 함수를 호출합니다. 이것은 데이터베이스의 값을 업데이트하기위한 API 요청을 시작합니다. 그런 다음 백엔드의 응답을 기다리지 않고 낙관적으로 UI를 업데이트하거나 응답을 기다린 후 단일 포켓몬의 결과를 프런트 엔드의 모든 포켓몬의 더 큰 데이터 세트에 병합 할 수 있습니다. 또한 모든 포켓몬을 다시 요청하고 프런트 엔드에 저장된 포켓몬 정보를 새로운 결과로 대체 할 수 있습니다. 


데이터베이스에서 데이터 삭제 


마지막 CRUD 작업은 "삭제"입니다. 사용자가 앱의 UI 내에서 Pokémon을 삭제하는 것을 허용하지 않습니다. 그러나 앱 관리자는 데이터베이스에서 실수 나 원치 않는 데이터를 삭제해야 할 수 있습니다. 이를 위해 API Explorer를 다시 사용할 수 있습니다.


예를 들어 Pokédex에 여분의 Bulbasaur가 있음을 발견하면 모든 Bulbasaur를 삭제할 수 있습니다.


mutation DeletePokemon {
  deletePokemon(filter: { name: { alloftext: "Bulbasaur" } }) {
    pokemon {
      name
    }
  }
}

Deleting all Bulbasaur Pokémon via the API Explorer 



그런 다음 Bulbasaur 하나를 다시 추가 할 수 있습니다.



mutation AddPokemon {
  addPokemon(input: [
    { id: 1, name: "Bulbasaur", captured: false, imgUrl: "http://img.pokemondb.net/artwork/bulbasaur.jpg", pokemonTypes: [Grass, Poison] }
  ]) {
    pokemon {
      id
      name
      captured
      imgUrl
      pokemonTypes
    }
  }
}

마무리 


그래서 우리는 무엇을 배웠습니까? 이제 우리는 React 앱의 맥락에서 Slash GraphQL로 작업하는 방법을 이해해야 합니다. 우리는 매우 달콤한 Pokédex 앱을 만들기 위해 모든 CRUD 작업을 다루었습니다. 도중에 포켓몬 몇 마리를 잡았을 수도 있습니다.


앱을 보호하기 위해 인증을 추가하는 방법이나 GraphQL 요청을 할 때 Apollo 클라이언트를 사용하는 방법에 대해서는 아직 다루지 않았지만 다른 기사에서 중요한 주제입니다!


숙련 된 프론트 엔드 개발자이지만 GraphQL을 사용한 경험이 많지 않았기 때문에 Slash GraphQL로 작업하는 것은 매우 쉬웠습니다. 설정하는 것은 매우 쉬웠으며 문서와 함께 API Explorer는 데이터로 만들 수 있는 다양한 쿼리와 변형을 탐색하는 데 중요한 역할을 했습니다.