[PY-012][실무] 로컬 HTTP API를 읽는 명령줄 도구 만들기
페이지 정보

본문
[이번 수업]
명령줄에서 HTTP API를 호출하고 JSON을 보여 주는 Python 도구를 만듭니다. 내 컴퓨터에 연습용 API를 띄워 요청·검증·오류 처리를 익힙니다.
[선수지식]
PY-004의 함수, PY-006의 JSON, PY-007의 예외 처리, BACK-002의 HTTP를 알면 좋습니다. 표준 라이브러리만 사용합니다.
[학습목표]
1. 명령줄 옵션으로 주소를 받아 GET 요청을 보낸다.
2. 제한 시간·응답 크기·JSON 구조를 검증한다.
3. 연결·HTTP·데이터 오류를 구분한다.
[핵심개념]
CLI(Command Line Interface)는 터미널에서 옵션으로 쓰는 프로그램입니다. HTTP API는 상태 코드·헤더·본문으로 응답하며 JSON은 본문 형식 중 하나입니다. 응답이 와도 크기, 문자 인코딩, 필요한 필드를 확인해야 합니다.
예제는 `urllib.request`로 요청하고 `argparse`로 `--url`을 읽습니다. 127.0.0.1과 localhost만 허용해 다른 시스템에는 요청하지 않습니다. 성공은 종료 코드 0, 실패는 1로 알려 자동화도 결과를 판단하게 합니다.
[따라하기]
빈 폴더에 sample.json을 저장하세요.
```json
{"service": "demo", "state": "ok"}
```
같은 폴더에 api_status.py를 만듭니다.
```python
import argparse
import json
from urllib.error import HTTPError, URLError
from urllib.parse import urlsplit
from urllib.request import urlopen
ALLOWED = {"127.0.0.1", "localhost"}
def fetch(url: str) -> dict:
target = urlsplit(url)
if target.scheme != "http" or target.hostname not in ALLOWED:
raise ValueError("로컬 HTTP 주소만 허용됩니다")
try:
with urlopen(url, timeout=3) as response:
raw = response.read(10_001)
except HTTPError as error:
raise RuntimeError(f"HTTP 오류: {error.code}")
except URLError:
raise RuntimeError("API에 연결할 수 없습니다")
if len(raw) > 10_000:
raise ValueError("응답이 너무 큽니다")
try:
data = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError):
raise ValueError("UTF-8 JSON 응답이 아닙니다")
if not isinstance(data, dict) or data.get("state") not in {"ok", "warning"}:
raise ValueError("응답 구조가 예상과 다릅니다")
return data
def main() -> int:
parser = argparse.ArgumentParser(description="로컬 API 상태 확인")
parser.add_argument("--url", default="http://127.0.0.1:8000/sample.json")
try:
data = fetch(parser.parse_args().url)
print(f"{data.get('service', 'unknown')}: {data['state']}")
return 0
except (ValueError, RuntimeError) as error:
print(f"오류: {error}")
return 1
if __name__ == "__main__":
raise SystemExit(main())
```
첫 터미널에서 macOS·Linux는 `python3 -m http.server 8000 --bind 127.0.0.1`, Windows는 `py -m http.server 8000 --bind 127.0.0.1`을 실행합니다. 둘째 터미널에서 `python3 api_status.py` 또는 `py api_status.py`를 실행하세요. 예상 결과는 `demo: ok`입니다. 서버는 Ctrl+C로 끝냅니다.
[흔한 실수]
필요한 키가 있다고 가정하거나 제한 시간 없이 기다리지 마세요. 모든 예외를 숨기거나 실패에도 종료 코드 0을 내보내면 원인과 결과를 판단하기 어렵습니다.
[보안 주의]
임의 URL을 허용하면 내부 주소 접근 문제가 생길 수 있습니다. 본인 소유의 로컬·격리 환경에서만 실습하세요. `http.server`는 연습용이며 외부 공개나 운영에 쓰지 않습니다. 토큰은 코드·주소·로그에 넣지 않습니다.
[직접 해볼 과제]
state를 warning으로 바꾼 뒤 unknown으로도 바꿔 보세요. 서버를 끈 경우까지 오류 메시지와 종료 코드 1이 나오는지 확인합니다.
[확인문제]
1. JSON을 읽기 전에 응답 크기를 제한하는 이유는 무엇인가요?
2. HTTPError를 URLError보다 먼저 처리한 이유는 무엇인가요?
3. 성공과 실패에 다른 종료 코드를 쓰면 무엇이 좋아지나요?
[다음 학습]
JSTS-012에서 타입을 적용한 작은 서비스로 같은 원칙을 확장합니다.
[공식 참고 자료]
https://docs.python.org/3/library/urllib.request.html
https://docs.python.org/3/library/argparse.html
https://docs.python.org/3/library/json.html
https://docs.python.org/3/library/http.server.html
명령줄에서 HTTP API를 호출하고 JSON을 보여 주는 Python 도구를 만듭니다. 내 컴퓨터에 연습용 API를 띄워 요청·검증·오류 처리를 익힙니다.
[선수지식]
PY-004의 함수, PY-006의 JSON, PY-007의 예외 처리, BACK-002의 HTTP를 알면 좋습니다. 표준 라이브러리만 사용합니다.
[학습목표]
1. 명령줄 옵션으로 주소를 받아 GET 요청을 보낸다.
2. 제한 시간·응답 크기·JSON 구조를 검증한다.
3. 연결·HTTP·데이터 오류를 구분한다.
[핵심개념]
CLI(Command Line Interface)는 터미널에서 옵션으로 쓰는 프로그램입니다. HTTP API는 상태 코드·헤더·본문으로 응답하며 JSON은 본문 형식 중 하나입니다. 응답이 와도 크기, 문자 인코딩, 필요한 필드를 확인해야 합니다.
예제는 `urllib.request`로 요청하고 `argparse`로 `--url`을 읽습니다. 127.0.0.1과 localhost만 허용해 다른 시스템에는 요청하지 않습니다. 성공은 종료 코드 0, 실패는 1로 알려 자동화도 결과를 판단하게 합니다.
[따라하기]
빈 폴더에 sample.json을 저장하세요.
```json
{"service": "demo", "state": "ok"}
```
같은 폴더에 api_status.py를 만듭니다.
```python
import argparse
import json
from urllib.error import HTTPError, URLError
from urllib.parse import urlsplit
from urllib.request import urlopen
ALLOWED = {"127.0.0.1", "localhost"}
def fetch(url: str) -> dict:
target = urlsplit(url)
if target.scheme != "http" or target.hostname not in ALLOWED:
raise ValueError("로컬 HTTP 주소만 허용됩니다")
try:
with urlopen(url, timeout=3) as response:
raw = response.read(10_001)
except HTTPError as error:
raise RuntimeError(f"HTTP 오류: {error.code}")
except URLError:
raise RuntimeError("API에 연결할 수 없습니다")
if len(raw) > 10_000:
raise ValueError("응답이 너무 큽니다")
try:
data = json.loads(raw.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError):
raise ValueError("UTF-8 JSON 응답이 아닙니다")
if not isinstance(data, dict) or data.get("state") not in {"ok", "warning"}:
raise ValueError("응답 구조가 예상과 다릅니다")
return data
def main() -> int:
parser = argparse.ArgumentParser(description="로컬 API 상태 확인")
parser.add_argument("--url", default="http://127.0.0.1:8000/sample.json")
try:
data = fetch(parser.parse_args().url)
print(f"{data.get('service', 'unknown')}: {data['state']}")
return 0
except (ValueError, RuntimeError) as error:
print(f"오류: {error}")
return 1
if __name__ == "__main__":
raise SystemExit(main())
```
첫 터미널에서 macOS·Linux는 `python3 -m http.server 8000 --bind 127.0.0.1`, Windows는 `py -m http.server 8000 --bind 127.0.0.1`을 실행합니다. 둘째 터미널에서 `python3 api_status.py` 또는 `py api_status.py`를 실행하세요. 예상 결과는 `demo: ok`입니다. 서버는 Ctrl+C로 끝냅니다.
[흔한 실수]
필요한 키가 있다고 가정하거나 제한 시간 없이 기다리지 마세요. 모든 예외를 숨기거나 실패에도 종료 코드 0을 내보내면 원인과 결과를 판단하기 어렵습니다.
[보안 주의]
임의 URL을 허용하면 내부 주소 접근 문제가 생길 수 있습니다. 본인 소유의 로컬·격리 환경에서만 실습하세요. `http.server`는 연습용이며 외부 공개나 운영에 쓰지 않습니다. 토큰은 코드·주소·로그에 넣지 않습니다.
[직접 해볼 과제]
state를 warning으로 바꾼 뒤 unknown으로도 바꿔 보세요. 서버를 끈 경우까지 오류 메시지와 종료 코드 1이 나오는지 확인합니다.
[확인문제]
1. JSON을 읽기 전에 응답 크기를 제한하는 이유는 무엇인가요?
2. HTTPError를 URLError보다 먼저 처리한 이유는 무엇인가요?
3. 성공과 실패에 다른 종료 코드를 쓰면 무엇이 좋아지나요?
[다음 학습]
JSTS-012에서 타입을 적용한 작은 서비스로 같은 원칙을 확장합니다.
[공식 참고 자료]
https://docs.python.org/3/library/urllib.request.html
https://docs.python.org/3/library/argparse.html
https://docs.python.org/3/library/json.html
https://docs.python.org/3/library/http.server.html
- 이전글[JSTS-012][실무] 타입으로 지키는 작은 상태 확인 서비스 만들기 26.09.06
- 다음글[TOOL-012][실무] 새 컴퓨터에서도 같은 개발환경 다시 만들기 26.09.06
댓글목록
등록된 댓글이 없습니다.
