FastAPI 서버개발, FastAPI 애플리케이션 구조화, 라우트 구현

FastAPI는 현대적인 웹 API 개발을 위한 프레임워크로, Python 언어를 기반으로 하여 비동기 프로그래밍을 지원합니다. FastAPI의 주요 장점 중 하나는 높은 성능을 유지하면서도 개발자가 직관적으로 이해하고 사용할 수 있도록 돕는 강력한 타입 시스템과 자동화된 문서 생성 기능입니다. 이 글에서는 FastAPI 서버를 개발하고 애플리케이션 구조화 방법, 그리고 라우트 구현에 대해 자세히 설명하고 예제 코드를 제공합니다.

1. FastAPI란?

FastAPI는 웹 애플리케이션의 백엔드 API를 쉽게 구축할 수 있도록 해주는 Python 프레임워크입니다. 다음과 같은 특징이 있습니다:

  • 빠른 성능: Starlette를 기반으로 하며, 비동기적이고 빠른 실행 속도를 가지고 있습니다.
  • 자동 문서 생성: Swagger UI 및 ReDoc을 통한 자동 API 문서화를 지원합니다.
  • 타입 힌팅: Python 3.6 이상의 타입 힌팅을 사용하여 코드 가독성과 유지 보수성을 높입니다.

2. FastAPI로 서버 개발 시작하기

FastAPI 설치는 간단합니다. pip를 사용하여 FastAPI와 uvicorn을 설치할 수 있습니다:

pip install fastapi uvicorn

3. 기본 FastAPI 애플리케이션 구조

FastAPI 애플리케이션은 다음과 같은 구조로 구성될 수 있습니다:

  • main.py: FastAPI 애플리케이션의 진입점입니다.
  • routers: 다양한 API 라우트를 정의하는 모듈입니다.
  • models: 데이터베이스 모델 및 Pydantic 모델을 정의하는 모듈입니다.
  • schemas: 요청 및 응답 모델을 정의하는 Pydantic 스키마입니다.
  • services: 비즈니스 로직을 처리하는 모듈입니다.

3.1 기본 FastAPI 애플리케이션 코드

아래 코드는 FastAPI의 기본 애플리케이션 구조를 보여줍니다:

from fastapi import FastAPI

app = FastAPI()

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

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

4. FastAPI 애플리케이션 라우팅

FastAPI에서 라우팅은 웹 경로와 HTTP 메서드(GET, POST, PUT, DELETE 등)에 따라 클라이언트의 요청을 처리하는 방식을 정의합니다. FastAPI는 각 라우트에 대한 HTTP 메서드를 데커레이터 형태로 작성할 수 있게 해줍니다.

4.1 라우트 구현하기

라우트를 구현하기 위해 FastAPI에 내장된 여러 데커레이터를 사용할 수 있습니다. 예를 들어, 클라이언트가 “/items/{item_id}” 경로로 GET 요청을 보낼 때 특정 아이템을 반환하는 라우트를 아래와 같이 정의할 수 있습니다:

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    return {"item_id": item_id, "q": q}

4.2 다양한 HTTP 메서드 사용하기

FastAPI는 GET, POST, PUT, DELETE와 같은 다양한 HTTP 메서드를 지원합니다. 아래 예제는 POST 요청을 처리하여 클라이언트로부터 입력 받은 데이터를 반환하는 라우트를 보여줍니다:

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float
    is_offer: bool = None

@app.post("/items/")
async def create_item(item: Item):
    return item

5. FastAPI 라우터로 애플리케이션 구조화하기

대규모 애플리케이션에서는 모든 라우트를 main.py 파일에 정의하는 것보다 모듈화하여 관리하는 것이 효율적입니다. FastAPI에서는 `APIRouter`를 사용하여 라우트 그룹을 만들 수 있습니다.

5.1 라우터 설정하기

프로젝트 구조를 다음과 같이 설정합니다:

  • app/
    • main.py
    • routers/
      • items.py
    • models/
      • item.py

5.2 items.py 라우터 구현

items.py 파일에서 아이템 관련 모든 라우트를 정의합니다:

from fastapi import APIRouter
from models.item import Item

router = APIRouter()

@router.post("/items/")
async def create_item(item: Item):
    return item

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

5.3 main.py 파일에서 라우터 포함시키기

라우터를 main.py에 포함시켜 사용합니다:

from fastapi import FastAPI
from routers.items import router as items_router

app = FastAPI()

app.include_router(items_router, prefix="/items", tags=["items"])

6. FastAPI와 데이터베이스 연동하기

FastAPI 애플리케이션에서 데이터베이스와 연동하는 것은 자주 필요하는 기능입니다. 아래 코드는 SQLAlchemy와 FastAPI를 통합하여 데이터베이스와 상호작용하는 방법을 보여줍니다.

6.1 SQLAlchemy 설치하기

SQLAlchemy와 데이터베이스 드라이버를 설치합니다:

pip install sqlalchemy databases

6.2 데이터베이스 연결 설정

database.py 파일을 생성하고 데이터베이스 연결을 설정합니다:

from sqlalchemy import create_engine, MetaData
from sqlalchemy.ext.declarative import declarative_base

SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
metadata = MetaData()
Base = declarative_base()

6.3 모델 정의하기

models/item.py에서 데이터베이스 모델을 정의합니다:

from sqlalchemy import Column, Integer, String
from database import Base

class Item(Base):
    __tablename__ = "items"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    price = Column(Integer)

6.4 CRUD 작동 구현하기

서비스 계층에 CRUD 작동을 구현합니다:

from sqlalchemy.orm import Session
from models.item import Item

def get_item(db: Session, item_id: int):
    return db.query(Item).filter(Item.id == item_id).first()

def create_item(db: Session, item: Item):
    db.add(item)
    db.commit()
    db.refresh(item)
    return item

7. FastAPI 애플리케이션 실행하기

FastAPI 애플리케이션을 실행하기 위해 다음 명령어를 사용합니다:

uvicorn main:app --reload

8. 결론

FastAPI는 훌륭한 성능과 직관적인 코드 작성을 제공하는 웹 API 프레임워크입니다. 본 강좌에서는 FastAPI를 사용하여 서버를 개발하고 애플리케이션 구조화 방법, 라우트 구현에 대해 다루었습니다. FastAPI가 제공하는 다양한 기능들을 활용하여 보다 나은 웹 애플리케이션을 구축할 수 있기를 바랍니다.

9. 추가 자료

FastAPI 공식 문서에서 더 많은 정보와 자료를 확인할 수 있습니다: FastAPI Documentation

FastAPI 서버개발, 중복 코드 줄이기를 위한 의존성 재사용

웹 애플리케이션을 개발할 때, 코드의 중복을 줄이는 것은 소프트웨어의 유지보수성과 확장성을 높이는 중요한 작업입니다. FastAPI는 이러한 작업을 효율적으로 처리하기 위한 다양한 방법을 제공하며, 특히 의존성 주입(Dependency Injection)의 개념을 통해 중복 코드를 줄일 수 있는 뛰어난 기능을 갖추고 있습니다.

1. FastAPI 소개

FastAPI는 Python으로 작성된 현대적인 웹 프레임워크로, 빠른 성능과 개발 생산성을 자랑합니다. ASGI(Asynchronous Server Gateway Interface)를 기반으로 하며, 비동기 프로그래밍을 지원합니다. 데이터 검증, 직렬화, OpenAPI 및 JSON Schema에 대한 자동화된 문서화를 쉽게 처리할 수 있습니다.

2. 의존성 주입(Dependency Injection)란?

의존성 주입이란 객체나 함수가 필요로 하는 외부의 의존 객체를 주입받아 사용하는 디자인 패턴입니다. 이를 통해 객체 간의 결합도가 낮아져 코드의 재사용성이 증가하고, 테스트가 쉬워지는 장점이 있습니다.

2.1 FastAPI에서 의존성 주입 사용하기

FastAPI에서는 의존성을 정의하고 주입하는 것이 매우 간단합니다. 의존성을 정의하기 위해서는 Depends 클래스를 사용합니다. 아래 코드 예제를 통해 이를 살펴보겠습니다.

from fastapi import FastAPI, Depends, HTTPException

app = FastAPI()

def get_query_param(q: str = None):
    return q

@app.get("/items/")
async def read_item(q: str = Depends(get_query_param)):
    if q:
        return {"query": q}
    return {"query": "No query provided"}

위 예제에서는 get_query_param이라는 함수를 정의하고 여기서 query parameter를 받아옵니다. Depends를 사용하여 이를 read_item 함수에 주입하고 있습니다.

3. 중복 코드 줄이기의 필요성

웹 애플리케이션의 크기가 커질수록, 비슷한 기능을 갖는 코드 블록이 여러 곳에서 반복되는 경우가 많습니다. 이러한 중복 코드는 코드의 복잡성을 증가시키고, 유지보수의 어려움을 초래합니다. 의존성 주입을 통해 이러한 중복 코드를 줄임으로써 효율적인 코드 작성을 할 수 있습니다.

4. 의존성 재사용을 통한 코드 최적화

의존성 재사용은 FastAPI의 핵심 특징 중 하나입니다. 기본적인 의존성을 여러 라우터에 재사용할 수 있고, 이를 통해 코드의 일관성을 유지하며 중복을 제거할 수 있습니다.

4.1 공통 의존성 정의하기

예를 들어, 사용자의 인증을 처리하는 공통 의존성을 정의할 수 있습니다. 아래 코드는 JWT 인증을 위한 예시입니다.

from fastapi import Security, Depends
from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

async def get_current_user(token: str = Depends(oauth2_scheme)):
    # 여기에 JWT 검증 로직을 추가합니다.
    if token != "valid-token":
        raise HTTPException(status_code=401, detail="Invalid authentication credentials")
    return {"username": "user1"}

@app.get("/users/me")
async def read_users_me(current_user: dict = Depends(get_current_user)):
    return current_user

여기서 get_current_user 함수는 JWT 토큰을 검증하는 역할을 하며, 여러 라우터에서 재사용될 수 있습니다.

4.2 동적으로 의존성 주입하기

FastAPI에서는 동적으로 의존성을 주입할 수도 있습니다. 이를 통해 상황에 따라 다른 의존성을 사용할 수 있습니다. 예를 들어, 환경에 따라 다른 데이터베이스 연결을 사용할 수 있습니다.

from fastapi import Depends

def get_database_connection():
    if app.state.environment == "production":
        return "Production DB Connection"
    return "Development DB Connection"

@app.get("/db/")
async def read_db(connection: str = Depends(get_database_connection)):
    return {"connection": connection}

위 코드에서 환경 변수를 통해 서로 다른 데이터베이스 연결을 주입받을 수 있습니다.

5. 의존성 재사용의 이점

  • 유지보수성: 중복이 제거됨으로써 코드 전반에 걸쳐 더 쉽게 수정할 수 있습니다.
  • 테스트 용이성: 더 적은 코드로 더 많은 기능을 구현할 수 있어, 테스트와 디버깅을 쉽게 할 수 있습니다.
  • 일관성: 의존성이 각기 다른 라우터에서 재사용됨으로써 코드의 일관성을 유지할 수 있습니다.

6. 고급 의존성 주입

FastAPI에서는 의존성 주입의 고급 기능도 제공하여, 클래스와 함께 사용할 수 있습니다. 이를 통해 더 복잡한 비즈니스 로직을 캡슐화할 수 있습니다. 아래는 클래스 기반의 의존성 주입 예제입니다.

class UserService:
    def get_user(self, user_id: int):
        # 데이터베이스에서 사용자 정보를 가져오는 로직
        return {"user_id": user_id, "name": "John Doe"}

def get_user_service() -> UserService:
    return UserService()

@app.get("/users/{user_id}")
async def read_user(user_id: int, user_service: UserService = Depends(get_user_service)):
    return user_service.get_user(user_id)

이 예제에서 UserService 클래스는 사용자의 정보를 제공하는 로직을 담당하며, 이를 의존성으로 주입받아 사용할 수 있습니다.

7. 결론

FastAPI는 의존성 주입을 통해 중복 코드를 최소화하고 코드의 재사용성을 극대화할 수 있는 훌륭한 도구입니다. 의존성을 통해 작성된 코드는 더 간결하고, 유지보수하기 쉬우며, 테스트가 용이합니다. 여러 가지 예제를 통해 의존성 재사용의 중요성과 이를 FastAPI에서 구현하는 방법을 살펴보았습니다.

참고 자료

FastAPI 서버개발, 스키마 – 응답, 라우터에 정의한 응답에 대한 설명

FastAPI는 현대 웹 API를 간편하게 개발할 수 있는 고속 웹 프레임워크로, Python으로 작성되었습니다. 요즘 많은 개발자들이 Flask나 Django 대신 FastAPI를 선택하는 이유는 빠르게 개발할 수 있는 장점과 다양한 기능을 제공하기 때문입니다. 본 강좌에서는 FastAPI를 이용한 서버 개발 과정에서 응답 스키마, 그리고 라우터에 정의한 응답에 대해 깊이 있게 알아보겠습니다.

1. FastAPI 소개

FastAPI는 다음과 같은 장점을 가지고 있습니다:

  • 신속하게 API 개발이 가능하다.
  • 데이터 검증을 자동으로 수행하며 인텔리센스 지원이 뛰어나다.
  • 비동기 프로그래밍을 지원하여 높은 성능을 발휘한다.
  • OpenAPI 및 JSON Schema를 자동으로 생성한다.

2. FastAPI의 응답 스키마

FastAPI는 Pydantic을 사용하여 데이터 검증 및 데이터 직렬화를 수행합니다. 이를 통해 API의 입력 및 출력 데이터의 구조를 간편하게 정의할 수 있습니다. 응답 스키마를 정의하기 위해서는 Pydantic 모델을 생성하고, 이를 FastAPI의 경로 함수의 응답 타입으로 설정하면 됩니다.

2.1 Pydantic 모델 정의

Pydantic 모델을 정의하기 위해서는 클래스를 만들어야 합니다. 아래의 예시는 사용자의 정보를 표현하기 위한 Pydantic 모델입니다:


from pydantic import BaseModel

class User(BaseModel):
    id: int
    username: str
    email: str
    full_name: str = None  # 선택적인 필드

위의 예에서 User 클래스는 FastAPI에서 사용할 응답 스키마를 정의합니다. 각 필드는 해당 데이터 타입을 지정하며, 선택적 필드는 기본값을 None으로 설정합니다.

2.2 경로 함수에서 응답 스키마 사용하기

정의된 Pydantic 모델을 FastAPI의 경로 함수의 반환 타입으로 사용하여, API의 응답 데이터 타입을 명확하게 나타낼 수 있습니다. 예를 들어:


from fastapi import FastAPI

app = FastAPI()

@app.get("/users/{user_id}", response_model=User)
async def read_user(user_id: int):
    return User(id=user_id, username="JohnDoe", email="john@example.com", full_name="John Doe")

위 코드에서 response_model=User 인자를 통해 FastAPI는 이 경로에서 반환되는 데이터가 User 모델에 맞는지 검증합니다. 만약 반환되는 데이터가 모델과 일치하지 않는 경우, FastAPI는 자동으로 오류 응답을 생성합니다.

3. 라우터에 정의한 응답

FastAPI에서는 경로를 정의하기 위해 라우터를 사용할 수 있습니다. 이는 코드의 구조를 개선하고, 여러 경로를 그룹화하여 관리하기 쉽게 합니다. 별도의 라우터를 생성하고 이를 메인 애플리케이션에 포함시켜 사용할 수 있습니다.

3.1 라우터 생성

아래는 사용자를 관리하는 라우터를 만드는 예시입니다:


from fastapi import APIRouter

user_router = APIRouter()

@user_router.get("/users/{user_id}", response_model=User)
async def get_user(user_id: int):
    return User(id=user_id, username="JohnDoe", email="john@example.com", full_name="John Doe")

위 코드는 사용자의 정보를 가져오는 API를 정의하고 있습니다. APIRouter 클래스를 인스턴스화하여 user_router 객체를 생성한 후, 경로 함수와 응답 모델을 설정합니다.

3.2 메인 애플리케이션에 라우터 포함하기

생성한 라우터를 메인 FastAPI 애플리케이션에 연결하기 위해서는 include_router 메서드를 사용합니다:


app.include_router(user_router)

이제 메인 애플리케이션은 user_router에서 정의한 모든 경로를 사용할 수 있습니다.

4. 응답의 직렬화 및 검증

FastAPI는 Pydantic을 사용하여 응답 데이터를 자동으로 직렬화합니다. 기본적으로 객체는 JSON 형식으로 변환되어 클라이언트에게 전송됩니다. 이 과정에서 FastAPI는 데이터의 유효성을 검증하기 때문에, 클라이언트는 항상 일관된 데이터를 수신할 수 있습니다.

4.1 응답 예제

이제 실제로 API를 호출해보겠습니다. 사용자가 고유한 ID를 요청하면, 서버는 해당 사용자 정보를 JSON 형식으로 반환하게 됩니다. 예를 들어:


curl -X GET "http://localhost:8000/users/1"

위 요청에 대한 응답은 다음과 같을 것입니다:


{
    "id": 1,
    "username": "JohnDoe",
    "email": "john@example.com",
    "full_name": "John Doe"
}

4.2 에러 처리

FastAPI는 모델 검증 과정에서 문제가 발생하면 자동으로 오류 응답을 생성합니다. 예를 들어, 잘못된 데이터형을 요청하면:


curl -X GET "http://localhost:8000/users/not_a_number"

서버는 다음과 같은 오류 응답을 리턴합니다:


{
    "detail": [
        {
            "loc": ["path", "user_id"],
            "msg": "value is not a valid integer",
            "type": "type_error.integer"
        }
    ]
}

5. 보안과 오류 관리

API 응답을 안전하게 유지하고 오류를 효과적으로 관리하는 것은 서버 개발의 핵심 요소입니다. FastAPI는 다양한 보안 기능을 제공하며, 모든 오류를 세밀하게 제어할 수 있는 방법을 갖추고 있습니다.

5.1 OAuth2 및 JWT 인증

FastAPI는 OAuth2 및 JWT를 사용한 인증을 지원합니다. 이를 통해 API 접근을 제한하고, 사용자 데이터를 보호할 수 있습니다.

5.2 사용자 정의 오류 응답

FastAPI는 사용자 정의 오류 처리를 쉽게 할 수 있도록 지원합니다. 아래와 같이 오류 발생 시 사용자 정의 응답을 리턴하도록 설정할 수 있습니다:


from fastapi import HTTPException

@app.get("/users/{user_id}", response_model=User)
async def get_user(user_id: int):
    if user_id <= 0:
        raise HTTPException(status_code=400, detail="Invalid user ID")
    return User(id=user_id, username="JohnDoe", email="john@example.com", full_name="John Doe")

위 예시는 사용자 ID가 유효하지 않을 경우 사용자 정의 오류를 발생시키는 예시입니다.

6. 마무리

FastAPI는 웹 API를 개발하는 데 많은 장점을 제공하며, 스키마 정의와 라우터를 이용한 구조적 접근 방식은 안정적이고 유지 보수가 용이한 코드를 만들 수 있게 합니다. 응답 스키마를 정의하고, 라우터를 포함하여 API 응답을 관리하는 방법에 대해 논의했습니다. 이러한 방법들을 통하여, 여러분은 더 나은 API를 설계할 수 있는 기초를 다졌을 것입니다.

FastAPI는 Python의 강력한 기능을 결합하여 웹 개발을 훨씬 용이하게 만들어 줍니다. 앞으로도 FastAPI와 함께 다양한 API를 개발해 보시길 바랍니다!

FastAPI 서버개발, 컨테이너 이미지 업로드 GCR 이용하기

FastAPI는 현대적인 웹 API를 개발하는 데 매우 유용한 프레임워크입니다. Python의 타입 힌트를 활용하여 빠르고 효율적으로 RESTful API를 구축할 수 있습니다. 이번 포스트에서는 FastAPI를 사용하여 컨테이너 이미지를 Google Cloud Registry(GCR)에 업로드하는 방법에 대해 자세히 설명하겠습니다.

1. FastAPI란?

FastAPI는 Python으로 작성된 웹 프레임워크로, 비동기 요청을 지원하며, 높은 성능과 직관적인 API를 제공합니다. 다음은 FastAPI의 주요 특징입니다:

  • 빠른 성능: Starlette와 Pydantic을 기반으로 하여 높은 성능을 보장합니다.
  • 타입 힌트 지원: Python의 타입 힌트를 활용하여 자동으로 API 문서를 생성할 수 있습니다.
  • 비동기 지원: 비동기 처리를 통해 성능을 극대화할 수 있습니다.

2. GCR(Google Container Registry)란?

GCR은 Google Cloud Platform(GCP)의 컨테이너 레지스트리로, Docker 컨테이너 이미지를 저장하고 관리하는 데 사용됩니다. GCR을 사용하면 Kubernetes 클러스터에서 쉽게 이미지를 배포할 수 있습니다. GCP의 여러 리소스와 통합되어 있어 효율적입니다.

3. FastAPI 프로젝트 설정

FastAPI 프로젝트를 설정하기 위해 다음과 같은 단계를 따릅니다:

3.1. 환경 세팅

bash
mkdir fastapi-gcr
cd fastapi-gcr
python3 -m venv venv
source venv/bin/activate
pip install fastapi uvicorn google-cloud-storage

여기서는 가상 환경을 만들고 FastAPI 및 관련 라이브러리를 설치합니다.

3.2. FastAPI 애플리케이션 구현

다음으로 FastAPI 애플리케이션을 구현합니다. 간단한 이미지 업로드 API를 생성할 것입니다.

python
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse
from google.cloud import storage
import os

app = FastAPI()

# GCS 클라이언트 초기화
storage_client = storage.Client()
bucket_name = 'YOUR_BUCKET_NAME'
bucket = storage_client.bucket(bucket_name)

@app.post("/upload/")
async def upload_image(file: UploadFile = File(...)):
    # 파일을 Google Cloud Storage에 업로드
    blob = bucket.blob(file.filename)
    blob.upload_from_file(file.file, content_type=file.content_type)
    
    return JSONResponse(content={"message": "File uploaded successfully!", "filename": file.filename})

위의 코드는 FastAPI의 이미지 업로드 API를 생성합니다. 파일을 GCS에 업로드하기 위해 google-cloud-storage 라이브러리를 사용합니다.

4. Google Cloud Storage 인증

GCP에 접근하기 위해서는 인증이 필요합니다. 서비스 계정 키 파일을 생성하고, 환경 변수를 설정합니다.

4.1. 서비스 계정 생성

1. GCP 콘솔에 로그인합니다.

2. IAM 및 관리 > 서비스 계정으로 이동합니다.

3. 새 서비스 계정을 만듭니다. 역할에 Storage Admin을 선택합니다.

4. JSON 키 파일을 생성하고, 이를 프로젝트 루트에 저장합니다.

4.2. 환경 변수 설정

bash
export GOOGLE_APPLICATION_CREDENTIALS="path/to/your/service-account-file.json"

5. FastAPI 애플리케이션 실행

FastAPI 웹 서버를 실행하려면 다음 명령어를 사용합니다:

bash
uvicorn main:app --host 0.0.0.0 --port 8000 --reload

6. GCR에 Docker 이미지 배포하기

Docker 이미지를 GCR에 배포하기 위한 설정을 진행합니다.

6.1. Dockerfile 생성

애플리케이션을 도커 이미지로 패키징하기 위해 Dockerfile을 만듭니다.

Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8

COPY ./app /app
WORKDIR /app
RUN pip install google-cloud-storage

EXPOSE 80
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

6.2. Docker 이미지 빌드

bash
docker build -t gcr.io/YOUR_PROJECT_ID/fastapi-gcr .

6.3. GCR에 로그인

bash
gcloud auth configure-docker

6.4. Docker 이미지 푸시

bash
docker push gcr.io/YOUR_PROJECT_ID/fastapi-gcr

7. 결론

이번 포스트에서는 FastAPI를 사용하여 GCR에 이미지를 업로드하는 간단한 API를 구현하는 과정을 살펴보았습니다. FastAPI는 쉽고 직관적인 API 개발을 지원하며, GCR은 Docker 이미지를 효율적으로 관리할 수 있게 해줍니다.

FastAPI와 GCR을 활용한 웹 서비스 구축은 클라우드 환경에서의 유연한 확장이 가능하게 하여, 실제 서비스에서 큰 장점을 제공합니다. 다양한 기능과 성능을 바랑하는 FastAPI를 통해 여러분의 웹 애플리케이션을 더욱 발전시키길 바랍니다.

FastAPI 서버개발, SQLAlchemy의 DB 모델 정의

FastAPI는 Python으로 웹 애플리케이션을 개발하기 위해 설계된 모던 웹 프레임워크로, 매우 빠르며 비동기 프로그래밍을 지원합니다. 이번 포스트에서는 FastAPI의 백엔드 개발 환경에서 SQLAlchemy를 사용하여 데이터베이스 모델을 정의하는 방법에 대해 자세히 설명하겠습니다. SQLAlchemy는 Python의 ORM(Object Relational Mapping) 라이브러리로, 데이터베이스와의 상호작용을 더 간편하게 만들어줍니다.

1. FastAPI와 SQLAlchemy 소개

FastAPI는 다음과 같은 몇 가지 주요 이점을 가지고 있습니다:

  • 비동기 지원: Python의 asyncawait 구문을 사용하여 비동기 프로그래밍을 간편하게 구현할 수 있습니다.
  • 자동화된 문서화: FastAPI는 OpenAPI 스펙을 기반으로 클라이언트를 위한 API 문서를 자동으로 생성합니다.
  • 고성능: Starlette 위에 구축되어 있어 높은 성능을 자랑합니다.

SQLAlchemy는 관계형 데이터베이스와 상호작용하기 위한 ORM으로 다음과 같은 이점이 있습니다:

  • 데이터베이스 독립성: 여러 종류의 데이터베이스와 호환됩니다.
  • 복잡한 쿼리를 쉽게 작성: SQLAlchemy의 쿼리 빌더를 사용하면 복잡한 SQL 쿼리를 쉽게 작성할 수 있습니다.
  • 모델 정의: Python 클래스 정의를 통해 데이터베이스 모델을 직관적으로 생성할 수 있습니다.

2. 개발 환경 설정

FastAPI와 SQLAlchemy를 사용하기 위해서는 우선 Python이 설치되어 있어야 합니다. 각 패키지를 설치하는 과정은 다음과 같습니다.

pip install fastapi[all] sqlalchemy

참고: 위의 명령어에서 fastapi[all]는 FastAPI와 함께 uvicorn과 같은 모든 의존성을 설치합니다.

3. SQLAlchemy DB 모델 정의하기

SQLAlchemy를 사용하여 데이터베이스 모델을 정의하려면 몇 가지 단계를 거쳐야 합니다:

  1. SQLAlchemy 엔진 생성
  2. 세션 클래스 정의
  3. 데이터베이스 모델 정의
  4. 테이블 생성

3.1 SQLAlchemy 엔진 생성

데이터베이스 연결을 위해 SQLAlchemy 엔진을 생성해야 합니다. 이는 데이터베이스 URL을 통해 이루어집니다. 예를 들어, SQLite 데이터베이스를 사용할 경우 아래와 같이 설정할 수 있습니다.

from sqlalchemy import create_engine

DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})

3.2 세션 클래스 정의

SQLAlchemy의 세션은 데이터베이스에 대한 트랜잭션을 관리합니다. 아래 코드를 통해 세션 클래스를 정의할 수 있습니다.

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

3.3 데이터베이스 모델 정의

이제 SQLAlchemy의 Base 클래스를 상속받아 원하는 데이터베이스 모델을 정의할 수 있습니다. 예를 들어, 사용자 정보를 저장하기 위한 User 모델은 다음과 같이 정의할 수 있습니다.

from sqlalchemy import Column, Integer, String, Boolean

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    username = Column(String, unique=True, index=True)
    email = Column(String, unique=True, index=True)
    is_active = Column(Boolean, default=True)

3.4 테이블 생성하기

모델 정의가 끝났다면 실제 데이터베이스에 테이블을 생성해야 합니다. 다음과 같은 코드를 사용하여 테이블을 생성할 수 있습니다:

Base.metadata.create_all(bind=engine)

4. FastAPI와 SQLAlchemy 통합하기

이제 FastAPI를 사용해 만든 API와 SQLAlchemy 데이터베이스 모델을 통합할 차례입니다. 이를 위해 FastAPI 경로에서 CRUD(Create, Read, Update, Delete) 작업을 처리하는 엔드포인트를 작성합니다.

4.1 FastAPI 애플리케이션 생성

from fastapi import FastAPI

app = FastAPI()

4.2 CRUD API 엔드포인트 작성

다음으로, CRUD 작업을 위한 엔드포인트를 정의합니다.

4.2.1 Create: 사용자 생성

from fastapi import Depends, HTTPException
from sqlalchemy.orm import Session

# 의존성 주입을 위한 함수
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.post("/users/", response_model=User)
def create_user(user: User, db: Session = Depends(get_db)):
    db.add(user)
    db.commit()
    db.refresh(user)
    return user

4.2.2 Read: 사용자 조회

@app.get("/users/{user_id}", response_model=User)
def read_user(user_id: int, db: Session = Depends(get_db)):
    user = db.query(User).filter(User.id == user_id).first()
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user

4.2.3 Update: 사용자 업데이트

@app.put("/users/{user_id}", response_model=User)
def update_user(user_id: int, user: User, db: Session = Depends(get_db)):
    db_user = db.query(User).filter(User.id == user_id).first()
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    
    db_user.username = user.username
    db_user.email = user.email
    db.commit()
    db.refresh(db_user)
    return db_user

4.2.4 Delete: 사용자 삭제

@app.delete("/users/{user_id}")
def delete_user(user_id: int, db: Session = Depends(get_db)):
    db_user = db.query(User).filter(User.id == user_id).first()
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    db.delete(db_user)
    db.commit()
    return {"detail": "User deleted"}

5. 결론

이 글에서 우리는 FastAPI와 SQLAlchemy를 사용하여 데이터베이스 모델을 정의하고, 이를 기반으로 CRUD API를 구축하는 방법에 대해 알아보았습니다. FastAPI의 비동기 지원과 SQLAlchemy의 ORM 기능을 결합하면 Python으로 웹 애플리케이션을 효율적으로 개발할 수 있습니다. 이 과정을 통해 더 나은 백엔드 시스템을 구축하고, 확장 가능한 API를 만들 수 있습니다.

추가적으로 FastAPI의 기능을 이용하여 데이터 유효성 검증, 쿼리 파라미터 처리, JWT 인증, CORS 설정 등 다양한 기능을 적용할 수 있습니다. 이러한 기능들은 실제 실무에서 매우 유용하게 사용될 수 있습니다.

FastAPI와 SQLAlchemy의 기본 사용법을 익힌 후에는 복잡한 비즈니스 로직을 추가하여 더욱 정교한 애플리케이션을 만들어 가는 과정을 통해 더 큰 발전을 이룰 수 있을 것입니다.