본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 1장: GraphQL의 등장과 핵심 가치
2026년 2월 6일·웹 개발·

1장: GraphQL의 등장과 핵심 가치

REST API의 한계인 오버페칭과 언더페칭 문제를 분석하고, GraphQL의 타입 시스템, 스키마, 리졸버 등 핵심 개념을 소개합니다. 2026년 GraphQL 생태계 현황과 도입 판단 기준을 다룹니다.

14분422자7개 섹션
graphqlapi-designfrontend
공유
graphql-architecture1 / 11
1234567891011
다음2장: 스키마 설계 패턴

학습 목표

  • REST API의 구조적 한계를 이해한다
  • GraphQL의 핵심 개념인 타입 시스템, 스키마, 리졸버를 파악한다
  • GraphQL 도입이 적합한 상황과 그렇지 않은 상황을 판단할 수 있다
  • 2026년 현재 GraphQL 생태계의 주요 도구와 트렌드를 살펴본다

REST API의 한계

REST는 지난 20년간 웹 API의 사실상 표준으로 자리 잡았습니다. 리소스 중심의 URL 설계와 HTTP 메서드를 활용한 직관적인 인터페이스는 많은 서비스에서 성공적으로 사용되어 왔습니다. 하지만 프런트엔드 애플리케이션이 복잡해지면서 REST의 구조적 한계가 뚜렷하게 드러나기 시작했습니다.

오버페칭(Over-fetching)

사용자 프로필 카드에 이름과 아바타만 필요한 상황을 생각해 보겠습니다.

rest-overfetching.ts
typescript
// GET /api/users/123
const response = await fetch('/api/users/123');
const user = await response.json();
 
// 실제 필요한 데이터: name, avatar
// 실제 받는 데이터: id, name, email, avatar, address, phone, 
//                  createdAt, updatedAt, preferences, ...

REST 엔드포인트는 리소스 전체를 반환하므로, 클라이언트가 필요하지 않은 데이터까지 전송됩니다. 단일 요청에서는 미미해 보이지만, 모바일 환경에서 수십 개의 API 호출이 발생하면 불필요한 대역폭 소비가 누적됩니다.

언더페칭(Under-fetching)

블로그 포스트 목록 페이지에서 각 포스트의 작성자 이름과 댓글 수를 함께 보여주어야 하는 상황입니다.

rest-underfetching.ts
typescript
// 1단계: 포스트 목록 조회
const posts = await fetch('/api/posts').then(r => r.json());
 
// 2단계: 각 포스트의 작성자 정보 조회 (N번 요청)
const authors = await Promise.all(
  posts.map(post => 
    fetch(`/api/users/${post.authorId}`).then(r => r.json())
  )
);
 
// 3단계: 각 포스트의 댓글 수 조회 (N번 요청)
const commentCounts = await Promise.all(
  posts.map(post => 
    fetch(`/api/posts/${post.id}/comments/count`).then(r => r.json())
  )
);

하나의 화면을 구성하기 위해 1 + N + N번의 네트워크 요청이 필요합니다. 이것이 바로 REST의 언더페칭(Under-fetching) 문제이며, 흔히 N+1 요청 문제라고도 부릅니다.

엔드포인트 폭발

클라이언트 요구사항이 다양해지면 REST API는 엔드포인트가 기하급수적으로 늘어납니다.

text
GET /api/posts
GET /api/posts?include=author
GET /api/posts?include=author,comments
GET /api/posts?fields=id,title,summary
GET /api/posts/feed          # 모바일용 경량 버전
GET /api/posts/admin         # 관리자용 확장 버전

이러한 문제들을 해결하기 위해 2015년 Facebook(현 Meta)이 공개한 것이 바로 GraphQL입니다.


GraphQL 핵심 개념

GraphQL은 API를 위한 쿼리 언어(Query Language) 이자 해당 쿼리를 실행하기 위한 런타임(Runtime) 입니다. 핵심은 세 가지 개념으로 구성됩니다.

타입 시스템(Type System)

GraphQL의 가장 강력한 특징은 강력한 타입 시스템입니다. 모든 데이터의 형태를 스키마(Schema) 로 명시적으로 정의합니다.

schema.graphql
graphql
type User {
  id: ID!
  name: String!
  email: String!
  avatar: String
  posts: [Post!]!
}
 
type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  comments: [Comment!]!
  createdAt: DateTime!
}
 
type Comment {
  id: ID!
  body: String!
  author: User!
}

!는 Non-null을 의미하며, [Post!]!는 "Post 타입의 non-null 요소로 이루어진 non-null 배열"을 뜻합니다. 이 타입 시스템 덕분에 클라이언트는 API 문서 없이도 어떤 데이터를 요청할 수 있는지 정확히 알 수 있습니다.

스키마와 루트 타입

GraphQL 스키마는 세 가지 루트 타입(Root Type) 을 가집니다.

root-types.graphql
graphql
type Query {
  user(id: ID!): User
  posts(first: Int, after: String): PostConnection!
}
 
type Mutation {
  createPost(input: CreatePostInput!): Post!
  updateUser(id: ID!, input: UpdateUserInput!): User!
}
 
type Subscription {
  postCreated: Post!
  commentAdded(postId: ID!): Comment!
}
  • Query: 데이터 조회 (READ)
  • Mutation: 데이터 변경 (CREATE, UPDATE, DELETE)
  • Subscription: 실시간 데이터 구독

리졸버(Resolver)

스키마가 "무엇을 요청할 수 있는가"를 정의한다면, 리졸버(Resolver) 는 "어떻게 데이터를 가져오는가"를 구현합니다.

resolvers.ts
typescript
const resolvers = {
  Query: {
    user: async (_parent: unknown, args: { id: string }, context: Context) => {
      return context.dataSources.users.findById(args.id);
    },
    posts: async (_parent: unknown, args: PaginationArgs, context: Context) => {
      return context.dataSources.posts.findAll(args);
    },
  },
  User: {
    posts: async (parent: User, _args: unknown, context: Context) => {
      return context.dataSources.posts.findByAuthorId(parent.id);
    },
  },
};

리졸버는 필드 단위로 동작합니다. User 타입의 posts 필드를 요청하면, 해당 리졸버가 호출되어 관련 데이터를 반환합니다. 이 구조가 GraphQL의 유연성을 만들어냅니다.


클라이언트의 선언적 데이터 요청

REST와 GraphQL의 가장 큰 차이는 클라이언트가 필요한 데이터를 직접 선언한다는 점입니다.

query.graphql
graphql
query BlogPostList {
  posts(first: 10) {
    edges {
      node {
        id
        title
        summary
        author {
          name
          avatar
        }
        commentCount
        createdAt
      }
    }
  }
}

이 단일 쿼리로 REST에서 1 + N + N번 필요했던 요청을 한 번에 해결합니다. 서버는 클라이언트가 요청한 필드만 정확히 반환하므로 오버페칭도 발생하지 않습니다.

response.json
json
{
  "data": {
    "posts": {
      "edges": [
        {
          "node": {
            "id": "post-1",
            "title": "GraphQL 시작하기",
            "summary": "GraphQL의 기본 개념을...",
            "author": {
              "name": "Kreath",
              "avatar": "/avatars/kreath.jpg"
            },
            "commentCount": 12,
            "createdAt": "2026-03-15T09:00:00Z"
          }
        }
      ]
    }
  }
}

응답의 구조가 쿼리의 구조와 정확히 일치합니다. 이 예측 가능성이 프런트엔드 개발 경험을 크게 향상시킵니다.


언제 GraphQL을 선택할 것인가

GraphQL이 모든 상황에서 REST보다 우월한 것은 아닙니다. 도입 판단 시 다음 기준을 고려해야 합니다.

GraphQL이 적합한 경우

  • 다양한 클라이언트: 웹, 모바일, 데스크톱 등 각 클라이언트가 서로 다른 데이터를 필요로 할 때
  • 복잡한 관계형 데이터: 엔티티 간 관계가 깊고 중첩 조회가 빈번할 때
  • 빠른 프런트엔드 이터레이션: 백엔드 변경 없이 프런트엔드가 독립적으로 데이터 요구사항을 변경해야 할 때
  • 마이크로서비스 통합: 여러 서비스의 데이터를 하나의 그래프로 통합해야 할 때

REST가 더 적합한 경우

  • 단순한 CRUD API: 리소스 구조가 단순하고 관계가 적을 때
  • 파일 업로드/다운로드 중심: 바이너리 데이터 처리가 주된 목적일 때
  • HTTP 캐싱 활용: GET 요청 기반 CDN 캐싱이 핵심 요구사항일 때
  • 팀 경험 부족: 러닝 커브를 고려했을 때 도입 비용이 이점을 초과할 때
Warning

GraphQL 도입은 기술적 결정인 동시에 조직적 결정입니다. 스키마 설계와 거버넌스에 대한 팀 합의가 선행되어야 합니다. "트렌디해서"가 아닌 "문제를 해결하기 위해" 도입해야 합니다.


2026년 GraphQL 생태계

2026년 현재 GraphQL 생태계는 성숙기에 접어들었습니다. 주요 도구와 트렌드를 살펴보겠습니다.

서버 프레임워크

도구특징
Apollo Server가장 넓은 생태계, Federation 지원, Apollo Studio 통합
GraphQL Yoga경량, 표준 준수, Envelop 플러그인 시스템
PothosTypeScript 코드 퍼스트 스키마 빌더, 뛰어난 타입 추론
Nexus코드 퍼스트 접근, Prisma 통합

클라이언트 라이브러리

도구특징
Apollo Client정규화 캐시, 풍부한 상태 관리
urql경량, 교환(Exchange) 기반 확장 구조
RelayFacebook 제작, Fragment Colocation, 컴파일러 기반 최적화
TanStack Query + graphql-request가볍게 시작하기 좋은 조합

주요 트렌드

Federation 2.0의 보편화: 대규모 조직에서 마이크로서비스를 하나의 슈퍼그래프(Supergraph) 로 통합하는 패턴이 표준으로 자리 잡았습니다. Apollo Router가 게이트웨이 역할을 수행하며, 각 팀이 독립적으로 서브그래프(Subgraph) 를 개발하고 배포합니다.

Persisted Queries의 일반화: 보안과 성능을 위해 사전 등록된 쿼리만 허용하는 방식이 프로덕션 환경의 기본 관행이 되었습니다.

코드 생성(Code Generation)의 필수화: GraphQL Code Generator를 통한 TypeScript 타입, React 훅, SDK 자동 생성이 개발 워크플로의 핵심 단계가 되었습니다.


정리

이번 장에서는 REST API의 구조적 한계인 오버페칭과 언더페칭 문제를 살펴보고, 이를 해결하기 위해 등장한 GraphQL의 핵심 개념을 학습했습니다. 타입 시스템, 스키마, 리졸버라는 세 기둥이 GraphQL의 유연성과 예측 가능성을 만들어냅니다.

GraphQL은 만능 도구가 아니며, 프로젝트의 복잡도와 팀 역량에 따라 신중하게 도입을 결정해야 합니다. 2026년 현재 생태계는 충분히 성숙했으며, Federation, Code Generation, Persisted Queries 등의 실전 패턴이 안정화되었습니다.

다음 장 미리보기

2장에서는 GraphQL API의 근간이 되는 스키마 설계 패턴을 다룹니다. 스키마 퍼스트와 코드 퍼스트 접근법의 차이, Node 인터페이스와 글로벌 ID를 활용한 타입 설계, 그리고 Relay 스타일 커서 페이지네이션 패턴을 상세히 살펴보겠습니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#graphql#api-design#frontend

관련 글

웹 개발

2장: 스키마 설계 패턴

GraphQL 스키마 설계의 두 가지 접근법과 실전 타입 설계 패턴을 다룹니다. Node 인터페이스, Relay 커서 페이지네이션, 유니온/인터페이스 등 프로덕션 수준의 스키마 설계 원칙을 학습합니다.

2026년 2월 8일·13분
웹 개발

3장: Resolver 구현과 데이터 로딩

GraphQL 리졸버 체인의 실행 흐름과 컨텍스트 설계를 이해합니다. 데이터 소스 추상화, 부모-자식 리졸버 관계, Apollo Server와 GraphQL Yoga에서의 실전 구현 패턴을 다룹니다.

2026년 2월 10일·14분
웹 개발

4장: N+1 문제와 DataLoader 최적화

GraphQL에서 가장 흔한 성능 문제인 N+1 문제의 원인을 분석하고, DataLoader를 통한 배칭과 캐싱 최적화를 구현합니다. 쿼리 복잡도 분석과 깊이 제한 전략도 다룹니다.

2026년 2월 12일·13분
다음 글2장: 스키마 설계 패턴

댓글

목차

약 14분 남음
  • 학습 목표
  • REST API의 한계
    • 오버페칭(Over-fetching)
    • 언더페칭(Under-fetching)
    • 엔드포인트 폭발
  • GraphQL 핵심 개념
    • 타입 시스템(Type System)
    • 스키마와 루트 타입
    • 리졸버(Resolver)
  • 클라이언트의 선언적 데이터 요청
  • 언제 GraphQL을 선택할 것인가
    • GraphQL이 적합한 경우
    • REST가 더 적합한 경우
  • 2026년 GraphQL 생태계
    • 서버 프레임워크
    • 클라이언트 라이브러리
    • 주요 트렌드
  • 정리
    • 다음 장 미리보기