본문으로 건너뛰기
yutils

REST 는 어떻게 동작할까?

Roy Fielding 이 원래 정의한 REST — resource·representation·stateless·uniform interface, Richardson Maturity level 0-3, HATEOAS, 그리고 'REST' 라고 불리는 대부분의 API 가 사실 'HTTP 위의 JSON' 일 뿐인 이유.

약 9분 읽기

"REST API" 라는 말은 거의 모든 백엔드 면접·문서·블로그에 등장한다. 그런데 막상 "REST 가 뭐냐" 물으면 답이 갈린다 — "HTTP + JSON", "URL 에 명사 쓰는 거", "GET/POST 잘 쓰는 거". 셋 다 틀린 답에 가깝다. REST 는 2000 년 Roy Fielding 의 박사 논문에서 정의된 한 아키텍처 스타일이고, 우리가 매일 부르는 "REST API" 의 90% 는 엄밀히 보면 REST 가 아니다. 이 가이드는 진짜 REST 의 원칙, 흔한 오해, Richardson Maturity Model, HATEOAS, 그리고 실전에서 "REST" 라 부르는 것의 정체를 정리한다.

Fielding 의 REST — 6 가지 제약

REST 는 protocol 도 spec 도 아니고 분산 hypermedia 시스템의 아키텍처 스타일. 6 가지 제약을 모두 만족할 때 "RESTful" 이라고 부른다.

1. Client-Server      — 관심사 분리 (UI 와 storage 분리)
2. Stateless          — 모든 요청에 필요한 모든 정보 포함, 서버 세션 X
3. Cacheable          — 응답이 cache 가능한지 명시
4. Uniform Interface  — 인터페이스가 표준화 (이게 REST 의 핵심)
5. Layered System     — proxy / gateway / LB 가 중간에 있어도 동작
6. Code on Demand     — (선택) 서버가 client 에 코드 보낼 수 있음 (JS 등)

이 중 "Uniform Interface" 가 REST 를 REST 답게 하는 차별점.
나머지는 사실 HTTP 자체에 거의 내장.

Uniform Interface — REST 의 네 기둥

1. Resource Identification (URI)
   /users/42 → "id 42 인 user 라는 resource"
   URL 은 resource 식별자, action 식별자 아님.

2. Representation
   resource 자체가 아니라 그것의 "표현" 을 주고받음.
   같은 user 가 JSON 일 수도, XML 일 수도, HTML 일 수도.
   Content-Type / Accept header 로 협상.

3. Self-descriptive Messages
   요청 / 응답이 스스로 충분히 설명 — Content-Type, Cache-Control,
   상태 코드 (status code) 등 표준 metadata.

4. HATEOAS (Hypermedia As The Engine Of Application State)
   응답에 다음 가능한 action 의 link 가 포함.
   client 는 link 를 따라가며 navigate — URL 구조를 사전에 알 필요 X.

Resource · Method · Representation — 세 축

# Resource
URL 은 명사 (resource 의 식별자), 동사 X
GOOD: /users/42, /orders/77/items
BAD:  /getUser?id=42, /createOrder

# Method (HTTP verb 가 action)
GET     /users/42  → 조회 (safe, idempotent, cacheable)
POST    /users     → 생성 (not idempotent)
PUT     /users/42  → 전체 교체 (idempotent)
PATCH   /users/42  → 부분 수정 (idempotency 는 정의에 따름)
DELETE  /users/42  → 삭제 (idempotent)

# Representation
같은 /users/42 가 여러 표현으로:
  Accept: application/json   → {"id":42,"name":"jade"}
  Accept: application/xml    → <user id="42">...</user>
  Accept: text/html          → <h1>jade</h1>

→ URL 은 안 바꾸고 표현만 협상.

Idempotency 의 깊은 의미는 idempotency-keys, 상태 코드 결정 트리는 http-status-codes 가이드 참조.

Richardson Maturity Model — REST 의 4 단계

Leonard Richardson 이 제안. API 가 얼마나 REST 에 가까운지 단계로 구분.

Level 0 — "The Swamp of POX" (Plain Old XML)
  POST /api  → 모든 요청 한 endpoint, body 안에 action 명시
  실질적으로 SOAP / RPC over HTTP. URL 도 method 도 의미 없음.
  예: { "action": "getUser", "id": 42 }

Level 1 — Resources
  /users/42, /orders/77 — URL 이 resource 식별자.
  그러나 method 는 여전히 POST 만 (또는 GET/POST 만).

Level 2 — HTTP Verbs
  GET /users/42, POST /users, DELETE /users/42 — 표준 method 사용.
  status code 도 200/201/404/409 등 의미 있게.
  → 우리가 "REST API" 라 부르는 것의 대부분이 여기 멈춤.

Level 3 — HATEOAS (Hypermedia Controls)
  응답에 다음 가능한 action 의 link 포함.
  client 가 URL 을 hardcoding 안 함 — 응답을 따라 navigate.
  → 진짜 Fielding 의 REST. 실전에서는 거의 없음.

HATEOAS — 가장 안 쓰이는 부분

Level 3 의 예 — 주문 응답에 가능한 다음 action 의 link:

GET /orders/77
{
  "id": 77,
  "status": "pending_payment",
  "total": 50000,
  "_links": {
    "self":    { "href": "/orders/77" },
    "pay":     { "href": "/orders/77/payment", "method": "POST" },
    "cancel":  { "href": "/orders/77", "method": "DELETE" }
  }
}

client 는 "pay" link 만 보고 POST → URL 구조 사전 지식 X.

장점:
- API 진화에 강함 — URL 바뀌어도 link 만 갱신
- discoverability — 응답이 가능한 다음 단계를 알려줌

왜 안 쓰이나?
- client (특히 SPA) 가 보통 URL 을 hardcoding (성능·UX)
- generic hypermedia client 가 사실상 없음 (브라우저 외)
- 응답 payload 크기 ↑, parse 복잡도 ↑
- 대부분 mobile/web client 는 미리 짜인 화면 흐름 → link 따라가는 의미 X

→ Fielding 본인이 "REST 가 아닌 API 가 REST 라 부르는 것에 짜증" 글
  (2008) 을 남겼지만, 업계는 Level 2 에 머묾.

Stateless — 가장 자주 깨지는 원칙

Stateless = 서버가 client 의 이전 요청을 기억하면 안 됨
         = 모든 요청에 필요한 모든 정보 포함

GOOD:
  GET /orders?page=3
  Authorization: Bearer <jwt>
  → 모든 정보 (인증·페이지) 가 요청에 있음

BAD (server-side session):
  GET /next-page
  Cookie: sessionid=abc123
  → 서버가 "abc123 의 마지막 페이지가 뭐였지" 기억해야 함

장점:
- 수평 확장 쉬움 (어떤 서버가 받아도 동일 응답)
- 캐시 효율 ↑
- 실패 시 단순 재시도

함정:
- 인증 token 마다 보내는 비용 (vs 세션 cookie 1 회)
- JWT 의 invalidation 문제 (token 발급 후 권한 변경 반영 어려움)

→ 인증 흐름 자체는 stateless 양립 가능, 단 setup 은 신중.
  자세히는 oauth2-explained 가이드.

"REST 가 아닌데 REST 라 불리는" 흔한 예

# 1. URL 에 동사
POST /users/42/setActive   ← action 이 URL 에 있음 (REST 위반)
권장: PATCH /users/42 { "active": true }

# 2. status code 무시
HTTP 200 OK
{ "success": false, "error": "not found" }   ← 진짜로 200 응답?
권장: HTTP 404 + body 에 상세

# 3. method 오용
GET /users/42/delete    ← GET 이 부수 효과 (safe 위반)
권장: DELETE /users/42

# 4. 응답에 link 없음 (Level 2 한계)
→ 엄밀히는 RESTful 아니지만, 실전 표준.

→ 즉 "REST API" 의 90% 는 Level 2 "HTTP + JSON + 명사 URL".
  나쁘다는 게 아니라, 진짜 REST 의 정의를 알고 쓰자는 의미.

흔한 오해

  • "REST = HTTP" — REST 는 protocol 독립. 이론상 AMQP / WebSocket 위에서도 가능 (실전엔 HTTP 가 거의 100%). HTTP 의 기능 (cache, status code, method) 이 REST 와 잘 맞아서 쓸 뿐.
  • "REST = JSON" — representation 은 협상 대상. XML, MessagePack, Protobuf, HTML 모두 가능.
  • "PUT 은 update, POST 는 create" — PUT 의 진짜 의미는 idempotent 한 전체 교체 (해당 URI 에 이 representation 으로 박아). create 도 client 가 ID 정하면 PUT 가능.
  • "REST 는 stateless 라 인증 못 함" — 인증 정보를 매 요청에 포함 (Bearer token) 하면 양립. 서버 세션을 금지할 뿐.
  • "REST 는 GraphQL 보다 옛날 거" — 둘은 다른 문제. REST 는 resource-oriented, GraphQL 은 query-oriented. how-graphql-actually-works 가이드 참조.

실전 REST 의 흔한 트레이드오프

  • over-fetching — /users/42 가 모든 field 반환 → mobile 에서 불필요한 byte. sparse fieldsets (?fields=id,name) 또는 GraphQL 로 해결.
  • under-fetching (N+1) — list + 각 item 의 detail = N 회 round trip. embed (?include=author) 또는 batch endpoint 로 완화.
  • version 관리 — /v1/users, /v2/users 의 path versioning 이 흔함. header (Accept-Version) 도 가능하지만 discoverability 떨어짐.
  • pagination 의 일관성 — offset vs cursor 의 트레이드오프는 how-pagination-actually-works 가이드.
  • CORS — 브라우저 client 가 cross-origin 호출 시 preflight + 헤더 협상 필요. 자세히는 cors-explained 가이드.
  • rate limit / abuse — public REST API 에 throttle 필수. rate-limiting-strategies 가이드 참조.

마무리

업계 통념: "REST API = HTTP + JSON + 명사 URL + 표준 method". 그게 Richardson Level 2 — 실용적이고 충분히 좋은 시작점. 그러나 그게 Fielding 의 REST 의 전부는 아니다. HATEOAS / 진짜 hypermedia 까지 가야 Level 3 = REST.

실전에서는 "REST 의 모든 제약 준수" 보다 "팀과 client 가 일관되게 합의한 규칙" 이 더 가치 있다. 메서드 강박·HATEOAS 광신 모두 별 도움 안 됨. 다만 "method 의 의미는 정확히", "status code 는 의미대로", "URL 은 resource 의 식별자" — 이 셋은 거의 무료의 규약.

가이드 목록으로