https://www.yalco.kr/lectures/graphql-apollo/
얄팍한 GraphQL과 Apollo
목차
Section 1. GraphQL이 뭐고 왜 쓰나요?
- GraphQL은 REST의 Overfetching, Underfetching을 해결한 명세, 형식이다.
1. 강의소개 & 준비물 : 생략
2. GraphQL 이전 - REST API 그리고 장단점?, 한계
2.1 REST API : 데이터를 주고받을 주체들간 약속된 형식
2.1.1 URI 형식(어떤 정보를) X 요청 방식(어떻게 할 것인가)
2.1.2 요청 형식용도
- Get : DATA READ
- POST : DATA INSERT
- PUT/PATCH : DATA UPDATE
- DELETE : DATA DELETE
2.2 REST API의 한계
2.2.1 Overfetching : 불필요한 정보까지 전달 -> 네트워크 낭비
2.2.2 Underfetching : 필요한 정보의 부족 -> 여러번 요청
3. GraphQL로 정보 주고받기 : 쿼리 뮤테이션
A. GraphQL 장점
- Overfetching 문제 해결 : 필요한 데이터만 요청(= 데이터 전송량 감소)
- Underfetching 문제 해결 : 여러 계층 같이 데이터 요청 가능(=요청 횟수 감소)
- EndPoint 한개 : 한개의 URI 에서 POST로 모든 요청 처리 함
B. Playground : Apollo Library 사용하면 graphQL 명령어 쉽게 날릴 수 있도록 화면 제공
- Postman 같은 것.
3.1 query(REST 에서 Get과 유사)
3.1.1 team 정보 요청
query {
teams {
id
manager
office
extension_number
mascot
cleaning_duty
project
}
}
3.1.2 필요한 정보만 딱 Pick해서 요청
query {
teams {
manager
office
}
}
query {
team(id: 1) {
manager
office
}
}
3.1.3 병렬요청
query {
teams {
manager
office
mascot
}
roles {
id
requirement
}
}
3.1.4 하위 연관정보 같이 요청(team+ member)
query {
team(id: 1) {
manager
office
members {
first_name
last_name
}
}
}
3.2 Mutation(데이터 변경 요청)
3.2.1 insert
mutation {
postTeam (input: {
manager: "John Smith"
office: "104B"
extension_number: "#9982"
mascot: "Dragon"
cleaning_duty: "Monday"
project: "Lordaeron"
}) {
manager
office
extension_number
mascot
cleaning_duty
project
}
}
3.2.2 update
mutation {
editTeam(id: 2, input: {
manager: "Maruchi Han"
office: "105A"
extension_number: "2315"
mascot: "Direwolf"
cleaning_duty: "Wednesday"
project: "Haemosu"
}) {
id,
manager,
office,
extension_number,
mascot,
cleaning_duty,
project
}
}
3.2.3 delete
mutation {
deleteTeam(id: 3) {
id,
manager,
office,
extension_number,
mascot,
cleaning_duty,
project
}
}
4. Apollo
4.1 GraphQL을 구현하기 위한 Library
- 목적
- 프론트엔드 요청 전송
- 백엔드에서 정보 제공,처리
- 백엔드&프론트엔드 모두 제공하며 간편,쉽고, 기능 풍성
4.2 Apollo Server 활용 백엔드
4.3 Apollo CLient 활용 프론트엔드
Section 2. GraphQL 서버 만들어보기
1. Apollo 서버 구축하기
1.1 Apollo Server : typeDef와 resolver를 인자로 가지는 서버
1.1.1 typeDef
A. GraphQL 명세에 사용될 데이터, 요청 타입 지정
B. gql(template literal tag)로 생성됨
> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
1.1.2 resolver
A. 서비스의 Action들을 함수로 지정
B. 요청을 받고, 그에 따라 데이터 CRUD (반환/입력/수정/삭제)
1.1.3 GraphQL Playground
A. GraphQL의 type, resolver 명세를 확인 가능
- Schema : type 명세 확인가능
- Docs : Action(=resolver) 확인가능
B. Data 요청 및 전송 가능
2. Query 구성요소 및 구현
2.1 구성요소 - Query 루트 타입
type Query {
teams: [Team]
}
- 자료요청에 사용될 쿼리들을 정의
- 쿼리 명령문마다 반환될 데이터 형태를 지정
- [] = 배열의 의미. teams 쿼리를 날리면 Team의 배열이 반환된다
2.2 구성요소 - Data Type
type Team {
id: Int
manager: String
office: String
extension_number: String
mascot: String
cleaning_duty: String
project: String
}
- 반환될 데이터의 형태를 지정
- 자료형을 가진 필드들로 구성
2.3 구성요소 - Resolver
const resolvers = {
Query: {
teams: () => database.teams
}
}
- query를 날렸을 때, 실제 동작을 정의하는 부분. 예시) teams 요청시 database.teams를 반환 함.
- 즉 메소드 정의
- Query란 object의 항목들로 데이터를 반환하는 함수 선언
- 실제 프로젝트에서는 MySQL 조회 코드 등..
2.4 실습
2.4.1 단순쿼리 구현 : equipments를 반환
- 작성 순서
- A. database Connection 선언
- B. TypeDefine
- 데이터 자료형 정의
- 쿼리 정의
- 쿼리 루트에 추가하기
- C. Resolver 선언
- D. Query Test해보기
/*** type define - Data ***/
type Equipment {
id: String,
used_by: String,
count: Int,
new_or_used: String
}
/*** type define - Query ***/
type Query {
...
equipments: [Equipment]
}
/*** type define - Add to Query Root ***/
type Query {
...
equipments: [Equipment]
}
/*** Resolver define ***/
Query: {
// ...
equipments: () => database.equipments
}
/*** Usage ***/
query {
equipments {
id
used_by
count
new_or_used
}
}
2.4.2 조건 쿼리 구현 : 특정 team만 받아오기
A. Resolver 선언(arg와 team의 id 같은 것에서 첫번째 것을 return 함)
Query: {
//...
team: (parent, args, context, info) => database.teams
.filter((team) => {
return team.id === args.id
})[0],
}
B. Root 쿼리 추가
type Query {
...
team(id: Int): Team
}
C. test해보기
query {
team(id: 1) {
id
manager
office
extension_number
mascot
cleaning_duty
project
}
}
2.4.3 여러계층 쿼리 구현(underfetching 해결, team에 supplies 연결)
A. Resolver 선언 : database.teams를 가져와서, 각 team에 해당하는 supplies 값 설정 후 team 반환
Query: {
// ...
teams: () => database.teams
.map((team) => {
team.supplies = database.supplies
.filter((supply) => {
return supply.team === team.id
})
return team
}),
}
B.기존 Team Data의 TypeDef 변경
type Team {
id: Int
manager: String
office: String
extension_number: String
mascot: String
cleaning_duty: String
project: String
supplies: [Supply]
}
3. Mutation(삽입, 수정, 삭제) 구현
> 사실 resolver를 어떻게 짜느냐에 따라, query문으로 Create,Update,Delete처리 가능하다.> 다만, mutation으로 처리하기로 약속되어 있다.(막힌건 아님!)
3.1 구성요소 : Query와 동일하다.
> Mutation 루트 타입 + DataType + Resolver
3.2 삽입 : Equipment 데이터
3.2.1 Mutation 루트타입 : 인자들을 받아 insert하고 그 Equipment를 반환
type Mutation {
insertEquipment(
id: String,
used_by: String,
count: Int,
new_or_used: String
): Equipment
...
}
3.2.2 DataType : 앞 부분에서 Equipment 이미 정의함
3.2.3 Resolver
> 요청받은 항목은 모두 args로 합쳐 받게 되어있다. equipment와 같은 구조이므로 그대로 push
Mutation: {
insertEquipment: (parent, args, context, info) => {
database.equipments.push(args)
return args
},
//...
}
3.2.4 실제사용
mutation {
insertEquipment (
id: "laptop",
used_by: "developer",
count: 17,
new_or_used: "new"
) {
id
used_by
count
new_or_used
}
}
3.3 수정 : Equipment 데이터
3.3.1 Mutation 루트타입
type Mutation {
editEquipment(
id: String,
used_by: String,
count: Int,
new_or_used: String
): Equipment
...
}
3.3.2 DataType : 앞 부분에서 Equipment 이미 정의함
3.3.3 Resolver : 요청한 id와 같은 것을 db에서 찾아서, 덮어 씌우고 반환하도록 정의
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.3.4 실제사용
mutation {
editEquipment (
id: "pen tablet",
new_or_used: "new",
count: 30,
used_by: "designer"
) {
id
new_or_used
count
used_by
}
}
3.4 삭제 : Equipment 데이터
3.4.1 Mutation 루트타입 : String의 id를 받아서 삭제 후 Equipment 반환(삭제 된 Equipment)
type Mutation {
deleteEquipment(id: String): Equipment
}
3.4.2 DataType : 앞 부분에서 Equipment 이미 정의함
3.4.3 Resolver : argument 기준으로 실행. 삭제될 equipment를 db에서 찾고, 삭제 후 반환
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
}
}
3.4.4 실제 사용
mutation {
deleteEquipment(id: "notebook") {
id
used_by
count
new_or_used
}
}
Section 3. GraphQL 백엔드 서버의 심화 기능들
1. 서버 구성요소 모듈화
2. GraphQL의 기본 타입들
2.1 스칼라 타입
2.1.1 GraphQL 내장 자료형
- ID : 기본적으로는 String이나, 고유 식별자 역할임을 나타냄
- String : UTF-8 문자열
- Int : 부호가 있는 32비트 정수
- Float : 부호가 있는 부동소수점 값
- Boolean : 참/거짓
2.1.2 Non Null : !
2.2 열거 타입 : 미리 지정된 값들 중에서만 반환
2.3 리스트 타입 : 특정 타입의 배열을 반환
2.4 객체 타입 : 사용자에 의해 정의된 타입
3. 유니언과 인터페이스
3.1 Union
A. 타입 여럿을 한 배열에 반환하고자 할 때 사용
B. Ex) Equipment와 Supply를 함께 반환하기
3.2 Interface
A. 유사한 객체 타입을 만들기 위한 공통 필드 타입
B. 추상 타입 - 다른 타입에 implement 되기 위한 타입
4. 인자와 인풋 타입
4.1 데이터 조건들로 필터 넣어 받아오기
4.2 페이지로 나누어 받아오기
4.3 별칭으로 받아오기
4.4 인풋 타입
Section 4. GraphQL 클라이언트 만들어보기
1. React와 Apollo Client
2. Query와 Mutation을 사용하여 웹페이지 만들기
3. Fragment 사용하기
A. 여러 쿼리에 사용될 수 있는, 재사용 가능한 필드셋
B. 중복을 줄임으로써 전체 코드를 간소화
3.1 재사용되는 요소들 fragment로 분리
3.2 쿼리에 적용
'Back' 카테고리의 다른 글
Docker -1 : 이론 (0) | 2023.05.16 |
---|---|
Airflow (0) | 2023.05.15 |