본문 바로가기
개발지식/CS 지식

REST 개념 정리 및 논문 분석

by 감중에홍시 2023. 8. 12.

'REST하다. RESTful하다' 라는 게 정확히 뭔데?

얼마 전에 회사에서 API를 만든 후, 코드 리뷰를 할 때, '이 코드는 Rest 하지 못한 거 같다.'라는 말을 들을 때, 그게 무슨 뜻인지 얼추 짐작은 가지만 정확히 무슨 뜻인지는 모르겠는 상황이 있었다. 아마 적지 않은 수의 개발자들도 정확히 'REST API가 무엇이냐. RESTful한 API가 무엇이냐'라는 물음에 대해서 상세하게 답을 하지 못할 것이라고 생각한다.

또한 요즘 올라오는 공고들을 보면 자격 요건이나 우대 사항에 ''REST API에 대한 이해가 있으신 분"을 항목을 적어놓는 공고들을 심심치 않게 볼 수 있다.

<채용 공고에 올라와 있는 REST의 개념을 알고 있거나 경험해 본 적이 있는 지에 대한 요구사항>

그래서 이 참에 REST라는 개념을 처음 만들어 낸 로이 필딩(Roy Fielding)의 논문 5장을 읽어보며 REST API에 대한 개념을 정리해보고자 한다.

 

 

REST란?

REST란 REpresentational State Transfer의 약자이다. 이렇게만 보면 무슨 뜻인지 모르겠다.

논문에 따르면 REST란 분산 하이퍼미디어 시스템(ex: 웹)을 위한 아키텍쳐 스타일(Representational State Transfer (REST) architectural style for distributed hypermedia systems)이라고 정의되어 있다.

여기서 아키텍처 스타일이란 제약조건의 집합을 의미한다. 즉, 'REST하다' 라는 뜻은 웹 같은 것을 만들 때 지켜야 할 제약 조건들을 모두 지켰을 때 비로소 'REST하다'라고 말할 수 있는 것이다.

그렇다면 REST를 구성하는 제약 조건들에는 어떤 것들이 있을까?

논문에는 다음 6가지 제약 조건들을 소개한다.

  • Client-Server
  • Stateless
  • Cache
  • Uniform Interface
  • Layered System
  • Code-On-Demand

 

 

1. Client-Server

Client-Server 원칙은 클라이언트-서버 아키텍처 소프트웨어 시스템의 구성 원칙 중 하나로, 사용자의 인터페이스와 데이터 저장 관련 사항을 분리하여 시스템을 구성하는 원칙이라고 나와있다. 쉽게 말하자면, 클라이언트와 서버의 역할을 명확하게 구분하여 독립적으로 시스템을 구축하라는 의미이다.

예를 들어 브라우저(클라이언트)는 요청만 하고, 서버는 응답만 하는 식으로 아키텍쳐를 분리해서 개발을 해야 한다.특히나 웹에서는 이 분리로 인해 클라이언트와 서버가 독립적으로 진화할 수 있게 되어, 여러 도메인에서 발생하는 요구 사항에 대해서 잘 지원할 수 있게 됨을 설명하고 있다.

Roy Fielding이 해당 논문을 발표한 시기가 2000년도인데, 실제로 Client와 Server가 독립적으로 진화하여 현재에 이르러서 프론트엔드와 백엔드 개발이 나뉜 것을 보면, Roy Fielding의 통찰력이 대단하다는 것을 느낄 수 있는 부분이다.

 

 

2. Stateless

Client-Stateless-Server

Stateless 원칙은 클라이언트에서 서버로의 각 요청은 해당 요청을 이해하기 위해 필요한 모든 정보를 포함해야 하며 각각의 요청들 사이에는 어떠한 상관관계가 있으면 안된다는 것을 의미한다.

즉, 각각의 요청들이 독립적으로 처리되어야 한다는 뜻이다. 예를 들어, 요청 1이 성공해야 요청 2도 성공이 될 수 있게 설계를 하면 안된다. 요청 1을 보낼 때, 요청 하나 만으로도 충분히 데이터를 가져오는 데 무리가 없도록 요청 1에 필요한 정보들을 다 넣어서 요청을 보내야 한다.

 

 

3. Cache

Cache 원칙은 네트워크 효율성을 향상시키기 위해, 웹 페이지 구성 요소들(html, img 파일들)은 캐싱이 가능해야 한다라는 원칙이다.

캐시란?
=> 데이터나 정보를 일시적으로 저장해 놓는 메모리나 저장 공간의 의미한다. 이렇게 저장된 데이터는 이후에 동일한 데이터에 대한 요청이 있을 때, 데이터에 빠르게 다시 접근할 수 있도록 도와준다. 예를 들어 웹 브라우저의 캐시는 이미 방문한 웹 페이지의 이미지, html, css 등을 임시로 저장하여 동일한 웹 페이지에 재방문 했을 때, 페이지를 이전보다 더 빠르게 렌더링 하도록 도와준다. 

또한 논문에서는 캐시 제약 조건에 대해서도 설명하는데, 캐시 제약 조건이란 응답되는 데이터가 캐시가 가능한 데이터인지 아닌지를 암묵적, 명시적으로 표시하는 것을 의미한다. 만약 응답된 데이터가 캐시 가능하면 클라이언트는 나중에 같은 요청에 대해서 응답 데이터를 재사용 할 수 있는 권한을 부여 받는다.

캐시는 데이터의 반복되는 요청에 대한 응답 속도를 향상시키는 장점이 있지만, 데이터가 변경 되었을 때, 캐시된 데이터와 원본 데이터가 일치하지 않는 문제가 발생할 수 있다. 따라서 논문에서는 캐시 제약 조건을 지정할 때, 트레이드오프(한 쪽이 개선되면 한 쪽이 안좋아지는 현상)가 발생할 수 밖에 없다고 설명하고 있다.(예를 들어, 캐시 저장 시간을 길게 하면, 해당 시간동안, 데이터의 응답 속도가 빨라져 네트워크의 효율성이 올라갈 수 있지만, 대신 해당 시간이 길어지면 길어질수록 데이터가 변경될 확률이 증가함으로 원본 데이터를 제대로 제공할 수 있는 확률을 떨어진다.)

사실 캐시에 관련해서는 현재 크롬이나 파이어폭스 등의 브라우저에서 잘 관리해주고 있기 때문에 크게 신경쓸 필요는 없는 부분이다.

 

 

4. Uniform Interface ☆

사실 API를 작성하거나 구성할 때, 이 Uniform Interface 원칙이 가장 중요한 부분이다.

논문에서는 REST 아키텍처 스타일이 다른 네트워크 기반 스타일과 구분되는 가장 핵심적인 특성으로 '구성 요소 간에 일관된 인터페이스에 있다'라고 설명한다.

또한 REST 아키텍처에서 API나 URL을 만들 때, 형식(interface)이 매우 간결하고, 예측이 가능하게 만들어야 한다고 말하고 있다. Uniform Interface 원칙은 소프트웨어 공학 원칙 중 일반성을 구성 요소 인터페이스에 적용함으로써 전체 시스템 아키텍처가 단순화되고 상호 작용의 가시성이 향상된다는 장점이 있다.

좋은 REST API Uniform Interface를 만들기 위해서는 4가지 제약 조건들이 존재한다.

  1. Identification of resources
  2. Manipulation of resources through representations
  3. Self-descriptive messages
  4. Hypermedia as the engine of application state(HATEOAS)

 

● Identification of resources: 리소스들이 URI로 식별되면 된다.

리소스들이 URI로 잘 식별되는 REST API 예시를 보도록 하자

  • www.example.com/product/66432
  • www.instagram/explore/tags/kpop/
  • www.facebook.com/natgeo/photos/

각각의 URI들이 어떤 주소를 의미하는 지 직관적으로 알 수 있겠는가? 그렇다.

첫 번째 URL의 경우 example.com의 66432번째 물건을 불러오도록,

두 번째 URL의 경우 인스타그램에서 kpop이라는 태그로 검색했을 때, 나오는 사진들을 불러오도록

세 번째 URL의 경우 페이스북에서 natgeo라는 사용자의 photos를 불러오도록 작명되어 진 것을 쉽게 알 수 있다.

리소스들을 URI를 통해 잘 식별되는 REST API를 만들기 위해서는 다음과 같은 작명 원칙들이 있다.
1. URL은 명사로 작성하는 것을 추천한다.
2. 하위 문서를 나타낼 때에는 /로 구분한다.
3. 파일 확장자명(ex: .html)을 사용하지 않는다.
4. 띄어쓰기는 - 기호를 사용한다.
5. 자료 하나당 하나의 API를 할당한다.

 

Manipulation of resources through representations: 리소스를 표현을 통해 조작한다

 자세하게 예를 들자면, 리소스를 만들거나 업데이트 하거나 삭제하거나 할 때, HTTP 메시지에다가 그 표현을 담아서 보낸 후, 그 목적을 달성하도록 하는 것을 의미한다. 이는 HTTP 메서드와 리소스 표현을 조합하여 클라이언트가 서버의 리소스를 조작하는 방식을 지칭한다. 이 제약을 따르는 좋은 REST API 예시를 보도록 하자

HTTP 메서드: POST
URI: /users

Request Body
{
  "name": "John Doe",
  "email": "johndoe@example.com"
}​


Response

{
  "id": "123456",
  "name": "John Doe",
  "email": "johndoe@example.com"
}​

이 예시에서는 /users 엔드포인트로 POST 요청을 보내면, 클라이언트가 새로운 사용자를 생성하려고 한다는 것을 나타낸다. Request 본문에는 사용자의 이름과 이메일이 포함되어 있고, 서버는 새로운 사용자를 생성하고 고유한 ID를 부여하여 응답한다. 이 경우, 클라이언트는 사용자 리소스를 표현(JSON 객체)을 통해 조작한다고 볼 수 있다.

이 예시에서는 HTTP POST 메서드를 사용하여 리소스를 생성하고 있지만, 다른 메서드(예: PUT, PATCH, DELETE)를 사용하여 리소스를 업데이트, 부분적으로 변경 또는 삭제하는 것도

"Manipulation of resources through representations"의 일부로 간주된다.

 

 Self-descriptive messages: 메시지가 스스로를 설명해야 한다.

Self-descriptive messages 원칙은 메시지 자체가 스스로 설명하는 형태를 가지도록 하는 것을 의미한다.

즉, 클라이언트와 서버 간의 통신에서 사용되는 메시지는 그 내용과 의미를 명확하게 나타내 주어야 한다. 이를 통해 클라이언트와 서버는 각각 어떤 동작을 해야 하는지 명확하게 이해하고, 추가 문서나 상호 작용 없이도 통신할 수 있어야 한다.

다음은 self-descriptive message 원칙을 잘 지킨 REST API의 예시이다

HTTP 메서드: GET
URI: /users/123456

Response
{
  "id": "123456",
  "name": "John Doe",
  "email": "johndoe@example.com",
  "created_at": "2023-07-28T10:00:00Z",
  "updated_at": "2023-07-28T14:30:00Z"
}​

 이 예시에서의 Response는 응답 메시지 자체가 본인에 대한 설명을 제공하고 있다. 각 속성의 이름들 자체로 의미를 가지고 있으며 (name -> 이름, email -> 이메일, created_at -> 생성 날짜, updated_at -> 업데이트 된 날짜) 이와 같이 자체 기술 메시지를 사용하면 클라이언트가 응답 메시지만 가지고도 리소스의 구조와 의미를 이해할 수 있다.
 또한 클라이언트는 별도의 문서나 지식 없이도 리소스와 상호 작용 할 수 있다는 특징이 있다.

 

Hypermedia as the engine of application state(HATEOAS)

이 원칙은 클라이언트가 서버와 상호 작용하기 위해 필요한 모든 정보를 서버의 응답 메시지에 포함 시키는 것을 의미한다. 즉, 클라이언트링크를 통해 리소스 간의 관계와 동작을 알 수 있어야 한다(링크를 통한 어플리케이션 상태의 전이)

의미이다. HATEOAS는 클라이언트와 서버 간의 결합도를 낮추고, 서버의 변경에 유연하게 대응할 수 있는 아키텍처를 제공한다.(왜냐하면 서버에서 코드가 바뀐다 한들, 클라이언트 단에서 링크를 통한 동작에는 영향을 끼치지 않기 때문)

사실 최근에 우리가 흔히 말하는 REST API에서 가장 잘 지켜지고 있지 않은 REST 설계 원칙이 바로 이 HATEOAS이다.

우리 회사만 하더라도 API를  작성하거나 할 때, 응답코드, 메시지, 기타 속성들을 response 객체에 넣어서 보내고, response 객체에 링크를 넣어서 API를 작성하는 경우는 없다... 

링크를 통해 어플리케이션의 기능, 상태들을 전이 시키는 간단한 todolist 예시

다음은 HATEOAS 원칙을 따르는 REST API 응답 예시이다.

HTTP 메서드: GET
URI: /users/123456

Response
{
  "id": "123456",
  "name": "John Doe",
  "email": "johndoe@example.com",
  "links": [  // link를 통해 클라이언트가 할 수 있는 동작들에 대해 알 수 있게 해 줌
    {
      "rel": "self",
      "href": "/users/123456"
    },
    {
      "rel": "edit",
      "href": "/users/123456/edit"
    },
    {
      "rel": "delete",
      "href": "/users/123456/delete"
    }
  ]
}​

이 예시에서 응답 메시지에는 리소스에 대한 기본 정보뿐만 아니라 "links" 배열이 포함되어 있다. 이 링크들은 클라이언트가 다음과 같은 작업을 할 수 있는지를 나타낸다
1. "self": 현재 리소스에 대한 링크
2. "edit": 리소스를 수정하는 링크
3. "delete": 리소스를 삭제하는 링크
이런 방식으로 클라이언트는 응답 메시지를 통해 가능한 동작을 알 수 있고, 이에 따라 다음 동작을 수행할 수 있다.

 

 

5. Layered System

인터넷 규모의 요구 사항을 더욱 개선하기 위해서는 Layered System이 필요하다고 말하고 있다. 계층 시스템 설계는 각 layer들이 상호작용하는 직접적인 계층을 넘어서 볼 수 없도록 제약을 둔 시스템 스타일을 의미한다. 이로써 시스템이 계층적으로 구성되어 단순화되고, 기반 독립성이 증진될 수 있는 스타일을 만들 수 있다.

Layered System에서 각 계층은 레거시 서비스를 캡슐화 하고 새로운 서비스를 레거시 클라이언트로부터 보호할 수 있으며, layer들 사이의 중개자는 여러 네트워크 및 프로세서 간에 서비스의 부하 분산 및 보안 정책 적용 등을 가능하게 하여 시스템의 확장성을 향상시키는 데 도움이 될 수 있다고 말하고 있다.

Layered System의 주요 단점으로는 데이터 처리에 지연 시간이 발생하여 클라이언트가 응답을 인지하는 성능을 감소시킨다는 것이 있다.(허나 이는 캐시를 통해 상쇄 가능하다고 설명하고 있다.)

 

 

6. Code-On-Demand

REST 제약 조건의 마지막 원칙은 Code-On-Demand(코드 실행 스타일)이다. Code-On-Demand코드를 서버에서 클라이언트로 보내서 실행할 수 있어야 됨을 의미한다(대표적인 예가 바로 자바스크립트!). 따라서 배포 후에도 여러 기능들이 담긴 코드들을 다운로드하여 확장할 수 있게 함으로써 시스템의 확장성을 향상 시킬 수 있다는 것에 의의가 있다.

이는 자바스크립트를 통해서 이미 잘 구현이 되어 있으므로 의미만 알고 넘어가면 될 듯하다.

 

 

끝맺음

REST의 아버지 Roy Fielding의 논문과 여러 자료들을 통해 REST의 개념과 원칙에 대해 자세히 살펴보았다.

REST를 요약하자면 웹과 같은 환경에서 어플리케이션을 만들 때, 지켜야할 제약 조건이라는 것을 알게 되었고,

REST 제약 조건들을 잘 지키면서 만든 API가 REST API라는 것을 알게 되었다. 당연하게도 REST 제약 조건들을 잘 지키면서 설계, 개발 하는 것이 RESTful 하다는 것도 알게 되었다.

우리는 REST의 원칙을 잘 따르는 API를 설계하고 개발함으로써 클라이언트와 서버 간의 원활한 상호 작용을 구현할 수 있고, 우리가 개발하는 소프트웨어가 더 효율적이고 확장 가능한 구조를 갖도록 도와줄 수 있다.

 

 

출처

Roy Fielding. CHAPTER 5: Representational State Transfer (REST)

[네이버 D2]Day1, 2-2. 그런 REST API로 괜찮은가

'개발지식 > CS 지식' 카테고리의 다른 글

네트워크 토폴로지 & 병목현상  (0) 2023.05.18