API 설계 가이드 | REST vs GraphQL vs gRPC 완벽 비교

API 설계 가이드 | REST vs GraphQL vs gRPC 완벽 비교

이 글의 핵심

API 설계 가이드입니다. REST, GraphQL, gRPC를 비교하고 프로젝트별 최적의 API 스타일 선택 기준을 제시합니다.

들어가며: API 설계의 중요성

”어떤 API 스타일을 선택해야 할까?”

백엔드 개발에서 API 설계는 가장 중요한 결정 중 하나입니다. REST, GraphQL, gRPC 중 무엇을 선택하느냐에 따라 개발 경험, 성능, 유지보수성이 크게 달라집니다.

이 글에서 다루는 것:

  • REST, GraphQL, gRPC의 핵심 개념
  • 각 API 스타일의 장단점
  • 성능 비교
  • 프로젝트별 선택 기준

목차

  1. REST API
  2. GraphQL
  3. gRPC
  4. 비교 분석
  5. 선택 가이드
  6. 정리

1. REST API

REST란?

REST(Representational State Transfer)는 HTTP를 기반으로 한 아키텍처 스타일입니다.

REST의 핵심 원칙:

graph TB
    A[REST 원칙] --> B[자원 Resource]
    A --> C[HTTP 메서드]
    A --> D[무상태 Stateless]
    A --> E[캐시 가능]
    
    B --> B1[URI로 식별]
    C --> C1[GET POST PUT DELETE]
    D --> D1[서버가 상태 저장 안 함]
    E --> E1[HTTP 캐시 활용]

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  # 사용자의 게시글 목록

Node.js Express 구현:

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

app.use(express.json());

// 사용자 목록 조회
app.get('/api/users', (req, res) => {
  const users = [
    { id: 1, name: 'Alice', email: '[email protected]' },
    { id: 2, name: 'Bob', email: '[email protected]' }
  ];
  res.json(users);
});

// 특정 사용자 조회
app.get('/api/users/:id', (req, res) => {
  const user = { id: req.params.id, name: 'Alice' };
  res.json(user);
});

// 사용자 생성
app.post('/api/users', (req, res) => {
  const newUser = req.body;
  res.status(201).json(newUser);
});

app.listen(3000);

REST의 장단점

장점:

  • 단순함: HTTP 표준 사용
  • 캐싱: HTTP 캐시 활용 가능
  • 도구 지원: Postman, curl 등
  • 학습 곡선 낮음: 직관적

단점:

  • Over-fetching: 불필요한 데이터도 받음
  • Under-fetching: 여러 요청 필요 (N+1 문제)
  • 버전 관리: /api/v1/, /api/v2/
  • 유연성 부족: 클라이언트 요구사항 변경 시 엔드포인트 추가

2. GraphQL

GraphQL이란?

GraphQL은 Facebook이 개발한 쿼리 언어로, 클라이언트가 필요한 데이터를 정확히 요청할 수 있습니다.

GraphQL 구조:

graph LR
    A[클라이언트] -->|Query| B[GraphQL 서버]
    B -->|정확한 데이터만| A
    
    C[REST] -->|고정된 응답| D[클라이언트]
    D -->|필요 없는 데이터도 받음| C

GraphQL 예제

스키마 정의:

# schema.graphql
type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

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

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

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

쿼리 예제:

# 사용자와 게시글 한 번에 조회
query {
  user(id: "123") {
    name
    email
    posts {
      title
      content
    }
  }
}

# 응답 (필요한 필드만)
{
  "data": {
    "user": {
      "name": "Alice",
      "email": "[email protected]",
      "posts": [
        {
          "title": "첫 번째 글",
          "content": "내용..."
        }
      ]
    }
  }
}

Node.js Apollo Server 구현:

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: () => [
      { id: '1', name: 'Alice', email: '[email protected]' },
      { id: '2', name: 'Bob', email: '[email protected]' }
    ],
    user: (_, { id }) => {
      return { id, name: 'Alice', email: '[email protected]' };
    }
  }
};

const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

GraphQL의 장단점

장점:

  • 정확한 데이터: 필요한 필드만 요청
  • 단일 엔드포인트: /graphql 하나로 모든 요청
  • 타입 시스템: 스키마로 타입 보장
  • 개발 경험: GraphQL Playground, 자동 문서화

단점:

  • 복잡도: 학습 곡선 높음
  • 캐싱 어려움: HTTP 캐시 활용 제한적
  • N+1 문제: DataLoader 등 추가 도구 필요
  • 오버헤드: 단순 CRUD에는 과함

3. gRPC

gRPC란?

gRPC는 Google이 개발한 고성능 RPC(Remote Procedure Call) 프레임워크입니다. Protocol Buffers를 사용하여 데이터를 직렬화합니다.

gRPC 구조:

graph LR
    A[클라이언트] -->|Binary Protobuf| B[gRPC 서버]
    B -->|Binary Protobuf| A
    
    C[REST] -->|JSON Text| D[서버]
    D -->|JSON Text| C

gRPC 예제

Protobuf 정의:

// user.proto
syntax = "proto3";

package user;

service UserService {
  rpc GetUser (GetUserRequest) returns (User);
  rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
  rpc CreateUser (CreateUserRequest) returns (User);
}

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
}

message GetUserRequest {
  int32 id = 1;
}

message ListUsersRequest {
  int32 page = 1;
  int32 page_size = 2;
}

message ListUsersResponse {
  repeated User users = 1;
}

message CreateUserRequest {
  string name = 1;
  string email = 2;
}

C++ 서버 구현:

#include <grpcpp/grpcpp.h>
#include "user.grpc.pb.h"

class UserServiceImpl final : public user::UserService::Service {
  grpc::Status GetUser(
      grpc::ServerContext* context,
      const user::GetUserRequest* request,
      user::User* response) override {
    
    response->set_id(request->id());
    response->set_name("Alice");
    response->set_email("[email protected]");
    
    return grpc::Status::OK;
  }
};

int main() {
  std::string server_address("0.0.0.0:50051");
  UserServiceImpl service;
  
  grpc::ServerBuilder builder;
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  builder.RegisterService(&service);
  
  std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;
  server->Wait();
  
  return 0;
}

gRPC의 장단점

장점:

  • 고성능: Binary 프로토콜, HTTP/2
  • 타입 안전: Protobuf 스키마
  • 스트리밍: 양방향 스트리밍 지원
  • 다국어 지원: 여러 언어로 클라이언트 생성

단점:

  • 브라우저 지원 제한: gRPC-Web 필요
  • 디버깅 어려움: Binary 프로토콜
  • 학습 곡선: Protobuf 문법
  • 도구 부족: Postman 등 REST 도구 사용 불가

4. 비교 분석

종합 비교표

특징RESTGraphQLgRPC
프로토콜HTTP/1.1HTTP/1.1HTTP/2
데이터 형식JSONJSONProtobuf (Binary)
타입 시스템없음있음 (Schema)있음 (Protobuf)
캐싱우수 (HTTP)제한적제한적
성능보통보통빠름
학습 곡선낮음중간높음
브라우저 지원우수우수제한적
스트리밍없음Subscription양방향

성능 벤치마크

테스트 환경: 10만 개 요청, 평균 응답 크기 1KB

처리량 (requests/sec):
1. gRPC:     50,000  ⭐
2. REST:     20,000
3. GraphQL:  15,000

응답 시간 (ms):
1. gRPC:     2ms   ⭐
2. REST:     5ms
3. GraphQL:  7ms

데이터 크기 (1000개 객체):
1. Protobuf: 82KB  ⭐
2. JSON:     120KB

사용 사례 비교

REST 적합한 경우:

  • 간단한 CRUD API
  • 공개 API (외부 개발자 사용)
  • 캐싱이 중요한 경우
  • 레거시 시스템 통합

GraphQL 적합한 경우:

  • 복잡한 데이터 요구사항
  • 모바일 앱 (데이터 절약)
  • 빠른 프론트엔드 개발
  • 다양한 클라이언트 (웹, 모바일, 데스크톱)

gRPC 적합한 경우:

  • 마이크로서비스 간 통신
  • 실시간 스트리밍
  • 고성능 요구
  • 내부 API (브라우저 불필요)

5. 선택 가이드

선택 플로우차트

flowchart TD
    A[API 설계 시작] --> B{브라우저 클라이언트?}
    B -->|아니오| C{성능 최우선?}
    C -->|예| D[gRPC]
    C -->|아니오| E[REST]
    
    B -->|예| F{복잡한 데이터 요구?}
    F -->|예| G[GraphQL]
    F -->|아니오| H{공개 API?}
    H -->|예| I[REST]
    H -->|아니오| J[GraphQL 또는 REST]

프로젝트별 권장

1. 스타트업 MVP

  • REST: 빠른 개발, 단순함
  • 예: Express + MongoDB

2. 모바일 앱 백엔드

  • GraphQL: 데이터 절약, 유연성
  • 예: Apollo Server + PostgreSQL

3. 마이크로서비스

  • gRPC: 고성능, 타입 안전
  • 예: gRPC + Kubernetes

4. 공개 API

  • REST: 표준, 도구 지원
  • 예: Stripe API, GitHub API

5. 실시간 앱

  • GraphQL Subscription 또는 gRPC Streaming
  • 예: 채팅, 알림, 대시보드

하이브리드 접근

많은 프로젝트에서 여러 API 스타일을 혼합합니다.

프론트엔드 (브라우저)
    ↓ GraphQL
API Gateway
    ↓ gRPC
마이크로서비스들

:

  • 외부 클라이언트: REST 또는 GraphQL
  • 내부 서비스: gRPC
  • 실시간 기능: WebSocket 또는 GraphQL Subscription

6. 정리

핵심 요약

REST:

  • HTTP 기반, 자원 중심
  • 단순하고 캐싱 우수
  • 공개 API에 적합

GraphQL:

  • 쿼리 언어, 클라이언트 중심
  • 정확한 데이터 요청
  • 복잡한 데이터 요구사항에 적합

gRPC:

  • Binary 프로토콜, 고성능
  • 타입 안전, 스트리밍
  • 마이크로서비스에 적합

선택 기준

우선순위선택
단순함REST
유연성GraphQL
성능gRPC
공개 APIREST
모바일GraphQL
마이크로서비스gRPC

다음 단계

각 API 스타일의 자세한 구현 방법은 아래 글을 참고하세요:

  • Node.js REST API 구축
  • GraphQL 서버 구축
  • C++ gRPC 가이드

관련 주제:

  • API 인증 전략
  • API 버전 관리
  • 마이크로서비스 아키텍처

추가 학습 자료

공식 문서: