2022.08.02(Tues)
[TIL] Day68
[SEB FE] Day69
์ค๋์ ์์นจ 8์๋ถํฐ ์ง ์ด์ฌํ๋ ๋ ์ด์ฌ์ 9์๋ถํฐ ์นดํ๋ก ํผ์ ํ๋ค..
๊ฒ๋ค๊ฐ ๋น๋ ์ฃผ๋ฅต์ฃผ๋ฅต ๐ญ
์์ทจ๋ฐฉ 2๋ฒ ์ด์ฌํ ๋๋ง๋ค ๋ชจ์กฐ๋ฆฌ ๋น ์์๋๋ฐ ๋ณธ๊ฐ ์ด์ฌํ ๋๋ ๋๋๋ ๋น๋ผ๋.. โ๏ธ
์ง ์์ดํ์ด๋ ํ์ด 2์ ์ ์ ๊ฒจ์ฐ ์ฐ๊ฒฐํ๊ตฌ, ํ์ด ์๊ฐ์ ์ฐ๋นํํ ๋๋ฌด ์๋์ค๋ฌ์์ ํ์ด๋๊ป ๋๋ฌด ์ฃ์ก,, โขแทโโขแท
์ผ๋ฅธ ๋ฐฉ ์ ๋ฆฌํ๊ณ ์๋ก์ด ๋ง์์ผ๋ก ๋ค์ ์ด์ฌํ ๊ณต๋ถํ์ใ ๐ฆพ
: Graph
+ Query Language
. ํ์ด์ค๋ถ์์ ๋ง๋ ์คํ ์์ค ์ฟผ๋ฆฌ ์ธ์ด โ API
๋ฅผ ์ํ ์ฟผ๋ฆฌ ์ธ์ด
๐ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๊ธฐ ์ํด ๊ทธ๋ํ๋ฅผ ํ์ํ๋ ์ฟผ๋ฆฌ ์ธ์ด
resolver
ํจ์๋ก ๊ฐ ํ๋ ๋ฐ์ดํฐ ์กฐํ ๊ฐ๋ฅ๐นย Graph
: ์ฌ๋ฌ ๊ฐ์ ์ ๋ค์ด ์๋ก ๋ณต์กํ๊ฒ ์ฐ๊ฒฐ๋์ด ์๋ ๊ด๊ณ๋ฅผ ํํํ ์๋ฃ๊ตฌ์กฐ
Node
/ ์ ์ (vertex)
: ํ๋์ ์ ๊ฐ์ (edge)
: ํ๋์ ์ ๐ธย ์ํฐํฐ(Entity): ์ฌ๋ฌผ์ ๊ตฌ์กฐ๋ ์ํ, ๋์ ๋ฑ์ ๋ชจ๋ธ๋ก ํํํ๋ ๊ฒฝ์ฐ, ๊ทธ ๋ชจ๋ธ์ ๊ตฌ์ฑ์์
๐นย Tree
: ๋ฐฉํฅ์ฑ ์กด์ฌ โญ๏ธ, ์ฌ์ดํด ์กด์ฌ โย โ ๋น์ํ ๊ทธ๋ํ
ใด ๋ฃจํธ & ๋ชจ์๋ฆฌ๋ฅผ ํตํด ๋ ธ๋๋ฅผ ๋ฐ๋ผ ์ํ๊ฐ๋ฅํ๋ ๋์ผ ๋ ธ๋๋ก ๋์์ฌ ์ ์๋ ์์ฑ์ ํน๋ณํ ๊ทธ๋ํ
Overfetch
: ํ์์๋ ๋ฐ์ดํฐ๊น์ง ์ ๊ณตUnderfetch
: endpoint๊ฐ ํ์ํ ์ ๋ณด๋ฅผ ์ถฉ๋ถํ ์ ๊ณต ๋ชปํจREST API | GraphQL |
---|---|
Resource์ ๋ํ ํํ ์ ์ & ๋ฐ์ดํฐ ์์ฒญ ๋ฐฉ๋ฒ ์ฐ๊ฒฐ | Resource์ ๋ํ ํํ ์ ์ & ๋ฐ์ดํฐ ์์ฒญ ๋ฐฉ๋ฒ ๋ถ๋ฆฌ |
Resource ํฌ๊ธฐ&ํํ๋ฅผ ์๋ฒ์์ ๊ฒฐ์ | Resource ์ ๋ณด๋ง ์ ์, |
ํ์ํ ํฌ๊ธฐ&ํํ๋ ํด๋ผ์ด์ธํธ ๋จ์์ ์์ฒญ์ ๊ฒฐ์ | |
URL๊ฐ Resource๋ฅผ ๋ํ๋ด๋ฉฐ, Method๊ฐ ์์ ์ ํ์ ๋ํ๋ | GraphQL Schema๊ฐ Resource๋ฅผ ๋ํ๋ด๋ฉฐ, Query, Mutation type์ด ์์ ์ ํ์ ๋ํ๋ |
์ฌ๋ฌ Resource์ ์ ๊ทผ์ ์ฌ๋ฌ๋ฒ์ ์์ฒญ ํ์ | ํ๋ฒ์ ์์ฒญ์ผ๋ก ์ฌ๋ฌ Resource์ ์ ๊ทผ ๊ฐ๋ฅ |
๊ฐ ์์ฒญ์ ํด๋น endpoint์ ์ ์๋ ํธ๋ค๋ง ํจ์๋ฅผ ํธ์ถํ์ฌ ์์ ์ฒ๋ฆฌ | ์์ฒญ ๋ฐ์ ๊ฐ ํ๋์ ๋ํ resolver๋ฅผ ํธ์ถํ์ฌ ์์ ์ฒ๋ฆฌ |
/graphql
๋ก ์์ฒญ์ ๋ฐ๊ณ , ์ด์ ๋ฐ๋ผ query
, mutation
์ resolver
ํจ์๋ก ์ ๋ฌํด์ ์์ฒญ์ ์๋ต (๋ชจ๋ ํด๋ผ์ด์ธํธ ์์ฒญ์ POST
๋ฉ์๋ ์ฌ์ฉ)playground
POST
๋ฉ์๋๋ง ์ด์ฉํด ์์ฒญ์ ๋ณด๋ด๋ฏ๋ก ๊ฐ ๋ฉ์๋์ ๋ฐ๋ฅธ ์บ์ฑ์ ์ง์๋ฐ์ ์ ์์Query
: ์ ์ฅ๋ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ (REST์ GET๊ณผ ์ ์ฌ)Mutation
: ์ ์ฅ๋ ๋ฐ์ดํฐ ์์ Create
: ์๋ก์ด ๋ฐ์ดํฐ ์์ฑUpdate
: ๊ธฐ์กด ๋ฐ์ดํฐ ์์ Delete
: ๊ธฐ์กด ๋ฐ์ดํฐ ์ญ์ Subscription
: ํน์ ์ด๋ฒคํธ ๋ฐ์์ ์๋ฒ๊ฐ ๋์ํ๋ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ํด๋ผ์ด์ธํธ์๊ฒ ์ ์ก๐นย Field
# query๋ก ๋ฐ์ดํฐ ์กฐํ
{
hero {
name
friends {
name
}
}
}
# ํ๋๋ฅผ ์ค์ฒฉํ์ฌ ์ฟผ๋ฆฌํ๋ ๊ฒ๋ ๊ฐ๋ฅ!
# ์์ ์ฟผ๋ฆฌ ์คํ ๊ฒฐ๊ณผ
{
"data": {
"hero": {
"name": "Beanxx",
"friends": [ # friends ํ๋ -> ๋ฐฐ์ด ๋ฐํ
{
"name": "Yollki"
},
{
"name": "Zzanggu"
}
]
}
}
}
๐นย ์ ๋ฌ์ธ์(Arguments)
# ํ๋์ ์ธ์์ ๋ฌ ๋ถ๋ถ ์ถ๊ฐ โ ์ฟผ๋ฆฌ์ ํ๋/์ค์ฒฉ๋ ๊ฐ์ฒด๋ค์ ์ ๋ฌ โ ์ํ๋ ๋ฐ์ดํฐ๋ง ๋ฐ์์ฌ ์ ์์
{
human(id: "10") {
name
height
}
}
# id๊ฐ 10์ธ human์ name, height ์ฟผ๋ฆฌ
{
"data": {
"human": {
"name": "Beanxx",
"height": 1.70
}
}
}
๐นย ๋ณ๋ช
(Aliases)
# ํ๋ ์ด๋ฆ ์ค๋ณต ์ฌ์ฉ ๋ถ๊ฐ -> ๋ณ๋ช
์ ๋ถ์ฌ์ ์ฟผ๋ฆฌ ์งํ
{
beanHero: hero(episode: BEAN) {
name
}
zzangHero: hero(episode: ZZANG) {
name
}
}
# BeanHero, ZzangHero๊ฐ ๋ณ๋ช
!
# ๋ณ๋ช
์ ๋ถ์ธ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ
{
"data": {
"beanHero": {
"name": "Beanxx"
},
"zzangHero": {
"name": "Zzanggu"
}
}
}
# ๋ค๋ฅธ ๋ณ๋ช
์ง์ -> 1๋ฒ์ ์์ฒญ์ผ๋ก 2๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ๋ ์ป์ด๋ผ ์ ์์
๐นย Operation name
# ์ถ์ํ ๊ตฌ๋ฌธ x -> ์ค์ ์ฑ์์ ์ฝ๋ ๋ชจํธํ์ง ์๊ฒ ์์ฑํ๋๊ฒ ์ค์!
# ๋งจ ์ 'query'๊ฐ Operation name!
# query ์ธ์๋ mutation, subscription, describes ๋ฑ ์กด์ฌ
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
{
"data": {
"hero": {
"name": "Beanxx",
"friends": [
{
"name": "Yollki"
},
{
"name": "Zzanggu"
},
{
"name": "Jane"
}
]
}
}
}
๐นย ๋ณ์(Variables)
# ๋์ ์ผ๋ก ์ธ์๋ฅผ ๋ฐ์ ์ฟผ๋ฆฌํ๋ ๊ฒฝ์ฐ '๋ณ์' ์ฌ์ฉ
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
# `$๋ณ์ ์ด๋ฆ: ํ์
`์ผ๋ก ์์ฑ
# `$ep: Episode` ๋ค์ `!`๊ฐ ๋ถ์ผ๋ฉด ep๋ ๋ฐ๋์ Episode์ฌ์ผ ํจ์ ์๋ฏธ (Optional)
# server์ธก ๋ฐ์ดํฐ ์์
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
# GraphQL Schema ๊ธฐ๋ณธ ๊ตฌ์ฑ ์์: ๊ฐ์ฒด ์ข
๋ฅ, ๊ฐ์ฒด ์ ํ(ํฌํจํ๋ ํ๋๋ฅผ ๋ํ๋)
type Character {
name: String!
appearsIn: [Episode!]!
}
# Character: GraphQL ๊ฐ์ฒด ํ์
=> ์ฆ, ํ๋๊ฐ ์๋ ํ์
์๋ฏธ (์คํค๋ง ๋๋ถ๋ถ์ ํ์
- ๊ฐ์ฒด ํ์
)
# name, appearIn: Character ํ์
์ ํ๋
# String: ๋ด์ฅ๋ ์ค์นผ๋ผ ํ์
์ค ํ๋๋ก, ๋จ์ผ ์ค์นผ๋ผ ๊ฐ์ฒด (์ฟผ๋ฆฌ์์ ํ์ ์ ํ ๊ฐ์ง ์ ์์)
# !: ํด๋น ํ๋๋ nullableํ์ง ์๊ณ ๋ฐ๋์ ๊ฐ์ด ๋ค์ด์จ๋ค๋ ์๋ฏธ
# [ ]: ๋ฐฐ์ด ์๋ฏธ. ๋ฐฐ์ด์๋ !๊ฐ ๋ถ์ ์ ์์. -> ํญ์ 0๊ฐ ์ด์์ ์์๋ฅผ ํฌํจํ ๋ฐฐ์ด ๊ธฐ๋
: ์์ฒญ์ ๋ํ ์๋ต์ ๊ฒฐ์ ํด์ฃผ๋ ํจ์๋ก์จ ๋ก์ง์ ์์ฑํจ.
๐ย ํด๋น ์คํค๋ง ํ๋์ ์ฌ์ฉ๋๋ ํจ์์ ์ค์ ํ๋์ Resolver
์์ ์ ์
const db = require("./../db")
const resolvers = {
Query: { # ์ ์ฅ๋ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
getUser: async (_, { email, pw }) => {
db.findOne({
where: { email, pw }
}) ... # ์ค์ DB์์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๋ ๋ก์ง ์์ฑํ๊ธฐ
...
}
},
Mutation: { # ์ ์ฅ๋ ๋ฐ์ดํฐ ์์ ํ๊ธฐ
createUser: async (_, { email, pw, name }) => {
...
}
}
Subscription: { # ์ค์๊ฐ ์
๋ฐ์ดํธ
newUser: async () => {
...
}
}
};
์ฒ์์ ํ์ด๋๊ณผ GraphQL๋ฅผ React์ ์ ์ฉํด์ผํ ์ง ๋ง๋งํด์ ๊ฑฐ์ 1์๊ฐ๋์ ํค๋งจ๋ฏ..
graphql.js ๊ณต์๋ฌธ์๋ฅผ ๋ณด๊ณ ๊ทธ๋ฅ App.js์ ๋ฃ์ด๋ดค๋๋ฐ๋ Error ๐ฑ
์๊ณ ๋ณด๋ github ๋ด์์ access token๋ ๋ฐ๊ธ๋ฐ์์ผ ํ๊ณ , await๋ก๋ง ์์ฑ๋์ด ์๋ ํจ์๋ฅผ async๋ก ๊ฐ์ธ์ค์ ์ฝ๊ฐ ์์ ํด์ผ ํ์๋ค..! ๋งํ์ ๋ฐ์์ค๋๊ฑฐ ์ฑ๊ณตํด์ ์ฌ๋ฌ ์์ฑ๋ค์ console๋ก ํ์ธํด๋ณด์๋ค.
์๋์ ๊ฐ์ด data๋ฅผ edges ๋ด์ ๋ฐฐ์ด ๋ด์ ๊ฐ์ฒด ํํ๋ก ๋ฐ์์ค๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
โจย ์์ ํ๊ธฐ ์์ ์ผ๋จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น๋ถํฐ!
# graphQL์ ์ฟผ๋ฆฌ๋ฅผ ๋ก์ปฌ ํ๊ฒฝ์์ ์ฝ๊ฒ ์ํํ ์ ์๊ฒ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
$ npm install @octokit/graphql
import { graphql } from "@octokit/graphql";
import { useEffect, useState } from "react";
async function getRepository() {
const { repository } = await graphql(
`
{
repository(name: "agora-states-fe", owner: "codestates-seb") {
discussions(first: 100) { // ๋ฐ์ดํฐ ๋ช ๊ฐ ์ถ๋ ฅํ ์ง
edges {
node {
id
title
createdAt
url
author {
login
avatarUrl
}
category {
name
}
answer {
author {
login
}
}
}
}
}
}
}
`,
{
headers: {
// ๊ผญ ์์ 'token' ๋ฌธ์์ด ๋ถ์ฌ์ฃผ๊ธฐ (์๋๋ฉด 'bearer'๋ ๊ฐ๋ฅ!)
authorization: `token ${MY_TOKEN_ID}`,
},
}
);
return repository;
}
function App() {
const [repository, setRepository] = useState({});
const { discussions } = repository;
// ์ฒ์ ํ๋ฒ๋ง ์คํ๋๋๋ก useEffect ์ฌ์ฉ!
useEffect(() => {
getRepository()
.then((data) => {
setRepository(data);
})
.catch((error) => {
console.log(Error, error);
});
}, []);
// map์ผ๋ก discussions.edges์ ์ ๊ทผํ์ฌ ๋ฐ์ดํฐ ๋ฟ๋ ค์ฃผ๊ธฐ
return (
<div className="main">
<ul>
{discussions.edges.map((edge) => {
return (
<li key={edge.node.id}>
<img
src={edge.node.author.avatarUrl}
alt={`avatar of ${edge.node.author.login}`}
/>
<div>
{`[${edge.node.category.name}]`}
</div>
<a href={edge.node.url}>{edge.node.title}</a>
<p>{edge.node.answer ? "โ" : "โ"}</p>
</li>
);
})}
</ul>
</div>
);
}
export default App;