GraphQL 기초
기본적인 지식 및 Apollo를 활용한 simple project
GraphQL
페이스북에서 만든 쿼리 언어로 sql과 같은 쿼리 언어이지만 구조의 차이가 존재.
gql(graphQL)은 웹 클라이언트가 서버로부터 데이터를 효율적으로 가져오는 것이 목적.
sql은 백엔드에서 주로 작성하지만, gql의 경우 주로 클라이언트 시스템에서 작성, 호출.
OverFetching, UnderFetching
GraphQL vs RestAPI
RestAPI
URL, Method(get,post,...)을 통한 다양한 Endpoint가 존재.
Endpoint에 따른 DB SQL 쿼리.
OverFetching, UnderFetching의 발생.
gql
단 하나의 Endpoint가 존재.
gql 스키마의 타입에 따른 DB SQL 쿼리.
OverFetching, UnderFetching에서 RestAPI에 비해 유리하다.
(OverFetching : 불필요한 데이터가 포함됨 / UnderFetching : 여러번의 API 호출)

GraphQL 구조
쿼리(query) : DB 데이터 get(R).
뮤테이션 (mutation) : DB 데이터 수정(C,U,D).
(RESTAPI와 같은 개념적인 규약)

GraphQL을 위한 라이브러리
GraphQL은 (명세, 형식) 쿼리 언어로 구현할 솔루션이 필요하다.
백엔드에서 정보를 제공 및 처리
프론트엔드에서 요청 전송
이를 위한 라이브러리로는 GraphQL.js, GraphQL Yoga, AWS Amplift, ...
Apollo GraphQL
백엔드와 프론트엔드 모두 제공
간편하고 쉬운 설정
스키마/타입 (schema/type)
const { ApolloServer, gql } = require('apollo-server')
const database = require('./database')
/**
* typeDef
* GraphQL 명세에서 사용될 데이터, 요청 타입 지정
* gql (template literal tag) 로 생성됨
*/
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
}
/**
* resolver
* 서비스의 액션들을 함수로 지정
* 요청에 따라 데이터를 반환, 입력, 수정, 삭제
*/
const resolvers = {
Query: {
teams: () => database.teams
.map((team) => {
team.supplies = database.supplies
.filter((supply) => {
return supply.team === team.id
})
return team
})
}
// ApolloServer : typeDef와 resolver를 인자로 받아 서버 생성
const server = new ApolloServer({ typeDefs, resolvers })
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`)
})
오브젝트 타입과 필드
오브젝트 타입 : Team
필드 : id, manager, office, ...
스칼라 타입 : String, ID, Int 등
느낌표(!) : 필수 값을 의미(non-nullable)
대괄호([, ]) : 배열을 의미(array)
스칼라 타입
ID
기본적으로는 String, 고유 식별자 역할을 나타냄
String
UTF-8 문자열
Int
부호가 있는 32비트 정수
Float
부호가 있는 부동소수점 값
Boolean
참/거짓
! Non Null
Null을 반환할 수 없음.
열거 타입
미리 지정된 값들중에서만 반환.(ex. enum)
리스트 타
특정 타입의 배열을 반환.
[String]
✔
✔
✔
[String!]
✔
✔
❌
[String]!
❌
✔
✔
[String!]!
❌
✔
❌
객체 타입
사용자에 의해 정의된 타입들
Union
타입 여럿을 한 배열에 반환하고자 할 때 사용
Interface
유사한 객체 타입을 만들기 위한 공통 필드 타입
추상 타입 - 다른 타입에 implements 되기 위한 타입
인자와 인풋 타입
필터
[Query type] type Query { ... peopleFiltered( team: Int, sex: Sex, blood_type: BloodType, from: String ): [People] ... } ======================================================================= [DB work] getPeople: (args) => dataFiltered('people', args) .map((person) => { person.tools = [ ...dbWorks.getEquipments({used_by: person.role}), ...dbWorks.getSoftwares({used_by: person.role}) ] person.givens = [ ...dbWorks.getEquipments({used_by: person.role}), ...dbWorks.getSupplies({team: person.team}) ] return person }), ====================================================================== [request] query { peopleFiltered ( team: 1 blood_type: B from: "Texas" ) { id first_name last_name sex blood_type serve_years role team from } }
페이징
[Query type] type Query { ... peoplePaginated( page: Int!, per_page: Int! ): [People] ... } ====================================================================== [DB work] if (args.page && args.per_page) { result = result.slice( (args.page - 1) * args.per_page, args.page * args.per_page) } ====================================================================== [request] query { peoplePaginated(page: 1, per_page: 7) { id first_name last_name sex blood_type serve_years role team from } }
별칭
query { badGuys: peopleFiltered(sex: male, blood_type: B) { first_name last_name sex blood_type } newYorkers: peopleFiltered(from: "New York") { first_name last_name from } }
인풋 타입
[Query type] const typeDefs = gql` .... input PostPersonInput { first_name: String! last_name: String! sex: Sex! blood_type: BloodType! serve_years: Int! role: Role! team: ID! from: String! } ` const resolvers = { // ... Mutation: { postPerson: (parent, args) => dbWorks.postPerson(args), } } ====================================================================== [request] mutation { postPerson(input: { first_name: "Hanna" last_name: "Kim" sex: female blood_type: O serve_years: 3 role: developer team: 1 from: "Pusan" }) { id first_name last_name sex blood_type role team from } }
마침
리팩토링을 위한 정리
RestAPI에 비해 Back과 Front의 협업이 한층 수월해 질 것으로 보인다. 기존 response를 Back에서 전적으로 담당하던 것에 대해 그 영역이 Front로 넘어간 것으로 overfetching이나 underfetching을 해소하기 위한 중복적인 개발도 감소 할 것으로 예상된다.
참고
Last updated