Graph QL(4) - Create, Read
๐์์ํ๋ฉฐ
์์์ Graph QL์ ๊ฐ๋จํ๊ฒ ํ์ด๋ดค๋๋ฐโฆ ๊ทธ๋ผ REST API์์ ๋งํ๋ CRUD๋ ์ด๋ป๊ฒ ๊ตฌํํ๋๊ฑฐ์ง?๐ค ๋ผ๋ ์๋ฌธ์ด ๋ค์๊ณ ๊ฐ๋จํ ์ดํ์ ๋ง๋ค์ด ๋ณด๋ฉด์ ๊ณต๋ถํ ๋ด์ฉ์ ์ ๋ฆฌํ๊ณ ์ํ๋ค.
(์ ํํ ๋งํ๋ฉด CRUD๋ ์๋์ง๋ง ์ด ๊ธ์์๋ ํธ์์ฑ์ ์ํด CRUD๋ผ๊ณ ์์ฑํ๋ค)
โ ์ ์ฒด ํ๋ฆ
์ด CRUD๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์๋ ํฌ๊ฒ ์ธ ๋ถ๋ถ์ ์์ฑํ๊ฒ ๋๋ค. (์์ธํ ๋ด์ฉ์ ์ด ํฌ์คํ ์ฐธ์กฐ)
๐๋ฐฑ์๋
typeDefs
resolvers
๐ํ๋ก ํธ์๋
useQuery
useMutation
โ Read
์ฝ๋ ๋ถ๋ถ์ type Query
๋ฅผ ์ฌ์ฉํ๋ค. ์ด๋, Query ๋ด๋ถ์ ์ฌ์ฉํ๋ ๋ค๋ฅธ type๋ ํจ๊ป ์ง์ ํ๋ ๊ฒ์ ์ฃผ์ํ๋ค!
์๋ ์์ ๋ฅผ ๋ณด๋ฉด, type Book
๊ณผ type Movie
์ ์ง์ ํ๊ณ type Query
์์ ์ฌ์ฉํ๊ณ ์๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// server.js
const typeDefs = gql`
type Book {
id: Int
title: String
author: String
}
type Movie {
id: Int
title: String
director: String
release: Int
}
type Query {
books: [Book]
movies: [Movie]
}
`
Apollo Server๊ฐ ์ด Query๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ๋ถ๋ฌ์ฌ์ง resolvers์ ํ ๋นํด ์ค๋ค. books์ movies๋ ๋ฏธ๋ฆฌ ๋ง๋ค์ด ๋ ๋ฐ์ดํฐ๋ฅผ ํ ๋นํด ์ฃผ์๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const books = [
{
id: 1,
title: "ํด๋ฆฌํฌํฐ",
author: "JK ๋กค๋ง",
},
{
id: 2,
title: "๊ทธ๋ฆฌ๊ณ ์๋ฌด๋ ์์๋ค",
author: "์ ๊ฑฐ์ ํฌ๋ฆฌ์คํฐ",
},
]
const movies = [
{
id: 1,
title: "์์๊ป๋ผ! ๊ฝํผ๋ ์ฒํ๋ก์ํ๊ต",
director: "ํ์นดํ์ ์ํ๋ฃจ",
release: 2022,
},
{
id: 2,
title: "๋ฒ์ฃ๋์4",
director: "ํ๋ช
ํ",
release: 2024,
},
]
const resolvers = {
Query: {
books: () => books,
movies: () => movies,
},
}
๋ฐฑ์๋์์์ ์์ ์ ๋ง๋ฌด๋ฆฌ ๋์์ผ๋, ์ค์ ์ปดํฌ๋ํธ์์ ์ด ๋ฐ์ดํฐ๋ค์ ์ฝ์ด๋ณด์.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { gql, useQuery } from "@apollo/client"
export const BOOKS = gql`
query {
books {
title
author
id
}
}
`
function Read() {
const { loading, error, data } = useQuery(BOOKS)
if (loading) return <p>๋ก๋ฉ์ค!</p>
if (error) return <p>์๋ฌ๋ฐ์!</p>
return (
<>
<h2>๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ(Get)</h2>
{data.books.map(
({
title,
author,
id,
}: {
title: string
author: string
id: number
}) => (
<div key={title} className="book">
<p>
{author} : {title}
</p>
</div>
)
)}
</>
)
}
export default Read
@apollo/client
์์ gql์ useQuery๋ฅผ ๊ฐ์ ธ์จ๋ค.BOOKS ์ฟผ๋ฆฌ๋ฅผ ์์ฑ
- ์๋ฒ๋ก๋ถํฐ ์ฑ ๊ณผ ์ํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํ GraphQL ์ฟผ๋ฆฌ
useQuery ํ ์ฌ์ฉ
- GraphQL ์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ณ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค. ์ด๋ loading, error, data ๊ฐ์ฒด๋ฅผ ํตํด ๋ฐ์ดํฐ ๋ก๋ฉ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ , ๋ก๋ฉ ์ค์ด๋ฉด โ๋ก๋ฉ์ค!โ์, ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด โ์๋ฌ๋ฐ์!โ์ ํ๋ฉด์ ํ์ํ๋ค.
- ๋ฐ์ดํฐ map
data.books.map
์ ์ฌ์ฉํด ๊ฐ ์ฑ ์ ์ ๋ณด๋ฅผ ๋ฐ๋ณตํ๊ณ ํ๋ฉด์ ์ถ๋ ฅํ๋ค.
โ Create
Create๋ถํฐ๋ type Query๊ฐ ์๋๋ผ type Mutation
์ ์ฌ์ฉํ๋ค. ์์ฑ ํ๋ฆ ์์ฒด๋ ์์ ๊ต์ฅํ ๋น์ทํ๋ค!
๋จผ์ typeDefs
๋ฅผ ์ด์ฉํด GraphQL ์คํค๋ง๋ฅผ ์ ์ํ์. ์์ ์์๋ Book ํ์
๊ณผ addBook ๋ฎคํ
์ด์
(Mutation)์ ์ ์ํ๋ค. ์ด๋ type Mutation
์ ๊ฒฝ์ฐ Book ํ์
์ ๋ฐํํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
const typeDefs = gql`
type Book {
id: Int
title: String
author: String
}
type Mutation {
addBook(id: Int, title: String, author: String): Book
}
`
๋ค์์ resolvers๋ฅผ ํตํด GraphQL ์๋ฒ๊ฐ ์ค์ ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ ์ํด์ค๋ค. addBook๋ฆฌ์กธ๋ฒ ํจ์์ ๋งค๊ฐ๋ณ์๋ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ฒซ ๋ฒ์งธ ์ธ์ _๋ ๋ถ๋ชจ ๊ฐ์ฒด๋ก, ์ฌ๊ธฐ์๋ ์ฌ์ฉ๋์ง ์์ผ๋ฏ๋ก ์ธ๋์ค์ฝ์ด๋ก ํ์ํ๋ค.
- ๋ ๋ฒ์งธ ์ธ์ { title, author }๋ ํด๋ผ์ด์ธํธ๊ฐ ๋ฎคํ ์ด์ ์ ์์ฒญํ ๋ ์ ๋ฌํ๋ ์ธ์๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const books = [
{
id: 1,
title: "ํด๋ฆฌํฌํฐ",
author: "JK ๋กค๋ง",
},
{
id: 2,
title: "๊ทธ๋ฆฌ๊ณ ์๋ฌด๋ ์์๋ค",
author: "์ ๊ฑฐ์ ํฌ๋ฆฌ์คํฐ",
},
]
let nextBookId = 3
const resolvers = {
Mutation: {
addBook: (_, { title, author }) => {
const newBook = { id: nextBookId++, title, author }
books.push(newBook)
return newBook
},
},
}
addBook ํจ์๋ ์์์ ๋ฏธ๋ฆฌ ์ ์ํด ๋ books ๋ฐฐ์ด์ newBook์ด๋ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์ถ๊ฐํ๊ณ , newBook์ returnํ๋ค. ์ด๋ ๊ฒ return ํ ๊ฐ์ ํด๋ผ์ด์ธํธ์ธก์์ `data.addBook`์ผ๋ก ์ ๊ทผํ ์ ์๋ค!
์ด์ ํ๋ก ํธ์๋ ์ธก ์ฝ๋๋ฅผ ์์ฑํ๋ค.
- GraphQL ์ฟผ๋ฆฌ(ADD_BOOK)์ ์์ฑํ๋ค.
mutation AddBook($title: String, $author: String)
์ ์ด์ฉํด AddBook์ด๋ผ๋ Mutation์ ์ ์ํ๋ค. ์ด Mutation์ title๊ณผ author๋ผ๋ ๋ ๊ฐ์ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ๋๋ค. addBook Mutation์ ๊ฒฐ๊ณผ๋กid
,title
,author
๊ฐ ๋ฐํ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
import { useMutation, gql } from "@apollo/client"
import { useEffect, useState } from "react"
const ADD_BOOK = gql`
mutation AddBook($title: String, $author: String) {
addBook(title: $title, author: $author) {
id
title
author
}
}
`
useMutation ํ ์ ์ฌ์ฉํด ์์์ ์ ์ํ
ADD_BOOK
GraphQL Mutation์ ํธ์ถํ๋ค. ์ด๋refetchQueries
์ต์ ์ ์ฌ์ฉํด Mutation์ด ์คํ๋ ํ ์๋์ผ๋ก BOOKS ์ฟผ๋ฆฌ๋ฅผ ๋ค์ ์คํํ๋๋ก ์ค์ ํ๋ค. ์ด ์ค์ ์ด ์๋ค๋ฉด, ์ ๋ฐ์ดํธ ํด๋ ์ด๊ฒ์ด ๋ฐ์๋์ง ์๋๋ค.useState ํ ์ ์ฌ์ฉํด ๋ด์ฉ์ ์ ๋ฐ์ดํธ ํ๋๋ก ํ๋ค.
5.loading์ด true์ธ ๊ฒฝ์ฐ โ์ถ๊ฐ ์คโฆโ ํ ์คํธ๋ฅผ ํ์ํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
export default function Create() {
const [addBook, { data, loading, error }] = useMutation(ADD_BOOK, {
refetchQueries: [{ query: BOOKS }],
})
const [title, setTitle] = useState<string>("")
const [author, setAuthor] = useState<string>("")
useEffect(() => {
if (error) {
console.error("์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค.:", error)
}
if (data) {
console.log("์ถ๊ฐ๋์์ต๋๋ค.:", data.addBook)
}
}, [data, error])
return (
<form
onSubmit={(e) => {
e.preventDefault()
if (title && author) {
addBook({
variables: { title, author },
}).catch((e) => {
console.error("์๋ฌ๊ฐ ๋ฐ์ํ์ต๋๋ค:", e)
})
setTitle("")
setAuthor("")
}
}}
>
<input
value={author}
onChange={(e) => setAuthor(e.target.value)}
placeholder="์๊ฐ"
/>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="์ฑ
์ ๋ชฉ"
/>
<button type="submit" disabled={loading}>
์ถ๊ฐํ๊ธฐ
</button>
{loading && <p>์ถ๊ฐ ์ค...</p>}
</form>
)
}