깨알 개념/기타

REST API (REpresentational State Transfer Application Programming Interface)

interfacer_han 2024. 5. 26. 13:29

#1 무엇(What)에 대한 API인가?

#1-1 개요

REST(REpresentational State Transfer) 또는 RESTful API는 서버의 자원(Resource)을 두고, 클라이언트와 서버 간의 통신 방법을 규정하는 API(Application Programming Interface)다. 자원이란 데이터베이스에 저장된 데이터다. 블로그를 예로 들면 게시글, 댓글 등이 자원이다.
 
이 자원은 서로 구분할 수 있는 고유한 식별자(URI)를 가지고 있다. REST API에서는 URI에 속하는 하위 범주인 URL을 자원의 식별자로서 사용한다. 그리고 이 URL이 바로 우리가 일상 생활에서 사용하는 단어인 인터넷 주소다.
 

#1-2 URL(Uniform Resource Locator)의 구조

이미지 출처: https://medium.com/@abhirup.acharya009/uri-vs-urn-vs-url-key-distinctions-explained-dec8e02ebd18

URL의 전체 구조는 위와 같다.
 

#1-3 Scheme

http
https
ftp
ssl
...

통신을 위한 체계(Scheme)로, 프로토콜이라고 한다.
 

#1-4 Host

www.naver.com
123.789.456.777
...

서버의 도메인 이름이나 IP 주소를 지정한다.
 

#1-5 Port (생략 가능)

:123
:777
:80
:443
...

서버의 어떤 포트와 통신할 지를 지정한다. 생략 시에는 기본 포트 번호가 할당되는데, HTTP의 기본 포트 번호는 80이고 HTTPS의 기본 포트 번호는 443이라고 한다.
 

#1-6 Path (생략 가능)

/137
/posts
/posts/4
...

자원의 위치다. 여러 폴더에 겹겹히 싸여있는 파일이라고 생각하면 된다. 예를 들어 본 게시글 #2-1의 이미지 하단에 있는 출처 링크에서는 /@abhirup.acharya009/uri-vs-urn-vs-url-key-distinctions-explained-dec8e02ebd18가 Path다.
 

#1-7 Query Parameters (생략 가능)

?id=2
?grade=1&major=computer%20science
...

불러올 자원을 선별하는, 필터링하는 일종의 조건문이다. 클라이언트는 자신이 필요한 정보만을 서버에 요청할 수 있어 좋고, 서버도 보내줄 자원의 데이터를 절약하는 효과가 있기에 이득이다.
 

#1-8 Fragment Identifier (생략 가능)

#summary
#inherited-fields
...

자원의 특정 문단을 의미한다. Fragment Identifier가 포함된 URL을 브라우저에서 열면, 해당 문단까지 스크롤이 내려간 상태로 웹 페이지가 열린다. 예를 들어, 이 링크이 링크는 Fragment Identifier가 서로 다르지만, 나머지는 똑같은 URL 링크다. 그럼에도 브라우저로 열었을 때 보이는 내용이 다르다.

#2 어떻게(How) 자원을 요청(Request)하는가?

#2-1 CRUD의 구현

데이터베이스의 기본적인 동작은 Create, Read, Update, Delete로 우리말로 하면 차례대로 데이터 생성, 읽기, 갱신, 삭제다. 영어 앞 글자를 하나씩 따서 CRUD라고도 부른다. #1에서 REST API는 데이터베이스에 저장된 자원을 다루는 API라고 했다. 따라서 REST API에는 CRUD을 구현(상속)하는 동작이 있다. Create를 구현한 Post, Read를 구현한 Get, Update를 구현한 Patch 또는 Put, Delete를 똑같은 이름으로 구현한 Delete가 그것이다. 이 Post, Get, Patch, Put, Delete를 HTTP 메소드라고 하며, REST API는 HTTP 메소드를 통해 자원을 요청한다. 이름에서 알 수 있듯 HTTP 메소드는 HTTP 프로토콜의 일부다.

 

여담으로 여기서 주의할 점은, HTTP 프로토콜은 REST API의 전유물이 아니라는 것이다. REST API는 HTTP 프로토콜을 사용하는 수 많은 API 중 하나에 불과하다.
 

#2-2 HTTP (Hypertext Transfer Protocol) 요청의 구조

[HTTP 메소드] [Path + Query Parameter + Fragment Identifier] [HTTP 버전]
Host: [Host + Port]
User-Agent: [클라이언트의 소프트웨어(운영체제, 브라우저 등) 종류와 버전 명시]
Content-Type: [(Body가 존재하는 경우만) 클라이언트가 서버로 보내는 미디어 타입(콘텐츠 유형) 명시]
Content-Length: [(Body가 존재하는 경우만) 본문(Body)만의 길이(바이트 단위)를 명시. Body가 없다면 0으로 두거나 생략]
Accept: [(생략 가능) 클라이언트가 서버로부터 받기를 원하는 미디어 타입(콘텐츠 유형) 명시]
Accept-Language: [(생략 가능) 클라이언트가 원하는 언어의 우선 순위를 지정]
Referer: [(생략 가능) 이전 페이지의 URL. 사용자가 어디에서 왔는지 명시]
Cache-Control: [(생략 가능) 캐시 사용에 대한 설정]
Connection: [(생략 가능) 클라이언트와 서버 간의 연결을 유지할 것인지에 관한 내용]

[Body (본문)]

전체적인 문법은 위와 같다. HTTP Method는 HTTP 프로토콜의 Header만 쓰는가? 아니면 Body까지 쓰는가?를 기준으로 나눌 수 있다. [Body]의 윗 부분이 전부 Header다. Body는 서버에 어떤 정보를 보내기 위한 공간이다. 글과 사진이 잔뜩 첨부된 게시글을 인터넷 게시판에 작성하는 경우를 예로 들 수 있다. Post, Patch, Put이 Body까지 쓰는(필요로 하는) HTTP Method다. 반면, Get과 Delete는 자원의 단순 참조, 단순 삭제이기 때문에 Body가 필요없다.
 
아래는 그 예시들이다.
 

#3-2 Post

POST /api/users HTTP/1.1
Host: www.example.com
Content-Type: application/json
Content-Length: 63

{
  "name": "John Doe",
  "email": "john.doe@example.com"
}

http://www.example.com/api/users에 새 user를 추가(Create)하는 Post Request다. 포트 번호는 생략되었으며, 이 경우 기본값 포트 번호로 요청된다.
 

#3-3 Get

GET /api/resource?id=123 HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: application/json
Accept-Language: en-US,en;q=0.9
Referer: https://www.google.com/
Cache-Control: max-age=0
Connection: keep-alive

www.example.com/api/resource?id=123을 Get(=Read)하는 요청(request)이다. 이 클라이언트는 해당 자원을 JSON 형식으로 받기를 원하고 있다.
 

#3-4 Patch

PATCH /api/users/123 HTTP/1.1
Host: http://www.example.com
Content-Type: application/json
Content-Length: 40

{
  "email": "new.email@example.com"
}

Patch는 부분 수정이다. 따라서, 수정이 필요한 부분만 새로 보낸다. 보내지 않은 부분은 기존의 자원이 그대로 유지된다.
 

#3-5 Put

PUT /api/users/123 HTTP/1.1
Host: www.example.com
Content-Type: application/json
Content-Length: 67

{
  "name": "John Smith",
  "email": "john.smith@example.com"
}

HTTP 메소드를 제외하면 Post와 완전히 똑같다. 다만, Post는 존재하지 않는 자원을 처음으로 만드는 요청이지만, Put은 이미 존재하는 자원에 가하는 요청이라는 차이가 있다. 
 

#3-6 Delete

DELETE /api/users/123 HTTP/1.1
Host: www.example.com

해당 Path의 자원을 삭제한다.
 

#4 어떻게(How) 자원을 응답(Response)해 주는가?

클라이언트가 요청(Request)했으므로, 이제는 서버가 응답(Response)해줄 차례다.
 

#4-1 HTTP (Hypertext Transfer Protocol) 응답의 구조

[HTTP 버전] [상태 코드] [(생략 가능) 상태 메시지]
Data: [응답이 생성된 날짜와 시간]
Content-Type: [응답의 미디어 타입(콘텐츠 유형) 명시]
Content-Length: [본문(Body)만의 길이(바이트 단위)를 명시]
Location: [(주로 상태코드가 3xx일 때) 클라이언트에게 전달할 자원의 위치]
Server: [(생략 가능) 서버의 소프트웨어(운영체제, 브라우저 등) 종류와 버전 명시]
Cache-Control: [(생략 가능) 캐시 사용에 대한 설정]
Expires: [(생략 가능) 응답의 유효기간을 클라이언트에게 알려줌. 유효기간 이후에는 해당 응답을 다시 요청해야 함]
Set-Cookie: [(생략 가능) 서버가 클라이언트에게 쿠키를 설정하도록 지시하는 데 사용]

[Body (본문)]

전체적인 문법은 위와 같다. 상태 코드는 3자리 숫자로, 크게 5가지의 범주로 나눠진다. 상태코드의 백의 자리 숫자가 1인 경우 즉 상태코드가 1XX인 경우는 정보, 예를 들어 클라이언트의 남은 요청을 더 받을 준비가 되었다는 것과 같은 정보를 알려주는 응답이다. 상태코드가 2XX는 요청이 성공적으로 접수되었음을 의미한다. 3XX는 클라이언트의 요청이 완성되기 위해서 리다이렉션등의 추가적인 조치가 필요한 경우를 의미한다. 4XX는 클라이언트 측의 오류로 인해 요청이 실패했음을, 5XX는 서버 측의 오류로 요청이 실패됐음을 의미한다. (참조: 전체 응답 코드의 리스트)
 
아래는 그 예시다.
 

#4-2 상태 코드 1XX

HTTP/1.1 100 Continue
Date: Tue, 01 Jan 2024 12:00:00 GMT
Server: Apache/2.4.38 (Ubuntu)
Content-Length: 0
Connection: keep-alive

클라이언트가 요청에 담긴 본문이 다 서버로 올 때까지 계속해서 보내라는 서버 측의 응답
 

#4-3 상태 코드 2XX - 본문(Body)이 HTML 형식인 응답

HTTP/1.1 200 OK
Date: Tue, 01 Jan 2024 12:00:00 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 129

<!DOCTYPE html>
<html>
<head>
  <title>Example Page</title>
</head>
<body>
  <h1>Hello, world!</h1>
</body>
</html>

 

#4-4 상태 코드 2XX - 본문(Body)이 JSON 형식인 응답

HTTP/1.1 200 OK
Date: Tue, 01 Jan 2024 12:00:00 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 61

{
  "name": "John",
  "age": 30,
  "city": "New York"
}

 

#4-5 상태 코드 3XX

HTTP/1.1 301 Moved Permanently
Date: Tue, 01 Jan 2024 12:00:00 GMT
Server: Apache/2.4.38 (Ubuntu)
Location: https://www.example.com/new-page
Content-Length: 0
Connection: keep-alive

클라이언트가 요청한 자원이 새 위치로 이동되었음을 나타내는 응답이다. 클라이언트는 Location에 명시된 자원으로 자동으로 이동된다.
 

#4-6 상태 코드 4XX

HTTP/1.1 404 Not Found
Date: Tue, 01 Jan 2024 12:00:00 GMT
Content-Type: text/plain; charset=UTF-8
Content-Length: 13

404 Not Found

클라이언트가 요청한 자원이 존재하지 않는 경우의 응답.
 

#4-7 상태 코드 5XX

HTTP/1.1 500 Internal Server Error
Date: Tue, 01 Jan 2024 12:00:00 GMT
Content-Type: text/plain; charset=UTF-8
Content-Length: 18

500 Internal Error

서버 내부 오류.
 

#5 REST API를 구현하는 서버가 지켜야할 규칙

#5-1 Stateless

클라이언트: "서버야, A 알려줘."
서버: "네."
...
클라이언트: "서버야, A 알려줘."
서버: "아까 전에 이미 내가 정보를 알려줬던 클라이언트잖아? 그러니까 안 돼."
클라이언트: "이 서버 Stateless 안 지키네..."

서버는 클라이언트에 대해 아무것도 기억하지 않아야 한다.
 

#5-2 Idempotent (멱등성)

클라이언트: "(인터넷 쇼핑 중) 컴퓨터가 렉이 심해서, 실수로 주문 완료 버튼이 5번 클릭돼버렸네."
서버: "요청하신 5건의 주문이 완료되었습니다."
클라이언트: "이 서버 멱등성 안 지키네..."

멱등(冪等): 연산을 여러 번 적용하더라도 결괏값이 달라지지 않는 일.
 

#5-3 Cacheability

클라이언트: "(동영상 사이트 이용 중) 이 뮤직 비디오 반복 재생 설정해서 틀어놔야지."
...
클라이언트: "벌써 휴대폰 데이터가 다 소진됐네 왜지?"
클라이언트: "아... 이 사이트가 동영상을 캐시에 저장않고, 반복 재생될 때마다 새로 다운로드하고 있었구나."
클라이언트: "이 서버 Cacheability 안 지키네..."

클라이언트와 서버는 서로에 대해 '기억'하지 않아야 하지만, 각각 어떤 응답을 보내거나 받았는지는 기억해두는 게 좋다. 클라이언트 측의 Cacheability를 다룬 위 예시와 반대 방향으로, 서버 측의 Cacheability도 그렇다. 예를 들어, 대부분의 클라이언트들이 요청하는 인기 자원이 있다면, 서버의 캐시 저장소에 둬서 재빨리 응답해줄 수 있게 한다.
 

#6 요약

REST API는 서버의 자원(Resource)을 두고, 클라이언트와 서버 간의 통신 방법을 규정하는 API다.
 

#7 이 개념이 사용된 글

-