{"id":27372796,"url":"https://github.com/polyglot-k/graphql-ts-practica","last_synced_at":"2026-04-12T04:32:20.755Z","repository":{"id":245488671,"uuid":"818388891","full_name":"polyglot-k/graphql-ts-practica","owner":"polyglot-k","description":"To address the issues of over-fetching and under-fetching that commonly occur in REST APIs, we will configure GraphQL and then conduct performance analysis on it.","archived":false,"fork":false,"pushed_at":"2024-06-24T14:17:45.000Z","size":322,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-13T10:46:56.895Z","etag":null,"topics":["apollo","apollo-server","backend","es6","express","graphql","nodejs","restapi","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/polyglot-k.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-21T18:35:11.000Z","updated_at":"2024-07-26T08:42:50.000Z","dependencies_parsed_at":"2024-06-24T15:49:26.773Z","dependency_job_id":null,"html_url":"https://github.com/polyglot-k/graphql-ts-practica","commit_stats":{"total_commits":31,"total_committers":1,"mean_commits":31.0,"dds":0.0,"last_synced_commit":"7eed5199bea53f035ed5acdb1e3291f5e03730df"},"previous_names":["knu-k/graphql-ts-practica","polyglot-k/graphql-ts-practica"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/polyglot-k/graphql-ts-practica","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyglot-k%2Fgraphql-ts-practica","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyglot-k%2Fgraphql-ts-practica/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyglot-k%2Fgraphql-ts-practica/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyglot-k%2Fgraphql-ts-practica/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/polyglot-k","download_url":"https://codeload.github.com/polyglot-k/graphql-ts-practica/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyglot-k%2Fgraphql-ts-practica/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31704492,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-11T21:17:31.016Z","status":"online","status_checked_at":"2026-04-12T02:00:06.763Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apollo","apollo-server","backend","es6","express","graphql","nodejs","restapi","typescript"],"created_at":"2025-04-13T10:39:23.975Z","updated_at":"2026-04-12T04:32:20.749Z","avatar_url":"https://github.com/polyglot-k.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 🗒️ 왜 해당 주제에 대한 예제를 제작하는가?\n\n-   graphql이 Rest API 의 Over-fetching / Under-fetching에 얼마나 잘 대응하는지를 보여주기 위해서.\n\n### Over-fetching이란\n\n가져온 정보에서 필요한 정보는 한정적일 수도 있다. 하지만 Rest API로 호출을 하게되면, 불필요한 정보까지 가지고 오는 경우가 있다 이를 `Over-fetching` 이라고 한다.\n\n\u003e 서버의 자원이 낭비가 됨.\n\n### Under-fetching이란?\n\n프론트엔드의 하나의 페이지를 구성하기 위해서 프로필 정보, 게시물 정보, 댓글 정보등 많은 정보를 가지고 와야하지만, Rest api에서는 이러한 것을 각 부분에 따라 요청해야한다. 필요한 정보를 한번에 가져오지 못하는 것을 `Under-fetching` 이라고 한다.\n\n\u003e 서버에 요청량이 증가하게 됨.\n\n### GraphQL의 등장\n\n이러한 문제를 해결하기 위해서, 데이터를 가져오는 것에 필요한 것을 최적화하여 한번의 요청에 필요한 데이터를 가지고 오는 것을 주 목표로 하여 만들어졌다.\n\n`GraphQL`과 `SQL`은 같은 Query Language이다. 간단히 말해서 질의형으로 요청하여 필요에 따른 응답을 받을 수 있는 것이다.\n\n## 😊 ch1. Query를 통해 데이터를 가져오기\n\n### hello world를 해보자\n\n```tsx\nimport express, { Application } from \"express\";\nimport { ApolloServer } from \"@apollo/server\";\nimport { expressMiddleware } from \"@apollo/server/express4\";\n\nconst typeDefs = `#graphql\n  type Query {\n    hello: String\n  }\n`;\n\nconst resolvers = {\n    Query: {\n        hello: () =\u003e \"world\",\n    },\n};\n\nexport default async function createApp(): Promise\u003cApplication\u003e {\n    const app = express();\n    const server = new ApolloServer({\n        typeDefs,\n        resolvers,\n    });\n\n    await server.start();\n    app.use(\"/graphql\", express.json(), expressMiddleware(server));\n\n    return app;\n}\n```\n\n위와 같이 코드를 구성하였다.\n\n기본적으로 express를 사용하지않고 graphQL를 제작할 수 있지만, express와 함께 유연하게 동작하는 graphQL을 사용하도록 하는 것을 목표로 한다.\n\nexpress의 미들웨어를 통해서 resolver에 접근하여 Query를 확인 후 답을 줄 수 있는 것이다.\n\n현재 챕터에서 `npm test`를 통해서 테스트를 진행해보자.\n\n```json\nquery : \"query { hello }\"\n```\n\n라는 형식의 쿼리를 json 형식으로 보내게 되면, 일반 Rest API와 같은 JSON형태로 응답을 받을 수 있게 된다. 해당 테스트는 이에 대한 부분을 직관적으로 확인할 수 있도록 구성했다..\n\n## 😊 ch2. 간단하게 RestAPI 와 GraphQL 간의 비교\n\n초기 챕터에서는 database를 사용하지않고 practice에 대해서 소개하려고 한다.\n\n### 🚩초기 시나리오 구성\n\n프로필 정보, 게시물, 댓글을 프론트에서 요청이 필요로 할 때:\n\n-   REST API : 프로필 정보, 게시물, 댓글에 대한 요청을 따로 구성한다.\n-   GraphQL : Query 기반으로 한번에 요청으로 가능하게 할 수 있다.\n\n### 🏴REST API로 구성\n\n`/api/profile`\n`/api/board/:id`\n`/api/comment`\n로 구성할 것이다. 현재는 인증하는 것이 목적이 아니기 때문에, 인증부 없이 무조건적으로 1번user의 profile을 주도록 하겠다.\n\n### 🏳️GraphQL로 구성\n\n```graphQL\nquery {\n    userProfile(id: 1) {\n        id\n        name\n        email\n        age\n        phone\n    }\n    getBoard(id:1){\n        postId\n        userId\n        title\n        content\n        timestamp\n    }\n    getComment(boardId:1){\n        commentId\n        postId\n        userId\n        content\n        timestamp\n    }\n}\n```\n\n해당 쿼리를 통해 한번에 접근 가능, 해당 부분에서도 설명에 불필요한 내용은 제외하여 진행했다.\n\n### ✅ 간단한 비교 분석\n\n#### : 클라이언트가 원하는 응답을 준비할 수 있는 graphQL\n\n`Rest API` 는 서버가 구성하고 있는 형태를 클라이언트에 필요에 따라 변경하려면 새로운 api 를 구성해야하는 번거러움이 존재할 수 있다.\n\n하지만, `GraphQL`의 경우에는 그 때 그때 필요한 인자만을 받을 수 있기 때문에, 응답을 받는 것에 자유롭다.\n\n#### : 과연 응답 속도가 정말 빠를까? 실제 실험을 해보자\n\n`Rest API` 와 `GraphQL`에서 요청에 따른 속도 차이가 나타날 수도 있다고 했는데, 정말인지 확인하기 위해서 동일 서버 스펙에서 총 5번의 테스트를 진행해봤다.\n\n`npm test perform`을 통해서 이를 확인할 수 있다!\n\n\u003cimg src=\"./images/image.png\" alt=\"nop\" width=\"300\" /\u003e\n\nperform에 대한 test를 진행한 결과 1.3 ~ 1.7 배 더 빠른 것으로 측정되었다. 이렇게 한 번에 많은 요청량이 client가 필요로 한다고 생각하면 편할 것이다.\n\n\u003e 많은 클라이언트는 하나의 페이지 단위에 여러 데이터를 얻기 위한 서로 다른 api요청을 반복한다.\n\n## 😊 ch3. Under-Fetching과 Over-Fetching에 대한 사례와 GraphQL을 통한 극복\n\n### ⬇️ Under-Fetching \u0026 📈 Over-Fetching\n\nGraphQL이 Rest API의 Under-Fetching과 Over-Fetching 같은 상황을 얼마나 잘 극복하는지에 대해서 소개하려고 한다.\n\n`Under-Fetching`는 위의 예제와 비슷하다.\n\n먼저 REST 구조를 시작으로 GraphQL까지 작성해보자.\n\n#### 🚩Under-Fetching 시나리오\n\n여행 리뷰 웹사이트에서 사용자의 프로필 정보를 포함한 특정 사용자의 작성한 리뷰 목록을 클라이언트에서 요청하는 상황을 고려해보자. 사용자는 여러 리뷰를 작성할 수 있으며, 각 리뷰에는 작성자의 정보가 포함되어 있다.\n\n#### 🚩Over-Fetching 시나리오\n\n현재 웹에서 사용자에 대한 기본 정보 (이름과 이메일)만을 보여주려고 한다. 해당 사항을 고려해보자.\n\n#### 🗂️ 요구사항에 따른 필요 요소 추출\n\n여기서 필요한 부분을 추출해보면 아래와 같다.\n\n-   RestAPI\n    -   유저 전체 정보 - `/api/user`\n    -   유저 프로필 정보 - `/api/user/:userId`\n    -   사용자 리뷰 목록 - `/api/user/:userId/review`\n    -   리뷰 상세 정보 - `/api/review/:reviewId`\n-   GraphQL\n\n    -   typeDef Schema\n\n        ```tsx\n        export default `#graphql\n            type User {\n                id: Int!\n                name: String!\n                email: String!\n                reviews: [Review]\n            }\n        \n            type Review {\n                reviewId: Int!\n                content: String!\n                score: Int!\n                timestamp: String!\n                author: User\n            }\n        \n            type Query {\n                user(id: Int): User\n                users: [User]\n                userReviews(userId: Int!): [Review]\n                review(id: Int!): Review\n            }\n        `;\n        ```\n\n        \u003e -   이러한 Schema 구조로 어떠한 데이터를 상호교환할지 표기함.\n        \u003e -   `reviews:[Review]`와 같은 Recursive한 구조 또한 가능\n\n위와 같은 형태로 구분이 가능하다. 본 챕터부터 예제는 가독성을 위해 모듈화를 진행하면서 구조화를 하도록 하겠다.\n\n#### 📁 폴더 구조\n\n-   graphql\n    -   loader\n        -   express.ts\n        -   graphql.ts\n        -   index.ts\n    -   schema\n        -   resolver.ts\n        -   typeDefs.ts\n    -   app.ts\n-   restapi\n    -   api\n        -   routes\n            -   review.ts\n            -   user.ts\n        -   index.ts\n    -   loaders\n        -   index.ts\n    -   app.ts\n\n#### 💫performance\n\n-   Under-Fetching\n\n    | Test Description    | REST API Performance (ms)                  | GraphQL API Performance (ms)                |\n    | ------------------- | ------------------------------------------ | ------------------------------------------- |\n    | Measure Performance | 38.17                                      | 14.33                                       |\n    | Image Data          | ![alt text](./images/ch3-rest-perform.png) | ![alt text](./images/ch3-graph-perform.png) |\n\n-   Over-Fetching\n\n    | Test Description    | REST API Performance (ms)                   | GraphQL API Performance (ms)                 |\n    | ------------------- | ------------------------------------------- | -------------------------------------------- |\n    | Measure Performance | 9.13                                        | 20.4                                         |\n    | Image Data          | ![alt text](./images/ch3-rest-perform2.png) | ![alt text](./images/ch3-graph-perform2.png) |\n\n`npm test perform`을 통해 성능을 측정해볼 수 있을 것이다.\n\n#### 💫성능에 대한 차이가 존재하는 이유\n\n간단하게 예시를 들어주겠다. 이 것은 마치 Buffering 개념과 비슷하다. 계란을 가지고 올 때, `바구니를 가지고 한번에 가져오는 것`과 `1개씩 왔다갔다 하면서 가져 오는 것` 딱 GraphQL과 Rest API는 해당 예시와 같은 차이가 난다.\n\n**_Under-Fetching의 경우_** 는 REST API 에서 필요로 하는 데이터를 얻기 위해서는 여러번의 요청이 필요한 것이다. 결국 동일한 데이터를 얻는다고 가정하면 `왔다 갔다거리는 수고`만 더 추가 된 것이다.\n\n이 것이 간단히 말해 성능차이에 큰 요인이 된 것이다.\n\n이렇게 말하면 graphql 맹신론자와 같이 느껴질 수도 있지만, 그렇지는 않다. 쿼리 구조가 아직까진 단순해서 괜찮지만, 위에 `recursive 한 구조를 허용`한다는 말을 봤다면 예상하겠지만, 쿼리 구조가 복잡해질 수록 graphql 에서의 오버헤드가 발생할 수 있다는 점도 염두해두면 좋을 것 같다.\n\n그러한 오버헤드를 제외하면 많은 이점들이 존재한다.\n\n1. 복수 요청의 감소\n2. 정확한 데이터 요청 (필요한 데이터만 가능)\n3. 네트워크 트래픽 감소(단일 요청으로 데이터를 얻을 수 있기 때문에)\n\n이러한, 이 점은 오히려 다양한 서비스를 만드는데 큰 도움을 줄 것이라 확신한다.\n\n**Over-Fetching 예제의 경우**는 graphql에서 `오버헤드`가 발생하는 좋은 사례라고 생각한다.\n\n\u003e 다른 사람들: 방금까지만해도 GraphQl이 좋다고 했잖아요!\n\n\u003e **물론이다. 하지만 오버헤드를 고려해야한다.**\n\n여기서 잠깐 주목하면 좋은 점은 Response Size이다. GraphQL이 Rest API 보다 훨씬 적다. 현재 실험 자체는 Local에서 진행했지만, 여러 remote 통신이 있을 때 Response Size가 낮은 즉, \"대역폭\"이 낮은 GraphQL이 유리해질 수 있다.\n\n#### GraphQL의 오버헤드가 발생하는 이유\n\nGraphQL에서 오버헤드가 발생하는 이유는 쿼리를 통한 질의나 요구에 맞게 데이터 해석과정을 거치기 때문에 응답할 데이터를 만들어내는데까지 시간이 오래걸리는 것이다.\n\n이를 쿼리 최적화, Resolver 최적화 등과 같은 방법으로 해소할 수 있다. 이는 추후 챕터에서 소개할 예정이다.\n\n지금까지의 내용으로 알았으면 좋겠는 점은 서로간의 **트레이드 오프**가 분명 존재한다는 것이다.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolyglot-k%2Fgraphql-ts-practica","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpolyglot-k%2Fgraphql-ts-practica","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolyglot-k%2Fgraphql-ts-practica/lists"}