REST 아키텍처 - 배경부터 핵심까지
TL;DR
- REST 아키텍처 - 배경부터 핵심까지의 핵심 개념과 사용 범위를 한눈에 정리
- 등장 배경과 필요한 이유를 짚고 실무 적용 포인트를 연결
- 주요 특징과 체크리스트를 빠르게 확인
1. 개념
┌─────────────────────────────────────────────────────────────────┐ │ REST 이전의 혼란 │ │ │ │ 1990년대 후반 ~ 2000년대 초반 │ │ │ │ 웹 서비스 통신 방법들: │ │ ├── CORBA (1991): 복잡한 바이너리 프로토콜 │ │ ├── DCOM (1996): Windows 전용, 방화벽 문제 │ │ ├── RMI (1997): Java 전용 │ │ └── SOAP (1998): XML 기반, 엄청나게 복잡 │ │ │ │ SOAP 메시지 예시: │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ <?xml version="1.0"?> │ │ │ │ <soap:Envelope │ │ │ │ xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> │ │ │ │ <soap:Header> │ │ │ │ <auth:Credentials>...</auth:Credentials> │ │ │ │ </soap:Header> │ │ │ │ <soap:Body> │ │ │ │ <m:GetUser> │ │ │ │ <m:UserId>123</m:UserId> │ │ │ │ </m:GetUser> │ │ │ │ </soap:Body> │ │ │ │ </soap:Envelope> │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ 문제점: │ │ ├── 너무 복잡하고 무거움 │ │ ├── WSDL (서비스 정의) 작성이 고통 │ │ ├── 모든 요청이 POST (HTTP를 단순 터널로만 사용) │ │ └── 디버깅 어려움 │ │ │ └─────────────────────────────────────────────────────────────────┘
2. 배경
REST 아키텍처 - 배경부터 핵심까지이(가) 등장한 배경과 기존 한계를 정리한다.
3. 이유
이 주제를 이해하고 적용해야 하는 이유를 정리한다.
4. 특징
- 1 REST 이전의 혼란
- 2 Roy Fielding과 REST의 등장
- 1 Client-Server
- 2 Stateless (무상태)
- 3 Cacheable (캐시 가능)
5. 상세 내용
작성일: 2026-01-29 카테고리: Backend / Architecture / API Design 포함 내용: REST, RESTful API, Uniform Interface, Stateless, HATEOAS, Richardson 성숙도 모델
1. REST의 탄생 배경
1.1 REST 이전의 혼란
┌─────────────────────────────────────────────────────────────────┐
│ REST 이전의 혼란 │
│ │
│ 1990년대 후반 ~ 2000년대 초반 │
│ │
│ 웹 서비스 통신 방법들: │
│ ├── CORBA (1991): 복잡한 바이너리 프로토콜 │
│ ├── DCOM (1996): Windows 전용, 방화벽 문제 │
│ ├── RMI (1997): Java 전용 │
│ └── SOAP (1998): XML 기반, 엄청나게 복잡 │
│ │
│ SOAP 메시지 예시: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ <?xml version="1.0"?> │ │
│ │ <soap:Envelope │ │
│ │ xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> │ │
│ │ <soap:Header> │ │
│ │ <auth:Credentials>...</auth:Credentials> │ │
│ │ </soap:Header> │ │
│ │ <soap:Body> │ │
│ │ <m:GetUser> │ │
│ │ <m:UserId>123</m:UserId> │ │
│ │ </m:GetUser> │ │
│ │ </soap:Body> │ │
│ │ </soap:Envelope> │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 문제점: │
│ ├── 너무 복잡하고 무거움 │
│ ├── WSDL (서비스 정의) 작성이 고통 │
│ ├── 모든 요청이 POST (HTTP를 단순 터널로만 사용) │
│ └── 디버깅 어려움 │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2 Roy Fielding과 REST의 등장
┌─────────────────────────────────────────────────────────────────┐
│ REST의 탄생 (2000년) │
│ │
│ Roy Fielding: │
│ ├── HTTP/1.0, HTTP/1.1 명세 공동 저자 │
│ ├── Apache HTTP Server 공동 개발자 │
│ └── "웹이 왜 이렇게 잘 작동하는지" 분석 │
│ │
│ 2000년, UC Irvine 박사 논문: │
│ "Architectural Styles and the Design of │
│ Network-based Software Architectures" │
│ │
│ 핵심 통찰: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ "웹(WWW)은 이미 거대하고 성공적인 분산 시스템이다. │ │
│ │ 웹이 성공한 아키텍처 원칙을 API에도 적용하자!" │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ REST = REpresentational State Transfer │
│ = 표현된 상태의 전송 │
│ = 리소스의 현재 상태를 표현해서 주고받자 │
│ │
└─────────────────────────────────────────────────────────────────┘
2. REST의 6가지 제약조건
┌─────────────────────────────────────────────────────────────────┐
│ REST의 6가지 아키텍처 제약조건 │
│ │
│ 1. Client-Server (클라이언트-서버 분리) │
│ 2. Stateless (무상태) │
│ 3. Cacheable (캐시 가능) │
│ 4. Uniform Interface (균일한 인터페이스) ← 가장 중요! │
│ 5. Layered System (계층화 시스템) │
│ 6. Code on Demand (선택적, 코드 전송) │
│ │
│ 이 6가지를 모두 지켜야 "RESTful" │
│ │
└─────────────────────────────────────────────────────────────────┘
2.1 Client-Server
┌─────────────────────────────────────────────────────────────────┐
│ 1. Client-Server 분리 │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Client │ ◄─── HTTP 요청/응답 ──► │ Server │ │
│ │ (UI) │ │ (Data) │ │
│ └──────────┘ └──────────┘ │
│ │
│ 핵심: │
│ ├── 관심사의 분리 (Separation of Concerns) │
│ ├── 클라이언트: UI, 사용자 경험 │
│ ├── 서버: 데이터 저장, 비즈니스 로직 │
│ └── 각각 독립적으로 발전 가능 │
│ │
│ 예: 모바일 앱 새 버전 출시해도 서버는 그대로 │
│ 서버 DB 교체해도 클라이언트는 모름 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.2 Stateless (무상태)
┌─────────────────────────────────────────────────────────────────┐
│ 2. Stateless (무상태) │
│ │
│ "서버는 클라이언트의 상태를 기억하지 않는다" │
│ │
│ ❌ Stateful (상태 유지): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 요청 1: "로그인해줘" (id=kim) │ │
│ │ 서버: "OK, 너를 기억할게" (세션 저장) │ │
│ │ │ │
│ │ 요청 2: "내 주문 목록 줘" │ │
│ │ 서버: "아까 kim이었지? 여기 있어" │ │
│ │ │ │
│ │ 문제: 서버 여러 대면? 다른 서버로 가면 모름! │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ✅ Stateless (무상태): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 요청 1: "로그인" → 서버: "여기 토큰(JWT)" │ │
│ │ │ │
│ │ 요청 2: "내 주문 목록 줘" + 토큰 │ │
│ │ 서버: "토큰 확인, kim이구나, 여기 있어" │ │
│ │ │ │
│ │ 장점: 어떤 서버든 처리 가능! (확장성) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 이점: │
│ ├── 서버 확장 쉬움 (로드밸런서 뒤에 서버 추가) │
│ ├── 서버 장애 시 다른 서버가 처리 │
│ └── 요청 간 의존성 없음 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.3 Cacheable (캐시 가능)
┌─────────────────────────────────────────────────────────────────┐
│ 3. Cacheable (캐시 가능) │
│ │
│ "응답이 캐시 가능한지 명시해야 한다" │
│ │
│ ┌──────────┐ ┌───────┐ ┌──────────┐ │
│ │ Client │───►│ Cache │───►│ Server │ │
│ └──────────┘ └───────┘ └──────────┘ │
│ │ │
│ └── 캐시 히트 시 서버 안 감 │
│ │
│ HTTP 헤더로 캐시 제어: │
│ ├── Cache-Control: max-age=3600 (1시간 캐시) │
│ ├── ETag: "abc123" (버전 태그) │
│ └── Last-Modified: ... (마지막 수정 시간) │
│ │
│ GET /users/1 → 캐시 가능 (같은 요청 = 같은 응답) │
│ POST /users → 캐시 불가 (매번 다른 결과) │
│ │
│ 이점: 성능 향상, 서버 부하 감소 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.4 Layered System (계층화)
┌─────────────────────────────────────────────────────────────────┐
│ 5. Layered System (계층화 시스템) │
│ │
│ "클라이언트는 중간 계층을 모른다" │
│ │
│ ┌──────────┐ ┌───────┐ ┌──────┐ ┌──────────┐ │
│ │ Client │──►│ CDN │──►│ LB │──►│ Server │ │
│ └──────────┘ └───────┘ └──────┘ └──────────┘ │
│ │ │ │
│ └─── 클라이언트는 그냥 요청만 보냄 ────┘ │
│ 중간에 뭐가 있는지 모름 │
│ │
│ 중간 계층 예시: │
│ ├── 로드 밸런서 │
│ ├── 캐시 서버 (Redis, Varnish) │
│ ├── API Gateway │
│ ├── 보안 프록시 │
│ └── CDN │
│ │
│ 이점: 보안, 확장성, 유연한 아키텍처 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.5 Code on Demand (선택적)
┌─────────────────────────────────────────────────────────────────┐
│ 6. Code on Demand (선택적) │
│ │
│ "서버가 클라이언트에 실행 가능한 코드 전송" │
│ │
│ 예시: │
│ ├── JavaScript 전송 (웹 페이지) │
│ ├── Java Applet (옛날) │
│ └── Flash (RIP) │
│ │
│ 유일한 선택적 제약조건 │
│ 대부분의 REST API에서는 해당 없음 │
│ │
└─────────────────────────────────────────────────────────────────┘
3. Uniform Interface (균일한 인터페이스) - 가장 중요!
REST를 REST답게 만드는 핵심 제약조건. 4가지 하위 제약으로 구성됨.
┌─────────────────────────────────────────────────────────────────┐
│ Uniform Interface의 4가지 하위 제약 │
│ │
│ ① Identification of Resources (리소스 식별) │
│ ② Manipulation through Representations (표현을 통한 조작) │
│ ③ Self-descriptive Messages (자기 서술적 메시지) │
│ ④ HATEOAS (하이퍼미디어를 통한 상태 전이) │
│ │
│ 왜 중요한가? │
│ ├── 다른 제약은 다른 아키텍처도 가질 수 있음 │
│ ├── Uniform Interface가 REST를 REST답게 만드는 핵심 │
│ └── Roy Fielding: "REST의 핵심 특징" │
│ │
└─────────────────────────────────────────────────────────────────┘
3.1 리소스 식별 (Identification of Resources)
┌─────────────────────────────────────────────────────────────────┐
│ ① 리소스 식별 (Identification of Resources) │
│ │
│ 핵심: "모든 리소스는 고유한 URI로 식별된다" │
│ │
│ 리소스란? │
│ ├── 명사로 표현되는 "것" (noun) │
│ ├── 사용자, 주문, 상품, 댓글 등 │
│ └── 동사가 아닌 명사! │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ URI 설계 │ │
│ │ │ │
│ │ ❌ 나쁜 예 (동사 사용): │ │
│ │ POST /getUsers │ │
│ │ POST /createUser │ │
│ │ POST /deleteUser?id=123 │ │
│ │ GET /getUserOrders?userId=123 │ │
│ │ │ │
│ │ ✅ 좋은 예 (명사 + HTTP 메서드): │ │
│ │ GET /users → 사용자 목록 │ │
│ │ GET /users/123 → 123번 사용자 │ │
│ │ POST /users → 사용자 생성 │ │
│ │ DELETE /users/123 → 123번 사용자 삭제 │ │
│ │ GET /users/123/orders → 123번 사용자의 주문 목록 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ URI 설계 원칙: │
│ ├── 복수형 사용: /users (not /user) │
│ ├── 계층 관계 표현: /users/123/orders/456 │
│ ├── 소문자 사용: /users (not /Users) │
│ ├── 하이픈 사용: /user-profiles (not /user_profiles) │
│ └── 파일 확장자 없음: /users/123 (not /users/123.json) │
│ │
└─────────────────────────────────────────────────────────────────┘
3.2 표현을 통한 조작 (Manipulation through Representations)
┌─────────────────────────────────────────────────────────────────┐
│ ② 표현을 통한 조작 (Manipulation through Representations) │
│ │
│ 핵심: "리소스 자체가 아닌 '표현'을 주고받는다" │
│ │
│ 리소스 vs 표현: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 리소스 (Resource): │ │
│ │ └── DB에 저장된 실제 데이터 │ │
│ │ └── users 테이블의 id=123 레코드 │ │
│ │ │ │
│ │ 표현 (Representation): │ │
│ │ └── 리소스의 현재 상태를 특정 형식으로 표현한 것 │ │
│ │ └── JSON, XML, HTML, 이미지 등 │ │
│ │ │ │
│ │ 같은 리소스, 다른 표현: │ │
│ │ GET /users/123 │ │
│ │ Accept: application/json → {"id":123, "name":"Kim"} │ │
│ │ Accept: application/xml → <user><id>123</id>...</user> │ │
│ │ Accept: text/html → <html>사용자 정보...</html> │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 조작 예시: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 1. 조회: 리소스의 "표현"을 받음 │ │
│ │ GET /users/123 │ │
│ │ 응답: {"id": 123, "name": "Kim", "email": "..."} │ │
│ │ │ │
│ │ 2. 수정: "표현"을 보내서 리소스 변경 │ │
│ │ PUT /users/123 │ │
│ │ Body: {"id": 123, "name": "Park", "email": "..."} │ │
│ │ → 서버가 이 표현으로 리소스를 업데이트 │ │
│ │ │ │
│ │ 3. 생성: "표현"을 보내서 새 리소스 생성 │ │
│ │ POST /users │ │
│ │ Body: {"name": "Lee", "email": "..."} │ │
│ │ → 서버가 이 표현으로 새 리소스 생성 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 의미: │
│ ├── 클라이언트는 표현만 다루면 됨 (실제 저장 방식 몰라도 됨) │
│ ├── 서버 내부 구조가 변해도 표현이 같으면 클라이언트 수정 불필요│
│ └── 콘텐츠 협상 (Content Negotiation) 가능 │
│ │
└─────────────────────────────────────────────────────────────────┘
3.3 자기 서술적 메시지 (Self-descriptive Messages)
┌─────────────────────────────────────────────────────────────────┐
│ ③ 자기 서술적 메시지 (Self-descriptive Messages) │
│ │
│ 핵심: "메시지만 보고 무엇을 해야 하는지 완전히 알 수 있어야 함" │
│ │
│ ❌ 자기 서술적이지 않은 메시지: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ GET /users/123 HTTP/1.1 │ │
│ │ Host: api.example.com │ │
│ │ │ │
│ │ 응답: │ │
│ │ HTTP/1.1 200 OK │ │
│ │ │ │
│ │ {"id": 123, "name": "Kim"} │ │
│ │ │ │
│ │ 문제: 이게 JSON인지 어떻게 알아? │ │
│ │ 이 숫자들의 의미는? │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ✅ 자기 서술적인 메시지: │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ GET /users/123 HTTP/1.1 │ │
│ │ Host: api.example.com │ │
│ │ Accept: application/json │ │
│ │ │ │
│ │ 응답: │ │
│ │ HTTP/1.1 200 OK │ │
│ │ Content-Type: application/json; charset=utf-8 │ │
│ │ Cache-Control: max-age=3600 │ │
│ │ ETag: "abc123" │ │
│ │ │ │
│ │ { │ │
│ │ "id": 123, │ │
│ │ "name": "Kim", │ │
│ │ "email": "kim@example.com" │ │
│ │ } │ │
│ │ │ │
│ │ 이제 알 수 있는 것: │ │
│ │ ├── 형식: JSON (Content-Type) │ │
│ │ ├── 캐시: 1시간 가능 (Cache-Control) │ │
│ │ ├── 버전: abc123 (ETag) │ │
│ │ └── 문자셋: UTF-8 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 더 완벽한 자기 서술 (엄격한 해석): │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Content-Type: application/vnd.myapi.user+json; v=1 │ │
│ │ │ │
│ │ → 커스텀 미디어 타입으로 스키마 정보까지 포함 │ │
│ │ → 이 타입의 명세를 보면 필드 의미를 알 수 있음 │ │
│ │ → 현실에서는 거의 안 함 (OpenAPI/Swagger로 대체) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 실무에서 자기 서술성: │
│ ├── Content-Type 필수 │
│ ├── 적절한 HTTP 상태 코드 사용 │
│ ├── 에러 응답에 명확한 메시지 포함 │
│ └── OpenAPI(Swagger) 문서로 보완 │
│ │
└─────────────────────────────────────────────────────────────────┘
HTTP 상태 코드로 자기 서술
┌─────────────────────────────────────────────────────────────────┐
│ HTTP 상태 코드 = 자기 서술 │
│ │
│ ┌──────────┬────────────────────────────────────────────────┐ │
│ │ 코드 │ 의미 (메시지 안 봐도 알 수 있음) │ │
│ ├──────────┼────────────────────────────────────────────────┤ │
│ │ 200 OK │ 성공 │ │
│ │ 201 Created│ 새 리소스 생성됨 │ │
│ │ 204 No Content│ 성공, 응답 본문 없음 │ │
│ │ 400 Bad Request│ 클라이언트 요청 오류 │ │
│ │ 401 Unauthorized│ 인증 필요 │ │
│ │ 403 Forbidden│ 권한 없음 │ │
│ │ 404 Not Found│ 리소스 없음 │ │
│ │ 409 Conflict│ 충돌 (중복 등) │ │
│ │ 422 Unprocessable│ 검증 실패 │ │
│ │ 500 Internal Error│ 서버 오류 │ │
│ └──────────┴────────────────────────────────────────────────┘ │
│ │
│ 상태 코드만 봐도 결과를 알 수 있다 = 자기 서술적 │
│ │
└─────────────────────────────────────────────────────────────────┘
3.4 HATEOAS (하이퍼미디어를 통한 상태 전이)
┌─────────────────────────────────────────────────────────────────┐
│ ④ HATEOAS (Hypermedia As The Engine Of Application State) │
│ │
│ 핵심: "응답에 다음에 할 수 있는 행동의 링크를 포함" │
│ │
│ 왜 필요한가? │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 일반 API (HATEOAS 없음): │ │
│ │ ───────────────────────── │ │
│ │ GET /users/123 │ │
│ │ 응답: {"id": 123, "name": "Kim"} │ │
│ │ │ │
│ │ 클라이언트: "이 사용자 삭제하려면 어디로 요청하지?" │ │
│ │ → 문서 보거나 코드에 하드코딩해야 함 │ │
│ │ → API 변경되면 클라이언트도 수정 필요 │ │
│ │ │ │
│ │ HATEOAS 적용: │ │
│ │ ───────────── │ │
│ │ GET /users/123 │ │
│ │ 응답: │ │
│ │ { │ │
│ │ "id": 123, │ │
│ │ "name": "Kim", │ │
│ │ "_links": { │ │
│ │ "self": {"href": "/users/123"}, │ │
│ │ "delete": {"href": "/users/123", "method": "DELETE"}│ │
│ │ "orders": {"href": "/users/123/orders"}, │ │
│ │ "update": {"href": "/users/123", "method": "PUT"} │ │
│ │ } │ │
│ │ } │ │
│ │ │ │
│ │ 클라이언트: 링크만 따라가면 됨! │ │
│ │ → URL 하드코딩 불필요 │ │
│ │ → API 변경되어도 링크만 따라가면 됨 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
HATEOAS 실제 예시
// 주문 조회 응답 (HATEOAS 적용)
{
"id": 456,
"status": "PENDING",
"total": 50000,
"items": [
{"productId": 1, "name": "키보드", "quantity": 1}
],
"_links": {
"self": {
"href": "/orders/456"
},
"cancel": {
"href": "/orders/456/cancel",
"method": "POST",
"title": "주문 취소"
},
"pay": {
"href": "/orders/456/payment",
"method": "POST",
"title": "결제하기"
},
"customer": {
"href": "/users/123"
}
}
}
// 주문 완료 후에는 cancel, pay 링크가 사라짐
{
"id": 456,
"status": "COMPLETED",
"total": 50000,
"_links": {
"self": {"href": "/orders/456"},
"receipt": {"href": "/orders/456/receipt"},
"refund": {"href": "/orders/456/refund", "method": "POST"}
}
}
HATEOAS의 현실
┌─────────────────────────────────────────────────────────────────┐
│ HATEOAS의 현실 │
│ │
│ 이상: │
│ ├── 클라이언트가 URL을 몰라도 됨 │
│ ├── 링크만 따라가면 모든 기능 사용 가능 │
│ ├── API 변경에 클라이언트가 자동 적응 │
│ └── 웹 브라우저처럼 동작! │
│ │
│ 현실: │
│ ├── 구현 복잡도 증가 │
│ ├── 응답 크기 증가 │
│ ├── 대부분의 클라이언트가 링크를 활용 안 함 │
│ ├── 프론트엔드 개발자: "그냥 URL 주세요" │
│ └── 거의 대부분 API가 HATEOAS 미적용 │
│ │
│ Roy Fielding의 불만: │
│ "HATEOAS 없으면 REST가 아니야!" │
│ → 그래서 대부분 "RESTful" (REST스러운)이라고 부름 │
│ │
│ 그래도 가치 있는 곳: │
│ ├── 공개 API (GitHub, Stripe 일부 적용) │
│ ├── 장기 유지보수 필요한 API │
│ └── 다양한 클라이언트 지원 필요 시 │
│ │
└─────────────────────────────────────────────────────────────────┘
4. Richardson 성숙도 모델
┌─────────────────────────────────────────────────────────────────┐
│ REST 성숙도 모델 (Richardson) │
│ │
│ Level 3: HATEOAS ← 진정한 REST │
│ (하이퍼미디어 컨트롤) (거의 없음) │
│ │ │
│ Level 2: HTTP Methods ← 대부분 여기 │
│ (GET, POST, PUT, DELETE) "RESTful API" │
│ │ │
│ Level 1: Resources ← 리소스 URI만 있음 │
│ (/users, /orders) │
│ │ │
│ Level 0: POX (Plain Old XML) ← SOAP 스타일 │
│ (하나의 엔드포인트에 모든 것) │
│ │
└─────────────────────────────────────────────────────────────────┘
각 레벨 예시
Level 0 (POX):
┌─────────────────────────────────────────────────────────────────┐
│ POST /api │
│ Body: {"action": "getUser", "userId": 123} │
│ │
│ POST /api │
│ Body: {"action": "createUser", "name": "Kim"} │
│ │
│ → 모든 요청이 같은 엔드포인트, action으로 구분 │
│ → RPC 스타일 │
└─────────────────────────────────────────────────────────────────┘
Level 1 (Resources):
┌─────────────────────────────────────────────────────────────────┐
│ POST /users/123 │
│ Body: {"action": "get"} │
│ │
│ POST /users │
│ Body: {"action": "create", "name": "Kim"} │
│ │
│ → 리소스 URI는 있지만, 여전히 POST만 사용 │
└─────────────────────────────────────────────────────────────────┘
Level 2 (HTTP Methods):
┌─────────────────────────────────────────────────────────────────┐
│ GET /users/123 │
│ POST /users Body: {"name": "Kim"} │
│ PUT /users/123 Body: {"name": "Park"} │
│ DELETE /users/123 │
│ │
│ → HTTP 메서드로 행위 표현 (대부분의 REST API) │
└─────────────────────────────────────────────────────────────────┘
Level 3 (HATEOAS):
┌─────────────────────────────────────────────────────────────────┐
│ GET /users/123 │
│ { │
│ "id": 123, "name": "Kim", │
│ "_links": { │
│ "self": {"href": "/users/123"}, │
│ "orders": {"href": "/users/123/orders"} │
│ } │
│ } │
│ │
│ → 응답에 다음 행동 링크 포함 (진정한 REST) │
└─────────────────────────────────────────────────────────────────┘
5. REST의 실질적 장점
┌─────────────────────────────────────────────────────────────────┐
│ REST가 주는 실질적 이점 │
│ │
│ 1. 단순함 │
│ ├── HTTP만 알면 됨 (이미 다 알고 있음) │
│ ├── WSDL, 복잡한 스펙 필요 없음 │
│ └── curl로 바로 테스트 가능 │
│ │
│ 2. 확장성 │
│ ├── Stateless → 서버 추가 쉬움 │
│ ├── 캐싱 → 부하 분산 │
│ └── 계층화 → 중간 서버 추가 쉬움 │
│ │
│ 3. 독립성 │
│ ├── 클라이언트/서버 독립 개발 │
│ ├── 언어/플랫폼 무관 │
│ └── 어떤 클라이언트든 같은 API 사용 │
│ │
│ 4. 가시성 │
│ ├── URL만 봐도 뭐하는지 알 수 있음 │
│ ├── GET /users/123 → 123번 사용자 조회 │
│ └── 디버깅 쉬움 │
│ │
│ 5. 웹 인프라 활용 │
│ ├── 기존 HTTP 캐시, 프록시, CDN 그대로 사용 │
│ └── 웹 생태계의 모든 도구 활용 가능 │
│ │
└─────────────────────────────────────────────────────────────────┘
6. 정리
┌─────────────────────────────────────────────────────────────────┐
│ REST 핵심 정리 │
│ │
│ REST의 본질: │
│ "웹이 성공한 아키텍처 원칙을 API에 적용하자" │
│ │
│ 6가지 제약조건: │
│ ├── 1. Client-Server: 관심사 분리 │
│ ├── 2. Stateless: 서버가 상태 안 기억 (확장성) │
│ ├── 3. Cacheable: 캐시 가능 (성능) │
│ ├── 4. Uniform Interface: 일관된 인터페이스 (핵심!) │
│ ├── 5. Layered System: 계층화 (유연성) │
│ └── 6. Code on Demand: 코드 전송 (선택적) │
│ │
│ Uniform Interface (가장 중요): │
│ ├── ① 리소스 식별: URI로 리소스 식별 (/users/123) │
│ ├── ② 표현으로 조작: JSON/XML로 상태 표현 │
│ ├── ③ 자기 서술적: 메시지만 보고 이해 가능 │
│ └── ④ HATEOAS: 응답에 다음 행동 링크 (거의 안 함) │
│ │
│ 현실: │
│ ├── 대부분 "Level 2 REST" (HTTP 메서드 활용) │
│ ├── HATEOAS까지 하는 곳 거의 없음 │
│ └── 그래도 SOAP보다 훨씬 나음! │
│ │
│ 핵심 가치: │
│ 단순함 + 확장성 + 웹 표준 활용 │
│ │
└─────────────────────────────────────────────────────────────────┘
관련 키워드
REST, RESTful, Uniform Interface, Stateless, HATEOAS, Richardson 성숙도, HTTP 메서드, 리소스, 표현, 자기 서술적 메시지