2026년의 REST: 모범 사례, 버전 관리 및 Richardson 성숙도 모델
스스로를 "RESTful"이라고 부르는 대부분의 API는 그렇지 않습니다. 그들은 다음과 같은 HTTP를 사용합니다. 전송, JSON 형식을 사용하고 거기에서 중지합니다. 그만큼 Richardson 성숙도 모델, 2008년 Leonard Richardson이 소개하고 Martin Fowler가 대중화한 일반 HTTP API(레벨 0)와 실제 API를 구별하는 4가지 레벨로 확장 RESTful(HATEOAS의 경우 레벨 3).
그러나 건축적 완벽주의는 이 가이드의 목적이 아닙니다. 목표는 당신을 가르치는 것입니다 REST API를 구축하기 위해 정확하고 유지 관리가 가능하며 잘 문서화되어 있습니다.: 적절한 HTTP 의미 체계 사용, 시간이 지남에 따라 지속 가능한 버전 관리 구현, 관리 ETag로 효과적으로 캐시하고 OpenAPI 3.1로 계약을 문서화하세요. 이들은 전문 API와 소비자에게 문제를 일으키는 API 간의 실제 차이점입니다.
무엇을 배울 것인가
- 구체적인 예를 포함하는 Richardson 성숙도 모델의 4가지 수준
- 올바른 HTTP 의미 체계: 메서드, 상태 코드, 헤더
- 멱등성과 안전 방법: 실제 적용을 통한 이론적 기반
- 버전 관리 전략: 추가 변경을 통한 URI, 헤더 및 버전 관리
- 효율적인 캐싱을 위한 ETag 및 조건부 요청
- OpenAPI 3.1: 문서 구조 및 모범 사례
Richardson 성숙도 모델: 4단계
Richardson 성숙도 모델은 규모에 따라 API의 "RESTfulness"를 측정합니다. 0~3. 이러한 수준을 이해하면 아키텍처 격차를 식별하는 데 도움이 됩니다. 기존 API에 통합하고 올바른 수준에서 새 API를 구축하세요.
수준 0: 전송으로서의 HTTP(터널링)
// Livello 0: tutto su un unico endpoint, azione nel body
POST /api
Content-Type: application/json
{
"action": "getUser",
"id": 123
}
POST /api
{
"action": "createUser",
"name": "Federico",
"email": "federico@example.com"
}
// HTTP e solo un canale: la semantica e tutta nell'applicazione
// Nessun caching possibile, nessuna semantica uniforme
레벨 1: 리소스(의미 있는 URI)
// Livello 1: URL per risorse, ma ancora solo POST
POST /users/123 // Non ha senso: POST per leggere?
POST /users/create
POST /users/delete/123
POST /users/get/all
// Migliore, ma i verbi HTTP non sono usati semanticamente
레벨 2: HTTP 동사(표준 레벨)
// Livello 2: URL meaningful + verbi HTTP corretti + status codes
GET /users -> 200 OK con lista
GET /users/123 -> 200 OK con utente, 404 Not Found
POST /users -> 201 Created con Location header
PUT /users/123 -> 200 OK aggiornato, 404 Not Found
PATCH /users/123 -> 200 OK parzialmente aggiornato
DELETE /users/123 -> 204 No Content, 404 Not Found
// Questo e il livello che la maggior parte delle API raggiunge
// ed e generalmente sufficiente per la produzione
레벨 3: HATEOAS(애플리케이션 상태 엔진으로서의 하이퍼미디어)
// Livello 3: ogni risposta contiene link alle azioni disponibili
GET /users/123
-> 200 OK
{
"id": 123,
"name": "Federico",
"email": "federico@example.com",
"status": "active",
"_links": {
"self": { "href": "/users/123" },
"orders": { "href": "/users/123/orders" },
"deactivate": { "href": "/users/123/deactivate", "method": "POST" },
"update": { "href": "/users/123", "method": "PUT" }
}
}
// Il client non deve "sapere" a priori gli URL: li scopre dalle risposte
// Permette di cambiare URL senza rompere i client (in teoria)
HATEOAS: 그럴만한 가치가 있나요?
HATEOAS는 이론적으로는 훌륭하지만 실제로는 거의 구현되지 않습니다. 이유:
- 응답 크기가 크게 증가합니다.
- 그러나 클라이언트는 성능을 위해 URL을 "하드코드"하는 경우가 많습니다.
- 링크를 탐색하려면 특정 도구가 필요합니다.
- 대부분의 팀은 대신 OpenAPI를 계약으로 사용합니다.
레벨 2 + OpenAPI 3.1 및 2026년 대부분의 API에 대한 실용적인 최적점. HATEOAS는 URL 안정성이 장기적으로 요구되는 공개 API에서 주로 의미가 있습니다. 용어와 비판.
올바른 HTTP 의미 체계
올바른 의미를 지닌 HTTP 동사를 사용하는 것은 단순히 미적인 문제가 아닙니다. 캐싱, 멱등성, 요청을 안전하게 철회하는 클라이언트의 능력.
// Proprietà dei metodi HTTP
Metodo | Safe | Idempotente | Body richiesta | Uso corretto
--------|------|-------------|----------------|----------------------------------
GET | SI | SI | No | Lettura, query, ricerca
HEAD | SI | SI | No | Verifica esistenza, metadata
OPTIONS | SI | SI | No | CORS preflight, capabilities
POST | NO | NO | SI | Creazione, azioni non-idempotenti
PUT | NO | SI | SI | Sostituzione completa di risorsa
PATCH | NO | NO* | SI | Modifica parziale
DELETE | NO | SI | Opzionale | Eliminazione
// *PATCH puo essere reso idempotente con patch semantics JSON Patch (RFC 6902)
안전한 이는 요청에 서버측 부작용이 없음을 의미합니다. (서버는 상태를 변경하지 않습니다). 클라이언트는 결과 없이 GET에서 "F5를 누를" 수 있습니다.
멱등성 이는 여러 개의 동일한 요청이 동일한 결과를 생성함을 의미합니다. 단일 요청의 결과입니다. 재시도에 중요: DELETE 중에 네트워크가 다운되면 클라이언트는 두 번 이상 삭제할 염려 없이 다시 시도할 수 있습니다.
상태 코드: 올바른 코드 사용
남용 200 OK 모든 것(본문 오류 포함) 및 안티 패턴 중 하나
더 일반적입니다. 올바른 코드를 사용하면 클라이언트, 프록시 및 모니터링 도구가
답변을 올바르게 해석하십시오.
// Status codes piu importanti con esempi di uso corretto
// 2xx: Success
200 OK - GET, PUT, PATCH con risorsa nel body
201 Created - POST che crea una risorsa (+ Location header)
202 Accepted - Operazione asincrona accettata (non ancora completata)
204 No Content - DELETE, PUT/PATCH senza body di risposta
// 3xx: Redirection
301 Moved Permanently - Redirect permanente (aggiornare i bookmark)
302 Found - Redirect temporaneo
304 Not Modified - ETag/If-None-Match: risorsa non cambiata, usa la cache
// 4xx: Client Error (il client ha sbagliato)
400 Bad Request - Input malformato, schema validation fallita
401 Unauthorized - Non autenticato (serve login)
403 Forbidden - Autenticato ma non autorizzato
404 Not Found - Risorsa non trovata
405 Method Not Allowed - Metodo HTTP non supportato su questo endpoint
409 Conflict - Conflitto di stato (es: email gia esistente)
410 Gone - Risorsa eliminata permanentemente (vs 404)
422 Unprocessable Entity - Sintassi ok ma semantica invalida
429 Too Many Requests - Rate limit raggiunto (+ Retry-After header)
// 5xx: Server Error (colpa del server)
500 Internal Server Error - Errore generico non gestito
502 Bad Gateway - Errore dal backend upstream
503 Service Unavailable - Server temporaneamente non disponibile
504 Gateway Timeout - Timeout dal backend upstream
PUT과 PATCH: 중요한 차이점
PUT과 PATCH 사이의 혼동은 흔하지만 구체적인 의미가 있습니다.
// PUT: sostituzione COMPLETA della risorsa (idempotente)
// Se ometti un campo, viene azzerato!
PUT /users/123
{
"name": "Federico Calo",
"email": "federico@example.com"
// Se il campo "phone" non e incluso, viene rimosso!
}
// PATCH: modifica PARZIALE (solo i campi specificati)
PATCH /users/123
{
"name": "Federico Calo"
// Solo name viene aggiornato, email e phone rimangono invariati
}
// PATCH con JSON Patch (RFC 6902): piu preciso e idempotente
PATCH /users/123
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/name", "value": "Federico Calo" },
{ "op": "add", "path": "/phone", "value": "+39 333 1234567" },
{ "op": "remove", "path": "/tempNote" }
]
버전 관리 전략
버전 관리는 공개 API를 설계할 때 가장 중요한 결정 중 하나입니다. 한 번 클라이언트가 API에 의존하므로 계약을 변경하면 애플리케이션이 중단됩니다. 르 주요 전략에는 서로 다른 장단점이 있습니다.
1. URI 버전 관리(가장 일반적)
GET /api/v1/users // Versione 1
GET /api/v2/users // Versione 2 con campi aggiuntivi
// Vantaggi:
// - Visibile e ovvio
// - Cacheable a livello HTTP (l'URL e diverso)
// - Facile da esplorare con il browser
// - Semplice da loggare e monitorare
// Svantaggi:
// - "Sporco" semanticamente (la versione non e parte della risorsa)
// - Proliferazione di URL nel tempo
2. 헤더 버전 관리
GET /api/users
Accept: application/vnd.myapi.v2+json
// oppure
API-Version: 2
// Vantaggi:
// - URL "puliti"
// - Piu vicino alla semantica HTTP originale
// Svantaggi:
// - Non cacheable con HTTP standard (Cache-Vary header necessario)
// - Invisibile dalla URL (difficile da debuggare)
// - Meno intuitivo per i nuovi consumatori dell'API
3. 추가 변경을 통한 버전 관리(최상)
// Strategia: non cambiare mai, solo aggiungere (non rompere mai i client)
// V1 risposta:
GET /api/users/123
{
"id": 123,
"name": "Federico",
"email": "federico@example.com"
}
// Aggiungi campi senza versione (i client vecchi ignorano i nuovi campi):
GET /api/users/123
{
"id": 123,
"name": "Federico",
"email": "federico@example.com",
"createdAt": "2025-01-15T10:30:00Z", // AGGIUNTO: non rompe i client vecchi
"avatarUrl": null // AGGIUNTO: nullable per retrocompat
}
// Quando DEVI rompere (rare):
// - Rimuovere un campo -> versione nuova
// - Cambiare tipo di un campo -> versione nuova
// - Cambiare semantica di un campo -> versione nuova
ETag 및 조건부 요청
L'E태그 (엔티티 태그) 및 캐시를 관리하는 HTTP 메커니즘과 낙관적인 경쟁. 각 리소스에는 해당 버전을 식별하는 해시 또는 타임스탬프가 있습니다. 현재:
// Server: risposta con ETag
GET /users/123
->
200 OK
ETag: "abc123def456"
Cache-Control: max-age=300
{
"id": 123,
"name": "Federico",
"version": 3
}
// Client: richiesta condizionale con If-None-Match
GET /users/123
If-None-Match: "abc123def456"
->
304 Not Modified // Risorsa non cambiata, usa la cache!
// Nessun body = traffico ridotto
// Se la risorsa e cambiata:
GET /users/123
If-None-Match: "abc123def456"
->
200 OK
ETag: "xyz789new123" // Nuovo ETag
{ /* dati aggiornati */ }
// ETag per concorrenza ottimistica (prevenire aggiornamenti in conflitto):
PUT /users/123
If-Match: "abc123def456" // "Aggiorna SOLO se la versione e ancora questa"
{...}
->
200 OK // Aggiornamento riuscito, nessun conflitto
// oppure
412 Precondition Failed // Qualcun altro ha modificato la risorsa!
// Il client deve rileggere prima di riaggiornare
OpenAPI 3.1을 사용한 문서화
OpenAPI 3.1은 REST API 문서화를 위한 업계 표준입니다. 멋진 OpenAPI 문서 서면은 정식 계약의 역할을 하며 클라이언트 SDK 생성을 가능하게 하고 권한을 부여합니다. Swagger UI 또는 Redoc을 사용한 대화형 문서:
// openapi.yaml - Struttura base di un documento OpenAPI 3.1
openapi: 3.1.0
info:
title: User Management API
version: 1.2.0
description: |
API per la gestione degli utenti dell'applicazione.
## Autenticazione
Usa Bearer token JWT nell'header Authorization.
contact:
name: API Support
email: api@example.com
license:
name: MIT
servers:
- url: https://api.example.com/v1
description: Production
- url: https://staging-api.example.com/v1
description: Staging
paths:
/users:
get:
operationId: listUsers
summary: Lista utenti
tags: [Users]
parameters:
- name: page
in: query
schema: { type: integer, minimum: 1, default: 1 }
- name: limit
in: query
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
- name: search
in: query
schema: { type: string }
responses:
'200':
description: Lista utenti paginata
content:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
'401':
$ref: '#/components/responses/Unauthorized'
post:
operationId: createUser
summary: Crea utente
tags: [Users]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
responses:
'201':
description: Utente creato
headers:
Location:
schema: { type: string }
description: URL del nuovo utente
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'409':
description: Email gia esistente
components:
schemas:
User:
type: object
required: [id, name, email, createdAt]
properties:
id: { type: integer, format: int64, readOnly: true }
name: { type: string, minLength: 1, maxLength: 100 }
email: { type: string, format: email }
createdAt: { type: string, format: date-time, readOnly: true }
CreateUserRequest:
type: object
required: [name, email, password]
properties:
name: { type: string, minLength: 1, maxLength: 100 }
email: { type: string, format: email }
password: { type: string, minLength: 8, writeOnly: true }
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- bearerAuth: []
오류 응답 패턴
오류 응답을 위한 일관된 구조로 개발자의 성능이 크게 향상됩니다. API 소비자 경험. 표준 RFC 9457(문제 세부정보) 2026년에 선호되는 선택이 되었습니다.
// RFC 9457 Problem Details for HTTP APIs
// Content-Type: application/problem+json
// 400 Bad Request
{
"type": "https://example.com/errors/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "The request body contains invalid data",
"instance": "/api/users",
"errors": [
{
"field": "email",
"message": "Invalid email format",
"value": "not-an-email"
},
{
"field": "password",
"message": "Password must be at least 8 characters",
"value": null
}
]
}
// 409 Conflict
{
"type": "https://example.com/errors/duplicate-email",
"title": "Duplicate Email",
"status": 409,
"detail": "An account with this email already exists",
"instance": "/api/users",
"email": "federico@example.com"
}
// 429 Too Many Requests
{
"type": "https://example.com/errors/rate-limited",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "Too many requests. Retry after 60 seconds.",
"retryAfter": 60
}
결론 및 다음 단계
2026년 전문 REST API는 Richardson 성숙도 모델의 레벨 2에서 작동합니다(리소스 + 올바른 HTTP 동사 + 적절한 상태 코드), 캐싱 및 낙관적 동시성을 위해 ETag를 사용합니다. 추가 변경을 기반으로 지속 가능한 버전 관리를 채택하고 다음과의 공식 계약을 문서화합니다. 오픈API 3.1. 대부분의 상황에서는 HATEOAS 레벨 3에 도달할 필요가 없습니다.
다음 기사에서 검토해 보겠습니다. GraphQL 심층 분석: 시스템이 어떻게 작동하는지 N+1 문제는 가장 일반적인 아키텍처 위험이고 DataLoader로서 데이터베이스 쿼리 일괄 처리를 해결합니다.
시리즈: API 디자인 — REST, GraphQL, gRPC 및 tRPC 비교
- 기사 1: 2026년 API 환경 — 의사결정 매트릭스
- 제2조(본): 2026년 REST — 모범 사례, 버전 관리 및 Richardson 성숙도 모델
- 기사 3: GraphQL — 쿼리 언어, 해결자 및 N+1 문제
- 기사 4: GraphQL 페더레이션 - Supergraph, Subgraph 및 Apollo Router
- 5조: gRPC - Protobuf, 성능 및 서비스 간 통신
- 기사 6: tRPC — 코드 생성 없이 종단 간 형식 안전성
- 기사 7: 웹후크 - 패턴, 보안 및 재시도 논리
- 8조: API 버전 관리 - URI, 헤더 및 지원 중단 정책
- 9조: 속도 제한 및 제한 - 알고리즘 및 구현
- 기사 10: 하이브리드 API 아키텍처 - 2026년 REST + tRPC + gRPC







