API 설계 가이드 | REST vs GraphQL vs gRPC 완벽 비교
이 글의 핵심
API 설계 가이드입니다. REST, GraphQL, gRPC를 비교하고 프로젝트별 최적의 API 스타일 선택 기준을 제시합니다.
들어가며: API 설계의 중요성
”어떤 API 스타일을 선택해야 할까?”
백엔드 개발에서 API 설계는 가장 중요한 결정 중 하나입니다. REST, GraphQL, gRPC 중 무엇을 선택하느냐에 따라 개발 경험, 성능, 유지보수성이 크게 달라집니다.
이 글에서 다루는 것:
- REST, GraphQL, gRPC의 핵심 개념
- 각 API 스타일의 장단점
- 성능 비교
- 프로젝트별 선택 기준
목차
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. 비교 분석
종합 비교표
| 특징 | REST | GraphQL | gRPC |
|---|---|---|---|
| 프로토콜 | HTTP/1.1 | HTTP/1.1 | HTTP/2 |
| 데이터 형식 | JSON | JSON | Protobuf (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 |
| 공개 API | REST |
| 모바일 | GraphQL |
| 마이크로서비스 | gRPC |
다음 단계
각 API 스타일의 자세한 구현 방법은 아래 글을 참고하세요:
- Node.js REST API 구축
- GraphQL 서버 구축
- C++ gRPC 가이드
관련 주제:
- API 인증 전략
- API 버전 관리
- 마이크로서비스 아키텍처
추가 학습 자료
공식 문서: