[GraphQL]Apollo, GraphQL 사용해하여 서버 구축

cooking_123·2024년 5월 1일

GraphQL

목록 보기
2/5

Apollo

https://www.apollographql.com/

GraphQL은 Rest API와 같이 명세,형식일 뿐이다.
GraphQL로 서비스를 만들기 위해서는, 그 형식에 따라 프론트엔드에서 데이터를 요청하고, 백엔드에서 이를 받아 수신하여 작업을 처리할 수 있도록 해주는 소프트웨어가 필요하다.
Apollo는 각각 백엔드 서버, 그리고 React 등의 프론트 웹이나 모바일 앱에서 GraphQL을 사용한 정보교환을 간편하게 구현할 수 있도록 한다.

  • 백엔드와 프론트엔드 모두 제공
  • 간편하고 쉬운 설정
  • 풍성한 기능들 제공

Apollo로 만들어 볼 것

Apollo Server 를 활용한 백엔드 서버 제작
Apollo Client와 React를 활용한 프론드엔드 웹 제작

Edit csv

extentions에서 Edit csv를 설치하면



위와 같은 csv 파일을 vscode의 우측상단에 Edit csv를 눌러서 아래와 같은 엑셀같은 파일로 확인할 수 있다.

csv안에 있는 데이터들을 활용해서 이를 GraphQL로 주고받는 서버를 만들려고 한다.

Apollo 서버 설치

$ npm i graphql apollo-server

index.js에 아래와 같은 코드를 작성하고 npm start를 해주면 GraphQL Apollo 서버가 열리는 것을 볼 수 있다.

const database = require('./database');
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
    type Query {
        teams: [Team]
    }
    type Team {
        id: Int
        manager: String
        office: String
        extension_number: String
        mascot: String
        cleaning_duty: String
        project: String
    }
`;
const resolvers = {
    Query: {
        teams: () => database.teams,
    },
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
    console.log(`🚀  Server ready at ${url}`);
});

상세 설명

  • ApolloServer
    • typeDef와 resolver를 인자로 받아 서버 생성
    • listen 명령어로 서버 생성
  • typeDef

    • GraphQL에서 사용할 데이터들의 구조와 타입을 지정해준 것

    • Query 루트 타입 : Team이라는 데이터가 어떤 항목들로 구성되어 있는지 선언

      • 자료요청에 사용될 쿼리들을 정의
      • 쿼리 명령문마다 반환될 데이터 형태를 지정
          type Query {
              teams: [Team]
          }
    • type Query : 이 Team이라는 데이터를 요청할 query에도 어떤 형식으로 요청이 들어가 있는지 선언

    • Type 살펴보기

      • 반환될 데이터의 형태를 지정
      • 자료형을 가진 필드들로 구성
              type Team {
              id: Int
              manager: String
              office: String
              extension_number: String
              mascot: String
              cleaning_duty: String
              project: String
          }
  • resolver

    • 서버에서 데이터를 반환, 입력, 수정, 삭제하는 액션들이 함수형태로 지정
    • Query란 dbject의 항목들로 데이터를 반환하는 함수 선언
        const resolvers = {
        Query: {
          teams: () => database.teams
        }
      }

1. 데이터 추가하여 Query 구현하기

아래와 같은 데이터typeQuery을 설정해준다.

  {
    id: 'machanical keyboard',
    used_by: 'developer',
    count: 24,
    new_or_used: 'used'
  },
// typeDefs 설정
const typeDefs = gql`
    type Query {
        teams: [Team]
        equipments:[Equipment]
    }
    type Team {
        id: Int
        manager: String
        office: String
        extension_number: String
        mascot: String
        cleaning_duty: String
        project: String
    }
    type Equipment {
        id: String
        used_by: String
        count: Int
        new_or_used: String
    }
`;

// resolvers 구현
const resolvers = {
    Query: {
        teams: () => database.teams,
        equipments : () => database.equipments
    },
};

query를 성공적으로 볼 수 있는 것을 볼 수 있다.

1-1. 특정 데이터만 가지고 오기

  • args로 주어진 id에 해당하는 team만 필터링하여 반환
Query: {
    //...
    team: (parent, args, context, info) => database.teams
        .filter((team) => {
            return team.id === args.id
        })[0],
}
  • id를 인자로 받아 하나의 Team 데이터를 반환
type Query {
    ...
    team(id: Int): Team
}
  • query 불러오기
query {
team(id: 1) {
    id
    manager
    office
    extension_number
    mascot
    cleaning_duty
    project
}
}

1-2. 데이터에 다른 데이터 연결해서 받아오기

  • Team 데이터 목록에 supplies 데이터를 추가로 넣어주기
Query: {
    // ...
    teams: () => database.teams
    .map((team) => {
        team.supplies = database.supplies
        .filter((supply) => {
            return supply.team === team.id
        })
        return team
    }),
}
... 
type Team {
    id: Int
    manager: String
    office: String
    extension_number: String
    mascot: String
    cleaning_duty: String
    project: String
    supplies: [Supply]
}

query를 불러오면 잘 불러와진다.

2. Mutation 구현하기

2-1. Mutation 삭제 루트 구현

  • String 인자 id를 받는 deleteEquipment : 삭제된 Equipment를 반환(삭제된 결과값을 반환. id를 반환해도 되고 boolen값을 반환해도 된다. 아래 코드에서는 삭제된 데이터의 정보를 결과값을 반환하도록 설정)
type Mutation {
    deleteEquipment(id: String): Equipment
}
  • 삭제 후 결과값으로 받아올 데이터를 deleted변수에 저장
  • 데이터에서 해당 데이터 삭제 후 deleted 반환
  • 실제 프로젝트에서 SQL의 DELETE 문 등으로 구현
// 삭제 resolver
Mutation: {
      deleteEquipment: (parent, args, context, info) => {
          const deleted = database.equipments
              .filter((equipment) => {
                  return equipment.id === args.id
              })[0]
          database.equipments = database.equipments
              .filter((equipment) => {
                  return equipment.id !== args.id
              })
          return deleted
      }
}

잘 삭제되는 것을 볼 수 있다.

2-2. Mutation 데이터 추가하기

  • 추가할 Equipment의 요소 값들을 인자로 받고 추가된 Equipment를 반환
type Mutation {
    insertEquipment(
        id: String,
        used_by: String,
        count: Int,
        new_or_used: String
    ): Equipment
    ...
}
...
//resolver
Mutation: {
    insertEquipment: (parent, args, context, info) => {
        database.equipments.push(args)
        return args
    },
    //...
}

잘 추가되는 것을 볼 수 있다.

2-3. Mutation 데이터 수정하기

  • 수정할 Equipment의 요소 값들을 인자로 받고 추가된 Equipment를 반환
type Mutation {
    editEquipment(
        id: String,
        used_by: String,
        count: Int,
        new_or_used: String
    ): Equipment
    ...
}
...
//resolver
Mutation: {
    // ...
    editEquipment: (parent, args, context, info) => {
        return database.equipments.filter((equipment) => {
            return equipment.id === args.id
        }).map((equipment) => {
            Object.assign(equipment, args)
            return equipment
        })[0]
    },
    // ...
}

잘 수정이 되는 것을 볼 수 있다

3. 서버 모듈화 하기

  • 특정 파일의 코드가 너무 길어지지 않도록 여러 파일에 걸쳐 모듈화해서 작성하려고 한다.
  • apollo-server 생성자 인자 모듈화로 진행하려고 한다.

equipments.js

const { gql } = require('apollo-server')
const dbWorks = require('../dbWorks')

const typeDefs = gql`
    type Equipment {
        id: String
        used_by: String
        count: Int
        new_or_used: String
    }
`
const resolvers = {
    Query: {
        equipments: (parent, args) => dbWorks.getEquipments(args),
    },
    Mutation: {
        deleteEquipment: (parent, args) => dbWorks.deleteItem('equipments', args),
    }
}

module.exports = {
    typeDefs: typeDefs,
    resolvers: resolvers
}

_mutations.js

const { gql } = require('apollo-server');

const typeDefs = gql`
    type Mutation {
        deleteEquipment(id: String): Equipment
        deleteSupply(id: String): Supply
    }
`;

module.exports = typeDefs;

_queries.js

const { gql } = require('apollo-server');

const typeDefs = gql`
    type Query {
        equipments: [Equipment]
        supplies: [Supply]
    }
`;

module.exports = typeDefs;

index.js

const { ApolloServer } = require('apollo-server');
const _ = require('lodash');

const queries = require('./typedefs-resolvers/_queries');
const mutations = require('./typedefs-resolvers/_mutations');
const equipments = require('./typedefs-resolvers/equipments');
const supplies = require('./typedefs-resolvers/supplies');

const typeDefs = [queries, mutations, equipments.typeDefs, supplies.typeDefs];

const resolvers = [equipments.resolvers, supplies.resolvers];

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
    console.log(`🚀  Server ready at ${url}`);
});

0개의 댓글