[개념 정리/네트워크] REST API
개발자 채용 공고에서 빠지지 않고 등장하는 용어 REST API.
REST API의 뜻과 API를 REST API로 만드는 방법에 대해 알아보자.
REST API란
REST API는 RESTful API라고도 하며,
RESTful하다는 것은 RESTful한 것으로 규정된 일련의 특징과 규칙 등을 지키는 것을 말한다.
2000년에 Roy Thomas Fielding이 쓴 논문에서 처음 등장한 개념으로,
논문에서는 REST API로서 갖춰야할 특징을 8가지로 규정했으나,
우리에게 실질적으로 주요한 특징 5가지만 살펴보자.
REST API 특징
1. Uniform-Interface
API에서 자원들은 각각의 독립적인 인터페이스를 가지며,
각각의 자원들이 url 자원 식별, 표현을 통한 자원 조작, Self-descriptive messages, HATEOAS 구조를 가지는 것을 말한다.
독립적인 인터페이스란 서로 종속적이지 않은 것을 말한다.
예컨대, 웹페이지를 변경했다고 해서 웹 브라우저를 업데이트 해야하는 일은 없어야 하고,
HTTP 명세나 HTML 명세가 변경되어도 웹페이지는 잘 작동해야 하는 것이다.
Uniform-Interface의 4가지 요소에 대해 살펴보자.
1) url 자원 식별
identification of resources로, 자원은 url로 식별되어야 함을 의미한다.
2) 표현을 통한 자원 조작
manipulation of resources through representations로,
url과 HTTP의 표준 메서드 GET, DELETE 등을 통해
자원을 조회, 삭제한다는 등
작업을 설명할 수 있는 정보가 담겨야 함을 의미한다.
3) Self-descriptive messages
self-descriptive는 스스로 표현한다는 의미이다.
HTTP Header에 데이터 타입을 명시하고, 각 메시지/자원들은 MIME types에 맞춰 표현되어야 한다.
예를 들어, .json 타입으로 데이터를 반환한다면, 헤더에 'Content-Type' = 'application/json'으로 명시해주어야 한다.
MIME types는 문서, 파일 등의 특성과 형식을 나타내는 표준이다.
IETF의 RFC6838에 정의 및 표준화되어 있다.
'font/ttf', 'text/plain', 'text/csv' 등이 있다.
아래는 self-descriptive messages 규칙을 지킨 경우(good)와, 그렇지 않은 경우(bad)의 예시 코드이다.
const express = require('express')
const app = express()
app.get('/good', (req, res) => {
res.status(200).json({"a":1})
})
app.geT('/bad', (req, res) => {
res.setHeader('content-type', 'application/json')
res.send("왈왈이")
})
const server = app.listen(3000, () => {
console.log("server is listening on port 3000")
})
good 케이스의 json() 메서드의 내부 코드를 살펴보면,
content-type = appliation/json으로 설정되어 있다.
4) HATEOAS 구조
HATEOAS(Hypermedia as the Engine of Application State)는
하이퍼링크에 따라 다른 페이지를 보여줘야 하며,
데이터마다 어떤 url로 요청했는지 명시해야 하는 것을 말한다.
링크임을 유추할 수 있는 변수명,
일반적으로 href, links, link, url 속성 중 하나에 데이터의 url을 담아 표기해야 한다.
2. Stateless
HTTP 자체가 Stateless하기 때문에
HTTP를 이용하는 것만으로 충족되는 규칙이다.
그 외 주의할 점은, 서버는 세션(session)을 서버 쪽에 유지하지 않아야 한다.
예를 들어, 서버에 세션 데이터를 저장하는 세션 기반 로그인 방식은
RESTful 하지 않다.
3. Cacheable
캐싱이 가능해야 함을 의미한다.
이 역시 HTTP는 원래 캐싱이 가능하므로, 아무런 로직을 구현하지 않아도 충족된다.
웹 사이트에서 새로고침을 하면 304가 뜨면서
캐싱된 js와 css이미지 등을 불러오는 것을 볼 수 있다.
이는 HTTP 메서드 중 GET 메서드에 한정되며,
'Cache-Control:max-age=100'(100초)와 같은 방식으로
한정된 시간으로 설정할 수 있다.
캐싱된 데이터가 유효한지 판단하기 위해
'Last-modified'와 'Etag'라는 헤더값을 사용한다.
'Etag'는 전달되는 값에 태그를 붙여, 캐싱되는 자원인지 확인하는 역할이다.
참고로, HTTP 헤더의 Cache-Contorl = no-store로 설정하면, 캐싱이 되지 않는다.
기본적으로는 Cache-Control=public으로 되어있어 캐싱 되도록 설정되어 있고,
HTTP Header를 기반으로 캐싱을 컨트롤 하는 것이 중요하다.
4. Client-Server 구조
클라이언트와 서버가 서로 독립적인 구조를 가져야한다는 규칙이다.
이 역시 HTTP를 통해 가능하다.
서버에서 HTTP 표준만 지킨다면
웹에서는 그에 따른 화면이 잘 나타나게 된다.
서버는 API를 제공하고, 그 API에 맞는 비즈니스 로직을 처리한다.
클라이언트는 HTTP로 데이터를 받는 로직만 잘 처리하면 된다.
5. Layered System
'클라이언트 > 클라이언트 캐시 > 서버 연결 > 서버 캐시 > 서버'와 같이
계층 구조로 나뉜 아키텍쳐를 뜻한다.
WEB기반 서비스들은 보통 이러한 시스템을 구축하게 된다.
REST API URI 규칙
RESTful API로서 자원을 표기하는 URI가 지켜야하는 규칙 6가지는 아래와 같다.
- 동작은 HTTP 메서드로만 표현해야 하며, url에 해당 내용이 포함되서는 안된다. 예를 들어, '/books/delete/1' 에서 delete와 같이 동작은 url에 포함되지 말아야 하며, '/books/1'으로 url 작성하여 DELETE 메서드를 사용해야 한다.
- .jpg, .png 등 확장자를 표시하지 말아야 한다.
- 동사가 아닌 명사로만 표기해야 한다. 예를 들어, 유저가 책을 소유한다는 것을 표현하는 url은 'user/userid/inclusion/book/bookid'와 같이 명사로만 표현되어야 하며, include, get와 같은 동사는 포함되지 말아야 한다.
- 계층적인 구조로 작성되어야 한다. '/집/아파트/전세' 식으로 좁혀져야 한다.
- 대문자가 아닌, 소문자로만 작성되어야 한다. 한 부분이 긴 경우에는 언더바(_)가 아닌, 바/하이픈(-)을 사용한다. 하이퍼링크 생성 시 밑줄이 생겨, 언더바를 식별하기 어렵기 때문이라는 추측이 있다.
- HTTP 응답 상태코드를 적절히 활용해야 한다. 성공 시에는 200, 리다이렉트는 301 등의 방식이다.
이 외에도 url은 쿼리스트링과 혼합하여 사용할 수 있다.
검색, 페이지네이션, 정렬 등 매개변수가 많거나 복잡할 때 쿼리스트링을 사용한다.
또한, url에 버전을 명시하는 것도 중요하다.
버전이 업데이트 되었을 때 필요하다.
'/wp/v2/pots?page=2'와 같이 작성될 수 있다.
참고: inflearn 강의 'CS 지식의 정석 - 큰돌'