Skip to content

라우팅과 요청 파라미터

기본 라우팅

/posts/ 주소에 대해 GET 요청이 들어오면 post_list 엔드포인트 함수를 호출하여 요청을 처리토록 합니다.

@app.get("/posts/")
def post_list():
    ...

/posts/ 주소에 대해 POST 요청이 들어오면 post_new 엔드포인트 함수를 호출하여 요청을 처리토록 합니다. 위 post_list 함수와 같은 주소이지만, 다른 method를 지정했기에 가능합니다. 만약 같은 주소/method의 함수를 정의한다면 앞선 함수를 뒷 함수가 덮어쓰기하여 뒷 함수를 통해서만 요청이 처리됩니다.

@app.post("/posts/")
def post_new():
    ...

REST API 설계 방식

REST API 설계에서는 같은 주소에 대해, 요청 method에 따라 다른 동작을 수행합니다.

예를 들어 /posts/ 주소에서:

  • GET /posts/ : 게시글 목록 조회 요청
  • POST /posts/ : 새 게시글 생성 요청

개별 리소스의 경우 /posts/{id}/에서:

  • GET /posts/{id}/ : 특정 게시글 조회 요청
  • PUT /posts/{id}/ : 특정 게시글 전체 업데이트 요청
  • PATCH /posts/{id}/ : 특정 게시글 부분 업데이트 요청
  • DELETE /posts/{id}/ : 특정 게시글 삭제 요청

일반 웹페이지 vs REST API URL 설계

일반 웹페이지에서는 서버가 데이터 처리와 UI 렌더링을 모두 담당하기 때문에 각 기능별로 다른 URL을 사용합니다:

  • GET /posts/ : 게시글 목록 페이지 (HTML 응답)
  • GET /posts/new/ : 게시글 작성 폼 페이지 (HTML 응답)
    • POST /posts/new/ : 게시글 생성 처리 후 리다이렉트
  • GET /posts/{id}/ : 게시글 상세 페이지 (HTML 응답)
  • GET /posts/{id}/edit/ : 게시글 수정 폼 페이지 (HTML 응답)
    • POST /posts/{id}/edit/ : 게시글 업데이트 처리 후 리다이렉트
  • GET /posts/{id}/delete/ : 게시글 삭제 확인 페이지 (HTML 응답)
    • POST /posts/{id}/delete/ : 게시글 삭제 처리 후 리다이렉트

Note

웹페이지의 <form> 태그에서도 GET 요청과 POST 요청 만을 지원합니다.

REST API에서는 서버가 데이터 처리에만 집중하고, 별도의 프론트엔드 애플리케이션이 UI를 담당합니다:

  • GET /api/posts/ : 게시글 목록 데이터 (JSON 응답)
  • POST /api/posts/ : 게시글 생성 (JSON 응답)
  • GET /api/posts/{id}/ : 게시글 상세 데이터 (JSON 응답)
  • PUT/PATCH /api/posts/{id}/ : 게시글 수정 (JSON 응답)
  • DELETE /api/posts/{id}/ : 게시글 삭제 (JSON 응답)

핵심 차이점:

  • 일반 웹페이지: 서버가 데이터 + UI를 모두 처리
    • 요청 내역에 따라 URL 필요 (웹에서는 일반적으로 URL을 통한 분기)
    • 웹 브라우저의 기본 기능을 최대한 활용하며, 부분적으로 JS를 통해 UX 향상
  • REST API: 서버는 데이터만 처리, 프론트엔드가 UI 처리
    • API 서버는 리소스 중심의 간결한 URL
    • 프론트 단에서 다양한 상황에 따른 UI 직접 구현

Note

웹페이지에서 PUT, PATCH, DELETE 요청을 보낼려면, 자바스크립트를 사용해야만 합니다.

바닐라 자바스크립트의 fetch 함수를 통해 요청을 보낼 수 있으며, 서비스에 따라 React, Vue.js, Angular 등의 UI 라이브러리를 활용해서 UI를 구성하고, axios와 같은 고수준의 HTTP 클라이언트 라이브러리를 사용하기도 합니다.

FastAPI vs Django 라우팅 비교

FastAPI에서는 요청 주소와 method를 모두 사용하여 요청을 처리할 함수를 분기합니다.

@app.get("/posts/")     # GET 요청만 처리
def get_posts(): ...

@app.post("/posts/")    # POST 요청만 처리  
def create_post(): ...

Django에서는 요청 주소만으로 요청을 처리할 View를 분기하며, View 내부에서 method를 통해 요청을 분기할 수 있습니다.

# Django CBV 예시
class PostListView(View):
    def get(self, request):     # GET 요청 처리
        ...
    def post(self, request):    # POST 요청 처리
        ...

경로 매개변수 (Path Parameters)

요청 URL에 매칭되는 함수를 찾을 때에는 단순한 경로 패턴 매칭을 합니다. 정규 표현식은 직접 지원하지 않기에, 경로 매개변수 타입이 달라도 무시됩니다.

@app.get("/posts/{pk}/")
def post_detail(pk: int):
    ...

# 이 함수는 절대 호출되지 않습니다.
@app.get("/posts/{slug}/")
def post_detail(slug: str):
    ...

경로 매개변수 타입 변환에 실패하면

/posts/hello-fastapi 주소로 요청

  1. post_detail(slug: str) 호출을 의도했지만,
  2. 같은 주소의 post_detail(pk: int) 함수가 앞서 정의가 되어있기에
  3. post_detail(pk: int) 함수가 호출됩니다.
  4. 'hello-fastapi' 문자열이 int를 통해 정수 변환을 시도하면 ValueError: invalid literal for int() with base 10: 'hello-fastapi' 예외가 발생합니다.
  5. 422 Unprocessable Entity 응답을 하게 됩니다.

Note

장고에서는 정규 표현식을 통해 요청 URL을 매칭하기 때문에, 아래 두 주소에 대해 각기 다른 함수를 통해 요청을 처리할 수 있습니다.

  • /posts/123/ => post_detail(pk: int) 를 통한 요청 처리
  • /posts/hello-fastapi/ => post_detail(slug: str) 를 통한 요청 처리

다수의 경로 매개변수를 지정하여 받을 수도 있습니다.

@app.get("/users/{user_id}/posts/{post_id}")
async def post_list_by_user(user_id: int, post_id: int):
    ...

엔드포인트 함수 정의 순서에 유의

경로 매칭은 엔드포인트 함수 정의 순서대로 순차적으로 매칭됩니다. 앞서 매칭된 패턴이 있다면, 뒤의 패턴에 대해서는 매칭을 시도하지 않습니다.

/users/{user_id} 정의 후에 /users/me 에 대해서는 절대 /users/me 함수는 호출될 수 없습니다. 두 함수를 모두 활용할려면 반드시 /users/me 함수를 먼저 정의해주세요.

다른 예)

  • /posts/new/ : 항상 먼저 배치
  • /posts/{post_id}

쿼리 매개변수 (Query Parameters)

URL의 ? 뒤에 오는 key=value 형태의 매개변수입니다. 엔드포인트 함수 결정에는 영향을 끼치지 않습니다.

@app.get("/posts/")
def post_list(
    q: str = "",    # 검색어
    page: int = 1,  # 응답 페이지 수
):
    # ...

장고에서는 View 함수의 첫번째 인자로 항상 HttpRequest 요청 객체가 명시적으로 전달되며, 현재 요청의 모든 내역이 담겨 있습니다.

from django.http import HttpRequest

def post_list(request: HttpRequest):
    q: str = request.GET.get("q", "")
    page = int(request.GET.get("page", 1))
    # ...

장고 내에서 FastAPI 스타일로 개발할 수 있는 django-ninja 라이브러리가 핫합니다.

@api.get("/posts/")
def post_list(
    request,
    q: str = "",
    page: int = 1,
):
    # ...

Comments