13장. REST API 와 FastAPI
RESTful 설계
도서관의 서가에는 "소설", "역사" 같은 이름표가 붙어 있고, 할 수 있는 일은 "빌리기, 반납하기"처럼 정해져 있습니다. REST 는 API 를 이렇게 짭니다. 주소는 자원(명사)을 가리키고, 할 일은 메서드(동사)로 나타냅니다.
네 카드는 자주 쓰는 메서드입니다. GET 은 읽기만 하고 서버의 상태를 바꾸지 않습니다. POST 는 새로 만들거나 처리를 맡깁니다. 예측처럼 본문을 받아 계산하는 일도 POST 입니다. PUT 은 통째로 바꾸고, DELETE 는 지웁니다. 여기서 서가의 이름표가 주소, 빌리기와 반납하기가 메서드입니다.
주소 짜기
| 좋은 꼴 | 피할 꼴 | 까닭 |
|---|---|---|
GET /orders/3001 | GET /getOrder?id=3001 | 동사는 메서드가 맡는다 |
POST /predict | GET /predict?지역=서울&... | 입력이 길고 기록에 남으면 안 되는 값은 본문으로 |
GET /orders?지역=서울 | GET /orders/seoul/list | 거르기는 쿼리 문자열로 |
POST /v2/predict | 같은 주소의 응답 꼴을 몰래 바꾸기 | 꼴이 바뀌면 판을 올린다 |
GET, PUT, DELETE 는 같은 요청을 여러 번 보내도 결과가 같아야 합니다(멱등). 네트워크가 끊겨 다시 보내도 안전하다는 뜻입니다. POST 는 그렇지 않을 수 있어서, 주문 만들기를 두 번 보내면 주문이 둘 생길 수 있습니다.
주소를 나눠 보기
from urllib.parse import urlsplit, parse_qs
# 주소는 경로(자원)와 쿼리 문자열(거르기 조건)로 나뉜다
url = "http://localhost:8013/orders?지역=서울&택배사=한결택배"
parts = urlsplit(url)
print(parts.path, parse_qs(parts.query))코드에 나오는 함수
| 코드 | 쉬운 뜻 |
|---|---|
urllib.parse | URL(Uniform Resource Locator, 자원의 위치를 적는 통일된 꼴)을 다루는 표준 모듈이다 |
urlsplit(url) | 주소를 방식(scheme), 서버(netloc), 경로(path), 쿼리 문자열(query) 등으로 나눈다 |
parts.path | 서버 뒤, ? 앞의 경로다. 자원을 가리킨다 |
parts.query | ? 뒤의 글이다. 이름=값 이 & 로 이어진다 |
parse_qs(...) | parse query string(쿼리 문자열 풀기)의 줄임이다. 쿼리를 이름과 값 목록의 사전으로 바꾼다 |
이 코드가 뜻하는 것
도서관 대출 신청서에는 도서관 이름, 서가 이름, 그리고 "서울 관련 책만" 같은 조건 칸이 있습니다. 여기서 도서관 이름이 서버 주소, 서가 이름이 경로, 조건 칸이 쿼리 문자열입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
url = "http://..." | 주문 목록을 지역과 택배사로 거르는 주소 | 대출 신청서 한 장 |
parts = urlsplit(url) | 주소를 부분별로 나눈다 | 신청서의 칸을 나눠 읽기 |
print(parts.path, parse_qs(parts.query)) | 경로와 조건을 보인다 | 서가 이름과 조건 칸 |
실행하면 /orders {'지역': ['서울'], '택배사': ['한결택배']} 가 나옵니다. 경로 /orders 가 자원(주문들)이고, 조건이 사전으로 풀렸습니다. 값이 목록으로 나오는 까닭은 아래에서 봅니다.
작게 줄여서 눈으로 보기
같은 이름이 두 번 나오는 주소로, 주소의 부분과 쿼리의 값이 목록인 까닭을 봅니다.
from urllib.parse import urlsplit, parse_qs
parts = urlsplit("http://127.0.0.1:8013/orders?지역=서울&지역=경기&택배사=한결택배")
print(parts.scheme, "/", parts.netloc, "/", parts.path)
print(parse_qs(parts.query))
print(parse_qs(parts.query)["지역"][0])http / 127.0.0.1:8013 / /orders
{'지역': ['서울', '경기'], '택배사': ['한결택배']}
서울| 결과 | 읽는 법 |
|---|---|
http / 127.0.0.1:8013 / /orders | 방식, 서버 주소와 포트 번호, 경로로 나뉘었다 |
'지역': ['서울', '경기'] | 쿼리에 같은 이름이 여러 번 올 수 있어서 parse_qs 는 값을 늘 목록에 담는다 |
서울 | 값 하나만 쓸 때는 [0] 으로 첫 값을 꺼낸다 |
주의할 점
- REST 는 규칙이 아니라 설계 방식입니다. 예측이나 질의처럼 자원으로 나타내기 어색한 일은
POST /predict,POST /ask처럼 동사형 주소를 쓰는 경우가 많습니다. 한 API 안에서 일관되게만 짭니다. - GET 의 쿼리 문자열은 서버 기록과 브라우저 기록에 남습니다. 개인정보나 비밀번호는 쿼리에 넣지 않습니다.