REST API 1일차 : REST API란? REST 아키텍처 6가지 원칙 완벽 가이드

REST API란? REST 아키텍처 스타일의 6가지 원칙 완벽 가이드

REST API 개념도

목차

  1. REST API란 무엇인가?
  2. REST의 탄생 배경
  3. REST 아키텍처의 6가지 핵심 원칙
  4. REST API 설계 모범 사례
  5. REST API의 장단점
  6. 결론

REST API란 무엇인가?

REST API(REpresentational State Transfer API)는 웹 서비스를 구축하기 위한 아키텍처 스타일이자 설계 원칙입니다. REST는 분산 하이퍼미디어 시스템(웹)을 위한 소프트웨어 아키텍처로, HTTP 프로토콜을 기반으로 클라이언트와 서버 간의 통신을 정의합니다.

REST API는 리소스(Resource) 중심으로 설계되며, 각 리소스는 고유한 **URI(Uniform Resource Identifier)**를 통해 식별됩니다. 이를 통해 애플리케이션, 웹 서비스, 데이터베이스 간의 데이터 교환을 효율적으로 수행할 수 있습니다.

REST API의 핵심 구성 요소

  1. 리소스(Resources): API를 통해 접근할 수 있는 모든 데이터나 서비스 (/users, /products 등)
  2. HTTP 메서드(Methods): 리소스에 대한 작업을 정의 (GET, POST, PUT, DELETE 등)
  3. 표현(Representations): 데이터 교환 형식 (주로 JSON 또는 XML)
  4. 상태 코드(Status Codes): 요청 결과를 나타내는 HTTP 상태 코드 (200, 404, 500 등)

REST의 탄생 배경

REST는 2000년 컴퓨터 과학자 Roy Fielding 박사가 자신의 박사 논문에서 처음 제안한 개념입니다. 그는 웹의 장점을 극대화하면서도 확장 가능하고 단순한 아키텍처를 만들고자 했습니다.

REST가 등장하기 전에는 SOAP(Simple Object Access Protocol)과 같은 복잡한 프로토콜이 주로 사용되었습니다. 하지만 REST는 HTTP의 기존 인프라를 그대로 활용하면서도 더욱 단순하고 유연한 접근 방식을 제공했습니다.

REST가 인기 있는 이유

  • 단순성: HTTP 메서드를 그대로 사용하여 학습 곡선이 낮음
  • 확장성: 무상태(Stateless) 특성으로 서버 확장이 용이함
  • 유연성: 다양한 데이터 형식 지원 (JSON, XML, HTML 등)
  • 성능: 캐싱 기능을 통한 성능 최적화
  • 독립성: 클라이언트와 서버의 독립적인 개발 가능

REST 아키텍처의 6가지 핵심 원칙

Roy Fielding이 정의한 REST 아키텍처는 **6가지 제약 조건(Architectural Constraints)**을 따라야 합니다. 이 원칙들을 준수할 때 비로소 진정한 RESTful API라고 할 수 있습니다.

1. 균일한 인터페이스 (Uniform Interface) 🎯

가장 중요한 REST의 핵심 원칙으로, API의 일관성을 보장합니다.

4가지 하위 제약 조건:

① 리소스 식별 (Resource Identification)

  • 모든 리소스는 고유한 URI로 식별되어야 합니다
  • 예: /users/123, /products/456

② 표현을 통한 리소스 조작 (Manipulation through Representations)

  • 클라이언트는 리소스의 표현(JSON, XML 등)을 받아 조작합니다
  • 서버는 실제 데이터베이스가 아닌 표현을 전달합니다

③ 자기 서술적 메시지 (Self-descriptive Messages)

  • 각 메시지는 자신을 어떻게 처리해야 하는지 충분한 정보를 포함해야 합니다
  • Content-Type 헤더, HTTP 메서드 등을 통해 명확히 표현

④ HATEOAS (Hypermedia As The Engine Of Application State)

  • 응답에 관련 리소스의 링크를 포함하여 클라이언트가 다음 행동을 선택할 수 있게 합니다
{
  "id": 123,
  "name": "홍길동",
  "email": "hong@example.com",
  "_links": {
    "self": { "href": "/users/123" },
    "posts": { "href": "/users/123/posts" },
    "friends": { "href": "/users/123/friends" }
  }
}

장점:

  • API 사용자가 한 엔드포인트만 알아도 전체 API를 탐색 가능
  • 클라이언트와 서버의 독립적인 발전 가능
  • 문서화 부담 감소

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

클라이언트와 서버는 완전히 독립적이어야 하며, 각자의 역할에만 집중해야 합니다.

원칙:

  • 클라이언트: 사용자 인터페이스와 사용자 경험에만 집중
  • 서버: 데이터 저장, 비즈니스 로직, 보안에만 집중
  • 통신: HTTP를 통해서만 상호작용
[클라이언트 (UI/UX)] ←→ HTTP 통신 ←→ [서버 (데이터/로직)]

장점:

  • 클라이언트와 서버를 독립적으로 개발 및 배포 가능
  • 팀 분리: 프론트엔드와 백엔드 팀이 병렬로 작업 가능
  • 플랫폼 독립성: 웹, 모바일, IoT 등 다양한 클라이언트 지원

실무 예시:

모바일 앱 (iOS/Android) ─┐
웹 애플리케이션 (React)  ├─→ [동일한 REST API 서버]
데스크톱 앱 (Electron)  ─┘

3. 무상태 (Stateless) 📊

서버는 클라이언트의 상태 정보를 저장하지 않습니다. 각 요청은 독립적이며 완전한 정보를 포함해야 합니다.

핵심 개념:

  • 서버는 이전 요청을 기억하지 않습니다
  • 모든 요청에는 필요한 모든 정보(인증 토큰, 파라미터 등)가 포함되어야 합니다
  • 세션 상태는 클라이언트가 관리합니다

❌ 상태를 유지하는 방식 (비추천):

1. 클라이언트: POST /login (username, password)
2. 서버: 세션 생성 및 서버에 저장 → session_id 반환
3. 클라이언트: GET /users (session_id만 전송)
4. 서버: session_id로 세션 정보 조회 후 응답

✅ 무상태 방식 (REST 원칙):

1. 클라이언트: POST /login (username, password)
2. 서버: JWT 토큰 생성 및 반환
3. 클라이언트: GET /users (Authorization: Bearer {JWT})
4. 서버: JWT 토큰 검증 후 응답 (서버에 상태 저장 없음)

장점:

  • 확장성: 로드 밸런서가 요청을 어떤 서버로든 전달 가능
  • 신뢰성: 서버 장애 시 세션 손실 없음
  • 단순성: 서버 구현이 간단해짐

실무 적용:

GET /api/users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Accept: application/json

4. 캐시 가능 (Cacheable) 💾

응답 데이터는 캐시 가능 여부를 명시해야 하며, 캐시를 통해 성능을 향상시킬 수 있어야 합니다.

HTTP 캐시 헤더 활용:

Cache-Control 헤더:

HTTP/1.1 200 OK
Cache-Control: max-age=3600, public
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 11 Dec 2024 12:00:00 GMT
Content-Type: application/json

{
  "id": 123,
  "name": "상품명"
}

캐시 전략:

  • max-age=3600: 3600초(1시간) 동안 캐시 유효
  • public: 모든 캐시(브라우저, CDN)에서 저장 가능
  • private: 브라우저에만 캐시 가능 (민감한 데이터)
  • no-cache: 캐시 사용 전 서버에 재검증 필요
  • no-store: 절대 캐시하지 않음

ETag를 활용한 조건부 요청:

# 클라이언트 요청
GET /api/products/123 HTTP/1.1
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

# 서버 응답 (변경 없음)
HTTP/1.1 304 Not Modified

장점:

  • 네트워크 대역폭 절약
  • 서버 부하 감소
  • 응답 속도 향상
  • 사용자 경험 개선

5. 계층화 시스템 (Layered System) 🏗️

클라이언트는 직접 연결된 계층만 알면 되며, 중간 계층의 존재를 알 필요가 없습니다.

계층 구조 예시:

[클라이언트]
    ↕
[CDN / 캐시 서버]
    ↕
[API 게이트웨이]
    ↕
[로드 밸런서]
    ↕
[REST API 서버 1] [REST API 서버 2] [REST API 서버 3]
    ↕
[데이터베이스]

각 계층의 역할:

  • CDN: 정적 리소스 캐싱
  • API 게이트웨이: 라우팅, 인증, Rate Limiting
  • 로드 밸런서: 트래픽 분산
  • REST API 서버: 비즈니스 로직 처리
  • 데이터베이스: 데이터 저장

장점:

  • 보안 강화: 중간 계층에서 인증/암호화 처리
  • 확장성 향상: 계층별 독립적 확장 가능
  • 유연성: 계층 추가/제거 용이
  • 레거시 시스템 통합 용이

실무 예시:

사용자 요청 → Cloudflare (DDoS 방어)
          → AWS API Gateway (인증)
          → Application Load Balancer (분산)
          → EC2 인스턴스 (API 서버)
          → RDS (데이터베이스)

6. 코드 온 디맨드 (Code on Demand) - 선택사항 ⚙️

서버는 클라이언트가 실행할 수 있는 코드를 전송할 수 있습니다. (유일한 선택적 제약 조건)

실제 적용 사례:

① JavaScript 위젯:

<!-- 클라이언트 페이지 -->
<div id="chat-widget"></div>
<script src="https://api.example.com/chat-widget.js"></script>

② 동적 폼 검증:

{
  "form": {
    "fields": [...],
    "validation": "function validate(data) { ... }"
  }
}

③ 플러그인 시스템:

// 서버에서 전송한 코드를 동적으로 로드
fetch('/api/plugins/latest.js')
  .then(response => response.text())
  .then(code => eval(code)); // 실무에서는 더 안전한 방법 사용

장점:

  • 클라이언트 기능을 동적으로 확장 가능
  • 초기 다운로드 크기 감소
  • 서버 측에서 클라이언트 로직 제어 가능

주의사항:

  • 보안 위험: XSS 공격 가능성
  • 실무에서는 신중하게 사용 (대부분 선택하지 않음)
  • CSP(Content Security Policy) 적용 필수

REST API 설계 모범 사례

이론을 넘어 실무에서 바로 적용 가능한 REST API 설계 가이드를 소개합니다.

1. 리소스 중심의 URI 설계 🎯

기본 원칙:

  • ✅ 명사 사용, 동사 사용 금지
  • ✅ 복수형 사용 (/users, /products)
  • ✅ 소문자 사용
  • ✅ 하이픈(-) 사용, 언더스코어(_) 지양
  • ✅ 계층 구조 표현 시 / 사용

좋은 예시:

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

나쁜 예시:

❌ GET  /api/getUsers              # 동사 사용
❌ POST /api/createUser            # 동사 사용
❌ GET  /api/user/123              # 단수형
❌ GET  /api/User_Posts            # 대문자, 언더스코어
❌ GET  /api/users/123/delete      # URI에 동작 포함

2. HTTP 메서드의 올바른 사용 📋

HTTP 메서드 용도 멱등성* 안전성** 예시
GET 리소스 조회 GET /users/123
POST 새 리소스 생성 POST /users
PUT 리소스 전체 교체 PUT /users/123
PATCH 리소스 부분 수정 PATCH /users/123
DELETE 리소스 삭제 DELETE /users/123
HEAD 메타데이터만 조회 HEAD /users/123
OPTIONS 지원하는 메서드 확인 OPTIONS /users

용어 설명:

  • *멱등성(Idempotent): 동일한 요청을 여러 번 보내도 결과가 같음
  • **안전성(Safe): 서버 상태를 변경하지 않음

실무 예제:

# POST - 새 사용자 생성 (매번 새로운 리소스 생성)
POST /api/users HTTP/1.1
Content-Type: application/json

{
  "name": "홍길동",
  "email": "hong@example.com"
}

# 응답
HTTP/1.1 201 Created
Location: /api/users/124
Content-Type: application/json

{
  "id": 124,
  "name": "홍길동",
  "email": "hong@example.com",
  "created_at": "2024-12-11T10:30:00Z"
}
# PUT - 전체 교체 (멱등성: 같은 요청 반복해도 결과 동일)
PUT /api/users/123 HTTP/1.1
Content-Type: application/json

{
  "name": "김철수",
  "email": "kim@example.com",
  "phone": "010-1234-5678"
}
# PATCH - 부분 수정 (이메일만 변경)
PATCH /api/users/123 HTTP/1.1
Content-Type: application/json

{
  "email": "newemail@example.com"
}

3. HTTP 상태 코드 활용 📊

2xx - 성공 (Success)

코드 의미 사용 시점
200 OK 요청 성공 GET, PUT, PATCH 성공 시
201 Created 생성 성공 POST로 리소스 생성 성공 시
204 No Content 성공 (응답 본문 없음) DELETE 성공 시

4xx - 클라이언트 오류 (Client Errors)

코드 의미 사용 시점
400 Bad Request 잘못된 요청 유효성 검증 실패
401 Unauthorized 인증 필요 로그인 필요
403 Forbidden 권한 없음 인증은 되었으나 권한 부족
404 Not Found 리소스 없음 존재하지 않는 리소스 요청
409 Conflict 충돌 중복된 데이터 생성 시도
429 Too Many Requests 요청 과다 Rate Limit 초과

5xx - 서버 오류 (Server Errors)

코드 의미 사용 시점
500 Internal Server Error 서버 내부 오류 예상치 못한 서버 에러
503 Service Unavailable 서비스 불가 서버 점검 중

실무 응답 예시:

// 200 OK - 조회 성공
{
  "status": "success",
  "data": {
    "id": 123,
    "name": "홍길동"
  }
}
// 400 Bad Request - 유효성 검증 실패
{
  "status": "error",
  "code": "VALIDATION_ERROR",
  "message": "입력값이 올바르지 않습니다",
  "errors": [
    {
      "field": "email",
      "message": "유효한 이메일 주소를 입력해주세요"
    },
    {
      "field": "password",
      "message": "비밀번호는 최소 8자 이상이어야 합니다"
    }
  ]
}
// 404 Not Found - 리소스 없음
{
  "status": "error",
  "code": "RESOURCE_NOT_FOUND",
  "message": "요청한 사용자를 찾을 수 없습니다",
  "details": "User ID 999 does not exist"
}

4. 버전 관리 전략 🔄

API는 시간이 지나면서 변경되므로 버전 관리가 필수적입니다.

① URI에 버전 포함 (가장 일반적)

https://api.example.com/v1/users
https://api.example.com/v2/users
https://api.example.com/v3/users

장점:

  • 직관적이고 명확함
  • 브라우저에서 바로 테스트 가능
  • 캐싱 전략이 명확함

② 헤더를 통한 버전 지정

GET /api/users HTTP/1.1
Host: api.example.com
Accept: application/vnd.myapp.v2+json

장점:

  • URI가 깔끔함
  • RESTful 원칙에 더 부합

③ 쿼리 파라미터 (비추천)

https://api.example.com/users?version=2

버전 관리 원칙:

  • 하위 호환성 유지
  • Deprecated 정책 명확히 (예: 6개월 후 종료)
  • 변경 사항 문서화
  • 주요 변경 시에만 버전 업

5. 필터링, 정렬, 페이징, 검색 🔍

대량의 데이터를 효율적으로 제공하기 위한 필수 기능입니다.

필터링 (Filtering):

GET /api/products?category=electronics&price_min=10000&price_max=50000
GET /api/users?status=active&role=admin
GET /api/posts?author=123&published=true

정렬 (Sorting):

GET /api/users?sort=name:asc               # 이름 오름차순
GET /api/users?sort=created_at:desc        # 생성일 내림차순
GET /api/users?sort=name:asc,created_at:desc  # 복수 정렬

페이징 (Pagination):

방법 1: Offset 기반 (가장 일반적)

GET /api/users?page=2&per_page=20

응답:

{
  "data": [...],
  "pagination": {
    "current_page": 2,
    "per_page": 20,
    "total_pages": 50,
    "total_items": 1000,
    "has_next": true,
    "has_prev": true
  }
}

방법 2: Cursor 기반 (실시간 데이터에 적합)

GET /api/posts?cursor=eyJpZCI6MTIzfQ&limit=20

응답:

{
  "data": [...],
  "pagination": {
    "next_cursor": "eyJpZCI6MTQzfQ",
    "has_more": true
  }
}

검색 (Searching):

GET /api/products?q=노트북&category=electronics
GET /api/users?search=김철수

복합 쿼리 예시:

GET /api/products?
  category=electronics
  &price_min=500000
  &price_max=1500000
  &brand=samsung,lg
  &sort=price:asc
  &page=1
  &per_page=20
  &q=노트북

6. 보안 강화 🔒

① HTTPS 사용 필수

✅ https://api.example.com/users
❌ http://api.example.com/users

② 인증 (Authentication)

JWT (JSON Web Token) 방식:

GET /api/users/me HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

③ Rate Limiting (속도 제한)

HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1639123456

429 응답:

{
  "status": "error",
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "API 호출 한도를 초과했습니다",
  "retry_after": 3600
}

④ CORS 설정

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

⑤ 입력 검증

  • SQL Injection 방지
  • XSS 공격 방어
  • 파라미터 타입 검증
  • 최대 요청 크기 제한

7. 문서화 📚

Swagger/OpenAPI 사용:

openapi: 3.0.0
info:
  title: User API
  version: 1.0.0
paths:
  /users:
    get:
      summary: 사용자 목록 조회
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
      responses:
        '200':
          description: 성공
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'

문서화에 포함할 내용:

  • 엔드포인트 목록
  • 요청/응답 예시
  • 인증 방법
  • 에러 코드 설명
  • Rate Limit 정책
  • 변경 이력

REST API의 장단점

장점 ✅

  1. 단순성과 명확성

    • HTTP 표준을 그대로 사용하여 학습 곡선이 낮음
    • 직관적인 리소스 중심 설계
  2. 확장성

    • 무상태 특성으로 서버 확장이 용이
    • 로드 밸런싱이 간단함
  3. 유연성

    • 다양한 데이터 형식 지원 (JSON, XML, HTML 등)
    • 플랫폼 독립적
  4. 캐싱

    • HTTP 캐싱 메커니즘 활용 가능
    • 성능 향상 및 서버 부하 감소
  5. 독립성

    • 클라이언트와 서버의 독립적인 개발 가능
    • 마이크로서비스 아키텍처에 적합

단점 ❌

  1. Over-fetching / Under-fetching

    • 필요한 것보다 많은 데이터를 받거나 (Over-fetching)
    • 여러 번 요청해야 하는 경우 발생 (Under-fetching)
    • 해결책: GraphQL 고려
  2. 표준의 부재

    • REST는 아키텍처 스타일이지 엄격한 표준이 아님
    • 구현 방식이 개발자마다 다를 수 있음
  3. 실시간 통신에 부적합

    • HTTP 요청-응답 모델의 한계
    • 해결책: WebSocket, Server-Sent Events 사용
  4. 복잡한 쿼리 처리

    • 복잡한 데이터 관계 처리 시 여러 요청 필요
    • 해결책: GraphQL 또는 전용 엔드포인트 설계
  5. 버전 관리의 어려움

    • API 변경 시 하위 호환성 유지가 어려움

REST API 대안 기술 🔄

1. GraphQL

  • 장점: 클라이언트가 필요한 데이터만 요청 가능
  • 단점: 학습 곡선, 캐싱 복잡도
  • 사용 시기: 복잡한 데이터 요구사항, 모바일 앱

2. gRPC

  • 장점: 고성능, 효율적인 직렬화
  • 단점: 브라우저 지원 제한
  • 사용 시기: 마이크로서비스 간 통신

3. WebSocket

  • 장점: 실시간 양방향 통신
  • 단점: 서버 리소스 소비
  • 사용 시기: 채팅, 게임, 실시간 알림

결론

REST API는 현대 웹 개발의 표준으로 자리 잡았습니다. Roy Fielding이 제시한 6가지 아키텍처 원칙을 이해하고 실무에 적용하면 확장 가능하고 유지보수하기 쉬운 API를 설계할 수 있습니다.

핵심 요약

6가지 REST 원칙:

  1. 균일한 인터페이스 (Uniform Interface)
  2. 클라이언트-서버 분리 (Client-Server)
  3. 무상태 (Stateless)
  4. 캐시 가능 (Cacheable)
  5. 계층화 시스템 (Layered System)
  6. 코드 온 디맨드 (Code on Demand) - 선택사항

설계 모범 사례:

  • 리소스 중심의 URI 설계
  • HTTP 메서드의 올바른 사용
  • 적절한 상태 코드 활용
  • 버전 관리
  • 보안 강화 (HTTPS, JWT, Rate Limiting)
  • 명확한 문서화

REST API는 모든 상황에 완벽한 솔루션은 아니지만, 대부분의 웹 서비스에서 최적의 선택입니다. 프로젝트의 요구사항에 따라 GraphQL, gRPC 등의 대안도 고려하되, REST의 원칙을 이해하는 것은 모든 API 설계의 기초가 됩니다.



Reactions

댓글 쓰기

0 댓글