[BACK-003][입문] 할 일 REST API 설계하고 실행하기
페이지 정보

본문
[이번 수업]
REST는 분산 시스템의 구성 원칙을 정한 아키텍처 스타일입니다. ‘할 일’을 리소스(resource, 이름 붙여 다루는 대상)로 정하고 주소·HTTP 메서드·상태 코드를 연결한 뒤 로컬 API를 실행합니다.
[선수지식]
BACK-001의 백엔드 역할과 BACK-002의 HTTP 요청·응답 구조를 알면 좋습니다. Python 3와 터미널이 필요합니다.
[학습목표]
1. 리소스 중심 주소를 설계합니다.
2. GET과 POST 및 상태 코드를 구분합니다.
3. 무상태 요청을 설명합니다.
[핵심개념]
주소에는 동사보다 리소스 이름을 씁니다. 할 일 모음은 `/tasks`, 한 건은 `/tasks/1`로 표현합니다. 조회는 GET, 생성은 POST, 교체·변경은 PUT·PATCH, 삭제는 DELETE가 일반적입니다. GET은 서버 상태 변경을 요청하지 않는 안전한 메서드입니다. PUT과 DELETE는 같은 요청을 반복해도 의도한 효과가 같아야 하는 멱등성을 가지지만 POST는 새 항목이 계속 생길 수 있습니다.
조회 성공은 200, 생성 성공은 201과 새 주소를 담은 Location 헤더, 본문 없는 성공은 204로 알립니다. 잘못된 입력은 400, 없는 리소스는 404, 현재 상태와 충돌하면 409를 검토합니다. 무상태성은 각 요청에 처리 정보가 들어 있어야 한다는 뜻이지 서버가 데이터베이스를 쓰지 않는다는 뜻이 아닙니다.
[따라하기]
다음을 `server.py`로 저장합니다. 고정 제목을 생성하는 학습용 메모리 API입니다.
```python
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
tasks = [{"id": 1, "title": "HTTP 배우기", "done": False}]
class API(BaseHTTPRequestHandler):
def reply(self, status, body, location=None):
data = json.dumps(body, ensure_ascii=False).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
if location:
self.send_header("Location", location)
self.end_headers()
self.wfile.write(data)
def do_GET(self):
if self.path == "/tasks":
self.reply(200, tasks)
else:
self.reply(404, {"status": 404})
def do_POST(self):
if self.path != "/tasks":
return self.reply(404, {"status": 404})
task = {"id": len(tasks) + 1, "title": "REST 연습", "done": False}
tasks.append(task)
self.reply(201, task, f"/tasks/{task['id']}")
HTTPServer(("127.0.0.1", 8000), API).serve_forever()
```
`python server.py` 또는 `python3 server.py`로 실행합니다. 다른 터미널에서 macOS/Linux는 `curl -i http://127.0.0.1:8000/tasks`, Windows PowerShell은 `curl.exe -i http://127.0.0.1:8000/tasks`를 실행하세요. 예상 결과는 200과 JSON 배열입니다. 생성은 `curl -i -X POST http://127.0.0.1:8000/tasks`이며 Windows에서는 `curl.exe`를 씁니다. 201, `Location: /tasks/2`, 새 JSON이 나오면 성공입니다. 반복하면 번호가 늘어납니다. 종료는 Ctrl+C입니다.
[흔한 실수]
`/createTask`처럼 동작을 주소에 반복하거나 모든 결과를 200으로 반환하지 마세요. 이름·날짜 형식·오류 구조가 API마다 다르면 클라이언트가 복잡해집니다. REST가 반드시 JSON만 뜻한다고 오해하지 않습니다.
[보안 주의]
실습 서버는 본인 컴퓨터의 `127.0.0.1`에서만 사용하세요. 외부 공개 시에는 HTTPS, 인증과 리소스별 권한 검사, 입력 형식·길이 검증, 요청 크기·속도 제한이 필요합니다. 비밀값과 예외 스택을 응답에 넣지 말고 로그에도 비밀번호·토큰을 남기지 않습니다.
[직접 해볼 과제]
`GET /tasks/1`을 추가해 한 건은 200, 없는 번호는 404로 반환하세요. 두 경우를 curl로 실행해 상태 코드와 본문을 기록합니다.
[확인문제]
1. `/tasks`가 `/getTasks`보다 리소스 중심 주소인 이유는 무엇인가요?
2. POST 응답에 201과 Location 헤더를 쓰는 이유는 무엇인가요?
3. 무상태성이 데이터 저장 금지를 뜻하지 않는 이유는 무엇인가요?
[다음 학습]
전체 순환의 다음 수업은 DB-003 ‘SQL 기본: SELECT·INSERT·UPDATE·DELETE’입니다. 이어서 BACK-004 ‘API 입력 검증과 오류 처리’를 학습하세요.
[공식 참고 자료]
Roy Fielding REST 아키텍처: https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
RFC 9110 HTTP Semantics: https://www.rfc-editor.org/rfc/rfc9110
RFC 9457 Problem Details for HTTP APIs: https://www.rfc-editor.org/rfc/rfc9457
REST는 분산 시스템의 구성 원칙을 정한 아키텍처 스타일입니다. ‘할 일’을 리소스(resource, 이름 붙여 다루는 대상)로 정하고 주소·HTTP 메서드·상태 코드를 연결한 뒤 로컬 API를 실행합니다.
[선수지식]
BACK-001의 백엔드 역할과 BACK-002의 HTTP 요청·응답 구조를 알면 좋습니다. Python 3와 터미널이 필요합니다.
[학습목표]
1. 리소스 중심 주소를 설계합니다.
2. GET과 POST 및 상태 코드를 구분합니다.
3. 무상태 요청을 설명합니다.
[핵심개념]
주소에는 동사보다 리소스 이름을 씁니다. 할 일 모음은 `/tasks`, 한 건은 `/tasks/1`로 표현합니다. 조회는 GET, 생성은 POST, 교체·변경은 PUT·PATCH, 삭제는 DELETE가 일반적입니다. GET은 서버 상태 변경을 요청하지 않는 안전한 메서드입니다. PUT과 DELETE는 같은 요청을 반복해도 의도한 효과가 같아야 하는 멱등성을 가지지만 POST는 새 항목이 계속 생길 수 있습니다.
조회 성공은 200, 생성 성공은 201과 새 주소를 담은 Location 헤더, 본문 없는 성공은 204로 알립니다. 잘못된 입력은 400, 없는 리소스는 404, 현재 상태와 충돌하면 409를 검토합니다. 무상태성은 각 요청에 처리 정보가 들어 있어야 한다는 뜻이지 서버가 데이터베이스를 쓰지 않는다는 뜻이 아닙니다.
[따라하기]
다음을 `server.py`로 저장합니다. 고정 제목을 생성하는 학습용 메모리 API입니다.
```python
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
tasks = [{"id": 1, "title": "HTTP 배우기", "done": False}]
class API(BaseHTTPRequestHandler):
def reply(self, status, body, location=None):
data = json.dumps(body, ensure_ascii=False).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
if location:
self.send_header("Location", location)
self.end_headers()
self.wfile.write(data)
def do_GET(self):
if self.path == "/tasks":
self.reply(200, tasks)
else:
self.reply(404, {"status": 404})
def do_POST(self):
if self.path != "/tasks":
return self.reply(404, {"status": 404})
task = {"id": len(tasks) + 1, "title": "REST 연습", "done": False}
tasks.append(task)
self.reply(201, task, f"/tasks/{task['id']}")
HTTPServer(("127.0.0.1", 8000), API).serve_forever()
```
`python server.py` 또는 `python3 server.py`로 실행합니다. 다른 터미널에서 macOS/Linux는 `curl -i http://127.0.0.1:8000/tasks`, Windows PowerShell은 `curl.exe -i http://127.0.0.1:8000/tasks`를 실행하세요. 예상 결과는 200과 JSON 배열입니다. 생성은 `curl -i -X POST http://127.0.0.1:8000/tasks`이며 Windows에서는 `curl.exe`를 씁니다. 201, `Location: /tasks/2`, 새 JSON이 나오면 성공입니다. 반복하면 번호가 늘어납니다. 종료는 Ctrl+C입니다.
[흔한 실수]
`/createTask`처럼 동작을 주소에 반복하거나 모든 결과를 200으로 반환하지 마세요. 이름·날짜 형식·오류 구조가 API마다 다르면 클라이언트가 복잡해집니다. REST가 반드시 JSON만 뜻한다고 오해하지 않습니다.
[보안 주의]
실습 서버는 본인 컴퓨터의 `127.0.0.1`에서만 사용하세요. 외부 공개 시에는 HTTPS, 인증과 리소스별 권한 검사, 입력 형식·길이 검증, 요청 크기·속도 제한이 필요합니다. 비밀값과 예외 스택을 응답에 넣지 말고 로그에도 비밀번호·토큰을 남기지 않습니다.
[직접 해볼 과제]
`GET /tasks/1`을 추가해 한 건은 200, 없는 번호는 404로 반환하세요. 두 경우를 curl로 실행해 상태 코드와 본문을 기록합니다.
[확인문제]
1. `/tasks`가 `/getTasks`보다 리소스 중심 주소인 이유는 무엇인가요?
2. POST 응답에 201과 Location 헤더를 쓰는 이유는 무엇인가요?
3. 무상태성이 데이터 저장 금지를 뜻하지 않는 이유는 무엇인가요?
[다음 학습]
전체 순환의 다음 수업은 DB-003 ‘SQL 기본: SELECT·INSERT·UPDATE·DELETE’입니다. 이어서 BACK-004 ‘API 입력 검증과 오류 처리’를 학습하세요.
[공식 참고 자료]
Roy Fielding REST 아키텍처: https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm
RFC 9110 HTTP Semantics: https://www.rfc-editor.org/rfc/rfc9110
RFC 9457 Problem Details for HTTP APIs: https://www.rfc-editor.org/rfc/rfc9457
- 이전글[DB-003][입문] 두 표를 JOIN으로 연결해 보기 26.08.30
- 다음글[WEB-003][입문] 작은 화면부터 반응형 카드 화면 만들기 26.08.30
댓글목록
등록된 댓글이 없습니다.
