Skip to main content

REST

•3 min•527 words

REST의 정의

REST(Representational State Transfer)는 네트워크에서 자원을 주고받기 위한 소프트웨어 아키텍처 스타일이다.
로이 필딩(Roy Fielding)이 2000년 박사학위 논문에서 제시했으며, 웹(HTTP)의 구조를 바탕으로 클라이언트와 서버가 자원을 일관된 방식으로 주고받도록 설계하는 원칙이다.
RESTful API는 REST 아키텍처의 제약 조건을 따르도록 설계한 API를 뜻한다.

예시

예를 들어 클라이언트가 사용자 정보를 조회한다고 하면 다음과 같이 요청할 수 있다.

GET /users/1

서버는 해당 자원의 표현을 응답한다.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "id": 1,
    "name": "동호"
}

여기서 /users/1은 자원을 식별하고, JSON은 자원의 표현이며, GET은 자원을 조회하는 동작을 나타낸다.

REST 아키텍처의 6가지 제약 조건

  • Client-Server: 클라이언트와 서버의 역할을 분리해 독립적으로 개발하고 확장할 수 있도록 한다.
  • Stateless: 각 요청은 처리에 필요한 정보를 포함하며, 서버가 이전 요청의 클라이언트 상태에 의존하지 않는다.
  • Cacheable: 응답을 캐시할 수 있는지 명시해 네트워크 통신과 서버 부하를 줄일 수 있다.
  • Uniform Interface: 클라이언트와 서버가 정해진 공통 규칙에 따라 통신한다.
  • Layered System: 클라이언트가 프록시나 로드 밸런서 같은 중간 계층의 존재를 알 필요 없이 통신할 수 있도록 한다.
  • Code on Demand (선택 사항)

Stateless

  • 서버가 클라이언트와 이전에 주고받은 요청의 상태에 의존하지 않고, 각 요청을 독립적으로 처리해야 한다는 제약 조건
  • 쉽게 말하면 서버가 이전 요청을 기억하지 않아도 현재 요청을 처리할 수 있어야 한다는 뜻

Stateless 한 경우 예시

GET /users/me
Authorization: Bearer <JWT>
  • 클라이언트가 요청마다 인증 토큰을 보낸다.
  • 서버는 토큰을 검증해 요청을 처리한다.
  • 서버가 이전 요청에서 누가 로그인했는지 기억할 필요가 없다.

Stateful한 경우 예시

  • 사용자가 로그인하면 서버가 세션에 로그인 상태를 저장한다.
  • 이후 요청에서 세션 ID를 받으면 서버는 저장해 둔 세션을 조회한다.
  • 서버가 이전에 생성한 로그인 상태에 의존한다.

엄밀히 말해 이 방식은 REST의 Stateless 제약 조건과 충돌한다고 할 수 있다.

Uniform Interface

  1. 자원 식별: URI를 통해 어떤 자원을 다루는지 명확하게 표현한다.
    • 예: GET /users/1
  2. 표현을 통한 자원 조작: 클라이언트가 자원의 표현을 보내거나 요청하면 서버가 그에 맞게 자원을 처리한다.
    • 예: PATCH
  3. 자기 서술적 메시지: 요청과 응답 자체에 메시지를 해석하고 처리하는 데 필요한 정보가 있어야 한다는 뜻이다.
    • 예: Content-Type: application/json
  4. HATEOAS: 서버가 응답에 관련된 다음 작업의 링크를 제공하는 방식이다.

REST의 장점

확장성

  • 클라이언트와 서버의 역할이 분리되어 있어 서로 독립적으로 개발하고 확장하기 쉽다.

유연성

  • JSON 등 다양한 표현 방식으로 데이터를 주고받을 수 있어 여러 플랫폼에서 활용하기 좋다.

캐싱을 통한 성능 개선

  • 캐시 가능한 응답을 활용하면 반복적인 요청에 대한 서버 부하와 네트워크 통신을 줄일 수 있다.

일관된 인터페이스

  • 자원 중심 URI와 HTTP 메서드 등을 일관되게 사용하면 API를 이해하고 사용하기 쉬워진다. (처음 보는 사람도)

REST의 한계

복잡한 기능의 표현

만약 로그인과 로그아웃을 REST의 자원 중심 설계 원칙에 맞춰 표현한다면 어떻게 해야 할까?
예를 들어 로그인과 로그아웃을 다음과 같이 설계했다고 가정해보자.

  1. 로그인
POST /users
Content-Type: application/json

{
  "id": "dongho",
  "password": "1234"
}

하지만 POST /users는 얼핏 보기에는 로그인이 아니라 회원가입처럼 보이기도 한다.
일반적으로 POST는 새로운 자원을 생성한다는 의미이기 때문이다.

  1. 로그아웃
DELETE /users/{id}

로그아웃은 어떨까? 이 요청은 사용자 계정을 삭제한다는 의미로 해석된다.
로그아웃은 계정을 삭제하는 것이 아니라 현재 로그인 상태를 종료하는 작업이므로 적절해 보이지 않는다.

그런데 실무에서는 다음과 같은 엔드포인트도 흔히 사용된다.

POST /login
POST /logout

URI에 행위를 직접 표현하고 있으니 REST 원칙을 어긴 것일까?
하지만 이것만으로 API 전체가 RESTful 하지 않다고 단정할 수는 없다.

REST 원칙을 이해하는 것도 중요하지만, 모든 기능을 억지로 자원과 CRUD에 맞추기보다는 기능의 의미가 명확하게 드러나는 API를 설계하는 것도 중요하다고 생각한다.

오버 헤드

  • HTTP 헤더와 텍스트 기반 데이터 등의 특성으로 인해 상황에 따라 데이터 크기나 통신 비용이 커질 수 있다.

여러 자원 조회 시 비효율성

  • 화면 하나를 구성하기 위해 여러 자원을 조회해야 한다면 여러 번 요청해야할 수 있다.