쿼리 매개변수

웹페이지를 들어가면 https://example.com?no=1&page=32등과 같은 주소 형식을 많이 볼 수 있다. 이때 example.com은 host 주소이고 ? 뒤에 오는 변수들을 쿼리 매개변수(Query parameters)라 부른다. 각 매개변수는 & 기호로 구분되고 key=value 와 같이 키/값 쌍으로 정의한다.

import uvicorn

from fastapi import FastAPI

app = FastAPI() 

@app.get('/users')
def get_users(limit: int):
    return {'limit': limit}

if __name__ == '__main__':
    uvicorn.run('main:app', reload=True)

응답 요청을 보내보자. 중요한 점은 FastAPI는 경로에 매개변수가 없으면 쿼리 매개변수로 함수 매개변수를 받는다.

(fastapi) PS D:\workspace> http :8000/users
HTTP/1.1 422 Unprocessable Entity
content-length: 90
content-type: application/json
date: Sat, 13 Aug 2022 08:47:56 GMT
server: uvicorn

{
    "detail": [
        {
            "loc": [
                "query",
                "limit"
            ],
            "msg": "field required",
            "type": "value_error.missing"
        }
    ]
}

위 처럼 쿼리 매개변수를 써주지 않으면 에러가 난다. ?limit=1을 추가해주면 정상적으로 응답을 받을 수 있다.

(fastapi) PS D:\workspace> http :8000/users?limit=1
HTTP/1.1 200 OK
content-length: 11
content-type: application/json
date: Sat, 13 Aug 2022 08:44:57 GMT
server: uvicorn

{
    "limit": 1
}

만약에 쿼리 매개변수를 옵션으로 주고 싶다면 기본 값을 할당해주면 된다.

import uvicorn

from fastapi import FastAPI

app = FastAPI() 

@app.get('/users')
def get_users(limit: int = None):
    return {'limit': limit}

if __name__ == '__main__':
    uvicorn.run('main:app', reload=True)

None 값이 자동으로 들어가서 Null 값을 반환해주는 것을 볼 수 있다.

(fastapi) PS D:\workspace> http :8000/users                                       
HTTP/1.1 200 OK
content-length: 14
content-type: application/json
date: Sat, 13 Aug 2022 08:51:59 GMT      
server: uvicorn

{
    "limit": null
}

다른 방법으로는 파이썬 IDLE에게 힌트를 주는 Optional을 사용하는 것이다. 그래서 사실상 typing.Optional은 의미가 없다. None 만 잘 사용하면 된다.

from typing import Optional

import uvicorn

from fastapi import FastAPI

app = FastAPI() 

@app.get('/users')
def get_users(limit: Optional[int] = None):
    return {'limit': limit}

if __name__ == '__main__':
    uvicorn.run('main:app', reload=True)

결과는 위와 동일하다.


열거체 쿼리 매개변수

파이썬은 열거체를 지원한다. 쿼리 매개변수를 제한하고 싶을 때는 아래와 같이 Enum class를 상속 받아서 사용한다.

Enum은 열거형(Enumerated Type)이라고 부른다. Enum은 서로 관련이 있는 여러 개의 상수 집합을 정의하는 역할을 하는 식별자로, 일부 열거자 자료형은 언어에 기본으로 포함되어 있다. 그 대표적인 예가 Boolean 자료형으로 False, True 값이 미리 정의된 열거형으로 볼 수 있다. False == 0, True == 1 이다.

from enum import Enum

class Rainbow(Enum):
    Red = 0
    Orange = 1
    Yellow = 2
    Green = 3
    Blue = 4
    Navy = 5
    Purple = 6

위 처럼 열거체를 만들었다. 열거체는 name과 value를 호출하는 방식으로 사용한다.

>>> Rainbow.Green      
<Rainbow.Green: 3>
>>> Rainbow.Green.name
'Green'
>>> Rainbow.Green.value
3

이제 열거체를 이용한 앱을 만들어보자.

from enum import Enum

from fastapi import FastAPI

app = FastAPI()

class UserLevel(str, Enum):
    a = "a"
    b = "b"
    c = "c"

@app.get("/users")
def get_users(grade: UserLevel):
    return {"grade": grade}

if __name__ == '__main__':
    uvicorn.run('main:app', reload=True)
(fastapi) PS D:\workspace> http :8000/users?grade=a
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Sat, 13 Aug 2022 09:04:02 GMT
server: uvicorn

{
    "grade": "a"
}

만약에 grade에 정의되지 않은 값을 전달하면 아래와 같이 에러가 발생하며 허용된 값이 무엇인지 알려준다.

(fastapi) PS D:\workspace> http :8000/users?grade=d
HTTP/1.1 422 Unprocessable Entity
content-length: 173
content-type: application/json
date: Sat, 13 Aug 2022 09:04:19 GMT
server: uvicorn

{
    "detail": [
        {
            "ctx": {
                "enum_values": [
                    "a",
                    "b",
                    "c"
                ]
            },
            "loc": [
                "query",
                "grade"
            ],
            "msg": "value is not a valid enumeration member; permitted: 'a', 'b', 'c'",
            "type": "type_error.enum"
        }
    ]
}

swagger에서 확인하면 UserLevel은 str임에도 불구하고 a, b, c 중 하나만 선택하게 되어있다.

image

열거체 쿼리 매개변수에도 기본값을 할당해보자.

from enum import Enum

from fastapi import FastAPI

app = FastAPI()

class UserLevel(str, Enum):
    a = "a"
    b = "b"
    c = "c"

@app.get("/users")
def get_users(grade: UserLevel = UserLevel.a):
    return {"grade": grade}

if __name__ == '__main__':
    uvicorn.run('main:app', reload=True)
(fastapi) PS D:\workspace> http :8000/users
HTTP/1.1 200 OK
content-length: 13
content-type: application/json
date: Sat, 13 Aug 2022 09:16:14 GMT
server: uvicorn

{
    "grade": "a"
}