Post

Graph QL(2) - Apollo

๐Ÿ“Œ์‹œ์ž‘ํ•˜๋ฉฐ

Graph QL ๊ด€๋ จ ์ž๋ฃŒ๋ฅผ ์ฐพ๋‹ค๋ณด๋‹ˆ, Apollo๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ณด์•˜๋‹ค.๐Ÿ˜Ž Apollo๋Š” ๋ฌด์—‡์ธ์ง€, ์–ด๋–ป๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๊ณ  Graph QL ๊ด€๋ จ๋œ ์œ ํŠœ๋ธŒ ์˜์ƒ์—์„œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋‚˜๋ฆ„๋Œ€๋กœ ์ •๋ฆฌํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

โœ…Apollo๋ž€?

๊ณต์‹ ๋ฌธ์„œ์˜ ์„ค๋ช…์„ ์‚ดํŽด๋ณด์ž.

Apollo๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ†ตํ•ฉํ•˜๊ณ  ์„œ๋น„์Šค๋“ค์„ ๋‹จ์ผ ๋ถ„์‚ฐ GraphQL API์ธ ์Šˆํผ๊ทธ๋ž˜ํ”„(supergraph)๋กœ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž ํ”Œ๋žซํผ๊ณผ ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. Apollo๋Š” ์ฒ˜์Œ์œผ๋กœ API๋ฅผ ๊ตฌ์ถ•ํ•˜๊ฑฐ๋‚˜ API๋ฅผ ์ฟผ๋ฆฌํ•˜๊ฑฐ๋‚˜, ํ”Œ๋žซํผ์„ ์Šˆํผ๊ทธ๋ž˜ํ”„๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ๋•Œ๊นŒ์ง€, ์–ด๋–ค ๋‹จ๊ณ„๋‚˜ ๊ทœ๋ชจ์—์„œ๋„ GraphQL์ด ํšจ๊ณผ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋„๋ก ๋„์™€์ค€๋‹ค.

์Šˆํผ๊ทธ๋ž˜ํ”„ : ์—ฌ๋Ÿฌ ๊ฐœ์˜ GraphQL ์„œ๋น„์Šค์™€ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ํ•˜๋‚˜์˜ ํ†ต์ผ๋œ GraphQL API๋กœ ํ•ฉ์นœ ๊ฒƒ

์ฆ‰, Apollo๋Š” ๋ฐ์ดํ„ฐ์™€ ์„œ๋น„์Šค๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ†ตํ•ฉํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๊ณ , GraphQL์„ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋„๊ตฌ๋‹ค.

โœ…Apollo Server

Apollo Server๋Š” ์‚ฌ์–‘์„ ์ค€์ˆ˜ํ•˜๋Š” ์˜คํ”ˆ ์†Œ์Šค GraphQL ์„œ๋ฒ„๋กœ, Apollo ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋น„๋กฏํ•œ ๋ชจ๋“  GraphQL ํด๋ผ์ด์–ธํŠธ์™€ ํ˜ธํ™˜๋œ๋‹ค.

Apollo Server๋ฅผ ํ†ตํ•ด ๊ฐ„๋‹จํ•œ ์„ค์ •์œผ๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋น ๋ฅด๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค!

โžก๏ธํ”„๋กœ์ ํŠธ ์‹œ์ž‘

๐Ÿ“Œ 1. ์ƒˆ ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ

1
2
mkdir graphql-server-example
cd graphql-server-example

๐Ÿ“Œ 2. package.json ์ƒ์„ฑ

1
npm init --yes && npm pkg set type="module"

๐Ÿ“Œ 3. ์ข…์†์„ฑ ์„ค์น˜

1
npm install @apollo/server graphql

โžก๏ธ GraphQL schema ์„ค์ •

์ฐธ๊ณ ๋กœ, typeDefs = `` ๋กœ ์ž‘์„ฑํ•˜๋ฉด ์•ˆ์˜ schema ๋“ค์ด string ์ฒ˜๋Ÿผ ํ‘œ์‹œ๋˜๋Š”๋ฐ, vsCode ํ™•์žฅํ”„๋กœ๊ทธ๋žจ์„ ์„ค์น˜ํ•˜๋ฉด ์ ์ ˆํ•˜๊ฒŒ ์ปฌ๋Ÿฌ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด ๋ณด๊ธฐ ์ข‹์•„์ง„๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { ApolloServer } from "@apollo/server"
import { startStandaloneServer } from "@apollo/server/standalone"

const typeDefs = `
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`

ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณด์ž.

์š”์†Œ์„ค๋ช…
typeDefsGraphQL ์Šคํ‚ค๋งˆ ์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ๋ฌธ์ž์—ด
type BookBook ํƒ€์ž…์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ์ •์˜
type Queryํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ฟผ๋ฆฌ๋ฅผ ์ •์˜
API์˜ ์ง„์ž…์  ์—ญํ• ๋กœ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ทœ์ •

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const typeDefs = `
  type Book {
    title: String
    author: String
  }

  type Movie {
    title: String
    release: Int
  }

  type Query {
    books: [Book]
    movies: [Movie]
  }
`

โžก๏ธdata set ์„ค์ •

์œ„์—์„œ๋Š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ–ˆ์œผ๋‹ˆ, ๋ฐ์ดํ„ฐ๋ฅผ ์ •์˜ํ•˜์ž. ์œ„์—์„œ type Query {books: [Book]}๋ฅผ ์ž‘์„ฑํ–ˆ๋Š”๋ฐ ์—ฌ๊ธฐ์„œ books๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
const books = [
  {
    title: "The Awakening",
    author: "Kate Chopin",
  },
  {
    title: "City of Glass",
    author: "Paul Auster",
  },
]

โžก๏ธresolver ์ •์˜

data set์„ ์ •์˜ํ–ˆ์ง€๋งŒ, Apollo Sever๋Š” ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ํ•ด๋‹น data set์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์—, resolver๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

1
2
3
4
5
const resolvers = {
  Query: {
    books: () => books,
  },
}

โžก๏ธApolloSever ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ

1
2
3
4
5
6
7
8
9
10
11
//์Šคํ‚ค๋งˆ ์ •์˜์™€ ๋ฆฌ์กธ๋ฒ„ ์ง‘ํ•ฉ ๋‘ ๊ฐœ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
const server = new ApolloServer({
  typeDefs,
  resolvers,
})

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
})

console.log(`๐Ÿš€  Server ready at: ${url}`)

โœ…Apollo Client

Apollo Client๋Š” JavaScript๋ฅผ ์œ„ํ•œ ์ข…ํ•ฉ์ ์ธ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด GraphQL๋กœ ๋กœ์ปฌ ๋ฐ ์›๊ฒฉ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‘ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ์บ์‹œํ•˜๊ณ , ์ˆ˜์ •ํ•˜๋Š” ๋™์‹œ์— UI๋ฅผ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•ต์‹ฌ @apollo/client ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” React์™€์˜ ํ†ตํ•ฉ์„ ์ œ๊ณตํ•œ๋‹ค.

โžก๏ธํ”„๋กœ์ ํŠธ ์‹œ์ž‘

๐Ÿ“Œ 1. vite ์ด์šฉํ•ด React ์„ธํŒ…

1
npm create vite@latest

๐Ÿ“Œ 2. ๋ชจ๋“ˆ ์„ค์น˜
๋‘ ๊ฐ€์ง€ ์ตœ์ƒ์œ„ ์ข…์†์„ฑ์„ ์„ค์น˜ํ•œ๋‹ค.

  • @apollo/client: Apollo ํด๋ผ์ด์–ธํŠธ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•œ๋‹ค. ์ธ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ, ๋กœ์ปฌ ์ƒํƒœ ๊ด€๋ฆฌ, ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋ฐ React ๊ธฐ๋ฐ˜ ๋ทฐ ๋ ˆ์ด์–ด๊ฐ€ ํฌํ•จ๋œ๋‹ค.
  • graphql: ์ด ํŒจํ‚ค์ง€๋Š” GraphQL ์ฟผ๋ฆฌ ๊ตฌ๋ฌธ ๋ถ„์„์„ ์œ„ํ•œ ๋กœ์ง์„ ์ œ๊ณตํ•œ๋‹ค.
1
npm install @apollo/client graphql

โžก๏ธApolloClient ์ดˆ๊ธฐํ™”

1
2
3
4
5
6
7
8
9
10
11
12
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  gql,
} from "@apollo/client"

//ํ˜„์žฌ ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ ๋˜๋ฏ€๋กœ uri๋Š” ์œ„์—์„œ ์„ค์ •ํ•œ 4000๋ฒˆ
const client = new ApolloClient({
  uri: "http://localhost:4000/",
  cache: new InMemoryCache(),
})
  • uri: GraphQL ์„œ๋ฒ„์˜ URL์„ ์ง€์ •.
  • cache: ์•„ํด๋กœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜จ ํ›„ ์บ์‹ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” InMemoryCache์˜ ์ธ์Šคํ„ด์Šค.

โžก๏ธ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ

๋™์ผํ•œ ํŒŒ์ผ ์•ˆ์—์„œ, ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด(gql ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด๋กœ ๋ž˜ํ•‘)์„ ์‚ฌ์šฉํ•˜์—ฌ client.query()๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
client
  .query({
    query: gql`
      query GetBooks {
        books {
          title
          author
        }
      }
    `,
  })
  .then((result) => console.log(result.data.books))
  .catch((error) => console.error(error))

โžก๏ธ๋ฆฌ์•กํŠธ์—์„œ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ

์œ„์™€ ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋ฆฌ์•กํŠธ์—์„œ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค.

๐Ÿ“Œ 1. ApolloProvider ๊ฐ์‹ธ๊ธฐ
React์˜ Context.Provider ์ฒ˜๋Ÿผ ApolloProvider๋ฅผ ์ด์šฉํ•ด React ์•ฑ์„ ๋ž˜ํ•‘ํ•˜๊ณ  Apollo ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ปจํ…์ŠคํŠธ์— ๋ฐฐ์น˜ํ•˜์ž.

1
2
3
4
5
6
7
const root = ReactDOM.createRoot(document.getElementById("root"))

root.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>
)

๐Ÿ“Œ 2. ApolloProvider ๊ฐ์‹ธ๊ธฐ
ApolloProvider๊ฐ€ ์—ฐ๊ฒฐ๋˜๋ฉด, useQuery๋กœ ๋ฐ์ดํ„ฐ ์š”์ฒญ์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค. useQuery ํ›…์€ GraphQL ๋ฐ์ดํ„ฐ๋ฅผ UI์™€ ๊ณต์œ ํ•˜๋Š” React ํ›…์ด๋‹ค!

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
import { useQuery, gql } from "@apollo/client"

// books ์ฟผ๋ฆฌ ์ •์˜
const GET_BOOKS = gql`
  query GetBooks {
    books {
      title
      author
    }
  }
`

// DisplayBooks ์ปดํฌ๋„ŒํŠธ
function DisplayBooks() {
  const { loading, error, data } = useQuery(GET_BOOKS)

  if (loading) return <p>Loading...</p>
  if (error) return <p>Error : {error.message}</p>

  return data.books.map(({ title, author }, index) => (
    <div key={index}>
      <h3>{title}</h3>
      <p>{author}</p>
      <br />
    </div>
  ))
}

// App ์ปดํฌ๋„ŒํŠธ ์—…๋ฐ์ดํŠธ
export default function App() {
  return (
    <div>
      <h2>My first Apollo app ๐Ÿš€</h2>
      <DisplayBooks />
    </div>
  )
}

๐Ÿ—‚๏ธ์ฐธ๊ณ  ์‚ฌ์ดํŠธ

This post is licensed under CC BY 4.0 by the author.