개발/FastAPI

FastAPI Path Parameter와 Query Parameter 차이 정리

notebase 2026. 5. 24. 12:17

FastAPI에서 Path Parameter와 Query Parameter를 어떻게 구분하고 사용하는지 예제 코드와 함께 정리합니다. 필수값, 선택값, 타입 변환, 검증 방식까지 함께 설명합니다.

 

FastAPI Path Parameter와 Query Parameter는 URL로 값을 받는 방식이다. 둘 다 API에서 자주 쓰이지만, 역할은 다르다.

FastAPI를 처음 배우면 이런 URL을 자주 보게 된다.

/items/1
/items?skip=0&limit=10

 

둘 다 서버에 값을 전달한다는 점은 같다.
하지만 /items/11Path Parameter이고, ?skip=0&limit=10Query Parameter다.

이 차이를 정확히 알아야 API 주소를 자연스럽게 설계할 수 있다.


 

Path Parameter와 Query Parameter의 차이

가장 먼저 기준부터 잡고 가면 편하다.

구분 Path Parameter Query Parameter
위치 URL 경로 안 ?
예시 /items/1 /items?skip=0&limit=10
주 용도 특정 리소스 지정 검색, 필터, 정렬, 페이지네이션
필수 여부 보통 필수 선택값으로 자주 사용
FastAPI 처리 방식 경로에 선언된 이름으로 인식 함수 매개변수 중 경로에 없는 값을 쿼리로 인식

 

FastAPI에서 Path Parameter는 {item_id}처럼 중괄호를 사용해 선언한다.

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

 

여기서 /items/{item_id}{item_id}가 Path Parameter다.


 

FastAPI Path Parameter 사용법

Path Parameter는 URL 경로 자체에 값이 들어가는 방식이다.

예를 들어 상품 상세 조회 API를 만든다고 해보자.

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

 

이 API는 다음 주소로 요청할 수 있다.

http://127.0.0.1:8000/items/1

 

그러면 FastAPI는 URL의 1item_id에 넣어준다.

응답은 다음과 비슷하다.

{
  "item_id": 1
}

 

여기서 중요한 부분은 item_id: int다.
URL로 들어오는 값은 원래 문자열이지만, FastAPI는 타입 힌트를 보고 int로 변환한다.

만약 아래처럼 숫자가 아닌 값을 넣으면 어떻게 될까?

/items/abc

 

item_idint여야 하는데 abc는 정수로 바꿀 수 없다.
이 경우 FastAPI는 자동으로 검증 오류를 반환한다.

직접 if문으로 타입 검사를 작성하지 않아도 된다는 점이 FastAPI의 편한 부분이다.


 

Path Parameter는 언제 사용할까?

Path Parameter는 무엇을 조회할지 URL 경로에서 바로 알 수 있어야 할 때 사용한다.

예를 들면 이런 경우다.

/users/10
/posts/35
/products/7
/orders/202

 

각 주소는 특정한 하나의 리소스를 가리킨다.

  • /users/10 → 10번 사용자
  • /posts/35 → 35번 게시글
  • /products/7 → 7번 상품

즉, Path Parameter는 보통 ID나 고유한 이름처럼 리소스를 식별하는 값에 잘 맞는다.


 

FastAPI Query Parameter 사용법

Query Parameter는 URL의 ? 뒤에 붙는 값이다.

예를 들어 게시글 목록을 조회하면서 검색어를 전달한다고 해보자.

from fastapi import FastAPI

app = FastAPI()

@app.get("/posts")
async def read_posts(keyword: str | None = None):
    return {"keyword": keyword}

 

요청 주소는 이렇게 만들 수 있다.

http://127.0.0.1:8000/posts?keyword=fastapi

 

그러면 keyword에는 "fastapi"가 들어간다.

응답은 다음과 비슷하다.

{
  "keyword": "fastapi"
}

 

FastAPI에서는 함수 매개변수 중 URL 경로에 선언되지 않은 값이 있으면, 기본적으로 Query Parameter로 인식한다.

 

참고: 이 글의 예제는 Python 3.10 이상 문법을 기준으로 작성했다. str | None 문법은 Python 3.10 이상에서 사용할 수 있다. Python 3.9 이하를 사용한다면 from typing import Optional을 가져온 뒤 Optional[str] = None 형태로 작성하면 된다.

 

Python 3.9 이하에서는 위 코드를 이렇게 바꿔 쓸 수 있다.

from typing import Optional

from fastapi import FastAPI

app = FastAPI()

@app.get("/posts")
async def read_posts(keyword: Optional[str] = None):
    return {"keyword": keyword}

 


 

Query Parameter는 언제 사용할까?

Query Parameter는 목록을 조회하거나 조건을 추가할 때 자주 쓴다.

예를 들면 이런 주소다.

/posts?keyword=fastapi
/posts?category=python
/posts?skip=0&limit=10
/posts?sort=latest

 

각 값은 특정 리소스를 직접 가리키기보다는 조회 조건에 가깝다.

  • keyword=fastapi → 검색어
  • category=python → 카테고리 필터
  • skip=0&limit=10 → 페이지네이션
  • sort=latest → 정렬 기준

이런 값들은 없어도 기본 목록 조회가 가능하다.
그래서 Query Parameter는 선택값으로 설계하는 경우가 많다.


 

Query Parameter의 선택값과 필수값

Query Parameter는 기본값을 주면 선택값이 된다.

@app.get("/posts")
async def read_posts(keyword: str | None = None):
    return {"keyword": keyword}

 

여기서 keyword의 기본값은 None이다.
그래서 아래 주소도 정상적으로 동작한다.

/posts

 

이 경우 keyword에는 None이 들어간다.

반대로 기본값을 주지 않으면 필수 Query Parameter가 된다.

@app.get("/posts/search")
async def search_posts(keyword: str):
    return {"keyword": keyword}

 

이 API는 반드시 다음처럼 요청해야 한다.

/posts/search?keyword=fastapi

 

기본값이 있으면 선택값, 기본값이 없으면 필수값이라고 보면 된다.


 

Path Parameter와 Query Parameter 함께 사용하기

실제 API에서는 둘을 같이 쓰는 경우가 많다.

예를 들어 특정 사용자의 게시글 목록을 조회하면서 검색어와 페이지 크기를 함께 받는 API를 만들어보자.

from fastapi import FastAPI

app = FastAPI()

@app.get("/users/{user_id}/posts")
async def read_user_posts(
    user_id: int,
    keyword: str | None = None,
    limit: int = 10
):
    return {
        "user_id": user_id,
        "keyword": keyword,
        "limit": limit
    }

 

요청은 이렇게 보낼 수 있다.

/users/3/posts?keyword=fastapi&limit=5

 

이때 값은 다음처럼 들어간다.

{
  "user_id": 3,
  "keyword": "fastapi",
  "limit": 5
}

 

여기서 user_id는 Path Parameter다.
keywordlimit은 Query Parameter다.

FastAPI는 함수 매개변수 순서에 의존해서 Path와 Query를 구분하지 않는다.
경로에 {user_id}가 있으므로 user_id는 Path Parameter가 되고, 나머지 값은 Query Parameter로 처리된다.


 

Boolean Query Parameter도 자동 변환된다

FastAPI는 Query Parameter의 타입도 자동으로 변환한다.

@app.get("/posts")
async def read_posts(short: bool = False):
    return {"short": short}

 

다음 요청들은 모두 short=True로 처리될 수 있다.

/posts?short=true
/posts?short=True
/posts?short=1
/posts?short=yes
/posts?short=on

 

FastAPI는 타입 힌트를 기준으로 /docs 문서도 자동 생성한다.
예를 들어 short: bool = False처럼 선언하면 Swagger UI에서는 해당 값이 boolean 타입으로 표시된다. API를 직접 테스트할 때도 어떤 타입을 기대하는지 바로 확인할 수 있다.

다만 실제 URL 요청에서는 true, 1, yes, on처럼 여러 표현이 True로 해석될 수 있다.
팀 내부나 외부 사용자에게 공개하는 API라면 예시 문서에서는 true / false처럼 하나의 표기 방식을 정해두는 편이 좋다.


 

PathQuery로 검증 조건 추가하기

단순히 타입만 지정하는 것보다 더 자세한 조건을 걸고 싶을 때가 있다.

예를 들어 item_id는 1 이상이어야 하고, 검색어 q는 최대 50자까지만 허용하고 싶다고 해보자.

이럴 때는 Path, Query, Annotated를 함께 사용할 수 있다.

from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(
    item_id: Annotated[int, Path(ge=1)],
    q: Annotated[str | None, Query(max_length=50)] = None
):
    return {
        "item_id": item_id,
        "q": q
    }

 

여기서 Path(ge=1)item_id가 1 이상이어야 한다는 뜻이다.

/items/0

 

이런 요청은 검증을 통과하지 못한다.

Query(max_length=50)q의 길이를 최대 50자로 제한한다.
이런 검증 조건은 FastAPI가 자동 생성하는 OpenAPI 문서에도 함께 반영된다.

숫자 검증에서는 자주 이런 옵션을 사용한다.

옵션 의미
gt greater than, 초과
ge greater than or equal, 이상
lt less than, 미만
le less than or equal, 이하

 

예를 들어 limit 값을 1 이상 100 이하로 제한하고 싶다면 이렇게 작성할 수 있다.

from typing import Annotated

from fastapi import Query

async def read_posts(
    limit: Annotated[int, Query(ge=1, le=100)] = 10
):
    return {"limit": limit}

 

실제로는 목록 API에서 limit에 이런 제한을 자주 둔다.
제한이 없으면 한 번에 너무 많은 데이터를 요청할 수 있기 때문이다.


 

Annotated 문법은 어떻게 읽으면 될까?

입문자 입장에서는 Annotated[int, Path(ge=1)]가 낯설 수 있다.

이 문법은 크게 두 부분으로 보면 된다.

Annotated[int, Path(ge=1)]

 

  • int → 실제 값의 타입
  • Path(ge=1) → FastAPI에 전달할 검증 조건이나 메타데이터

즉, 위 코드는 “이 값은 int 타입이고, FastAPI에서는 경로 매개변수로 다루되 1 이상이어야 한다”는 뜻이다.

Query도 같은 방식으로 읽으면 된다.

Annotated[str | None, Query(max_length=50)]

 

이 코드는 “문자열이거나 None이 될 수 있고, 쿼리 파라미터로 받을 때 최대 길이는 50자까지 허용한다”는 의미다.

처음에는 문법이 길어 보일 수 있지만, 타입과 검증 조건을 한곳에 모아둘 수 있어서 코드가 커질수록 관리하기 편하다.


 

Path Parameter는 항상 필수다

여기서 자주 헷갈리는 부분이 있다.

Query Parameter는 기본값을 None으로 두면 선택값이 될 수 있다.
하지만 Path Parameter는 기본적으로 선택값이 될 수 없다.

예를 들어 이 주소를 보자.

/items/{item_id}

 

이 API를 호출하려면 item_id가 반드시 URL에 있어야 한다.

/items/1

 

아래처럼 호출하면 같은 경로가 아니다.

/items

 

Path Parameter는 URL 경로의 일부다.
그래서 선택적으로 받을 값이라면 Path Parameter가 아니라 Query Parameter로 설계하는 편이 자연스럽다.


 

실제 API 설계 기준

둘 중 무엇을 써야 할지 헷갈릴 때는 이 기준으로 보면 된다.

특정 대상을 가리키면 Path Parameter

/users/1
/posts/10
/products/3

 

이런 값은 URL에서 빠지면 API의 의미가 바뀐다.
따라서 Path Parameter가 잘 맞는다.

@app.get("/posts/{post_id}")
async def read_post(post_id: int):
    return {"post_id": post_id}

 

조회 조건을 조절하면 Query Parameter

/posts?keyword=fastapi
/posts?limit=10
/posts?sort=latest

 

이런 값은 목록 조회의 조건을 바꾼다.
값이 없어도 기본 조회는 가능하다.

@app.get("/posts")
async def read_posts(
    keyword: str | None = None,
    limit: int = 10
):
    return {
        "keyword": keyword,
        "limit": limit
    }

 

실제로는 Path와 Query를 같이 쓰는 경우가 많다.

/users/1/posts?limit=10&sort=latest

 

이 주소는 이렇게 읽을 수 있다.

  • users/1 → 1번 사용자의
  • posts → 게시글 목록을
  • limit=10&sort=latest → 10개씩 최신순으로 조회한다

URL만 봐도 API의 의도가 꽤 명확해진다.


 

자주 하는 실수

1. 검색 조건을 Path Parameter로 만드는 경우

/posts/fastapi

 

이 주소가 “fastapi라는 검색어로 게시글을 찾는다”는 의미라면 Query Parameter가 더 자연스럽다.

/posts?keyword=fastapi

 

/posts/fastapi는 보통 fastapi라는 고유 ID나, 글 제목을 URL에 쓰기 좋게 바꾼 식별자(slug)를 가진 게시글을 조회하는 느낌에 가깝다.


2. ID를 Query Parameter로만 처리하는 경우

/posts?post_id=10

 

동작은 가능하지만, 특정 게시글 하나를 조회하는 API라면 보통 아래가 더 자연스럽다.

/posts/10

 

리소스 식별자는 Path Parameter로 두는 쪽이 REST API 주소 구조에 더 잘 맞는다.


3. 선택값을 Path Parameter로 만들려는 경우

/posts/{category}

 

카테고리가 항상 필요한 API라면 괜찮다.
하지만 카테고리 없이 전체 게시글도 조회해야 한다면 Query Parameter가 낫다.

/posts
/posts?category=python

 

선택 조건은 Query Parameter로 빼는 편이 API를 확장하기 쉽다.


 

전체 예제 코드

마지막으로 지금까지 본 Path Parameter, Query Parameter, 타입 변환, 검증 조건을 하나로 조합해보자.

from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get("/users/{user_id}/posts")
async def read_user_posts(
    user_id: Annotated[int, Path(ge=1)],
    keyword: Annotated[str | None, Query(max_length=50)] = None,
    limit: Annotated[int, Query(ge=1, le=100)] = 10,
    sort: str = "latest"
):
    return {
        "user_id": user_id,
        "keyword": keyword,
        "limit": limit,
        "sort": sort
    }

 

이 예제는 특정 사용자의 게시글 목록을 조회하면서 검색어, 개수 제한, 정렬 기준까지 함께 받는 형태다.

요청 예시는 다음과 같다.

/users/1/posts?keyword=fastapi&limit=20&sort=latest

 

이 예제에서 각 값의 역할은 다음과 같다.

종류 의미
user_id Path Parameter 어떤 사용자의 게시글인지 지정
keyword Query Parameter 검색어
limit Query Parameter 가져올 개수
sort Query Parameter 정렬 기준

 

user_id는 URL 경로에서 빠지면 API가 성립하지 않는다.
반면 keyword, limit, sort는 조회 조건이다.

이렇게 구분하면 API 주소가 훨씬 읽기 쉬워진다.


 

FAQ

Path Parameter와 Query Parameter는 둘 다 GET 요청에서만 쓰나요?

아니다. GET 요청에서 자주 보일 뿐이다. Path Parameter는 URL 경로의 일부이므로 다른 HTTP 메서드에서도 사용할 수 있다. 예를 들어 DELETE /posts/10처럼 특정 게시글을 삭제할 때도 Path Parameter를 쓴다.

Query Parameter는 항상 선택값인가요?

아니다. 기본값을 주면 선택값이 되지만, 기본값 없이 선언하면 필수값이 된다.

Path Parameter에 기본값을 줄 수 있나요?

Path Parameter는 URL 경로의 일부라서 항상 필요하다. 선택값처럼 쓰고 싶다면 Query Parameter로 설계하는 편이 자연스럽다.

Path, Query는 꼭 써야 하나요?

기본적인 값 전달만 할 때는 꼭 필요하지 않다. 하지만 최소값, 최대값, 문자열 길이, 별칭, 설명 같은 조건을 추가하려면 Path, Query를 쓰는 편이 좋다.

Annotated를 꼭 써야 하나요?

간단한 예제에서는 꼭 필요하지 않다. 다만 Path, Query로 검증 조건을 함께 넣을 때는 Annotated를 쓰면 타입과 검증 조건을 분리해서 읽기 좋다. FastAPI 공식 문서에서도 최신 예제에서는 Annotated 사용을 권장하는 흐름이다.

 


 

마무리

FastAPI에서 Path Parameter는 특정 리소스를 지정하는 값에 어울린다.
Query Parameter는 조회 조건을 조절하는 값에 어울린다.

글을 작성하거나 코드를 짤 때는 이 기준만 먼저 적용해도 대부분의 API 주소가 자연스럽게 정리된다.

  • 특정 사용자, 게시글, 상품처럼 하나를 가리키면 Path Parameter
  • 검색어, 정렬, 필터, 페이지네이션처럼 조건을 바꾸면 Query Parameter

FastAPI는 타입 힌트만으로도 값 변환과 검증을 처리해준다. 여기에 Path, Query, Annotated를 함께 쓰면 실무에서 쓰기 좋은 API 형태로 조금 더 단단하게 다듬을 수 있다.