FastAPI 서버개발, RESTful API 설계 원칙과 FastAPI의 활용

RESTful API 설계 원칙과 FastAPI의 활용

1. FastAPI란?

FastAPI는 Python으로 작성된 현대적인 웹 프레임워크로, 빠르고, 사용하기 쉬우며, RESTful API 서버를 구축하기 위해 최적화되었습니다. 이 프레임워크는 Pydantic과 Starlette를 기반으로 하고 있으며, 비동기 프로그래밍을 지원하여 높은 성능을 자랑합니다. FastAPI의 주요 강점 중 하나는 데이터 유효성 검사와 스키마 생성을 자동으로 수행할 수 있다는 점입니다. 이러한 기능은 개발자 경험을 향상시키고 개발 시간을 단축시킵니다.

2. RESTful API란?

REST(Representational State Transfer)는 웹에서 자원을 정의하고 자원에 대한 상호작용을 정의하는 아키텍처 스타일입니다. RESTful API는 이러한 원칙을 따르는 웹 서비스로, 자원에 HTTP 메서드를 사용하여 접근합니다. REST 아키텍처는 다음과 같은 기본 원칙을 가지고 있습니다:

  • 자원 지향: API는 고유한 URI로 자원을 식별합니다.
  • 무상태: 클라이언트와 서버 간의 상호작용은 독립적이며, 서버는 클라이언트의 상태를 유지하지 않습니다.
  • 표현: 자원은 다양한 표현 형식(JSON, XML 등)으로 제공될 수 있습니다.
  • HTTP 메서드 활용: GET, POST, PUT, DELETE 등의 메서드를 사용하여 자원에 대한 CRUD(Create, Read, Update, Delete) 작업을 수행합니다.

3. FastAPI 설치하기

FastAPI를 사용하기 위해서는 Python과 pip가 필요합니다. 아래는 FastAPI와 함께 Uvicorn 서버를 설치하는 방법입니다:

pip install fastapi uvicorn

Uvicorn은 FastAPI 애플리케이션을 실행하는 ASGI 서버입니다.

4. FastAPI로 간단한 RESTful API 구축하기

4.1. 기본 예제

FastAPI로 간단한 RESTful API를 만들어보겠습니다. 다음은 사용자 정보를 처리하는 API입니다:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    id: int
    name: str
    age: int

users = []

@app.post("/users/", response_model=User)
def create_user(user: User):
    users.append(user)
    return user

@app.get("/users/")
def get_users():
    return users

@app.get("/users/{user_id}", response_model=User)
def get_user(user_id: int):
    for user in users:
        if user.id == user_id:
            return user
    return {"error": "User not found"}

@app.delete("/users/{user_id}")
def delete_user(user_id: int):
    for user in users:
        if user.id == user_id:
            users.remove(user)
            return {"message": "User deleted successfully"}
    return {"error": "User not found"}

위의 코드는 FastAPI를 사용하여 사용자 정보를 추가, 조회, 삭제할 수 있는 간단한 API를 생성합니다.

5. RESTful API 설계 원칙

5.1. URI 설계

RESTful API에서는 자원의 URI를 명확하게 설계하는 것이 중요합니다. URI는 자원을 표현하고, 클라이언트가 REST API를 통해 자원에 접근할 수 있게 합니다. 다음은 좋은 URI 설계의 예입니다:

  • GET /users: 모든 사용자 목록 조회
  • POST /users: 새 사용자 추가
  • GET /users/{id}: 특정 사용자 조회
  • DELETE /users/{id}: 특정 사용자 삭제

URI를 설계할 때는 명사 형태를 사용하는 것이 좋습니다. 동사는 HTTP 메서드를 통해 전달됩니다.

5.2. HTTP 메서드 사용

RESTful API에서는 자원에 대한 행위를 표현하기 위해 HTTP 메서드를 사용합니다. 각 메서드의 용도는 다음과 같습니다:

  • GET: 서버로부터 자원 조회
  • POST: 서버에 새로운 자원 생성
  • PUT: 서버의 자원 수정
  • DELETE: 서버의 자원 삭제

5.3. 상태 코드 사용

RESTful API에서는 적절한 HTTP 상태 코드를 사용하여 클라이언트에게 요청의 결과를 전달해야 합니다. 주요 상태 코드는 다음과 같습니다:

  • 200 OK: 요청 성공
  • 201 Created: 자원 생성 성공
  • 204 No Content: 자원 삭제 성공
  • 404 Not Found: 요청한 자원 없음
  • 400 Bad Request: 잘못된 요청
  • 500 Internal Server Error: 서버 오류

6. FastAPI의 고급 기능

6.1. 데이터 검증 및 스키마

FastAPI는 Pydantic을 사용하여 데이터 검증과 스키마 관리를 제공합니다. 요청의 본문에서 전달되는 데이터에 대해 자동으로 검증을 수행하고, 잘못된 데이터가 들어오면 에러를 반환합니다. 예를 들어, 다음과 같이 데이터 검증을 추가할 수 있습니다:

from pydantic import EmailStr

class User(BaseModel):
    id: int
    name: str
    age: int
    email: EmailStr

여기서 email 필드는 이메일 형식의 문자열만 허용됩니다.

6.2. 의존성 주입

FastAPI는 의존성 주입을 지원하여 코드의 재사용성을 높이고, 테스트를 쉽게 할 수 있습니다. 의존성은 FastAPI의 @Depends를 사용하여 정의할 수 있습니다. 예를 들어:

from fastapi import Depends

def get_query_param(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

@app.get("/items/")
def get_items(queries: dict = Depends(get_query_param)):
    return {"queries": queries}

여기서 get_query_param 함수는 쿼리 파라미터를 처리하는 의존성으로 사용됩니다.

7. FastAPI와 Swagger UI

FastAPI는 Swagger UI를 자동으로 생성하여 API 문서를 쉽게 볼 수 있도록 제공합니다. 서버를 실행한 후, 브라우저에서 http://localhost:8000/docs로 이동하면 API 문서를 확인할 수 있습니다. 이 문서에서는 각 API 엔드포인트의 요청 및 응답 형식, 설명 등을 확인할 수 있습니다.

8. FastAPI 프로젝트 구조

FastAPI 프로젝트의 구조는 의견에 따라 다를 수 있지만, 일반적으로 다음과 같이 구성할 수 있습니다:

my_fastapi_app/
    ├── app/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── models.py
    │   ├── routers/
    │   │   ├── __init__.py
    │   │   ├── user.py
    │   │   └── item.py
    │   └── services/
    │       ├── __init__.py
    │       └── user_service.py
    └── requirements.txt

여기에서 main.py는 FastAPI 애플리케이션을 실행하는 파일이며, models.py는 데이터 모델을 정의하고, routers/에는 각각의 API 엔드포인트를 정의하는 라우터를 구현합니다. services/ 폴더에는 비즈니스 로직을 분리하여 관리할 수 있습니다.

9. FastAPI로 안전한 API 만들기

9.1. 인증 및 인가

FastAPI는 OAuth2와 JWT(Json Web Token)를 사용하여 인증 및 인가 기능을 쉽게 구현할 수 있습니다. 예를 들어, JWT 토큰을 생성하고 검증하는 간단한 방법은 다음과 같습니다:

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.post("/token")
async def login(form: OAuth2PasswordRequestForm = Depends()):
    # 사용자 인증 로직 구현 (mock)
    if form.username == "user" and form.password == "password":
        return {"access_token": "your_token", "token_type": "bearer"}
    raise HTTPException(status_code=400, detail="Invalid credentials")

@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
    # 토큰 검증 로직 구현

10. FastAPI에서 테스트하기

FastAPI는 테스트를 매우 쉽게 만들어줍니다. Python의 비동기 테스트 기능을 사용하여 API를 테스트할 수 있습니다. 아래는 테스트의 간단한 예입니다:

from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_create_user():
    response = client.post("/users/", json={"id": 1, "name": "John", "age": 30})
    assert response.status_code == 200
    assert response.json() == {"id": 1, "name": "John", "age": 30}

위의 예제에서는 FastAPI의 TestClient를 사용하여 API 호출을 테스트합니다.

11. FastAPI 배포하기

FastAPI 애플리케이션을 배포하기 위해 Uvicorn을 사용하여 서버를 실행할 수 있습니다. 기본적인 실행 명령어는 다음과 같습니다:

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

여기서 --reload 옵션은 개발 모드에서 소스 코드를 수정할 때 자동으로 서버를 다시 시작합니다. 프로덕션 환경에서는 `–host`, `–port`, `–reload` 옵션을 적절히 설정해야 합니다.

12. 마치며

FastAPI는 손쉬운 RESTful API 개발을 위해 필요한 모든 기능을 제공하는 강력한 프레임워크입니다. 이 글에서 다룬 내용을 통해 FastAPI를 사용하여 RESTful API를 설계하고 구현하는 방법을 익혔기를 바랍니다. FastAPI의 다양한 기능을 활용하여 더욱 효율적이고 안전한 API를 개발해보세요.