-
Notifications
You must be signed in to change notification settings - Fork 0
Description
๐ Graphql๊ณผ REST API, ๊ทธ๋ฆฌ๊ณ BFF
๐ ์ฃผ์ :
GraphQL, REST API, ๊ทธ๋ฆฌ๊ณ Backend for Frontend(BFF)์ ๊ฐ๋
๊ณผ ์ฐจ์ด์ ์ ๋น๊ตํ๊ณ , ๊ฐ๊ฐ์ ์ฅ๋จ์ ๊ณผ ํ์ฉ ์ฌ๋ก๋ฅผ ์กฐ์ฌํ๋ค.
๐ฏ ์คํฐ๋ ๋ชฉํ
GraphQL๊ณผ REST API์ ๊ธฐ๋ณธ ๊ฐ๋
์ ์ดํดํ๊ณ , BFF์ ํ์์ฑ๊ณผ ์ญํ ์ ๋ถ์ํ์ฌ ์ค๋ฌด์์ ์ ์ ํ ์ํคํ
์ฒ๋ฅผ ์ ํํ ์ ์๋๋ก ํ๋ค.
๐ ํต์ฌ ๋ด์ฉ:
Grpahql
GraphQL์ ์ฟผ๋ฆฌ(Query) ๊ธฐ๋ฐ API๋ก, ํด๋ผ์ด์ธํธ๊ฐ ํ์ํ ๋ฐ์ดํฐ๋ง ์ ํ์ ์ผ๋ก ์์ฒญ ํ ์ ์๋ ๋ฐฉ์
- ๋จ์ผ ์๋ํฌ์ธํธ(/grpahql)๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํ ํ๋๋ง ์ ํ ํ๊ณ , ํ ๋ฒ์ ์์ฒญ์ผ๋ก ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
์ฅ์
- ์ค๋ฒํ์นญ ๋ฐฉ์ง โ ํ์ํ ๋ฐ์ดํฐ๋ง ๊ฐ์ ธ์ฌ ์ ์์
- ์ธ๋ํ์นญ ๋ฐฉ์ง โ ํ ๋ฒ์ ์์ฒญ์ผ๋ก ์ฐ๊ด ๋ฐ์ดํฐ๊น์ง ๊ฐ์ ธ์ฌ ์ ์์
- ์ ์ฐํ API ํ์ฅ ๊ฐ๋ฅ โ ํ๋ ์ถ๊ฐ ์ ๊ธฐ์กด API ๋ณ๊ฒฝ ์์ด ์ฌ์ฉ ๊ฐ๋ฅ
๋จ์
- ์ด๊ธฐ ์ค์ ์ด ๋ณต์ก โ ์คํค๋ง ์ ์ ๋ฐ ๋ฐฑ์๋ ์ค์ ํ์
- ์บ์ฑ์ด ์ด๋ ต๋ค โ ๋ชจ๋ ์์ฒญ์ด
POST๋ก ์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ CDN ์บ์ฑ ํ์ฉ์ด ์ด๋ ค์ - N+1 ๋ฌธ์ ๋ฐ์ ๊ฐ๋ฅ โ ๋ฆฌ์กธ๋ฒ ์ต์ ํ ํ์ (DataLoader ์ฌ์ฉ)
grpahql์ ๋ฆฌ์กธ๋ฒ(resolver)?
๐ย graphql์์ ์ฟผ๋ฆฌ๋ฅผ ํด์ํ๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๋ ํจ์
์ฆ, ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญํ ๋ฐ์ดํฐ๋ฅผ ๋ฐฑ์๋์์ ์ฐพ์์ ๋ฐํํ๋ ์ญํ
๊ทผ๋ฐ ์ ๋ฆฌ์กธ๋ฒ๊ฐ ๋ ๋ง์์ ธ?
-
graphql์ ๊ฐ ํ๋๋ง๋ค ๋ฆฌ์กธ๋ฒ๋ฅผ ๋ฐ๋ก ๋ง๋ค์ด์ผ ํ๊ธฐ ๋๋ฌธ!
-
์์
โ ์ฌ์ฉ์ ์ ๋ณด์ ๊ฒ์๊ธ์ ํจ๊ป ์์ฒญํ๋ GraphQL ์ฟผ๋ฆฌ
query { user(id: 1) { name posts { title content } } }โ ๋ฐฑ์๋ ๋ฆฌ์กธ๋ฒ (๊ฐ ํ๋๋ณ๋ก ๋ฐ๋ก ์กด์ฌ)
const resolvers = { Query: { user: (_, { id }) => getUserById(id) // ์ฌ์ฉ์ ์ ๋ณด ๊ฐ์ ธ์ค๋ ๋ฆฌ์กธ๋ฒ }, User: { posts: (parent) => getPostsByUserId(parent.id) // ์ฌ์ฉ์์ ๊ฒ์๊ธ ๊ฐ์ ธ์ค๋ ๋ฆฌ์กธ๋ฒ } };๐ โก
user๋ฆฌ์กธ๋ฒ์posts๋ฆฌ์กธ๋ฒ๊ฐ ๋ฐ๋ก ์์!๐ โก ๋ฐ์ดํฐ๊ฐ ๋ง์์ง์๋ก ๋ฆฌ์กธ๋ฒ๊ฐ ๊ณ์ ์ฆ๊ฐํ๊ฒ ๋จ.
๐ โก ์ ์ง ๋ณด์๊ฐ ์ด๋ ค์์ง!!
๐ โก ๋ฐ์ดํฐ ํ์ ๋ง๋ค ๋ฆฌ์กธ๋ฒ๊ฐ ๊ณ์ ์ถ๊ฐ๋จ
๐โก ์ฝ๋๊ฐ ๋ง์์ง๊ณ , ์ ์ง๋ณด์๊ฐ ์ด๋ ค์์ง
๐โก ๊ฐ ๋ฆฌ์กธ๋ฒ๊ฐ ๋ฐ๋ก ๋์ํ๋ค ๋ณด๋ฉด
N+1 ๋ฌธ์ ๋ฐ์ ๊ฐ๋ฅ
Graphql์ N+1 ๋ฌธ์
๋ฐ์ดํฐ๋ฅผ ์์ฒญํ ๋ ๋นํจ์จ์ ์ธ ์ฟผ๋ฆฌ ์คํ์ผ๋ก ์ธํด ๋ฐ์ํ๋ ์ฑ๋ฅ ๋ฌธ์ ์ธ๋ฐ, ๋ฆฌ์กธ๋ฒ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ๋ฐ์
์์ ์ฌ์ฉ์ ๋ชฉ๋ก๊ณผ ๊ฐ ์ฌ์ฉ์์ ๊ฒ์๊ธ ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ ธ์ค๋ graphql ์ฟผ๋ฆฌ๋ฅผ ์์ฑ
query { users { id name posts { id title } } }๐ก ์คํ ํ๋ฆ (๋ฌธ์ ๋ฐ์ ๊ณผ์ )
users๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด 1๊ฐ์ ์ฟผ๋ฆฌ ์คํSELECT * FROM users; -- (1๊ฐ์ ์ฟผ๋ฆฌ ์คํ)
posts๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด N๊ฐ์ ์ฟผ๋ฆฌ ์คํSELECT * FROM posts WHERE user_id = 1; -- (N๋ฒ ์คํ) SELECT * FROM posts WHERE user_id = 2; SELECT * FROM posts WHERE user_id = 3; ... SELECT * FROM posts WHERE user_id = N;๐ฉ๐ปโ๐ป
users๋ฐ์ดํฐ๋ฅผ ๋จผ์ ๊ฐ์ ธ์จ ํ, ๊ฐ ์ฌ์ฉ์๋ณposts๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ๊ฐ์ ธ์ค๋ฉด ์ด N+1๊ฐ์ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์(1๊ฐ์users์กฐํ + N๊ฐ์posts์กฐํ)์ฌ์ฉ์๊ฐ ๋ง์์ง์๋ก (
N์ด ์ปค์ง์๋ก) ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ์์ฒญ์ด ๊ธฐํ๊ธ์์ ์ผ๋ก ์ฆ๊ฐํ๊ฒ ๋๊ณ , ์ด๋ ์ฑ๋ฅ ์ ํ๋ฅผ ์ด๋ํ๊ฒ ๋๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ
- DataLoader(Facebook์ด ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ)๋ฅผ ํ์ฉ - ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ
- ์๋ฆฌ๋ ์ฟผ๋ฆฌ ๊ฐฏ์๋ฅผ 1๊ฐ๋ก ์ค์ฌ ์ฑ๋ฅ์ ํฅ์ ์ํค๋ ๊ฒ
- data loader ์ฌ์ฉ์ batching(์ผ๊ด ์ฒ๋ฆฌ)์ caching(์บ์ฑ)์ ์ ์ฉํ์ฌ ์ฟผ๋ฆฌ ๊ฐฏ์๋ฅผ ์ค์ด๋ ๊ฒ
- ์๋ฆฌ๋ ์ฟผ๋ฆฌ ๊ฐฏ์๋ฅผ 1๊ฐ๋ก ์ค์ฌ ์ฑ๋ฅ์ ํฅ์ ์ํค๋ ๊ฒ
REST API
REST API๋ ๋ฆฌ์์ค ๊ธฐ๋ฐ์ HTTP ์์ฒญ ๋ฐฉ์์ผ๋ก, ๊ฐ ๋ฆฌ์์ค(๋ฐ์ดํฐ)์ ๋ํด ๊ณ ์ ๋ ์๋ํฌ์ธํธ(URL)์ ์ฌ์ฉํด ์์ฒญํ๋ ๋ฐฉ์
์ฅ์
- ๊ตฌ์กฐ๊ฐ ๋จ์ํ๊ณ ์ต์ํจ (RESTful ์์น ๋ฐ๋ฆ)
- ์บ์ฑ ํ์ฉ ๊ฐ๋ฅ (๋ธ๋ผ์ฐ์ ๋ฐ CDN ์ง์)
- HTTP ์ํ ์ฝ๋ ํ์ฉ ๊ฐ๋ฅ (200, 404, 500 ๋ฑ)
๋จ์
- ์ค๋ฒํ์นญ(Over-fetching) โ ๋ถํ์ํ ๋ฐ์ดํฐ๊น์ง ํฌํจ๋ ์ ์์
- ์ธ๋ํ์นญ(Under-fetching) โ ์ํ๋ ๋ฐ์ดํฐ๋ฅผ ์ํด ์ฌ๋ฌ ๋ฒ ์์ฒญํด์ผ ํ ์๋ ์์
- API ๋ฒ์ ๊ด๋ฆฌ ํ์ โ ์๋ก์ด ํ๋ ์ถ๊ฐ ์
/v1/,/v2/๊ฐ์ ์๋ํฌ์ธํธ ์ถ๊ฐ ํ์
์ค๋ฒ ํ์นญ
๐ ํ์ํ ๋ฐ์ดํฐ๋ณด๋ค ๋ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ ๋ฌธ์
์ฆ, ํด๋ผ์ด์ธํธ๊ฐ ์ํ์ง ์๋ ์ ๋ณด๊น์ง ๊ฐ์ด ์ ๋ฌ๋๋ ์ํฉ
์์ ์ฌ์ฉ์์ ์ด๋ฆ(name)๋ง ํ์ํ ์ํฉ
REST ๋ฐฉ์ (์ค๋ฒํ์นญ ๋ฌธ์ ๋ฐ์)
GET /users/1
{
"id": 1,
"name": "Alice",
"email": "[email protected]",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Seoul"
},
"phone": "010-1234-5678"
}
- ํด๋ผ์ด์ธํธ๋ ์ด๋ฆ(name)๋ง ํ์ํ๋ฐ,
- ์ด๋ฉ์ผ, ๋์ด, ์ฃผ์, ์ ํ๋ฒํธ ๋ฑ ๋ถํ์ํ ์ ๋ณด๊น์ง ๋ฐ์์ด
- ๐ ๋ถํ์ํ ๋ฐ์ดํฐ ์ ์ก์ผ๋ก ๋คํธ์ํฌ ๋น์ฉ ์ฆ๊ฐ & ์ฑ๋ฅ ์ ํ
GraphQL ๋ฐฉ์ (์ค๋ฒํ์นญ ํด๊ฒฐ)
query {
user(id: 1) {
name
}
}
{ "data": { "user": { "name": "Alice" } } }
์ค์ง ํ์ํ name ํ๋๋ง ๊ฐ์ ธ์ค๊ธฐ ๋๋ฌธ์ ์ค๋ฒํ์นญ ๋ฐฉ์ง ๊ฐ๋ฅ! ๐ฅ
์ธ๋ํ์นญ
๐ย ํ์ํ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ์์ฒญ์ผ๋ก ๊ฐ์ ธ์ค์ง ๋ชปํ๊ณ , ์ฌ๋ฌ ๋ฒ ์์ฒญํด์ผ ํ๋ ๋ฌธ์
์์ ํด๋ผ์ด์ธํธ์์ ํน์ ์ฌ์ฉ์(user)์ ํด๋น ์ฌ์ฉ์์ ๊ฒ์๊ธ(posts)์ ํ ๋ฒ์ ๊ฐ์ ธ์ค๊ณ ์ถ๋ค๋ฉด?
REST ๋ฐฉ์
GET /users/1โ ์ฌ์ฉ์ ์ ๋ณด ์์ฒญGET /users/1/postsโ ํด๋น ์ฌ์ฉ์์ ๊ฒ์๊ธ ์์ฒญ
์ด๋ ๊ฒ 2๋ฒ์ API ํธ์ถ์ด ํ์
๋ง์ฝ ์ฌ์ฉ์์ ๋๊ธ ์ ๋ณด๊น์ง ๊ฐ์ด ๊ฐ์ ธ์ค๋ ค๋ฉด 3๊ฐ์ ์์ฒญ์ด ํ์!
์ธ๋ํ์นญ์ API๊ฐ ๋ถ๋ฆฌ ๋์ด ์์ด ์ํ๋ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ๊ฐ์ ธ์ค์ง ๋ชปํ๋ ๊ฒฝ์ฐ ๋ฐ์
GraphQL ๋ฐฉ์
query { user(id: 1) { name posts { title comments { text } } } }
ํ ๋ฒ์ ์์ฒญ์ผ๋ก ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์.
BFF(backend for frontend)
- ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํน์ฑ์ ๋ง๊ฒ ๋ฐฑ์๋ API๋ฅผ ์ ๊ณตํ๋ ์ํคํ ์ฒ ํจํด
- BFF๋ ํน์ ํ๋ก ํธ์๋ ํด๋ผ์ด์ธํธ(์น, ๋ชจ๋ฐ์ผ, ๋ฐ์คํฌํฑ ๋ฑ)์ ์ต์ ํ๋ API๋ฅผ ์ ๊ณต
- BFF๋ ํ๋ก ํธ์๋์ ๋ฐฑ์๋ ์ฌ์ด์ '๋ง์ถคํ API ๋ ์ด์ด' ์ญํ
๐ BFF์ GraphQL ๋น๊ต
| ย | BFF | GraphQL |
|---|---|---|
| ๊ฐ๋ฐ ๋ฐฉ์ | ํ๋ก ํธ์๋๋ณ ๋ง์ถคํ API ์๋ฒ ๊ฐ๋ฐ | ํด๋ผ์ด์ธํธ๊ฐ ์ํ๋ ๋ฐ์ดํฐ๋ง ์์ฒญ ๊ฐ๋ฅ |
| ๋ฐ์ดํฐ ๊ฐ๊ณต | BFF๊ฐ ๋ฐ์ดํฐ ์กฐํฉ ๋ฐ ๊ฐ๊ณต | GraphQL ์๋ฒ๊ฐ ๋ฐ์ดํฐ ์กฐํฉ |
| ์ค๋ฒํ์นญ ํด๊ฒฐ | ๋ง์ถค API ์ ๊ณต | ์ํ๋ ํ๋๋ง ์์ฒญ ๊ฐ๋ฅ |
| ์ธ๋ํ์นญ ํด๊ฒฐ | ์ฌ๋ฌ API๋ฅผ ์กฐํฉํด์ ์ ๊ณต | ํ ๋ฒ์ ์์ฒญ์ผ๋ก ํ์ํ ๋ฐ์ดํฐ ์ ๊ณต |
๐ BFF vs GraphQL ์ฐจ์ด์
- GraphQL์ ๋จ์ผ API๋ก ํด๊ฒฐ, ํ์ง๋ง BFF๋ ๋ง์ถคํ API ์๋ฒ๋ฅผ ์ถ๊ฐ๋ก ์ด์
- GraphQL์ ์ฌ์ฉํ๋ฉด BFF ์์ด๋ ํ๋ก ํธ์๋๊ฐ ํ์ํ ๋ฐ์ดํฐ๋ง ์์ฒญ ๊ฐ๋ฅ
๐ก ์ฐธ๊ณ ์๋ฃ:
https://graphql.org/learn/
https://tech.kakao.com/posts/364
https://blog.toktokhan.dev/rest-api-vs-graphql-7348f54a220b
https://blog.postman.com/graphql-vs-rest/
https://fe-developers.kakaoent.com/2022/220310-kakaopage-bff/
https://docs.github.com/ko/rest/about-the-rest-api/comparing-githubs-rest-api-and-graphql-api?apiVersion=2022-11-28
https://kosaf04pyh.tistory.com/341