REST(Representational State Transfer)는 2000년 Roy Fielding이 UC Irvine에서 발표한 박사 학위 논문 “Architectural Styles and the Design of Network-based Software Architectures"에서 처음 소개된 분산 하이퍼미디어 시스템을 위한 아키텍처 스타일로, HTTP 프로토콜의 주요 저자 중 한 명이었던 Fielding이 웹의 성공 요인을 분석하고 이를 체계적인 아키텍처 원칙으로 정리한 것이며, 현대 웹 API 설계의 사실상 표준으로 자리잡아 마이크로서비스 아키텍처, 모바일 애플리케이션, SPA(Single Page Application) 등 다양한 분산 시스템 간의 통신에 널리 활용되고 있다.

REST란?

REST(Representational State Transfer)는 네트워크 기반 소프트웨어 아키텍처 스타일로, 리소스를 URI로 식별하고 HTTP 메서드를 통해 상태를 전송(transfer)하는 방식이다. “Representational"은 리소스의 표현(JSON, XML 등)을 의미하며, “State Transfer"는 클라이언트와 서버 간에 리소스 상태가 전송되는 것을 의미한다.

REST의 역사와 배경

웹 서비스의 진화

REST가 등장하기 전 웹 서비스는 주로 SOAP(Simple Object Access Protocol)과 XML-RPC를 사용했으며, 이 프로토콜들은 복잡한 XML 기반 메시지 포맷, 엄격한 타입 시스템, WSDL(Web Services Description Language)을 통한 서비스 정의를 요구하여 구현과 유지보수가 어려웠다. SOAP은 엔터프라이즈 환경에서 트랜잭션, 보안, 메시지 신뢰성 등을 지원하는 장점이 있었지만, 단순한 데이터 조회에도 복잡한 XML 봉투(envelope)를 사용해야 하는 오버헤드가 있었다.

Roy Fielding의 기여

Roy Fielding은 HTTP/1.0과 HTTP/1.1 명세의 주요 저자이자 Apache HTTP Server 프로젝트의 공동 창립자로, 웹의 아키텍처를 깊이 이해하고 있었다. 그는 박사 학위 논문에서 웹이 성공할 수 있었던 이유를 분석하고, 이를 REST라는 아키텍처 스타일로 체계화했다. REST는 기존 웹 인프라(HTTP, URI, 캐시, 프록시)를 최대한 활용하면서 단순하고 확장 가능한 시스템을 구축할 수 있도록 설계되었다.

REST API의 확산

REST가 실제로 널리 채택되기 시작한 것은 2000년대 중반 이후로, Flickr(2004년), Amazon Web Services(2006년), Twitter(2006년) 등이 REST API를 공개하면서 웹 API의 표준으로 자리잡았다. JSON 포맷의 부상과 함께 REST는 SOAP의 복잡성을 피하고 싶어하는 개발자들에게 매력적인 대안이 되었으며, Ajax와 모바일 앱의 등장으로 그 인기가 더욱 높아졌다.

REST의 6가지 제약 조건

REST는 특정 프로토콜이나 기술이 아닌 아키텍처 스타일이며, 다음 6가지 제약 조건을 따르는 시스템을 RESTful하다고 한다.

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

클라이언트와 서버의 관심사를 분리하여 독립적인 발전을 가능하게 하는 제약 조건으로, 서버는 데이터 저장과 비즈니스 로직을 담당하고 클라이언트는 사용자 인터페이스를 담당함으로써 각각 독립적으로 개발, 배포, 확장할 수 있다.

관심사 분리의 이점

클라이언트-서버 분리는 서버의 단순화를 통해 확장성을 향상시키고, 다양한 클라이언트(웹, 모바일, IoT)가 동일한 서버 API를 사용할 수 있게 하며, 각 컴포넌트의 독립적인 발전을 가능하게 한다.

2. 무상태 (Stateless)

각 요청은 독립적이며 서버는 클라이언트의 세션 상태를 저장하지 않아야 하는 제약 조건으로, 요청에 필요한 모든 정보(인증 토큰, 컨텍스트 데이터)는 요청 자체에 포함되어야 한다.

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

무상태의 장점:

  • 확장성: 서버가 세션을 유지하지 않으므로 수평적 확장이 용이
  • 신뢰성: 서버 장애 시 다른 서버가 요청을 처리할 수 있음
  • 단순성: 서버 구현이 단순해지고 리소스 효율성 향상

무상태의 단점:

  • 매 요청마다 인증 정보를 포함해야 하므로 요청 크기 증가
  • 클라이언트 측에서 상태 관리 책임이 증가

3. 캐시 가능 (Cacheable)

응답은 캐시 가능 여부를 명시적으로 표시해야 하며, 캐시 가능한 응답은 클라이언트나 중간 캐시(CDN, 프록시)에서 재사용될 수 있어 서버 부하를 줄이고 응답 시간을 단축한다.

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=3600, must-revalidate
ETag: "a1b2c3d4e5f6"
Last-Modified: Sat, 20 Jul 2024 10:00:00 GMT

{"id": 123, "name": "홍길동", "email": "[email protected]"}

캐시 관련 헤더:

헤더설명예시
Cache-Control캐시 정책 지정max-age=3600, private
ETag리소스 버전 식별자"a1b2c3d4e5f6"
Last-Modified마지막 수정 시간Sat, 20 Jul 2024 10:00:00 GMT
Expires캐시 만료 시간 (레거시)Sun, 21 Jul 2024 10:00:00 GMT

4. 계층화 시스템 (Layered System)

클라이언트는 직접 연결된 계층만 알면 되며 그 너머의 시스템 구조를 알 필요가 없는 제약 조건으로, API Gateway, 로드 밸런서, 캐시 서버, CDN 등 중간 계층을 추가하여 시스템의 확장성, 보안성, 성능을 향상시킬 수 있다.

[클라이언트] → [CDN] → [로드 밸런서] → [API Gateway] → [마이크로서비스]

계층화의 이점:

  • 보안: 방화벽, 인증 서버 등을 중간 계층에 배치
  • 확장성: 로드 밸런서를 통한 수평적 확장
  • 성능: CDN과 캐시 서버를 통한 응답 시간 단축
  • 유연성: 클라이언트에 영향 없이 내부 구조 변경 가능

5. 인터페이스 일관성 (Uniform Interface)

REST의 가장 중요한 제약 조건으로, 일관된 인터페이스를 통해 시스템 아키텍처를 단순화하고 클라이언트-서버 간 결합도를 낮춘다.

인터페이스 일관성의 4가지 요소

  1. 리소스 식별: URI를 통해 리소스를 고유하게 식별
  2. 표현을 통한 리소스 조작: JSON, XML 등의 표현을 통해 리소스 상태 전송
  3. 자기 서술적 메시지: 메시지 자체에 처리 방법에 대한 정보 포함
  4. HATEOAS: 응답에 다음 가능한 행위에 대한 하이퍼링크 포함

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

서버가 클라이언트에게 실행 가능한 코드(JavaScript 등)를 전송하여 클라이언트 기능을 동적으로 확장할 수 있는 선택적 제약 조건이다. 웹 브라우저에서 JavaScript를 다운로드받아 실행하는 것이 대표적인 예시이며, 이 제약 조건은 REST의 유일한 선택 사항이다.

Richardson 성숙도 모델

Leonard Richardson은 2008년 QCon 컨퍼런스에서 REST API의 성숙도를 4단계로 분류하는 모델을 발표했으며, 이 모델은 API가 얼마나 RESTful한지를 평가하는 기준으로 널리 사용된다.

수준이름특징예시
Level 0The Swamp of POX단일 URI, 단일 HTTP 메서드 (주로 POST)SOAP, XML-RPC
Level 1Resources개별 리소스에 URI 부여, 단일 메서드/users/123에 모든 작업을 POST
Level 2HTTP VerbsHTTP 메서드와 상태 코드 적절히 활용GET, POST, PUT, DELETE 구분
Level 3Hypermedia ControlsHATEOAS 구현, 응답에 링크 포함_links 필드에 관련 리소스 링크

RESTful API vs REST-like API

RESTful API는 HATEOAS를 포함한 모든 REST 제약 조건을 완벽히 준수하는 API를 의미하며, REST-like API는 HTTP 메서드와 리소스 기반 URL을 사용하지만 HATEOAS 등 일부 제약 조건을 준수하지 않는 API를 의미한다. 현실에서 대부분의 API는 Richardson 성숙도 모델 Level 2에 해당하는 REST-like API이며, Level 3의 완전한 RESTful API를 구현하는 경우는 드물다.

REST API 설계 원칙

1. 리소스 중심 URL 설계

REST API의 URL은 동작(action)이 아닌 리소스(resource)를 나타내야 하며, 명사를 사용하고 복수형을 권장한다.

좋은 예시:

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

나쁜 예시:

GET    /getUsers
POST   /createUser
POST   /deleteUser?id=123
GET    /user/123/get

계층적 리소스 표현:

GET /users/123/posts                    # 사용자 123의 게시글 목록
GET /users/123/posts/456               # 사용자 123의 게시글 456
GET /users/123/posts/456/comments      # 게시글 456의 댓글 목록

URL 설계 규칙:

  • 소문자 사용
  • 단어 구분은 하이픈(-) 사용 (언더스코어 지양)
  • 파일 확장자 포함하지 않음
  • 마지막 슬래시(/) 포함하지 않음
  • 3단계 이상의 중첩은 피함

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

각 HTTP 메서드는 고유한 의미를 가지며, 안전성(safe)과 멱등성(idempotent) 속성을 이해하고 올바르게 사용해야 한다.

메서드용도안전멱등요청 본문응답 본문
GET리소스 조회OOXO
POST리소스 생성XXOO
PUT리소스 전체 수정XOOO
PATCH리소스 부분 수정XXOO
DELETE리소스 삭제XOXX/O
HEAD헤더만 조회OOXX
OPTIONS지원 메서드 조회OOXO

안전성과 멱등성

안전(Safe): 요청이 서버 상태를 변경하지 않음 (GET, HEAD, OPTIONS) 멱등(Idempotent): 동일한 요청을 여러 번 실행해도 결과가 같음 (GET, PUT, DELETE)

3. HTTP 상태 코드의 적절한 사용

HTTP 상태 코드를 통해 요청 처리 결과를 명확하게 전달해야 한다.

성공 응답 (2xx):

코드의미사용 상황
200 OK성공GET, PUT, PATCH 성공 시
201 Created생성 성공POST로 리소스 생성 시 (Location 헤더 필수)
204 No Content성공, 본문 없음DELETE 성공, PUT으로 업데이트만
206 Partial Content부분 콘텐츠Range 요청 처리 시

클라이언트 에러 (4xx):

코드의미사용 상황
400 Bad Request잘못된 요청요청 구문 오류, 유효하지 않은 데이터
401 Unauthorized인증 필요인증 정보 없음 또는 유효하지 않음
403 Forbidden권한 없음인증됨, 권한 부족
404 Not Found리소스 없음존재하지 않는 리소스
409 Conflict충돌리소스 상태 충돌
422 Unprocessable Entity처리 불가문법 올바름, 의미적 오류
429 Too Many Requests요청 제한 초과Rate limiting

서버 에러 (5xx):

코드의미사용 상황
500 Internal Server Error서버 오류예상하지 못한 서버 에러
502 Bad Gateway게이트웨이 오류업스트림 서버 응답 오류
503 Service Unavailable서비스 불가서버 과부하, 유지보수
504 Gateway Timeout게이트웨이 시간 초과업스트림 응답 시간 초과

4. 버전 관리 전략

API는 시간이 지남에 따라 변경될 수 있으므로 하위 호환성을 유지하면서 새로운 기능을 추가하기 위한 버전 관리 전략이 필요하다.

URL 경로 버전 관리:

GET /api/v1/users
GET /api/v2/users

장점: 명확하고 직관적, 브라우저에서 테스트 용이, 캐싱 간단 단점: URL이 길어짐, REST 원칙 논란 (URI는 리소스를 식별해야 함)

헤더 버전 관리:

GET /api/users HTTP/1.1
Accept: application/vnd.myapp.v1+json

장점: URL이 깔끔, REST 원칙에 부합, 콘텐츠 협상 활용 단점: 브라우저 테스트 어려움, 캐싱 구성 복잡

쿼리 파라미터 버전 관리:

GET /api/users?version=1

장점: 구현 간단, 브라우저 테스트 용이 단점: 버전이 선택적으로 보일 수 있음, 캐싱 복잡

실무 권장: URL 경로 버전 관리가 가장 널리 사용되며, 대부분의 대형 API(Google, Facebook, Twitter)에서 채택하고 있다.

5. 페이지네이션 전략

대량의 데이터를 반환할 때는 페이지네이션을 통해 응답 크기를 제한해야 한다.

오프셋 기반 페이지네이션:

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

커서 기반 페이지네이션:

GET /api/users?cursor=eyJpZCI6MTAwfQ==&limit=20
방식장점단점적합한 상황
오프셋 기반구현 간단, 특정 페이지 직접 이동 가능대량 데이터에서 성능 저하, 데이터 변경 시 중복/누락소규모 데이터, 페이지 번호 필요
커서 기반대량 데이터에서도 일관된 성능, 실시간 데이터에 안정적특정 페이지 직접 이동 불가, 구현 복잡대규모 데이터, 무한 스크롤, 실시간 피드

페이지네이션 응답 예시:

{
  "data": [...],
  "pagination": {
    "total": 1000,
    "page": 2,
    "per_page": 20,
    "total_pages": 50,
    "next_cursor": "eyJpZCI6MTIwfQ=="
  },
  "_links": {
    "self": "/api/users?page=2",
    "next": "/api/users?page=3",
    "prev": "/api/users?page=1",
    "first": "/api/users?page=1",
    "last": "/api/users?page=50"
  }
}

6. 필터링, 정렬, 검색

쿼리 파라미터를 통해 필터링, 정렬, 검색 기능을 제공한다.

필터링:

GET /api/users?status=active&role=admin
GET /api/posts?created_after=2024-01-01&tag=javascript

정렬:

GET /api/users?sort=created_at           # 오름차순
GET /api/users?sort=-created_at          # 내림차순 (- 접두사)
GET /api/users?sort=name,-created_at     # 다중 정렬

검색:

GET /api/users?q=홍길동
GET /api/posts?search=REST+API

필드 선택 (Sparse Fieldsets):

GET /api/users?fields=id,name,email
GET /api/posts?fields[posts]=title,content&fields[author]=name

7. HATEOAS (Hypermedia as the Engine of Application State)

응답에 관련 리소스의 링크를 포함하여 클라이언트가 다음 가능한 행위를 발견할 수 있게 하는 REST의 핵심 원칙이다.

{
  "id": 123,
  "name": "홍길동",
  "email": "[email protected]",
  "status": "active",
  "_links": {
    "self": {"href": "/api/users/123"},
    "posts": {"href": "/api/users/123/posts"},
    "deactivate": {"href": "/api/users/123/deactivate", "method": "POST"},
    "delete": {"href": "/api/users/123", "method": "DELETE"}
  }
}

HATEOAS의 이점

클라이언트가 하드코딩된 URL 대신 응답의 링크를 따라가므로, 서버가 URL 구조를 변경해도 클라이언트 수정 없이 작동할 수 있다. 그러나 구현 복잡성이 증가하고 대부분의 클라이언트가 이를 활용하지 않아 실무에서는 선택적으로 구현한다.

8. 에러 처리

일관된 에러 응답 형식을 사용하여 개발자 경험을 향상시킨다.

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "요청 유효성 검증 실패",
    "details": [
      {
        "field": "email",
        "message": "유효하지 않은 이메일 형식입니다",
        "code": "INVALID_FORMAT"
      },
      {
        "field": "age",
        "message": "나이는 양수여야 합니다",
        "code": "INVALID_VALUE"
      }
    ],
    "timestamp": "2024-07-20T16:23:17Z",
    "path": "/api/users",
    "request_id": "req_abc123"
  }
}

에러 응답 필드:

  • code: 프로그래밍 방식 처리를 위한 에러 코드
  • message: 사람이 읽을 수 있는 에러 설명
  • details: 필드별 상세 에러 정보
  • request_id: 로그 추적을 위한 고유 식별자

보안 고려사항

인증 방식

방식특징적합한 상황
API Key간단, 요청 헤더나 쿼리에 포함서버 간 통신, 내부 시스템
Basic Auth사용자명:비밀번호 Base64 인코딩간단한 인증, HTTPS 필수
Bearer Token (JWT)무상태, 자체 포함된 토큰일반적인 사용자 인증
OAuth 2.0위임된 권한 부여제3자 앱 통합, 소셜 로그인

Rate Limiting

API 남용을 방지하고 서버 리소스를 보호하기 위해 요청 제한을 설정한다.

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1721484600
Retry-After: 3600
Content-Type: application/json

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "요청 제한을 초과했습니다. 1시간 후 다시 시도해주세요."
  }
}

Rate Limiting 알고리즘:

  • Token Bucket: 버스트 트래픽 허용, 구현 복잡
  • Sliding Window: 정확한 제한, Redis 활용
  • Fixed Window: 구현 간단, 경계 시점에 버스트 가능

CORS (Cross-Origin Resource Sharing)

다른 도메인에서 API에 접근할 수 있도록 CORS 헤더를 설정한다.

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

API 문서화

OpenAPI (Swagger)

OpenAPI Specification은 REST API를 정의하기 위한 업계 표준으로, YAML 또는 JSON 형식으로 API를 기술하고 Swagger UI를 통해 인터랙티브한 문서를 제공할 수 있다.

openapi: 3.0.0
info:
  title: 사용자 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: array
                items:
                  $ref: '#/components/schemas/User'

문서화 도구 선택:

  • 공개 API: OpenAPI/Swagger (표준, 도구 생태계 풍부)
  • 내부 API: Postman Collections (팀 협업 용이)
  • 빠른 프로토타입: API Blueprint (Markdown 기반)

REST의 한계와 대안

REST는 범용적이고 널리 채택된 아키텍처 스타일이지만, 모든 상황에 최적인 것은 아니다.

기술장점단점적합한 상황
REST단순, 표준화, 캐싱 용이Over-fetching, 다중 요청 필요일반적인 CRUD, 공개 API
GraphQL정확한 데이터 요청, 단일 엔드포인트캐싱 복잡, 학습 곡선복잡한 데이터 요구, 모바일 앱
gRPC고성능, 강타입, 양방향 스트리밍브라우저 지원 제한, 디버깅 어려움마이크로서비스 간 통신
WebSocket실시간 양방향 통신, 낮은 지연무상태 아님, 로드 밸런싱 복잡채팅, 실시간 알림, 게임

결론

REST API 설계는 단순히 기술적 결정을 넘어 사용자 경험과 비즈니스 요구사항을 모두 고려해야 하는 복잡한 과정으로, REST의 6가지 제약 조건을 이해하고 Richardson 성숙도 모델을 참고하되 맹목적으로 따르기보다는 각 설계 결정의 트레이드오프를 파악하고 프로젝트 특성에 맞게 유연하게 적용하는 것이 중요하다. 완벽한 RESTful API(Level 3)를 구현하는 것보다 실용적이고 일관성 있는 REST-like API(Level 2)를 목표로 하면서 충실한 문서화와 개발자 경험을 중시하는 것이 성공적인 API 설계의 핵심이다.

참고 자료