FastAPI 경로·쿼리 매개변수 — 주소로 값 받기
같은 코드를 봐도 매번 다른 결과를 주는 API. 그 "값"이 주소에 실려 들어온다. 두 가지 경로가 있다.
1편의 {"message": "안녕"} 은 누가 불러도 똑같은 답만 줬다. 진짜 API 는 다르다. /users/7 은 7번 사용자를, /users/15 는 15번 사용자를 돌려줘야 한다. 같은 함수에 값을 실어 보내는 통로가 필요하다는 뜻이다.
FastAPI 에서 그 통로는 둘이다 — 주소 경로 자체에 박는 경로 매개변수(Path), 그리고 주소 끝 물음표 뒤에 붙이는 쿼리 매개변수(Query). 2편은 이 둘을 구분하고, 타입힌트가 어떻게 값을 자동으로 변환·검증하는지, 그리고 둘을 언제 골라 쓰는지를 다룬다.
1. 경로 매개변수 — 주소 안에 박는 값
중괄호로 주소 한 조각을 변수로 비워 두면, 그 자리에 들어온 값이 함수 인자로 그대로 넘어온다.
/users/7 로 요청하면 {"user_id": 7, "is_int": true} 가 온다. 주목할 곳은 user_id: int 한 줄이다. URL 은 원래 전부 글자(문자열)인데, 타입힌트를 int 로 적었더니 FastAPI 가 "7" 이라는 글자를 숫자 7 로 바꿔서 넘겨 줬다. 우리가 변환 코드를 한 줄도 안 적었는데도.
더 좋은 건 틀린 입력을 막아 주는 부분이다. /users/abc 처럼 숫자가 아닌 값을 보내면, 함수는 실행조차 되지 않고 이런 응답이 돌아온다.
/users/me 같은 고정 경로는 /users/{user_id} 보다 먼저 선언해야 한다. 순서가 바뀌면 me 가 user_id 값으로 빨려 들어가 엉뚱하게 동작한다. 헷갈리면 "구체적인 경로 먼저, 변수 경로 나중" 으로 기억.
2. 쿼리 매개변수 — 물음표 뒤에 붙는 값
함수 인자로 적었는데 그 이름이 경로 { } 안에 없으면, FastAPI 는 자동으로 그걸 쿼리 매개변수로 해석한다. 목록을 거르거나 페이지를 넘길 때 쓰는 그 ?key=value 다.
/items?skip=20&limit=5 로 요청하면 {"skip": 20, "limit": 5} 가 온다. 여러 개를 붙일 땐 & 로 잇는다. 여기서 핵심은 = 0, = 10 이라는 기본값이다. 기본값을 주면 그 매개변수는 선택이 된다. 그래서 /items 만 호출해도 skip=0, limit=10 으로 동작한다.
반대로 꼭 받아야 하는 값이면 기본값을 빼면 된다. 그러면 그 쿼리는 필수가 되고, 빠뜨린 채 호출하면 곧장 검증 에러가 난다. "있어도 되고 없어도 되는" 값은 str | None = None 으로 적어 "비어 있어도 OK" 를 명시한다.
3. 경로냐 쿼리냐 — 가르는 한 가지 질문
둘 다 값을 받는데 언제 무엇을 쓰나. 외울 필요 없이 한 가지만 물어보면 된다 — "이 값이 자원을 콕 집어 가리키나, 아니면 결과를 거르거나 다듬나?"
| 구분 | 경로 매개변수 | 쿼리 매개변수 |
|---|---|---|
| 위치 | 주소 경로 안 /users/7 | 물음표 뒤 ?page=2 |
| 역할 | 자원을 콕 지정 (이 사용자) | 거르기·정렬·페이지 |
| 필수 여부 | 사실상 필수 | 기본값 주면 선택 |
| 여러 개 | 계층으로 /users/7/posts/3 | & 로 나열 |
| 예시 | 상세 페이지 1건 | 검색·목록 필터 |
실전 감을 한 줄로 — /products/42 는 "42번 상품" 을 가리키니 경로, /products?category=shoes&sort=price 는 상품 목록을 거르고 정렬하니 쿼리다. 한 주소에 둘을 섞을 수도 있다. /users/7/orders?status=paid — 7번 사용자(경로)의 결제된 주문(쿼리).
4. 검증을 한 단계 더 — Path 와 Query
타입만으로 부족할 때가 있다. "페이지 번호는 1 이상", "검색어는 2글자 이상" 같은 규칙이다. FastAPI 는 Path·Query 함수로 이런 제약을 한 줄 더 얹게 해 준다.
ge=1 은 "greater than or equal" — 1 미만이면 거부. min_length=2 는 너무 짧은 검색어를 막는다. 이 제약들은 검증만 강화하는 게 아니라 1편에서 본 /docs 문서에도 그대로 반영된다. "pid 는 1 이상" 이라는 규칙이 문서에 자동으로 적혀, 프론트엔드 개발자가 코드를 안 봐도 규칙을 안다.
q: str = Query(min_length=2) 처럼 기본값을 안 주면 그 쿼리는 필수가 된다. 선택으로 두고 싶으면 반드시 default=None 을 명시할 것. 타입에 | None 만 붙이고 기본값을 빠뜨리는 실수가 입문자에게 가장 잦다.
요약 — 값을 받는 두 통로
정리하면 이렇다. 경로 매개변수는 주소에 박혀 자원을 콕 집고(거의 필수), 쿼리 매개변수는 물음표 뒤에 붙어 결과를 거르거나 다듬는다(기본값 주면 선택). 둘 다 타입힌트만으로 글자→숫자 변환과 검증이 공짜로 따라오고, Path·Query 로 "1 이상"·"2글자 이상" 같은 규칙까지 한 줄로 얹을 수 있다. 그런데 값이 여러 개 묶인 덩어리, 가령 회원가입 폼 전체를 받으려면? 그건 주소가 아니라 요청 본문에 담아야 한다. 3편에서 Pydantic 모델로 그 본문을 받는다.
다음 편 예고 — Pydantic 모델로 요청 본문 받기
이름·이메일·비밀번호처럼 묶인 데이터를 모델 하나로 받고 검증하는 법.