첫 단계 (First Steps)

첫 단계 (First Steps)

원문: First Steps - FastAPI

가장 단순한 FastAPI 파일은 이렇게 생겼어요. 코드만 보면 뭘 하는 앱인지 감이 바로 오죠? 하나씩 뜯어볼게요.

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

이 코드를 main.py 파일로 저장해 볼게요.

— FastAPI에는 VS Code(그리고 Cursor)용 공식 확장 프로그램이 있어요. 이 확장은 path operation 탐색기, path operation 검색, 테스트에서 정의로 점프하는 CodeLens 내비게이션, FastAPI Cloud 배포와 로그까지 에디터 안에서 제공해 줘요.

서버 실행해 보기

이제 실제 서버를 띄워볼까요. 개발 서버는 fastapi dev 명령으로 실행해요.

$ uv run fastapi dev

실행하면 터미널에 이런 로그가 보일 거예요.

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

이 줄이 바로 우리 앱이 로컬 머신에서 서비스되고 있는 URL을 알려주는 줄이에요.

확인해 보기 (Check it)

브라우저를 열어 http://127.0.0.1:8000에 접속해 볼게요. 그러면 JSON 응답이 이렇게 보여요.

{"message": "Hello World"}

인터랙티브 API 문서

이번엔 http://127.0.0.1:8000/docs로 가 볼게요. 자동으로 만들어진 인터랙티브 API 문서를 볼 수 있어요. 이 문서는 Swagger UI가 제공해 줘요.

Swagger UI

대체 API 문서

그리고 이번엔 http://127.0.0.1:8000/redoc로 가 볼게요. 또 한 가지 대체 문서를 볼 수 있는데, 이건 ReDoc이 제공해 줘요.

ReDoc

이렇게 문서가 문서인데, 이게 어떻게 자동으로 만들어지는 걸까요? 그 비밀을 파헤쳐 볼게요.

OpenAPI

FastAPI는 우리 API 전체에 대한 "스키마(schema)"를 OpenAPI 표준을 사용해서 자동으로 만들어요.

"스키마(Schema)"란

"스키마"는 어떤 것에 대한 정의 또는 설명을 뜻해요. 그걸 구현하는 코드가 아니라, 추상적인 설명일 뿐이죠.

API "스키마"

여기서 OpenAPI는 API의 스키마를 어떻게 정의할지를 정하는 **명세(specification)**예요. 이 스키마 정의에는 API의 경로(path), 그 경로가 받는 파라미터 등이 모두 포함돼요.

데이터 "스키마"

한편 "스키마"라는 말은 어떤 데이터의 형태를 가리킬 때도 써요. 예를 들어 JSON 콘텐츠 같은 경우가 그렇죠. 그럴 땐 JSON의 속성들과 각 속성의 데이터 타입 등을 뜻하게 돼요.

OpenAPI와 JSON Schema

OpenAPI는 우리 API에 대한 API 스키마를 정의해요. 그리고 그 API 스키마 안에는 우리 API가 주고받는 데이터의 정의(즉 "스키마")가 포함돼 있어요. 이 데이터 정의는 JSON 데이터 스키마의 표준인 JSON Schema를 사용하지요.

openapi.json 확인해 보기

원시(state) OpenAPI 스키마가 어떤 모양인지 궁금하다면, FastAPI가 우리 API 전체의 설명을 담은 JSON 스키마를 자동으로 생성해 줘요. http://127.0.0.1:8000/openapi.json에서 바로 볼 수 있어요. 이렇게 생긴 JSON이 보일 거예요.

{
    "openapi": "3.1.0",
    "info": {
        "title": "FastAPI",
        "version": "0.1.0"
    },
    "paths": {
        "/items/": {
            "get": {
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {

...

OpenAPI는 어디에 쓰나요

이 OpenAPI 스키마 덕분에 앞서 본 두 가지 인터랙티브 문서 시스템이 동작하는 거예요. 그리고 이 OpenAPI를 기반으로 한 수십 가지 대안도 있어요. 필요하면 그중 아무거나 손쉽게 우리 FastAPI 앱에 붙일 수 있어요.

또 이 스키마를 이용해서 우리 API와 통신하는 클라이언트 코드를 자동으로 생성할 수도 있어요. 예를 들어 프론트엔드, 모바일, IoT 애플리케이션 같은 곳에 활용할 수 있죠.

pyproject.toml에서 앱 entrypoint 설정하기

우리 앱이 어디 있는지 pyproject.toml 파일에서 설정할 수 있어요.

[tool.fastapi]
entrypoint = "main:app"

entrypointfastapi 명령이 앱을 이렇게 import 하라고 알려주는 값이에요.

from main import app

만약 코드가 이렇게 구성되어 있다면,

.
├── backend
│   ├── main.py
│   ├── __init__.py

entrypoint를 이렇게 설정해요.

[tool.fastapi]
entrypoint = "backend.main:app"

이건 다음 코드와 같은 뜻이에요.

from backend.main import app

fastapi dev에 경로를 넣거나 --entrypoint CLI 옵션 사용하기

fastapi dev 명령에 파일 경로를 넘길 수도 있어요. 그러면 사용할 FastAPI 앱 객체를 자동으로 추측해 줘요.

$ uv run fastapi dev main.py

또는 --entrypoint 옵션을 줄 수도 있어요.

$ uv run fastapi dev --entrypoint main:app

하지만 이렇게 하면 fastapi 명령을 부를 때마다 올바른 경로나 entrypoint를 매번 기억해서 넘겨야 하는 불편함이 있어요.

게다가 다른 도구들은 이걸 못 찾을 수도 있어요. 예를 들어 VS Code 확장 프로그램이나 FastAPI Cloud 같은 것들이요. 그래서 pyproject.tomlentrypoint를 넣어 두는 방식을 권장해요.

앱 배포하기 (선택 사항)

FastAPI 앱을 FastAPI Cloud에 한 줄 명령으로 배포할 수도 있어요. 🚀

$ uv run fastapi deploy

그러면 CLI가 우리 FastAPI 애플리케이션을 자동으로 찾아 클라우드에 배포해 줘요. 로그인이 되어 있지 않다면 브라우저가 열리면서 인증 과정을 진행하게 돼요.

이것만 하면 끝! 이제 그 URL에서 우리 앱에 접근할 수 있어요. ✨

단계별로 다시 정리해 볼게요 (Recap, step by step)

지금까지 본 내용을 코드 중심으로 단계를 나눠 다시 한번 정리해 볼게요.

1단계: FastAPI import 하기

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

FastAPI는 우리 API에 필요한 모든 기능을 제공하는 Python 클래스예요.

기술적인 세부사항FastAPIStarlette에서 직접 상속받은 클래스예요. 그래서 Starlette의 기능도 FastAPI에서 그대로 쓸 수 있어요.

2단계: FastAPI "인스턴스" 만들기

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

여기서 app 변수는 FastAPI 클래스의 **"인스턴스"**가 돼요. 이게 우리 API를 만드는 모든 작업의 중심 접점이 되는 거예요.

3단계: 경로 연산(path operation) 만들기

경로 (Path)

여기서 "경로"는 URL에서 첫 번째 /부터 시작하는 마지막 부분을 가리켜요. 예를 들어 URL이

https://example.com/items/foo

이라면, 경로는 이 부분이에요.

/items/foo

참고 — "path"는 흔히 "endpoint"나 "route"라고도 불러요. API를 만들 때 "path"는 "관심사(concerns)"와 "리소스(resources)"를 구분하는 주요한 기준이 돼요.

연산 (Operation)

여기서 "연산"은 HTTP "메서드(method)" 중 하나를 가리켜요. 다음과 같은 것들 중 하나죠.

  • POST
  • GET
  • PUT
  • DELETE

그리고 조금 더 특이한 것들도 있어요.

  • OPTIONS
  • HEAD
  • PATCH
  • TRACE

HTTP 프로토콜에서 각 경로에는 이 "메서드" 중 하나(또는 그 이상)로 통신할 수 있어요.

API를 만들 때는 보통 이런 HTTP 메서드를 특정한 동작과 짝지어 사용해요.

  • POST: 데이터를 만들 때.
  • GET: 데이터를 읽을 때.
  • PUT: 데이터를 갱신할 때.
  • DELETE: 데이터를 삭제할 때.

그래서 OpenAPI에서는 각 HTTP 메서드를 **"operation"**이라고 불러요. 우리도 앞으로 이들을 "연산"이라고 부를게요.

경로 연산 데코레이터(path operation decorator) 정의하기

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/")FastAPI에게 바로 아래 있는 함수가 다음 요청을 처리하는 책임을 맡는다고 알려줘요.

  • 경로 /
  • get 연산을 사용하는 요청

@decorator 정보 — Python에서 @something 문법을 "decorator(데코레이터)"라고 불러요. 함수 위에 데코레이터를 얹어 두죠. 장식용 예쁜 모자를 얹어 둔다고 생각하면 돼요(아마 거기서 이름이 나온 것 같아요).

"decorator"는 아래 있는 함수를 가져다가 어떤 작업을 해 줘요. 우리 경우엔 이 데코레이터가 FastAPI에게 "아래 함수는 경로(path)/연산(operation)get에 대응하는 함수야"라고 알려주는 거예요. 이게 바로 "경로 연산 데코레이터"예요.

다른 연산들도 쓸 수 있어요.

  • @app.post()
  • @app.put()
  • @app.delete()

더 특이한 것들도 있죠.

  • @app.options()
  • @app.head()
  • @app.patch()
  • @app.trace()

— 각 연산(HTTP 메서드)을 어떻게 쓰는지는 우리 마음대로예요. FastAPI는 어떤 특정한 의미를 강제하지 않아요. 여기서 소개한 내용은 "요구사항"이 아니라 "가이드라인"으로 보면 돼요. 예를 들어 GraphQL을 쓸 땐 보통 모든 동작을 POST 연산 하나로 처리하기도 해요.

4단계: 경로 연산 함수(path operation function) 정의하기

이게 우리의 "경로 연산 함수"예요.

  • 경로(path): /
  • 연산(operation): get
  • 함수(function): "decorator" 아래(@app.get("/") 아래) 있는 함수
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

이건 하나의 Python 함수예요. FastAPI가 URL "/"에 GET 연산으로 요청을 받을 때마다 이 함수를 호출해 줘요.

이 경우엔 async 함수로 정의되어 있어요.

이렇게 async def 대신 일반 함수로도 정의할 수 있어요.

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def root():
    return {"message": "Hello World"}

참고 — 이 둘의 차이를 모른다면 Async: "서두르는 중이세요?"를 확인해 보세요.

5단계: 내용 반환(return)하기

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

dict, list, 그리고 str, int 같은 단일 값도 반환할 수 있어요. Pydantic 모델을 반환할 수도 있는데, 이건 나중에 더 자세히 볼게요. 그 외에도 JSON으로 자동 변환되는 객체와 모델이 아주 많아요(ORM 등을 포함해서요). 좋아하는 걸 골라 써 보세요. 이미 지원되는 걸 쓸 확률이 아주 높아요.

6단계: 배포하기

앱을 **FastAPI Cloud**에 한 줄 명령으로 배포해요: fastapi deploy. 🎉

FastAPI Cloud에 대해 — **FastAPI Cloud**는 FastAPI를 만든 같은 저자와 팀이 만든 서비스예요. API를 구축하고 배포하고 접근하는 과정을 최소한의 노력으로 간소화해 줘요. FastAPI로 앱을 만들 때의 그 개발자 경험(developer experience)을 클라우드 배포까지 그대로 가져온다고 보면 돼요. 🎉

FastAPI Cloud는 FastAPI and friends 오픈소스 프로젝트의 주요 스폰서이자 자금 제공자예요. ✨

다른 클라우드 제공자에 배포하기

FastAPI는 오픈소스이고 표준을 기반으로 해요. 그래서 선택한 어떤 클라우드 제공자에도 FastAPI 앱을 배포할 수 있어요. 각 제공자의 배포 가이드를 따라가면 돼요. 🤓

정리 (Recap)

지금까지 배운 걸 마지막으로 한 번 더 정리해 볼게요.

  • FastAPI를 import 해요.
  • app 인스턴스를 만들어요.
  • @app.get("/") 같은 데코레이터로 경로 연산 데코레이터를 작성해요.
  • 경로 연산 함수를 정의해요. 예: def root(): ...
  • fastapi dev 명령으로 개발 서버를 실행해요.
  • 선택적으로 fastapi deploy로 앱을 배포해요.

여기까지가 FastAPI의 시작이에요. 이제 "Hello World" 하나로 이 도구가 어떤 철학을 가졌는지 느낄 수 있었죠? 다음 장부터는 조금씩 더 실용적인 기능을 쌓아 나가 볼게요.