[Daily morning study] GraphQL vs REST API ๋น๊ต
#daily morning study
REST API ๊ธฐ๋ณธ ๊ฐ๋
REST(Representational State Transfer)๋ HTTP ํ๋กํ ์ฝ ๊ธฐ๋ฐ์ ์ํคํ ์ฒ ์คํ์ผ์ด๋ค. ๊ฐ ๋ฆฌ์์ค๋ ๊ณ ์ ํ URL๋ก ํํ๋๊ณ , HTTP ๋ฉ์๋๋ก CRUD ์์ ์ ์ํํ๋ค.
GET /users โ ์ฌ์ฉ์ ๋ชฉ๋ก
GET /users/1 โ ํน์ ์ฌ์ฉ์
POST /users โ ์ฌ์ฉ์ ์์ฑ
PUT /users/1 โ ์ฌ์ฉ์ ์์
DELETE /users/1 โ ์ฌ์ฉ์ ์ญ์
GraphQL ๊ธฐ๋ณธ ๊ฐ๋
GraphQL์ Facebook(ํ Meta)์ด 2015๋ ์ ๊ณต๊ฐํ ์ฟผ๋ฆฌ ์ธ์ด์ด์ ๋ฐํ์์ด๋ค. ํด๋ผ์ด์ธํธ๊ฐ ํ์ํ ๋ฐ์ดํฐ์ ๊ตฌ์กฐ๋ฅผ ์ง์ ์ ์ํด์ ์์ฒญํ๋ค.
query {
user(id: "1") {
name
email
posts {
title
createdAt
}
}
}
๋จ์ผ ์๋ํฌ์ธํธ(/graphql)๋ก ๋ชจ๋ ์์ฒญ์ ์ฒ๋ฆฌํ๋ค๋ ์ ์ด REST์ ๊ฐ์ฅ ํฐ ๊ตฌ์กฐ์ ์ฐจ์ด๋ค.
ํต์ฌ ์ฐจ์ด์ ๋น๊ต
| ํญ๋ชฉ | REST | GraphQL |
|---|---|---|
| ์๋ํฌ์ธํธ | ๋ฆฌ์์ค๋ง๋ค ๋ค๋ฆ | ๋จ์ผ ์๋ํฌ์ธํธ |
| ์๋ต ํํ ๊ฒฐ์ ๊ถ | ์๋ฒ | ํด๋ผ์ด์ธํธ |
| Over-fetching | ์์ฃผ ๋ฐ์ | ํด๊ฒฐ๋จ |
| Under-fetching | ์์ฃผ ๋ฐ์ | ํด๊ฒฐ๋จ |
| HTTP ์บ์ฑ | ๊ธฐ๋ณธ ์ง์ | ๋ณ๋ ๊ตฌํ ํ์ |
| ํ์ผ ์ ๋ก๋ | ๊ฐ๋จ | ์ถ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ |
| ํ์ต ๊ณก์ | ๋ฎ์ | ์๋์ ์ผ๋ก ๋์ |
| ๋ฒ์ ๊ด๋ฆฌ | /v1/, /v2/ ๋ฐฉ์ | ์คํค๋ง ์งํ๋ก ์ฒ๋ฆฌ |
Over-fetching๊ณผ Under-fetching
REST API์์ ์์ฃผ ๋ฐ์ํ๋ ๋ ๊ฐ์ง ๋นํจ์จ์ด๋ค.
Over-fetching: ํ์ ์ด์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ ๊ฒ
์ฌ์ฉ์ ์ด๋ฆ๋ง ํ์ํ๋ฐ /users/1์ ํธ์ถํ๋ฉด name, email, age, address, createdAt ๋ฑ ๋ชจ๋ ํ๋๊ฐ ์๋ต์ผ๋ก ์จ๋ค.
Under-fetching: ํ ๋ฒ์ ์์ฒญ์ผ๋ก ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ค ๋ชป ๋ฐ๋ ๊ฒ
์ฌ์ฉ์ ์ ๋ณด์ ๊ฒ์๊ธ ๋ชฉ๋ก์ ํจ๊ป ํ์ํ๋ ค๋ฉด ๋ ๋ฒ ํธ์ถํด์ผ ํ๋ค.
GET /users/1 โ ์ฌ์ฉ์ ์ ๋ณด
GET /users/1/posts โ ๊ฒ์๊ธ ๋ชฉ๋ก
์ด ํจํด์ด ๋ฐ๋ณต๋๋ฉด N+1 ๋ฌธ์ ๋ก ์ด์ด์ง๋ค. GraphQL์ ํ ๋ฒ์ ์ฟผ๋ฆฌ๋ก ํ์ํ ํ๋๋ง ์ ํํ ๊ฐ์ ธ์ ๋ ๋ฌธ์ ๋ฅผ ๋์์ ํด๊ฒฐํ๋ค.
GraphQL ํต์ฌ ๊ตฌ์ฑ ์์
Schema์ Type System
GraphQL์ ๊ฐํ์ ์์คํ ์ ๊ฐ๋๋ค. ์๋ฒ๋ ์คํค๋ง๋ก ๋ฐ์ดํฐ ํํ๋ฅผ ๋ฏธ๋ฆฌ ์ ์ํ๊ณ , ํด๋ผ์ด์ธํธ๋ ๊ทธ ๋ฒ์ ์์์ ์์ ๋กญ๊ฒ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ์ฑํ๋ค.
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
type Query {
user(id: ID!): User
users: [User!]!
}
type Mutation {
createUser(name: String!, email: String!): User!
deleteUser(id: ID!): Boolean!
}
!๋ null ๋ถ๊ฐ๋ฅผ ์๋ฏธํ๋ค.
Query, Mutation, Subscription
- Query: ๋ฐ์ดํฐ ์กฐํ (REST์ GET์ ํด๋น)
- Mutation: ๋ฐ์ดํฐ ๋ณ๊ฒฝ (POST, PUT, DELETE์ ํด๋น)
- Subscription: ์ค์๊ฐ ๋ฐ์ดํฐ ๊ตฌ๋ (WebSocket ๊ธฐ๋ฐ)
subscription {
messageAdded(roomId: "1") {
content
author {
name
}
}
}
Subscription์ REST๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํ์ง ์๋ ์ค์๊ฐ ๊ธฐ๋ฅ์ ์คํค๋ง ์์ค์์ ์ ์ํ ์ ์๊ฒ ํด์ค๋ค.
Resolver
ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญํ ๊ฐ ํ๋์ ๋ํด ์ค์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ํจ์๋ค.
const resolvers = {
Query: {
user: (_, { id }) => User.findById(id),
users: () => User.findAll(),
},
User: {
posts: (user) => Post.findByAuthorId(user.id),
},
};
๊ฐ ํ๋๋ง๋ค resolver๊ฐ ์คํ๋๋ฏ๋ก, ์ค์ฒฉ๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ์์๋ N+1 ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋ค.
N+1 ๋ฌธ์ ์ DataLoader
์ฌ์ฉ์ 10๋ช ์ ๊ฒ์๊ธ์ ํ ๋ฒ์ ๊ฐ์ ธ์ค๋ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ฉด:
users์ฟผ๋ฆฌ ์คํ โ 1๋ฒ DB ํธ์ถ- ๊ฐ User์
postsresolver ์คํ โ 10๋ฒ DB ํธ์ถ - ์ด 11๋ฒ DB ์ฟผ๋ฆฌ ๋ฐ์
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด DataLoader๋ฅผ ์ฌ์ฉํ๋ค. ์์ฒญ์ ๋ฐฐ์น(batch)๋ก ๋ฌถ์ด ๋จ 1๋ฒ์ DB ์ฟผ๋ฆฌ๋ก ์ฒ๋ฆฌํ๋ค.
const postLoader = new DataLoader(async (userIds) => {
const posts = await Post.findByAuthorIds(userIds); // IN ์ฟผ๋ฆฌ 1๋ฒ
return userIds.map(id => posts.filter(p => p.authorId === id));
});
// User resolver์์
User: {
posts: (user) => postLoader.load(user.id),
}
DataLoader๋ ๊ฐ์ ์ด๋ฒคํธ ๋ฃจํ ๋ด์์ ๋ฐ์ํ๋ load() ํธ์ถ์ ์๋์ผ๋ก ๋ฌถ์ด์ ์ฒ๋ฆฌํ๋ค.
REST๊ฐ ๋ ๋์ ๊ฒฝ์ฐ
- ๋จ์ํ CRUD API: ๋ณต์กํ ๋ฐ์ดํฐ ๊ด๊ณ๊ฐ ์๋ ๊ฒฝ์ฐ
- ํ์ผ ์ ๋ก๋: multipart/form-data ์ฒ๋ฆฌ๊ฐ ๊ฐ๋จ
- HTTP ์บ์ฑ ์ ๊ทน ํ์ฉ: CDN, ๋ธ๋ผ์ฐ์ ์บ์๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉ
- ๊ณต๊ฐ API: ๋ค์ํ ์ธ๋ถ ํด๋ผ์ด์ธํธ๊ฐ ์ฌ์ฉํ๋ ํผ๋ธ๋ฆญ API
- ํ์ด REST์ ์ต์ํ๊ณ ํ์ต ๋น์ฉ์ ์ค์ฌ์ผ ํ๋ ๊ฒฝ์ฐ
GraphQL์ด ๋ ๋์ ๊ฒฝ์ฐ
- ๋ชจ๋ฐ์ผ ์ฑ: ๋คํธ์ํฌ ๋น์ฉ ์ ๊ฐ, ํ์ํ ํ๋๋ง ์์
- ๋ณต์กํ ๋ฐ์ดํฐ ๊ด๊ณ: ์ฌ๋ฌ ๋ฆฌ์์ค๋ฅผ ํ ์ฟผ๋ฆฌ๋ก ๊ฐ์ ธ์์ผ ํ ๋
- ๋ค์ํ ํด๋ผ์ด์ธํธ: ์น, ๋ชจ๋ฐ์ผ, IoT๊ฐ ๊ฐ๊ฐ ๋ค๋ฅธ ํํ์ ๋ฐ์ดํฐ๋ฅผ ํ์๋ก ํ ๋
- ๋น ๋ฅธ ํ๋ก ํธ์๋ ๊ฐ๋ฐ: ๋ฐฑ์๋ ๋ณ๊ฒฝ ์์ด ํด๋ผ์ด์ธํธ๊ฐ ์ํ๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ง์ ์ ์
- ์ค์๊ฐ ๊ธฐ๋ฅ: Subscription์ผ๋ก WebSocket ๊ธฐ๋ฐ ์ค์๊ฐ ํต์
๋ ๋ฐฉ์์ ํจ๊ป ์ฐ๋ ํ์ด๋ธ๋ฆฌ๋ ํจํด
์ค๋ฌด์์๋ REST์ GraphQL์ ๋์์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๋ง๋ค.
- ์ธ์ฆ, ํ์ผ ์
๋ก๋ โ REST (
/auth/login,/upload) - ๋ฐ์ดํฐ ์กฐํ ๋ฐ ๋ณ๊ฒฝ โ GraphQL (
/graphql)
์บ์ฑ์ด ์ค์ํ ํผ๋ธ๋ฆญ ์ฝํ ์ธ ๋ REST๋ก, ๋ณต์กํ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ๋ด๋ถ API๋ GraphQL๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ด๋ค.
์ ๋ฆฌ
REST์ GraphQL์ ๊ฐ์ ์ ๋ง๋ ์ํฉ์ด ๋ค๋ฅด๋ค. REST๋ ๋จ์ํ๊ณ HTTP ํ์ค๊ณผ ์ ๋ง๋ ๋ฐ๋ฉด, GraphQL์ ํด๋ผ์ด์ธํธ ์๊ตฌ์ฌํญ์ด ๋ค์ํ๊ณ ๋ฐ์ดํฐ ๊ด๊ณ๊ฐ ๋ณต์กํ ๋ ๊ฐ์ ์ ๋ฐํํ๋ค. ์ ํ ๊ธฐ์ค์ ํ๋ก์ ํธ ๋ณต์ก๋, ํ ์ญ๋, ํด๋ผ์ด์ธํธ ๋ค์์ฑ์ด๋ค.