2026년 6월 21일

마스터하기: GraphQL - 현대 API 설계의 새로운 패러다임

10
마스터하기: GraphQL - 현대 API 설계의 새로운 패러다임

마스터하기: GraphQL - 현대 API 설계의 새로운 패러다임

마스터하기: GraphQL - 현대 API 설계의 새로운 패러다임

안녕하세요, 10년 경력의 소프트웨어 엔지니어이자 기술 교육자입니다. 오늘은 현대 웹 개발에서 점점 더 중요해지고 있는 API 설계 패러다임, 바로 GraphQL에 대해 깊이 있게 알아보는 시간을 갖겠습니다. REST API가 여전히 널리 사용되지만, GraphQL은 특히 모바일 환경이나 복잡한 프론트엔드 애플리케이션에서 그 진가를 발휘하며 개발 생산성과 사용자 경험을 혁신하고 있습니다. 초중급 개발자 여러분들이 GraphQL의 핵심 원리를 이해하고 실제 프로젝트에 적용할 수 있도록 쉽고 명확하게 설명해 드리겠습니다.

1. 개념 소개: GraphQL이란 무엇이며 왜 중요한가?

1. 개념 소개: GraphQL이란 무엇이며 왜 중요한가?

정의

GraphQL은 API를 위한 쿼리 언어(Query Language)이자, 이 쿼리를 사용하여 서버에서 데이터를 가져오는 런타임(Runtime)입니다. 페이스북이 2012년에 모바일 애플리케이션의 복잡한 데이터 요구사항을 해결하기 위해 내부적으로 개발했으며, 2015년에 오픈소스로 공개했습니다. 클라이언트는 필요한 데이터의 구조를 직접 정의하여 서버에 요청하고, 서버는 그 요청에 정확히 일치하는 데이터를 응답합니다.

탄생 배경: REST API의 한계 극복

REST API는 웹 서비스 설계의 사실상 표준으로 자리 잡았지만, 몇 가지 고질적인 문제점을 가지고 있습니다. 특히 모바일 환경이 보편화되고 프론트엔드 애플리케이션의 복잡도가 증가하면서 이러한 한계가 더욱 두드러졌습니다.

  1. 오버페칭(Over-fetching)과 언더페칭(Under-fetching):

    • 오버페칭: 클라이언트가 필요한 데이터보다 더 많은 데이터를 서버로부터 받는 현상입니다. 예를 들어, GET /users/1 요청 시 사용자 이름만 필요한데, 주소, 전화번호, 가입일 등 모든 정보가 함께 오는 경우입니다. 이는 네트워크 대역폭 낭비와 불필요한 데이터 처리로 이어집니다.
    • 언더페칭: 필요한 데이터를 얻기 위해 여러 번의 API 요청을 보내야 하는 현상입니다. 예를 들어, 사용자의 게시글 목록을 보여주기 위해 GET /users/1로 사용자 정보를 가져온 후, 다시 GET /users/1/posts로 해당 사용자의 게시글을 가져와야 하는 경우입니다. 이는 여러 번의 왕복(Round-trip) 통신으로 인해 애플리케이션의 응답 속도를 저하시킵니다.
  2. 고정된 응답 구조: REST API는 일반적으로 서버에서 미리 정의된 고정된 형태의 응답을 제공합니다. 클라이언트의 요구사항이 조금만 달라져도 새로운 엔드포인트를 만들거나 기존 엔드포인트를 수정해야 할 때가 많습니다. 이는 API 유지보수와 진화를 어렵게 만듭니다.

  3. 버전 관리의 어려움: API의 변경사항이 발생하면 v1, v2와 같은 버전 관리가 필요해지는데, 이는 클라이언트와 서버 양쪽에서 복잡성을 증가시킵니다.

GraphQL은 이러한 문제점들을 해결하기 위해 탄생했습니다. 클라이언트가 "정확히 필요한 것만" 요청할 수 있게 함으로써, 데이터 통신의 효율성을 극대화하고 API 개발 및 유지보수를 훨씬 유연하게 만듭니다.

왜 중요한가?

GraphQL이 현대 개발에서 중요한 이유는 다음과 같습니다.

  • 효율적인 데이터 통신: 오버페칭과 언더페칭 문제를 해결하여 네트워크 트래픽을 줄이고 애플리케이션 성능을 향상시킵니다. 특히 모바일 환경에서 데이터 사용량과 배터리 소모를 줄이는 데 기여합니다.
  • 유연한 API 개발: 클라이언트가 쿼리하는 방식에 따라 다양한 형태의 데이터를 받을 수 있으므로, 단일 엔드포인트(POST /graphql)만으로도 무한한 조합의 데이터 요청이 가능합니다. 이는 백엔드 개발자가 클라이언트의 모든 요구사항에 맞춰 엔드포인트를 일일이 만들 필요가 없게 합니다.
  • 빠른 피처 개발: 프론트엔드 개발자는 백엔드 변경 없이도 필요한 데이터를 직접 구성하여 가져올 수 있으므로, 새로운 기능을 빠르게 개발하고 배포할 수 있습니다.
  • 강력한 타입 시스템: GraphQL은 강력한 타입 시스템을 기반으로 합니다. 이는 API의 스키마를 명확하게 정의하고, 개발 과정에서 발생할 수 있는 오류를 미리 방지하며, 클라이언트와 서버 간의 계약을 명확히 합니다. 자동 완성 기능이나 문서화 도구와 통합되어 개발 경험을 크게 향상시킵니다.
  • 단일 API 게이트웨이: 여러 마이크로서비스로 구성된 백엔드 시스템에서 GraphQL 서버를 단일 진입점(API Gateway)으로 활용하여, 클라이언트는 복잡한 백엔드 구조를 알 필요 없이 GraphQL 서버에만 요청하면 됩니다.

2. 핵심 원리 설명: GraphQL의 동작 방식

2. 핵심 원리 설명: GraphQL의 동작 방식

GraphQL은 스키마(Schema), 쿼리(Query), 뮤테이션(Mutation), 리졸버(Resolver), 그리고 강력한 **타입 시스템(Type System)**이라는 핵심 요소들로 이루어져 있습니다. 이들을 이해하는 것이 GraphQL을 마스터하는 첫걸음입니다.

비유: 레스토랑 주문 시스템

GraphQL의 동작 방식을 레스토랑 주문 시스템에 비유해 봅시다.

  1. 레스토랑 메뉴판 (Schema): 레스토랑에 어떤 요리(타입)가 있고, 각 요리에 어떤 재료(필드)가 들어가는지, 그리고 각 요리를 주문할 때 어떤 추가 옵션(인자)을 선택할 수 있는지 정의되어 있습니다. GraphQL 스키마는 API가 제공하는 데이터의 모든 종류와 관계를 정의하는 청사진입니다.

  2. 손님의 주문서 (Query): 손님은 메뉴판을 보고 "스테이크 1개와 함께 샐러드를 추가해주세요. 샐러드에는 토마토와 양상추만 넣어주세요."와 같이 정확히 원하는 요리와 재료를 적어서 주문합니다. GraphQL 쿼리는 클라이언트가 API로부터 정확히 어떤 데이터를 어떤 구조로 원하는지 명시하는 요청입니다.

  3. 손님의 특별 요청 (Mutation): 손님은 주문서에 "이 요리는 이렇게 바꿔주세요" 또는 "새로운 요리를 만들어주세요"와 같이 데이터 변경을 요청할 수 있습니다. GraphQL 뮤테이션은 데이터를 생성, 수정, 삭제하는 작업입니다.

  4. 주방장 (Resolver): 주문서를 받은 주방장은 주문된 요리와 재료를 확인하고, 식재료 창고(데이터베이스)나 다른 부엌(다른 마이크로서비스)에서 필요한 재료를 가져와 요리를 만듭니다. GraphQL 리졸버는 클라이언트의 쿼리나 뮤테이션 요청에 따라 실제 데이터를 데이터베이스나 다른 백엔드 서비스로부터 가져오거나 변경하는 역할을 하는 함수입니다.

  5. 식재료 분류표 (Type System): 메뉴판에 '채소'는 '토마토', '양상추', '양파' 등으로 분류되고, '고기'는 '소고기', '돼지고기' 등으로 분류되어 있듯이, GraphQL은 데이터의 타입을 명확히 정의합니다. 이는 주문이 잘못되거나 요리 과정에서 오류가 나는 것을 방지합니다.

핵심 요소 다이어그램

graph TD
    A[클라이언트] -->|GraphQL 쿼리/뮤테이션 요청| B(GraphQL 서버)
    B --> C{GraphQL 스키마}
    B --> D[리졸버]
    C -- 정의 --> D
    D --> E[데이터베이스/다른 서비스]
    E -->|데이터 반환| D
    D -->|처리된 데이터 반환| B
    B -->|GraphQL 응답 (JSON)| A

위 다이어그램은 GraphQL의 기본적인 요청-응답 흐름을 보여줍니다.

  1. 클라이언트 (Client): 웹 브라우저, 모바일 앱 등에서 GraphQL 쿼리나 뮤테이션을 작성하여 GraphQL 서버로 HTTP POST 요청을 보냅니다.
  2. GraphQL 서버 (GraphQL Server): 클라이언트로부터 요청을 받으면, 미리 정의된 GraphQL 스키마를 기반으로 요청의 유효성을 검사합니다.
  3. GraphQL 스키마 (GraphQL Schema): API가 제공하는 모든 데이터의 타입, 필드, 관계를 정의한 설계도입니다. 서버는 이 스키마를 통해 클라이언트가 요청한 데이터가 유효한지, 어떤 구조로 데이터를 반환해야 하는지 파악합니다.
  4. 리졸버 (Resolver): 스키마에 정의된 각 필드에 대해 실제 데이터를 가져오거나 변경하는 로직을 담고 있는 함수입니다. 클라이언트 쿼리의 각 필드에 해당하는 리졸버가 순차적으로 실행되어 필요한 데이터를 수집합니다.
  5. 데이터베이스/다른 서비스 (Database/Other Services): 리졸버는 데이터를 가져오기 위해 데이터베이스 쿼리를 실행하거나, REST API, 다른 마이크로서비스 등 다양한 백엔드 소스와 통신합니다.
  6. GraphQL 응답 (GraphQL Response): 리졸버들이 수집한 데이터를 클라이언트가 요청한 쿼리 구조에 맞춰 JSON 형태로 변환하여 클라이언트에 응답합니다.

타입 시스템 (Type System)

GraphQL의 핵심 중 하나는 강력한 타입 시스템입니다. 스키마 정의 언어(Schema Definition Language, SDL)를 사용하여 API의 구조를 명확하게 정의합니다.

  • 스칼라 타입 (Scalar Types): 기본 데이터 타입 (Int, Float, String, Boolean, ID).
  • 객체 타입 (Object Types): 커스텀 타입. 예를 들어 User, Post 등.
  • 목록 타입 (List Types): [Post]와 같이 특정 타입의 목록을 나타냅니다.
  • Non-nullable 타입: String!와 같이 필드가 null이 될 수 없음을 나타냅니다.
  • 인자 (Arguments): 필드에 인자를 전달하여 특정 조건의 데이터를 요청할 수 있습니다 (예: user(id: "1")).
  • 인터페이스 (Interfaces): 특정 필드 집합을 구현하는 여러 객체 타입의 공통 규약을 정의합니다.
  • 유니온 (Unions): 여러 타입 중 하나가 될 수 있는 필드를 정의합니다.

이러한 타입 시스템 덕분에 개발자는 API의 구조를 쉽게 이해하고, 클라이언트와 서버 간의 데이터 계약이 명확해져 오류 발생 가능성이 줄어듭니다.

3. 코드 예제 (Python & JavaScript)

GraphQL 서버를 구축하는 방법은 여러 언어에서 다양한 라이브러리를 통해 지원됩니다. 여기서는 Python의 Strawberry와 JavaScript의 Apollo Server를 사용하여 간단한 예제를 보여드리겠습니다.

예제 1: Python (Strawberry) - 간단한 사용자 정보 조회 서버

Strawberry는 Python의 타입 힌트(type hints)를 적극 활용하는 현대적인 GraphQL 라이브러리입니다.

# app.py
import strawberry
from typing import List, Optional

# 1. 스키마 정의: 데이터 모델(타입)을 정의합니다.
@strawberry.type
class User:
    id: strawberry.ID
    name: str
    email: str
    posts: List["Post"] # 순환 참조를 위해 문자열로 참조

@strawberry.type
class Post:
    id: strawberry.ID
    title: str
    content: str
    author: User

# 2. Mock 데이터 (실제 DB 대신 사용)
mock_users = {
    "1": User(id="1", name="Alice", email="[email protected]", posts=[]),
    "2": User(id="2", name="Bob", email="[email protected]", posts=[]),
}

mock_posts = {
    "101": Post(id="101", title="GraphQL 소개", content="GraphQL은...", author=mock_users["1"]),
    "102": Post(id="102", title="Python 개발", content="Python은...", author=mock_users["1"]),
    "103": Post(id="103", title="새로운 기술", content="최신 기술...", author=mock_users["2"]),
}

# 사용자 객체에 게시글 연결
mock_users["1"].posts = [mock_posts["101"], mock_posts["102"]]
mock_users["2"].posts = [mock_posts["103"]]

# 3. 쿼리 정의: 클라이언트가 요청할 수 있는 진입점(Entry Point)과 리졸버를 정의합니다.
@strawberry.type
class Query:
    @strawberry.field
    def hello(self) -> str:
        return "Hello Strawberry!"

    @strawberry.field
    def user(self, id: strawberry.ID) -> Optional[User]:
        """ID로 특정 사용자를 조회합니다."""
        return mock_users.get(id)

    @strawberry.field
    def users(self) -> List[User]:
        """모든 사용자를 조회합니다."""
        return list(mock_users.values())

    @strawberry.field
    def post(self, id: strawberry.ID) -> Optional[Post]:
        """ID로 특정 게시글을 조회합니다."""
        return mock_posts.get(id)

    @strawberry.field
    def posts(self) -> List[Post]:
        """모든 게시글을 조회합니다."""
        return list(mock_posts.values())

# 4. 스키마 객체 생성
schema = strawberry.Schema(query=Query)

# 5. FastAPI와 통합하여 서버 실행 (uvicorn 필요)
# pip install "strawberry-graphql[fastapi]" uvicorn
from fastapi import FastAPI
from strawberry.asgi import GraphQL

app = FastAPI()
app.add_route("/graphql", GraphQL(schema))

# 실행 방법: uvicorn app:app --reload
# http://127.0.0.1:8000/graphql 로 접속하여 GraphQL Playground 확인

GraphQL 쿼리 예시:

# 모든 사용자 이름과 이메일 조회
query AllUsers {
  users {
    id
    name
    email
  }
}

# ID가 "1"인 사용자 정보와 해당 사용자의 게시글 제목 조회
query UserWithPosts {
  user(id: "1") {
    id
    name
    email
    posts {
      id
      title
    }
  }
}

# ID가 "101"인 게시글의 제목, 내용, 저자의 이름 조회
query PostDetails {
  post(id: "101") {
    title
    content
    author {
      name
    }
  }
}

예제 2: JavaScript (Apollo Server & Client) - 게시글 관리 시스템

이 예제는 Node.js 기반의 Apollo Server로 서버를 구현하고, 간단한 클라이언트 측 쿼리 방법을 보여줍니다.

// server.js (Node.js)
const { ApolloServer, gql } = require('apollo-server');

// 1. 스키마 정의 (SDL 사용)
const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
  }

  # 쿼리 타입: 클라이언트가 데이터를 조회할 때 사용할 수 있는 루트 필드들을 정의
  type Query {
    hello: String
    user(id: ID!): User
    users: [User!]!
    post(id: ID!): Post
    posts: [Post!]!
  }

  # 뮤테이션 타입: 클라이언트가 데이터를 변경(생성, 수정, 삭제)할 때 사용할 수 있는 루트 필드들을 정의
  type Mutation {
    createPost(title: String!, content: String!, authorId: ID!): Post!
    updatePost(id: ID!, title: String, content: String): Post
    deletePost(id: ID!): Boolean!
  }
`;

// 2. Mock 데이터
const users = [
  { id: '1', name: 'Alice', email: '[email protected]' },
  { id: '2', name: 'Bob', email: '[email protected]' },
];

let posts = [
  { id: '101', title: 'Apollo Server 시작하기', content: 'Apollo Server는...', authorId: '1' },
  { id: '102', title: 'GraphQL 실습', content: 'GraphQL은...', authorId: '1' },
  { id: '103', title: 'Node.js 개발', content: 'Node.js는...', authorId: '2' },
];

// 3. 리졸버 정의: 스키마에 정의된 각 필드에 대한 실제 데이터 처리 로직
const resolvers = {
  Query: {
    hello: () => 'Hello Apollo!',
    user: (parent, { id }) => users.find(user => user.id === id),
    users: () => users,
    post: (parent, { id }) => posts.find(post => post.id === id),
    posts: () => posts,
  },
  Mutation: {
    createPost: (parent, { title, content, authorId }) => {
      const newPost = {
        id: String(posts.length + 101), // 간단한 ID 생성
        title,
        content,
        authorId,
      };
      posts.push(newPost);
      return newPost;
    },
    updatePost: (parent, { id, title, content }) => {
      const postIndex = posts.findIndex(post => post.id === id);
      if (postIndex === -1) return null;
      
      const updatedPost = { ...posts[postIndex], title: title || posts[postIndex].title, content: content || posts[postIndex].content };
      posts[postIndex] = updatedPost;
      return updatedPost;
    },
    deletePost: (parent, { id }) => {
      const initialLength = posts.length;
      posts = posts.filter(post => post.id !== id);
      return posts.length < initialLength; // 삭제 성공 여부 반환
    },
  },
  // 부모 객체의 특정 필드를 어떻게 resolve할지 정의 (중요!)
  User: {
    posts: (parent) => posts.filter(post => post.authorId === parent.id),
  },
  Post: {
    author: (parent) => users.find(user => user.id === parent.authorId),
  },
};

// 4. Apollo Server 인스턴스 생성 및 실행
const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

// 실행 방법: node server.js
// http://localhost:4000/ 로 접속하여 Apollo Studio Sandbox 확인

GraphQL 뮤테이션 예시:

# 새로운 게시글 생성
mutation CreateNewPost {
  createPost(title: "GraphQL 시작하기", content: "정말 쉬워요!", authorId: "1") {
    id
    title
    author {
      name
    }
  }
}

# 게시글 업데이트
mutation UpdateExistingPost {
  updatePost(id: "101", content: "Apollo Server로 GraphQL API를 만들어봅시다!") {
    id
    title
    content
  }
}

# 게시글 삭제
mutation DeletePost {
  deletePost(id: "101")
}

4. 실무 적용 사례

GraphQL은 다양한 실무 환경에서 그 가치를 입증하고 있습니다.

  • 모바일 앱 백엔드: 네트워크 환경이 불안정하고 데이터 사용량에 민감한 모바일 앱에서 GraphQL은 오버페칭을 방지하고 여러 API 호출을 한 번으로 줄여 성능을 크게 향상시킵니다. 인스타그램, 넷플릭스 등 많은 모바일 앱이 GraphQL을 활용합니다.
  • 프론트엔드 통합 API (BFF - Backend For Frontend): 여러 백엔드 서비스(마이크로서비스)에서 데이터를 가져와야 하는 복잡한 프론트엔드 애플리케이션에서 GraphQL 서버는 클라이언트와 여러 백엔드 서비스 사이의 단일 게이트웨이 역할을 합니다. 프론트엔드 개발자는 이 GraphQL 서버에만 요청하면 필요한 모든 데이터를 한 번에 얻을 수 있어 개발 생산성이 높아집니다.
  • CMS (콘텐츠 관리 시스템) 개발: 콘텐츠의 종류와 구조가 유동적인 CMS에서 GraphQL은 유연한 데이터 모델링과 쿼리 기능을 제공하여 다양한 형태의 콘텐츠를 효율적으로 관리하고 제공할 수 있게 합니다.
  • 서비스 간 Gateway 역할: 마이크로서비스 아키텍처에서 GraphQL은 다양한 내부 서비스들을 통합하는 API Gateway 역할을 수행하며, 외부 클라이언트에게 단일하고 일관된 API 인터페이스를 제공합니다.
  • 공개 API (Public API): GitHub의 공개 API는 GraphQL로 제공되어 개발자들이 필요한 데이터를 정확히 요청하고 복잡한 데이터를 쉽게 탐색할 수 있도록 돕습니다.

5. 자주 하는 실수와 해결법

GraphQL은 강력하지만, 잘못 사용하면 오히려 성능 저하나 보안 취약점을 초래할 수 있습니다.

5.1 N+1 문제

  • 문제: 리졸버가 데이터를 가져올 때, 부모-자식 관계의 데이터를 자식 수만큼 반복적으로 쿼리하여 데이터베이스에 불필요한 부하를 주는 문제입니다. 예를 들어, users를 조회한 후 각 userposts를 조회할 때, 사용자 수만큼 posts 쿼리가 실행되는 경우입니다.
  • 해결법: DataLoader 패턴을 사용하세요. DataLoader는 여러 필드 요청을 한 번의 데이터베이스 쿼리로 묶어서(batching) 실행하고, 동일한 ID에 대한 요청을 캐싱하여 N+1 문제를 효과적으로 해결합니다. 대부분의 GraphQL 라이브러리(Apollo, Graphene 등)는 DataLoader를 지원하거나 유사한 기능을 제공합니다.

5.2 복잡한 쿼리 오용 및 무제한 쿼리

  • 문제: 클라이언트가 너무 깊거나 넓은 쿼리를 작성하여 서버에 과도한 부하를 주거나, 의도적인 서비스 거부(DoS) 공격에 악용될 수 있습니다.
  • 해결법:
    • 쿼리 깊이 제한 (Query Depth Limiting): 쿼리의 최대 중첩 깊이를 제한하여 너무 깊은 쿼리를 방지합니다.
    • 쿼리 복잡도 분석 (Query Complexity Analysis): 각 필드에 비용을 할당하고, 총 쿼리 비용이 일정 임계값을 초과하면 요청을 거부합니다.
    • 쿼리 타임아웃: 특정 쿼리가 너무 오래 실행될 경우 강제로 종료합니다.
    • 영속 쿼리 (Persisted Queries): 클라이언트가 쿼리 해시만 보내고 서버에 미리 저장된 쿼리를 실행하도록 하여, 쿼리 파싱 오버헤드를 줄이고 안전성을 높입니다.

5.3 인증(Authentication) 및 인가(Authorization) 관리

  • 문제: GraphQL은 HTTP 레이어에서 인증/인가를 직접 처리하지 않습니다. 각 리졸버에서 수동으로 권한 검사를 해야 하므로 누락될 위험이 있습니다.
  • 해결법:
    • 미들웨어 (Middleware) 또는 디렉티브 (Directives): Apollo Server의 schema-directives나 Strawberry의 미들웨어 기능을 활용하여 리졸버 실행 전 공통된 인증/인가 로직을 적용할 수 있습니다. 예를 들어, @auth 디렉티브를 필드에 붙여 해당 필드 접근 시 권한을 검사하도록 합니다.
    • 컨텍스트 (Context): 인증 정보를 GraphQL 컨텍스트 객체에 담아 모든 리졸버에서 접근할 수 있도록 합니다.

5.4 캐싱 전략의 어려움

  • 문제: REST API는 HTTP 캐싱 메커니즘(ETag, Last-Modified, Cache-Control)을 활용하기 용이하지만, GraphQL은 단일 엔드포인트(POST /graphql)를 사용하므로 HTTP 레벨의 캐싱이 어렵습니다.
  • 해결법:
    • 클라이언트 측 캐싱: Apollo Client, Relay 등 GraphQL 클라이언트 라이브러리는 자체적인 인메모리 캐싱 기능을 제공합니다.
    • 프록시 캐싱 (Proxy Caching): CDN(Content Delivery Network) 또는 리버스 프록시 서버에서 쿼리 해시 기반으로 캐싱을 구현할 수 있습니다.
    • 데이터베이스/애플리케이션 레벨 캐싱: 리졸버 내부에서 Redis와 같은 캐시 저장소를 활용하여 데이터베이스 쿼 결과를 캐싱합니다.

5.5 파일 업로드/다운로드 처리

  • 문제: GraphQL의 기본 사양에는 파일 업로드/다운로드에 대한 명시적인 지원이 없습니다.
  • 해결법:
    • 멀티파트(Multipart) 요청: graphql-multipart-request-spec과 같은 확장 사양을 사용하여 파일을 업로드합니다. 대부분의 GraphQL 라이브러리는 이를 지원합니다.
    • Base64 인코딩: 작은 파일의 경우 Base64로 인코딩하여 문자열 형태로 전송할 수 있지만, 큰 파일에는 비효율적입니다.
    • 별도 REST 엔드포인트 사용: 파일 업로드/다운로드만을 위한 별도의 REST API 엔드포인트를 두고, GraphQL은 파일의 메타데이터나 URL만 관리하는 하이브리드 방식을 사용할 수도 있습니다.

6. 더 공부할 리소스 추천

GraphQL은 계속해서 발전하고 있는 기술이며, 관련된 자료도 풍부합니다.

  • GraphQL 공식 웹사이트: https://graphql.org/ (개념, 사양, 구현체 등 모든 정보의 출발점)
  • Apollo GraphQL 문서: https://www.apollographql.com/docs/ (JavaScript/TypeScript 생태계에서 가장 널리 사용되는 GraphQL 플랫폼으로, 서버, 클라이언트, 개발 도구 등 방대한 자료 제공)
  • How To GraphQL: https://www.howtographql.com/ (다양한 언어와 프레임워크별로 GraphQL을 배우는 실용적인 튜토리