FastAPI 서버개발, Flask와 비교

웹 애플리케이션 개발에 있어서 프레임워크는 매우 중요한 역할을 합니다. Python 생태계에서 많이 사용되는 두 가지 프레임워크인 Flask와 FastAPI에 대해 비교하고, FastAPI의 장점을 강조하면서 서버 개발의 기초를 다루어 보겠습니다.

1. 개요

웹 프레임워크는 클라이언트의 요청을 처리하고, 데이터를 처리하여 클라이언트에게 응답하는 역할을 합니다. Flask는 경량화된 마이크로 프레임워크로, 간단한 웹 애플리케이션을 만드는 데 특히 유용합니다. 반면, FastAPI는 비동기적 기능과 자동 API 문서화를 지원하여 현대적인 웹 서버 개발에 적합합니다.

2. Flask 소개

Flask는 2010년 Armin Ronacher에 의해 만들어진 마이크로 웹 프레임워크입니다. Flask는 간결하고 유연한 구성을 특징으로 하여, 개발자가 원하는 대로 프레임워크를 확장할 수 있습니다.

2.1. Flask의 특징

  • 가벼운 구조: 최소한의 코드베이스로 확장 가능
  • 유연성: 다양한 플러그인과 확장 기능을 지원
  • Jinja2 템플릿 엔진: HTML 템플릿을 쉽게 작성할 수 있도록 지원
  • 직관적인 라우팅 시스템

2.2. Flask 예제 코드

from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/api/hello', methods=['GET'])
def hello_world():
    return jsonify(message='Hello, World!')

@app.route('/api/greet', methods=['POST'])
def greet_user():
    data = request.get_json()
    name = data.get('name', 'Guest')
    return jsonify(message=f'Hello, {name}!')

if __name__ == '__main__':
    app.run(debug=True)

3. FastAPI 소개

FastAPI는 2018년 Sebastián Ramírez에 의해 만들어진 웹 프레임워크로, Pydantic과 Starlette를 기반으로 하고 있습니다. FastAPI는 비동기 프로그래밍을 지원하고, 타입 힌팅을 통해 데이터 검증 및 자동 문서화를 제공하는 점에서 현대적인 애플리케이션 개발에 적합합니다.

3.1. FastAPI의 특징

  • 비동기 지원: async 및 await를 통해 고성능 비동기 애플리케이션 개발 가능
  • 자동 API 문서화: OpenAPI와 Swagger UI를 통해 API 문서를 자동 생성
  • 타입 검증: Python 3.6 이상의 타입 힌트를 사용하여 데이터 유효성 검증
  • 우수한 성능: Starlette를 기반으로 하여 경량화 및 빠른 속도 제공

3.2. FastAPI 예제 코드

from fastapi import FastAPI

app = FastAPI()

@app.get('/api/hello')
async def hello_world():
    return {'message': 'Hello, World!'}

@app.post('/api/greet')
async def greet_user(name: str = 'Guest'):
    return {'message': f'Hello, {name}!'}

4. Flask와 FastAPI 비교

특징 Flask FastAPI
비동기 지원 비동기 작업을 지원하지만 기본적으로 동기 방식 비동기 방식 기본 지원
자동 문서화 문서화 도구 필요 (예: Flask-RESTPlus) 자동 생성 (OpenAPI, Swagger UI)
타입 힌팅 있으나 필수는 아님 강력한 타입 힌팅 지원
성능 모든 작업을 동기적으로 수행 비동기 처리로 높은 성능 제공

5. FastAPI의 장점

FastAPI는 특히 API 서버 개발과 관련하여 여러 가지 장점을 제공합니다.

5.1. 성능

FastAPI는 Starlette를 기반으로 하여 비동기 처리를 통해 높은 성능을 발휘합니다. 이를 통해 적은 리소스로도 수많은 요청을 처리할 수 있습니다.

5.2. 개발 생산성

자동 문서화 및 타입 힌팅을 통한 데이터 검증 덕분에 코드 작성이 간편해지고, 유지 보수성도 높아집니다. 또한, 클라이언트와의 소통을 원활하게 해주는 API 문서가 자동으로 생성되므로 개발자와 비개발자 간의 협업이 용이해집니다.

5.3. 유연한 데이터 처리

Pydantic을 활용하여 데이터 검증을 강력하고 유연하게 처리할 수 있습니다. 유효하지 않은 데이터는 자동으로 오류 메시지를 반환하므로, 클라이언트 측에서 보다 명확한 피드백을 받을 수 있습니다.

6. 결론

FastAPI는 높은 성능과 개발 생산성을 제공하는 현대적인 웹 프레임워크입니다. Flask와 같은 전통적인 프레임워크와 비교할 때 비동기 처리와 자동 문서화, 강력한 타입 힌팅 등에서 많은 장점을 가지고 있습니다. 이러한 특성 덕분에 FastAPI는 RESTful API 서버 개발에 매우 적합하다고 할 수 있습니다. 기존의 Flask 개발자도 FastAPI의 장점을 활용하여 더 나은 웹 서비스를 구축할 수 있을 것입니다.

7. 참고 자료

FastAPI 서버개발, OAuth2 및 JWT(JSON Web Token) 인증 구현

1. 개요

FastAPI는 Python으로 작성된 최신 웹 프레임워크로, 매우 빠른 성능과 현대적인 웹 개발의 다양한 요구를 충족하기 위한 강력한 기능들을 제공합니다. 본 문서에서는 FastAPI를 사용하여 OAuth2 및 JSON Web Token(JWT) 인증 방식을 구현하는 방법을 소개하고, 이를 통해 안전한 API를 설계하는 방법을 살펴보겠습니다.

2. 요구 사항

본 강좌를 진행하기 위해 필요한 사항은 다음과 같습니다:

  • Python 3.6 이상이 설치된 환경
  • FastAPI 라이브러리 및 관련 패키지
  • Uvicorn: ASGI 서버
  • Pydantic: 데이터 검증 및 설정 관리 라이브러리

3. FastAPI 및 필요한 패키지 설치

콘솔에서 아래의 명령어를 실행하여 FastAPI와 필요한 패키지를 설치합니다:

pip install fastapi uvicorn python-jose passlib[bcrypt]

4. FastAPI 서버 설정

FastAPI 서버를 설정하려면, 간단한 FastAPI 애플리케이션을 만들어 보겠습니다. 다음 코드를 사용하여 기본적인 FastAPI 애플리케이션을 만들어 보세요:

from fastapi import FastAPI

app = FastAPI()

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

이 코드를 main.py라는 파일로 저장한 후, 다음 명령어로 서버를 실행합니다:

uvicorn main:app --reload

이제 http://127.0.0.1:8000로 접속하면 “Hello World” 메시지를 확인할 수 있습니다.

5. OAuth2 및 JWT 인증 방식

OAuth2는 인증 및 권한 부여를 위한 프로토콜로, 사용자가 리소스에 접근할 수 있도록 권한을 부여하는 방식입니다. JWT는 세션 정보를 저장할 수 있는 JSON 객체로, 다양한 정보를 담을 수 있으며, 서명을 통해 데이터의 무결성을 확인할 수 있는 장점이 있습니다.

6. 사용자 인증 및 JWT 생성

회원 가입 및 로그인을 구현할 필요가 있습니다. 다음 예제는 사용자 정보를 저장하기 위한 Pydantic 모델과 JWT 생성을 위한 방법을 보여줍니다:

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from datetime import datetime, timedelta
from pydantic import BaseModel
from passlib.context import CryptContext

# 비밀 키와 알고리즘 설정
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# 비밀번호 암호화 설정
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# 사용자 모델
class User(BaseModel):
    username: str
    email: str | None = None
    full_name: str | None = None
    disabled: bool | None = None

# 로그인 사용자 모델
class UserInDB(User):
    hashed_password: str

# OAuth2PasswordBearer 인스턴스 생성
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# 사용자 데이터 저장소 (예: 데이터베이스 대신 사용)
fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": pwd_context.hash("password"),
        "disabled": False,
    }
}

# 비밀번호 검증 함수
def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

# 사용자 조회 함수
def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)

# JWT 생성 함수
def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

# FastAPI 앱 설정
app = FastAPI()

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = get_user(fake_users_db, form_data.username)
    if not user or not verify_password(form_data.password, user.hashed_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="잘못된 사용자명 또는 비밀번호",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username}, expires_delta=access_token_expires
    )
    return {"access_token": access_token, "token_type": "bearer"}

위의 코드를 통해 로그인 엔드포인트가 생성되었습니다. 사용자는 사용자명을 입력하고 비밀번호를 제공하여 JWT를 받을 수 있습니다.

7. JWT 인증이 필요한 엔드포인트

인증이 필요한 API 엔드포인트를 만들기 위해 FastAPI의 종속성을 활용할 수 있습니다. 다음은 JWT가 필요한 API 엔드포인트를 만드는 방법입니다:

from fastapi import Security

def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="유효하지 않은 토큰",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = User(username=username)
    except JWTError:
        raise credentials_exception
    user = get_user(fake_users_db, username=token_data.username)
    if user is None:
        raise credentials_exception
    return user

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

위의 코드에서는 /users/me 엔드포인트를 만들어 권한이 있는 경우에만 사용자가 본인의 정보를 조회할 수 있도록 설정했습니다.

8. 전체 코드

이제까지 구현한 전체 코드를 정리하면 다음과 같습니다:

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from datetime import datetime, timedelta
from pydantic import BaseModel
from passlib.context import CryptContext

SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

class User(BaseModel):
    username: str
    email: str | None = None
    full_name: str | None = None
    disabled: bool | None = None

class UserInDB(User):
    hashed_password: str

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": pwd_context.hash("password"),
        "disabled": False,
    }
}

def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

app = FastAPI()

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = get_user(fake_users_db, form_data.username)
    
    if not user or not verify_password(form_data.password, user.hashed_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="잘못된 사용자명 또는 비밀번호",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(data={"sub": user.username}, expires_delta=access_token_expires)
    return {"access_token": access_token, "token_type": "bearer"}

def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="유효하지 않은 토큰",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = User(username=username)
    except JWTError:
        raise credentials_exception
    user = get_user(fake_users_db, username=token_data.username)
    if user is None:
        raise credentials_exception
    return user

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

9. 마무리

이제 FastAPI를 사용하여 OAuth2와 JWT를 활용한 인증 기능을 구현하는 방법을 알아보았습니다. 이 예제를 바탕으로 실제 애플리케이션에 필요한 인증 및 권한 부여 로직을 추가하여, 보다 안전한 API를 설계할 수 있습니다. FastAPI의 뛰어난 성능과 간편한 사용법 덕분에 웹 서비스 개발이 훨씬 수월해질 것입니다.

이 글이 FastAPI와 OAuth2, JWT 인증을 이해하는 데 도움이 되었기를 바랍니다. 더 나아가 FastAPI의 다양한 기능을 탐색하시기를 권장합니다.

FastAPI 서버개발, FastAPI 설치

안녕하세요! 오늘은 FastAPI를 사용하여 백엔드 서버를 개발하는 방법에 대해 알아보겠습니다. FastAPI는 현대적인, 빠른 (고성능), 비동기 웹 프레임워크로, Python 3.6 이상에서 작동합니다. RESTful API를 구축하는 데 최적화되어 있으며, API 개발 속도를 빠르게 해주는 유용한 도구입니다. 이 튜토리얼에서는 FastAPI의 설치 방법과 간단한 서버를 만드는 실습을 진행합니다.

FastAPI란?

FastAPI는 Python에서 비동기 API 구축을 쉽게 해주는 웹 프레임워크로, StarlettePydantic 위에 구축되어 있습니다. FastAPI는 데코레이터(Decorator)를 통해 라우트를 쉽게 정의하고, 자동으로 Swagger 문서화 시스템을 생성해줍니다. 특히, 타입 힌트를 기반으로 하여 데이터 유효성 검사 및 자동 완성을 제공하는 점이 큰 장점입니다.

FastAPI 설치하기

FastAPI를 사용하기 위해서는 우선 Python이 설치되어 있어야 합니다. Python이 설치되어 있지 않다면, Python 웹사이트에서 다운로드 후 설치하시기 바랍니다.

1단계: Virtual Environment 설정하기

프로젝트를 진행하기에 앞서, 가상 환경을 사용하는 것을 추천합니다. 가상 환경을 통해 의존성을 분리할 수 있습니다. 아래 명령어로 가상 환경을 생성하고 활성화합니다.

python -m venv fastapi_env
# Windows
fastapi_env\Scripts\activate
# macOS/Linux
source fastapi_env/bin/activate

가상 환경이 활성화되면, 커맨드 프롬프트에 `(fastapi_env)`가 표시됩니다.

2단계: FastAPI 및 Uvicorn 설치하기

FastAPI를 설치하려면 pip를 사용하여 설치할 수 있습니다. Uvicorn은 FastAPI 앱을 실행하는 ASGI 서버입니다.

pip install fastapi uvicorn

3단계: FastAPI 설치 확인하기

FastAPI가 올바르게 설치되었는지 확인하기 위해, 간단한 Python 스크립트를 작성해 보겠습니다. 아래와 같이 main.py 파일을 생성하세요.

# main.py
from fastapi import FastAPI

app = FastAPI()

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

FastAPI 서버 실행하기

이제 FastAPI 서버를 실행할 준비가 되었습니다. 아래와 같이 다음 명령어를 입력하여 서버를 실행하세요.

uvicorn main:app --reload

여기서 main:appmain.py 파일의 app 객체를 참조합니다. --reload 플래그는 코드 변경 시 서버를 자동으로 다시 로드해줍니다.

브라우저에서 http://127.0.0.1:8000에 접속하면 다음과 같은 JSON 응답을 확인할 수 있습니다.

{"Hello": "World"}

Swagger 문서 확인하기

FastAPI는 자동으로 Swagger UI를 생성하여 API 문서를 제공합니다. 아래 주소로 이동하면 API 문서를 확인할 수 있습니다.

http://127.0.0.1:8000/docs

Swagger UI에서는 우리가 정의한 API의 엔드포인트를 확인하고, 직접 호출해볼 수 있습니다.

FastAPI 기본 구조 소개

위의 예제에서 사용된 기본 FastAPI 구조에 대해 설명하겠습니다. FastAPI의 주요 구성 요소는 다음과 같습니다:

  • FastAPI 인스턴스: app = FastAPI()로 인스턴스를 생성합니다. 이 ‘app’ 객체는 라우터 및 미들웨어를 포함합니다.
  • 라우트 정의: @app.get(“/”)는 GET 요청을 처리하는 라우트를 정의합니다. 요청을 받으면 read_root 함수가 호출됩니다.
  • 응답: JSON 형태로 응답을 반환하며, FastAPI는 자동으로 Content-Type 헤더와 상태 코드를 설정해줍니다.

FastAPI에서 사용할 수 있는 여러 기능들

FastAPI는 다양한 기능을 제공합니다. 몇 가지 주요 기능은 다음과 같습니다:

  • 타입 시스템: 파라미터와 응답의 데이터 타입을 명확히 정의하고 유효성 검사를 자동으로 수행합니다.
  • 거대한 구성요소: 다양한 인증 및 권한 부여 옵션을 지원합니다.
  • 비동기 프로그래밍: async/await을 활용하여 비동기 요청 처리 가능.
  • 데이터베이스 통합: SQLAlchemy와 같이 다른 ORM을 쉽게 통합할 수 있습니다.

기본적인 CRUD API 구현해보기

이제 FastAPI의 기본 구조를 이해했으니, 간단한 CRUD(Create, Read, Update, Delete) API를 구현해 보겠습니다. 아래는 메모를 관리하는 API의 예입니다.

# main.py
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List

app = FastAPI()

class Memo(BaseModel):
    id: int
    content: str

memo_list = []

@app.post("/memos/", response_model=Memo)
def create_memo(memo: Memo):
    memo_list.append(memo)
    return memo

@app.get("/memos/", response_model=List[Memo])
def read_memos():
    return memo_list

@app.put("/memos/{memo_id}", response_model=Memo)
def update_memo(memo_id: int, memo: Memo):
    for idx, m in enumerate(memo_list):
        if m.id == memo_id:
            memo_list[idx] = memo
            return memo
    return None

@app.delete("/memos/{memo_id}")
def delete_memo(memo_id: int):
    global memo_list
    memo_list = [m for m in memo_list if m.id != memo_id]
    return {"message": "삭제되었습니다."}

CRUD API 설명

  • Create: /memos/ 경로로 POST 요청을 보내면 새로운 메모를 생성할 수 있습니다.
  • Read: /memos/ 경로로 GET 요청을 보내면 모든 메모를 조회할 수 있습니다.
  • Update: /memos/{memo_id} 경로로 PUT 요청을 보내면 특정 메모를 수정할 수 있습니다.
  • Delete: /memos/{memo_id} 경로로 DELETE 요청을 보내면 특정 메모를 삭제할 수 있습니다.

결론

이번 포스트에서는 FastAPI의 설치 방법과 기본적인 서버 구현 방법에 대해 알아보았습니다. FastAPI는 빠르고 유연한 API 개발을 할 수 있게 도와주며, 비동기 처리가 가능하기 때문에 높은 성능을 제공합니다. 앞으로 더 많은 기능을 활용하여 고급 API를 개발할 수 있습니다. 추가적인 학습을 위해 FastAPI의 공식 문서를 참고하시기를 권장합니다.

시청해 주셔서 감사합니다! 여러분의 프로젝트에 도움이 되었기를 바랍니다.

FastAPI 서버개발, 컨테이너 구동 Cloud Run 설정 및 실행

FastAPI는 현대적인 웹 API를 구축하는 데 유용한 프레임워크로, 비동기 Python 기능을 활용하여 성능을 극대화합니다. 이 글에서는 FastAPI를 사용하여 간단한 웹 애플리케이션을 개발하고, 이를 Google Cloud Run에 배포하는 과정을 자세히 설명합니다. Cloud Run은 컨테이너화를 통해 서버리스 방식의 애플리케이션 배포를 가능하게 하며, 매우 유연합니다.

1. FastAPI 설치 및 기본 서버 구축

FastAPI를 설치하고 기본 서버를 만드는 과정부터 시작하겠습니다. FastAPI는 Python 3.6 이상에서 사용할 수 있습니다. 다음 명령어를 통해 FastAPI를 설치하세요:

pip install fastapi uvicorn

이제 간단한 FastAPI 서버를 설정해보겠습니다.

1.1 FastAPI 서버 코드

from fastapi import FastAPI

app = FastAPI()

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

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

위 코드를 main.py라는 파일에 저장한 후, 다음 명령어로 서버를 실행합니다:

uvicorn main:app --reload

서버가 성공적으로 시작되면 http://127.0.0.1:8000에서 “Hello World” 메시지를 확인할 수 있습니다.

2. Docker로 FastAPI 애플리케이션 컨테이너화

이제 FastAPI 애플리케이션을 Docker 컨테이너로 패키징하는 방법을 알아보겠습니다. 먼저, Docker를 설치해야 합니다. 시스템에 맞는 Docker를 설치한 후, 다음 단계를 진행해주세요.

2.1 Dockerfile 작성

프로젝트 루트 디렉토리에 Dockerfile이라는 파일을 생성하고 다음 내용을 추가합니다:

FROM python:3.9

# 작업 디렉토리 생성
WORKDIR /app

# requirements.txt 복사
COPY requirements.txt .

# 의존성 설치
RUN pip install --no-cache-dir -r requirements.txt

# 앱 코드 복사
COPY . .

# Uvicorn으로 앱 실행
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

위의 Dockerfile에서는 Python 3.9 이미지를 기반으로 FastAPI 애플리케이션을 실행하도록 설정했습니다. 의존성을 설치하고, FastAPI 애플리케이션을 실행하는 Uvicorn을 명령어로 지정합니다.

2.2 requirements.txt 파일 작성

다음으로 requirements.txt 파일을 생성하여 FastAPI와 Uvicorn을 설치할 수 있도록 합니다. 이 파일의 내용은 다음과 같습니다:

fastapi
uvicorn

2.3 Docker 이미지 빌드 및 실행

Dockerfile과 requirements.txt 파일이 준비되었으면, 다음 명령어를 통해 Docker 이미지를 빌드합니다:

docker build -t fastapi-app .

빌드가 완료되면 아래의 명령어로 컨테이너를 실행합니다:

docker run -d -p 8080:8080 fastapi-app

이제 http://localhost:8080에서 FastAPI 애플리케이션을 확인할 수 있습니다.

3. Google Cloud Run에 배포

이제 우리의 FastAPI 애플리케이션을 Google Cloud Run에 배포해보겠습니다. 이를 위해 먼저 Google Cloud Console에 로그인하고 새로운 프로젝트를 생성합니다.

3.1 Google Cloud SDK 설치 및 초기화

Google Cloud SDK가 필요합니다. 아래의 명령어를 통해 설치하고 초기화합니다:

curl https://sdk.cloud.google.com | bash
gcloud init

초기화 과정에서 생성할 프로젝트를 선택하거나 새로 생성합니다.

3.2 Google Container Registry에 이미지 푸시

Google Cloud Run에 배포하기 전에 이미지를 Google Container Registry에 푸시해야 합니다. 다음 명령어를 실행합니다:

gcloud auth configure-docker

이후 이미지를 푸시하기 위한 태그를 설정합니다:

docker tag fastapi-app gcr.io/YOUR_PROJECT_ID/fastapi-app

설정한 태그로 이미지를 푸시합니다:

docker push gcr.io/YOUR_PROJECT_ID/fastapi-app

여기서 YOUR_PROJECT_ID 부분은 Google Cloud Console에서 확인할 수 있는 자신의 프로젝트 ID로 대체해야 합니다.

3.3 Cloud Run 서비스 생성

이제 Cloud Run에서 서비스를 생성할 수 있습니다. 다음 명령어를 실행하세요:

gcloud run deploy fastapi-app --image gcr.io/YOUR_PROJECT_ID/fastapi-app --platform managed --region us-central1 --allow-unauthenticated

배포가 완료되면 Cloud Run에서 생성된 URL을 통해 FastAPI 애플리케이션에 접근할 수 있습니다.

4. Cloud Run에서 FastAPI 애플리케이션 확인

Cloud Run에 배포한 FastAPI 애플리케이션을 브라우저에서 확인해보세요. 배포 후 제공되는 URL로 이동하면 “Hello World” 메시지를 확인할 수 있습니다. 또한, /items/{item_id} 엔드포인트도 동일하게 작동하는 것을 확인할 수 있습니다.

5. 마무리

FastAPI를 사용하여 간단한 웹 애플리케이션을 구축하고, 이를 Docker로 컨테이너화한 후 Google Cloud Run에 배포하는 방법을 살펴보았습니다. 이러한 흐름은 실제 개발에서 유용한 경험이 될 것입니다. 앞으로 더 복잡한 FastAPI 애플리케이션을 개발하면서 다양한 기능과 모듈을 적용해보면 좋겠습니다.

이 글이 FastAPI와 Cloud Run에 대한 이해를 돕는 데 도움이 되었길 바랍니다. 성공적인 개발을 기원합니다!

© 2023 Your Name. All rights reserved.

FastAPI 서버개발, docker compose 관련 파일 생성

최근 웹 애플리케이션 개발에서 FastAPI는 파이썬 기반의 비동기 웹 프레임워크로 많은 인기를 얻고 있습니다. 이 글에서는 FastAPI 서버 개발 과정과 함께 Docker Compose를 활용하여 개발 환경을 설정하고 배포할 수 있는 방법에 대해 상세히 설명하겠습니다.

FastAPI 소개

FastAPI는 Python 3.6 이상에서 동작하는 현대적인 웹 프레임워크로, 특히 비동기 프로그래밍을 지원합니다. 그 주요 특징으로는 다음과 같은 것들이 있습니다:

  • 고속: 페이로드와 까다로운 요청을 처리하는 데 최적화되어 있습니다.
  • 직관적: Python 타입 힌트를 사용하여 코드의 가독성을 높입니다.
  • 자동화된 문서화: Swagger UI 및 ReDoc을 통해 API 문서를 자동 생성합니다.
  • 비동기 지원: Async/Await를 활용하여 더 많은 동시 요청을 처리할 수 있습니다.

FastAPI 서버 설정하기

FastAPI를 사용하기 위해서 먼저 환경을 설정해야 합니다. 아래의 예제에서는 FastAPI를 설치하고 간단한 API 서버를 만드는 과정을 보여줍니다.

1. 가상 환경 설정

FastAPI 프로젝트를 위한 가상 환경을 설정합니다.

python -m venv venv
source venv/bin/activate  # 리눅스/macOS
venv\Scripts\activate     # 윈도우

2. FastAPI 및 Uvicorn 설치

FastAPI와 ASGI 서버인 Uvicorn을 설치합니다.

pip install fastapi uvicorn

3. 간단한 API 코드 작성

아래와 같은 구조의 main.py 파일을 생성합니다.

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"message": "Hello, FastAPI!"}

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

4. 서버 실행

다음 명령어로 FastAPI 서버를 실행합니다.

uvicorn main:app --reload

서버가 실행되면 http://127.0.0.1:8000/docs에서 자동 생성된 Swagger UI를 통해 API를 테스트할 수 있습니다.

Docker Compose를 이용한 FastAPI 서버 배포

Docker Compose를 이용하면 여러 서비스를 쉽게 관리할 수 있습니다. FastAPI 서버와 데이터베이스(MySQL)를 함께 설정할 수 있습니다.

1. Docker 및 Docker Compose 설치

Docker와 Docker Compose가 설치되어 있어야 합니다. 리눅스에서는 다음 명령어로 설치할 수 있습니다:

sudo apt-get update
sudo apt-get install docker docker-compose

2. Dockerfile 작성

프로젝트 루트 디렉토리에 Dockerfile을 생성합니다.

FROM python:3.9

# Set the working directory
WORKDIR /app

# Copy the requirements file
COPY requirements.txt .

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the application code
COPY . .

# Expose the port the app runs on
EXPOSE 8000

# Command to run the application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

3. requirements.txt 파일 생성

fastapi
uvicorn
mysql-connector-python

4. docker-compose.yml 파일 작성

다음으로 docker-compose.yml 파일을 작성합니다. 이 파일은 Docker Compose의 설정 파일입니다.

version: '3.8'

services:
  app:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      - db
    environment:
      - DATABASE_URL=mysql://user:password@db:3306/db_name

  db:
    image: mysql:latest
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_DATABASE: db_name

5. Docker Compose로 서비스 실행

아래 명령어로 Docker Compose를 실행하여 서비스들을 시작합니다.

docker-compose up --build

위 명령어를 실행하면 FastAPI 서버와 MySQL 데이터베이스가 컨테이너에서 실행됩니다. FastAPI는 http://localhost:8000에서 접근 가능합니다.

FastAPI와 MySQL 연동하기

이제 FastAPI와 MySQL을 연동하여 데이터를 저장하고 가져올 수 있는 API를 작성하겠습니다.

1. 데이터베이스 모델 정의

SQLAlchemy를 사용하여 데이터베이스 모델을 정의합니다. 먼저 SQLAlchemy와 MySQL 커넥터를 설치합니다.

pip install sqlalchemy pymysql

이제 models.py 파일을 생성하고 아래와 같은 코드를 작성합니다.

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Item(Base):
    __tablename__ = 'items'
    
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(50))
    description = Column(String(255))

2. 데이터베이스 연결 설정

데이터베이스 연결을 위해 database.py 파일을 생성합니다.

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "mysql+pymysql://user:password@db:3306/db_name"

engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

3. CRUD API 작성

FastAPI를 사용하여 CRUD(Create, Read, Update, Delete) API를 작성합니다. main.py 파일을 다음과 같이 업데이트합니다.

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from models import Item, Base
from database import SessionLocal, engine

# 데이터베이스 테이블 생성
Base.metadata.create_all(bind=engine)

app = FastAPI()

# 의존성 주입을 통해 세션 생성
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.post("/items/", response_model=Item)
async def create_item(item: Item, db: Session = Depends(get_db)):
    db.add(item)
    db.commit()
    db.refresh(item)
    return item

@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int, db: Session = Depends(get_db)):
    item = db.query(Item).filter(Item.id == item_id).first()
    if item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return item

@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: int, item: Item, db: Session = Depends(get_db)):
    db_item = db.query(Item).filter(Item.id == item_id).first()
    if db_item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    
    db_item.name = item.name
    db_item.description = item.description
    db.commit()
    db.refresh(db_item)
    return db_item

@app.delete("/items/{item_id}")
async def delete_item(item_id: int, db: Session = Depends(get_db)):
    db_item = db.query(Item).filter(Item.id == item_id).first()
    if db_item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    
    db.delete(db_item)
    db.commit()
    return {"message": "Item deleted successfully"}

결론

이번 글에서는 FastAPI를 이용하여 간단한 API 서버를 구축하고, Docker Compose를 통해 개발 환경을 설정하는 방법을 배웠습니다. 또한 MySQL과의 연동을 통해 데이터를 관리하는 API를 작성하였습니다. FastAPI의 비동기 성능과 간결한 코드 작성을 통해 더 효율적인 웹 애플리케이션을 개발할 수 있습니다. Docker Compose를 이용하여 손쉽게 프로젝트를 배포하고 관리할 수 있어 개발자에게 유용한 도구입니다.

FastAPI와 Docker Compose를 이용한 개발은 현대적인 웹 개발을 추구하는 많은 인프라에서 필수적인 부분으로 자리잡고 있습니다. 앞으로 더 발전된 기능과 아키텍처를 활용하여 보다 혁신적인 애플리케이션을 만들어 나갈 수 있기를 기대합니다.