Python WSGI로 REST 엔드포인트 구축하기

REST 아키텍처는 HTTP의 기본 메서드를 활용해 자원을 다루는 대표적인 설계 패턴이다. 이번 글에서는 표준 라이브러리만으로 경량 REST 서비스를 구현하고, 외부에서 이를 호출하는 방법을 살펴본다.

전체 흐름 설계

구현할 서비스는 다음 두 가지 엔드포인트를 제공한다.

  • /greet — 이름을 받아 인사말 HTML을 반환
  • /now — 현재 시각 정보를 XML로 반환

핵심 구성 요소는 RouteRegistry라는 분배자(dispatch) 클래스이며, WSGI 규약을 따른다.

RouteRegistry: 요청 라우팅 처리

import cgi

def default_404(environ, start_response):
    start_response('404 Not Found', [('Content-Type', 'text/plain')])
    return [b'Resource unavailable']


class RouteRegistry:
    def __init__(self):
        self._routes = {}

    def __call__(self, environ, start_response):
        route_key = (environ['REQUEST_METHOD'].upper(), environ['PATH_INFO'])
        payload = cgi.FieldStorage(environ['wsgi.input'], environ=environ)
        environ['args'] = {k: payload.getvalue(k) for k in payload}
        handler = self._routes.get(route_key, default_404)
        return handler(environ, start_response)

    def add(self, verb, uri, callback):
        self._routes[(verb.upper(), uri)] = callback
        return callback

RouteRegistry는 (HTTP 메서드, 경로) 쌍을 키로 하여 핸들러 함수를 관리한다. __call__ 메서드 덕분에 WSGI 애플리케이션으로 직접 사용할 수 있다.

비즈니스 로직: 인사말과 시각

import time

_GREET_TEMPLATE = '''<!DOCTYPE html>
<html>
  <head><title>Greeting for {user}</title></head>
  <body><h1>Welcome, {user}!</h1></body>
</html>'''

def greet_user(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html; charset=utf-8')])
    user = environ['args'].get('user', 'Guest')
    yield _GREET_TEMPLATE.format(user=user).encode('utf-8')


_TIMESTAMP_TEMPLATE = '''<?xml version="1.0"?>
<timestamp>
  <year>{dt.tm_year}</year>
  <month>{dt.tm_mon}</month>
  <day>{dt.tm_mday}</day>
  <hour>{dt.tm_hour}</hour>
  <minute>{dt.tm_min}</minute>
  <second>{dt.tm_sec}</second>
</timestamp>'''

def current_timestamp(environ, start_response):
    start_response('200 OK', [('Content-Type', 'application/xml')])
    yield _TIMESTAMP_TEMPLATE.format(dt=time.localtime()).encode('utf-8')

greet_user는 쿼리 파라미터 user를 받아 동적 HTML을, current_timestamp는 시스템 시계를 참조해 XML을 생성한다. 두 함수 모두 제너레이터를 사용해 바이트열을 반환한다.

서버 기동

if __name__ == '__main__':
    from wsgiref.simple_server import make_server

    app = RouteRegistry()
    app.add('GET', '/greet', greet_user)
    app.add('GET', '/now', current_timestamp)

    srv = make_server('0.0.0.0', 8080, app)
    print('WSGI server listening on http://0.0.0.0:8080')
    srv.serve_forever()

0.0.0.0을 바인딩하면 동일 네트워크 내 다른 기기에서도 접근할 수 있다.

원격 호출 검증

별도 터미널에서 다음 스크립트를 실행해 서버를 테스트한다.

import urllib.request

with urllib.request.urlopen('http://localhost:8080/greet?user=Alice') as r:
    print(r.read().decode('utf-8'))

with urllib.request.urlopen('http://localhost:8080/now') as r:
    print(r.read().decode('utf-8'))

정상 응답 시 Alice를 위한 HTML 페이지와 현재 시각이 담긴 XML을 확인할 수 있다.

응답 예시

/greet?user=Alice 요청:

<!DOCTYPE html>
<html>
  <head><title>Greeting for Alice</title></head>
  <body><h1>Welcome, Alice!</h1></body>
</html>

/now 요청:

<?xml version="1.0"?>
<timestamp>
  <year>2025</year>
  <month>1</month>
  <day>15</day>
  <hour>9</hour>
  <minute>42</minute>
  <second>3</second>
</timestamp>

이 구조에 새로운 경로와 메서드를 추가하면 다양한 원격 제어 기능을 손쉽게 확장할 수 있다.

태그: WSGI python REST urllib wsgiref

6월 6일 18:49에 게시됨