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_BOOKGraphQL 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>
)
}