백엔드·5분 읽기

REST API vs GraphQL 완벽 비교 가이드

RESTful API와 GraphQL의 핵심 개념, 설계 원칙, 장단점을 비교하고 프로젝트에 적합한 기술을 선택하는 실전 가이드를 제공합니다.

공유:

REST API vs GraphQL 완벽 비교 가이드

현대 웹 개발에서 클라이언트와 서버 간의 데이터 통신은 핵심적인 요소입니다. 그 중심에는 REST API와 GraphQL이라는 두 가지 대표적인 기술이 자리하고 있습니다. 이 글에서는 두 기술의 핵심 개념부터 실전 활용법까지 심도 있게 다루겠습니다.

REST API란 무엇인가?

REST(Representational State Transfer)는 2000년 로이 필딩(Roy Fielding)의 박사 논문에서 처음 소개된 아키텍처 스타일입니다. REST API는 HTTP 프로토콜을 기반으로 리소스를 정의하고, HTTP 메서드를 통해 해당 리소스에 대한 CRUD(Create, Read, Update, Delete) 작업을 수행합니다.

RESTful API의 6가지 설계 원칙

REST API를 올바르게 설계하려면 다음 6가지 원칙을 반드시 이해해야 합니다.

1. 클라이언트-서버 분리 (Client-Server)

클라이언트와 서버는 서로 독립적으로 존재해야 합니다. 클라이언트는 UI와 사용자 경험에 집중하고, 서버는 데이터 저장과 비즈니스 로직에 집중합니다. 이를 통해 각각 독립적으로 개선하고 확장할 수 있습니다.

2. 무상태성 (Stateless)

서버는 클라이언트의 상태를 저장하지 않습니다. 각 요청은 필요한 모든 정보를 포함해야 하며, 서버는 이전 요청의 컨텍스트를 기억하지 않습니다.

3. 캐시 가능 (Cacheable)

응답 데이터는 캐시 가능 여부가 명시되어야 합니다. 적절한 캐싱은 네트워크 트래픽을 줄이고 성능을 크게 향상시킵니다.

4. 계층 구조 (Layered System)

클라이언트는 중간 서버(프록시, 로드 밸런서 등)의 존재를 알 필요가 없습니다. 각 계층은 인접한 계층과만 상호작용합니다.

5. 통일된 인터페이스 (Uniform Interface)

리소스는 URI로 식별되며, HTTP 메서드를 통해 조작합니다. 이는 REST API의 가장 핵심적인 원칙입니다.

6. 주문형 코드 (Code on Demand, 선택적)

서버가 클라이언트에게 실행 가능한 코드를 전달할 수 있습니다. 이 원칙은 선택적이며 자주 사용되지는 않습니다.

REST API 설계 예시

일반적인 REST API의 엔드포인트 설계는 다음과 같습니다.

GET    /api/users          # 사용자 목록 조회
GET    /api/users/123      # 특정 사용자 조회
POST   /api/users          # 새 사용자 생성
PUT    /api/users/123      # 사용자 정보 수정
DELETE /api/users/123      # 사용자 삭제
GET    /api/users/123/posts  # 특정 사용자의 게시글 목록

Express.js를 사용한 구현 예시를 살펴보겠습니다.

const express = require('express');
const app = express();

// 사용자 목록 조회
app.get('/api/users', async (req, res) => {
  const users = await User.findAll();
  res.json(users);
});

// 특정 사용자 조회
app.get('/api/users/:id', async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) return res.status(404).json({ error: 'User not found' });
  res.json(user);
});

// 새 사용자 생성
app.post('/api/users', async (req, res) => {
  const user = await User.create(req.body);
  res.status(201).json(user);
});

GraphQL이란 무엇인가?

GraphQL은 2015년 페이스북(현 메타)에서 공개한 API용 쿼리 언어이자 런타임입니다. REST와 달리 단일 엔드포인트를 사용하며, 클라이언트가 필요한 데이터의 구조를 직접 정의할 수 있습니다.

GraphQL의 핵심 개념

스키마 정의 언어 (SDL)

GraphQL에서는 타입 시스템을 통해 API의 구조를 정의합니다.

type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

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

type Query {
  users: [User!]!
  user(id: ID!): User
  posts: [Post!]!
}

type Mutation {
  createUser(name: String!, email: String!): User!
  updateUser(id: ID!, name: String, email: String): User!
  deleteUser(id: ID!): Boolean!
}

쿼리 (Query)

클라이언트가 필요한 데이터만 정확히 요청할 수 있습니다.

query {
  user(id: "123") {
    name
    email
    posts {
      title
      createdAt
    }
  }
}

위 쿼리는 사용자의 이름, 이메일, 그리고 해당 사용자의 게시글 제목과 생성일만 반환합니다. 불필요한 데이터는 전송되지 않습니다.

뮤테이션 (Mutation)

데이터를 변경하는 작업은 뮤테이션을 통해 수행합니다.

mutation {
  createUser(name: "홍길동", email: "hong@example.com") {
    id
    name
    email
  }
}

서브스크립션 (Subscription)

실시간 데이터 업데이트가 필요한 경우 서브스크립션을 사용합니다.

subscription {
  newPost {
    title
    author {
      name
    }
  }
}

Node.js에서 GraphQL 서버 구현

Apollo Server를 사용한 GraphQL 서버 구현 예시입니다.

const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
  }

type Query { users: [User!]! user(id: ID!): User } `;

const resolvers = { Query: { users: () => User.findAll(), user: (_, { id }) => User.findById(id), }, };

const server = new ApolloServer({ typeDefs, resolvers }); server.listen().then(({ url }) => { console.log(서버 실행 중: ${url}); });


## REST API vs GraphQL 핵심 비교

두 기술의 주요 차이점을 구체적으로 비교해 보겠습니다.

### 1. 데이터 페칭 방식

**REST API의 과다 페칭(Over-fetching) 문제**

REST API에서 사용자 이름만 필요한 경우에도 전체 사용자 객체를 받아야 합니다. 이는 불필요한 네트워크 대역폭 사용으로 이어집니다.

// GET /api/users/123 응답 - 이름만 필요하지만 전체 데이터를 받음 { "id": 123, "name": "홍길동", "email": "hong@example.com", "phone": "010-1234-5678", "address": "서울시 강남구", "createdAt": "2025-01-15T10:00:00Z" }


**REST API의 과소 페칭(Under-fetching) 문제**

사용자 정보와 함께 게시글 목록이 필요한 경우, 여러 엔드포인트를 호출해야 합니다.

GET /api/users/123 # 첫 번째 요청 GET /api/users/123/posts # 두 번째 요청 GET /api/posts/456/comments # 세 번째 요청


**GraphQL의 해결 방식**

GraphQL에서는 단 한 번의 요청으로 필요한 모든 데이터를 가져올 수 있습니다.

query { user(id: "123") { name posts { title comments { content } } } }


### 2. 엔드포인트 관리

REST API는 리소스마다 별도의 엔드포인트가 필요합니다. 프로젝트 규모가 커질수록 수십, 수백 개의 엔드포인트를 관리해야 합니다. 반면 GraphQL은 단일 엔드포인트(`/graphql`)만으로 모든 데이터 요청을 처리합니다.

### 3. 버전 관리

REST API에서는 API가 변경될 때 새로운 버전을 만들어야 하는 경우가 많습니다. `/api/v1/users`, `/api/v2/users`처럼 버전별 엔드포인트를 유지해야 합니다.

GraphQL에서는 스키마에 새로운 필드를 추가하더라도 기존 클라이언트에 영향을 주지 않습니다. 더 이상 사용하지 않는 필드는 `@deprecated` 디렉티브로 표시하면 됩니다.

type User { id: ID! name: String! fullName: String! username: String @deprecated(reason: "fullName을 사용하세요") }


### 4. 캐싱

REST API는 HTTP 캐싱 메커니즘을 자연스럽게 활용할 수 있습니다. URL 기반 캐싱이 가능하므로 CDN, 브라우저 캐시 등을 쉽게 적용할 수 있습니다.

GraphQL은 단일 엔드포인트를 사용하고 POST 요청이 대부분이므로 HTTP 수준의 캐싱이 어렵습니다. 대신 Apollo Client 같은 라이브러리에서 제공하는 클라이언트 측 캐싱을 활용해야 합니다.

### 5. 에러 처리

REST API는 HTTP 상태 코드를 통해 에러를 직관적으로 전달합니다.

200 OK - 성공 201 Created - 생성 완료 400 Bad Request - 잘못된 요청 404 Not Found - 리소스 없음 500 Internal Server Error - 서버 오류


GraphQL은 항상 HTTP 200을 반환하고, 응답 본문 내의 `errors` 배열로 에러를 전달합니다.

{ "data": null, "errors": [ { "message": "사용자를 찾을 수 없습니다", "locations": [{ "line": 2, "column": 3 }], "path": ["user"] } ] }


## 언제 무엇을 선택해야 할까?

### REST API를 선택해야 하는 경우

- **단순한 CRUD 작업이 중심인 프로젝트**: 리소스 구조가 명확하고 관계가 단순한 경우 REST가 더 직관적입니다.
- **캐싱이 중요한 프로젝트**: CDN이나 HTTP 캐싱을 적극적으로 활용해야 하는 경우 REST가 유리합니다.
- **팀의 경험이 REST에 집중된 경우**: GraphQL 학습 곡선을 고려하면 REST가 빠른 개발에 유리할 수 있습니다.
- **파일 업로드가 빈번한 경우**: REST API에서 파일 업로드 처리가 더 간단합니다.
- **마이크로서비스 간 통신**: 서비스 간 통신에는 REST가 더 적합한 경우가 많습니다.

### GraphQL을 선택해야 하는 경우

- **복잡한 데이터 관계를 가진 프로젝트**: 여러 리소스를 연결해서 조회해야 하는 경우 GraphQL이 효율적입니다.
- **다양한 클라이언트를 지원해야 하는 경우**: 웹, 모바일, IoT 등 다양한 클라이언트가 각기 다른 데이터를 필요로 할 때 GraphQL이 유리합니다.
- **빠른 프론트엔드 개발이 필요한 경우**: 프론트엔드 개발자가 백엔드 변경 없이 필요한 데이터를 자유롭게 조회할 수 있습니다.
- **실시간 데이터가 필요한 경우**: Subscription을 통한 실시간 데이터 스트리밍이 필요한 경우 GraphQL이 적합합니다.
- **API 문서 자동화가 중요한 경우**: GraphQL은 스키마 자체가 문서 역할을 하며, GraphiQL 같은 도구로 탐색이 가능합니다.

## 실전에서의 하이브리드 접근법

실무에서는 두 기술을 함께 사용하는 경우도 많습니다. 예를 들어, 외부 공개 API는 REST로 제공하면서 내부 프론트엔드와의 통신에는 GraphQL을 사용하는 방식입니다.

[외부 클라이언트] --REST--> [API Gateway] [프론트엔드 앱] --GraphQL--> [API Gateway] --REST--> [마이크로서비스들]


이러한 하이브리드 접근법은 각 기술의 장점을 극대화하면서 단점을 보완할 수 있습니다.

## 성능 최적화 팁

### REST API 성능 최적화

- **페이지네이션 적용**: 대량의 데이터는 `limit`과 `offset` 또는 커서 기반 페이지네이션을 적용합니다.
- **필드 필터링**: `?fields=name,email` 같은 쿼리 파라미터로 필요한 필드만 반환합니다.
- **ETag 캐싱**: 변경되지 않은 리소스에 대해 304 응답을 반환합니다.
- **압축**: gzip 또는 Brotli 압축을 적용합니다.

### GraphQL 성능 최적화

- **쿼리 깊이 제한**: 무한 중첩 쿼리를 방지하기 위해 최대 깊이를 설정합니다.
- **쿼리 복잡도 분석**: 복잡한 쿼리에 비용을 부여하고 한도를 설정합니다.
- **DataLoader 패턴**: N+1 문제를 해결하기 위해 배치 처리와 캐싱을 적용합니다.
- **Persisted Queries**: 자주 사용하는 쿼리를 서버에 미리 저장하여 네트워크 비용을 줄입니다.

// DataLoader 사용 예시 const DataLoader = require('dataloader');

const userLoader = new DataLoader(async (userIds) => { const users = await User.findByIds(userIds); return userIds.map(id => users.find(user => user.id === id)); });

// 리졸버에서 사용 const resolvers = { Post: { author: (post) => userLoader.load(post.authorId), }, };


## 마무리

REST API와 GraphQL은 각각 고유한 강점과 약점을 가지고 있습니다. 중요한 것은 어떤 기술이 더 우수한가가 아니라, 프로젝트의 요구사항과 팀의 역량에 맞는 기술을 선택하는 것입니다.

REST API는 검증된 아키텍처로서 단순하고 예측 가능하며, HTTP 표준을 충실히 활용합니다. GraphQL은 유연한 데이터 페칭과 강력한 타입 시스템으로 복잡한 데이터 요구사항을 효과적으로 처리합니다.

두 기술 모두 깊이 이해하고, 상황에 따라 적절히 선택하거나 조합하여 사용하는 것이 현명한 백엔드 개발자의 자세입니다. 프로젝트를 시작하기 전에 데이터 구조의 복잡성, 클라이언트의 다양성, 팀의 기술 스택, 성능 요구사항 등을 종합적으로 고려하여 결정하시기 바랍니다.
#API#REST#GraphQL#백엔드#설계