콘텐츠로 이동

쿼리 매개변수#

2024 iThome 철인 대회

이 글은 Django Ninja 입문 가이드의 11번째 글이에요.

지난 글에서는 요청 URL에서 경로 매개변수를 어떻게 처리하는지 다루었어요.

이번 글에서는 RESTful API에서 필터링 조건추가적인 정보를 전달하는 데 사용되는 중요한 부분인 쿼리 매개변수(query parameters)를 소개할게요.

Django Ninja에서 쿼리 매개변수를 처리하는 방법은 매우 간단하고 직관적이며, 여러 가지 방식으로 이를 구현할 수 있어요.

이번 글의 모든 코드 변경 사항은 이 PR에서 확인하실 수 있어요.

GitHub 예제 프로젝트#

👉 Django-Ninja-Tutorial


1. 쿼리 매개변수란 무엇인가요?#

쿼리 매개변수는 URL에서 선택적으로 사용되는 매개변수로, 보통 path 뒤에 위치하며 ?key=value 형태를 띠고 추가적인 정보를 전달하는 데 쓰여요.

예를 들어 특정 작가의 글을 필터링해야 할 때 URL의 path는 다음과 같이 작성될 수 있어요:

/posts/?author=john

URL에 쿼리 매개변수 author=john이 전달되었는데, 이는 John이 작성한 글을 필터링해 달라는 의미예요.

2. 예제 프로젝트 변경 사항#

쿼리 매개변수를 좀 더 실감 나게 설명하기 위해, 기존의 "모든 글 가져오기" API를 수정하여 간단한 "필터링" 기능을 추가해 볼게요.

참고로, 복잡한 필터링 기능에 대해서는 〈23편: 필터링(Filtering)〉에서 자세히 다룰 예정이에요.

수정 후에는 요청에 쿼리 매개변수가 포함되어 있을 때, API가 이 매개변수를 활용하여 조회 결과를 제한할 수 있게 돼요. 다음과 같아요:

@router.get(path='/posts/')
def get_posts(request: HttpRequest, title: None | str = None):
    posts = Post.objects.all()
    if title:
        posts = posts.filter(title__icontains=title)  # 필터링 로직 구현
    return posts

여기서는 "글 제목"을 기준으로 필터링을 수행해요.

💡 팁: 현재 프로젝트의 API는 아직 사용할 수 없어요. 첫째로 DB에 데이터가 없고, 둘째로 아직 관련된 Schema를 작성하지 않았기 때문이에요. 현 단계에서는 그저 읽고 이해하기 위한 참고 자료로 생각해주세요. 너무 걱정하지 마세요. 곧 제대로 작동하게 될 테니까요! ☺️

자, 코드 수정을 마쳤으니 설명을 이어나갈게요.


3. Django Ninja에서 Query Parameters 사용하기#

Django Ninja에서 쿼리 매개변수를 처리하는 가장 간단한 방법은, 뷰 함수의 선택적 매개변수(optional parameter)로 직접 지정하는 것이에요. 매개변수 기본값으로 None을 설정해 주는 식이죠:

@router.get(path='/posts/')
def get_posts(request: HttpRequest, title: None | str = None):

이 예제에서 title 매개변수는 선택적인 문자열(None | str = None)로 정의되었어요.

  • URL에 title 쿼리 매개변수가 포함되어 있다면, Django Ninja는 자동으로 그 값을 인자로 삼아 get_posts 함수에 전달해요.
  • URL에 해당 쿼리 매개변수가 없다면, 함수는 인자를 받지 못하며, 이 때 title의 값은 함수 내에서 None이 돼요. 기본값이 있기 때문이죠.

이 예제와 관련해서 다음 사항들도 반드시 기억해 두어야 해요:

  1. 쿼리 매개변수는 router 데코레이터의 path 문자열 경로 내에 작성할 필요가 없어요.
  2. 쿼리 매개변수는 구체적인 값이든 앞서 본 None이든 보통 기본값을 가져요. 만약 기본값이 없는데 쿼리 매개변수도 제공되지 않는다면, Django Ninja는 422 응답을 반환해요.
  3. 기본값이 None일 때 type hints 작성법에 주의하세요: None | str = None (이는 Optional[str] = None과 같아요).
  4. 쿼리 매개변수도 경로 매개변수처럼 함수의 type hints에 따라 타입 변환이 이루어져요. 타입을 지정하지 않으면 둘 다 기본 타입이 str이 돼요. URL은 본질적으로 모두 문자열이니까요.

위의 방식은 간단하고 직관적이라 대부분의 상황에 적합해요.

하지만 쿼리 매개변수에 대해 더 복잡한 검증이나 제한을 두어야 할 때는 고급 기법인 Query를 사용해야 해요.


4. Query 객체 사용하기#

쿼리 매개변수의 길이나 범위를 제한하거나, API 문서에 추가 정보를 제공하는 등 좀 더 세밀한 제어가 필요할 때는 Query를 이용해 쿼리 매개변수를 설정하고 처리할 수 있어요.

사실 실무 개발을 하면서 저도 Query를 자주 사용하진 않았지만, 이것의 가장 중요한 20%의 특징을 이해해 두면 분명 큰 도움이 될 거예요.

Query 소개#

Query 객체를 통해 우리는 쿼리 매개변수를 더욱 정교하게 정의하고 검증할 수 있어요.

실제로 Django Ninja의 소스 코드를 들여다보면 흥미로운 점을 발견할 수 있어요: 이것은 사실 함수예요. 단지 같은 이름의 클래스 객체를 반환할 뿐이죠.

설명을 편하게 하기 위해 여기서는 통칭하여 Query 객체라고 부를게요. 어쨌든 Python에서는 모든 것이 객체니까요.

수정된 다음 예제를 참고해 보세요:

from ninja import Query, Router
...

@router.get(path='/posts/')
def get_posts(request: HttpRequest, title: None | str = Query(None)):
    ...

이 코드는 원래의 방식과 거의 동일하게 동작해요: (미세한 차이가 있긴 하지만 일단 무시해도 좋아요)

def get_posts(request: HttpRequest, title: None | str = None):

'아무런 추가 이점도 없는데 왜 굳이 더 복잡하게 쓰지?'라고 이상하게 생각하실 수 있어요.

당연하게도, 더 복잡한 작성법을 사용하면 그만큼 할 수 있는 일도 많아지기 때문이에요.

쿼리 문자열 길이 제한하기#

예를 들어, title이라는 쿼리 문자열이 너무 길거나 너무 짧지 않도록 제한하고 싶다고 가정해 볼게요.

요구되는 길이를 2자에서 10자 사이라고 해봅시다.

이때는 다음과 같이 작성할 수 있어요:

def get_posts(
    request: HttpRequest,
    title: None | str = Query(None, min_length=2, max_length=10),
):

이 예제에서는 Query를 사용해 title 쿼리 매개변수를 정의하고, 추가로 Query를 초기화하는 인자로 min_lengthmax_length 두 가지 설정을 부여했어요.

이렇게 함으로써 title 쿼리 매개변수의 길이가 2자에서 10자 사이가 되도록 보장할 수 있죠.

만약 사용자가 입력한 title이 이 길이 요건을 충족하지 못하면, 이전 글에서 설명했듯 Django Ninja가 자동으로 상태 코드 422인 응답을 반환해 줘요. 우리가 직접 검증 로직을 짜거나 관련 응답을 처리할 필요가 전혀 없답니다.

// 422 Unprocessable Entity
{
    "detail": [
        {
            "type": "string_too_short",  // 쿼리 매개변수가 너무 짧음
            "loc": [
                "title",
                "title"
            ],
            "msg": "String should have at least 2 characters",
            "ctx": {
                "min_length": 2
            }
        }
    ]
}

Query의 기타 유용한 매개변수들#

min_lengthmax_length 외에도, Query는 검색 조건을 제한하거나 API 문서에 부가적인 정보를 보충할 수 있도록 실용적인 매개변수들을 다수 제공해요. 자주 쓰이는 것은 다음과 같아요:

  • gt, ge: 쿼리 매개변수의 값이 특정 숫자보다 크거나 크거나 같아야 해요.
  • lt, le: 쿼리 매개변수의 값이 특정 숫자보다 작거나 작거나 같아야 해요.
  • example, examples: API 문서를 위해 쿼리 매개변수의 예시 값을 제공하여, 사용자가 매개변수의 쓰임새를 더 쉽게 이해하도록 도와요.

이 부분에 대한 예시 코드는 생략할게요.


소결 및 다음 단계#

쿼리 매개변수는 RESTful API에서 흔히 사용되는 매우 중요한 구성 요소예요. Django Ninja에서는 단순한 방법으로 쿼리 매개변수를 처리할 수도 있고, Query를 이용해 더욱 고급 수준의 검증과 제어를 수행할 수도 있어요.

Django Ninja가 URL 관련 매개변수를 어떻게 처리하는지 이해하셨다면, 다음은 본격적인 하이라이트가 기다리고 있어요.

다음 글에서는 Django Ninja에서 HTTP request body를 어떻게 처리하는지 살펴보고, Schema를 사용하여 데이터를 검증하고 역직렬화(deserialize)하는 방법을 알아볼 거예요. 이를 통해 복잡한 요청 정보를 유연하게 다루는 법을 배울 수 있답니다.