728x90
1. FastAPI란?
- ⭐️ ⭐️ 표준 파이썬 타입 힌트와 비동기 프로그래밍을 바탕으로 하는 파이썬 3.6 이상에서 작동하는, 현대적이고 빠른 API 서버 웹 프레임워크이다.
- ⭐️ 타입 힌트 : Python 코드에서 변수, 함수 인자, 반환 값의 타입을 명시하는 방법 (FastAPI의 Pydantic 모델은 타입 힌트를 사용하여 데이터의 유효성을 검사를 한다)
## 1. 변수 타입 힌트
# 정수형 변수
age: int = 25
# 문자열 변수
name: str = "Alice"
# 부울형 변수
is_active: bool = True
## 2. 함수 인자 및 반환값 타입 힌트
# 함수 인자와 반환 값의 타입을 지정
def greet(name: str) -> str:
return f"Hello, {name}!"
# 반환 타입이 없는 경우
def print_message(message: str) -> None:
print(message)
2. Intelli - j에서 기본적인 Run/Debug Configurations 설정
- 좌측 상단의 디버그 버튼 옆 Run/Debug Configuartions 클릭 -> Application file을 main.py 위치로 설정
- Unicorn options 또한 --reload로 설정해두면 터미널이 아닌 Run 버튼(혹은 ^R)을 통해 application 바로 실행 가능
3. ORM (Object Relational Manager)
- 어떤 기술인가?
- ⭐️ 객체와 관계를 mapping, 즉 연결해주는 기술
- 객체 지향 프로그래밍에서의 클래스와 관계형 데이터베이스의 테이블은 서로 일치하지 않기에 ORM을 통해 그 불일치를 해결한다.
- 어떤 기능을 하는가?
- 데이터는 DB에서 관리하기에 SQL 동작은 ORM 기능이 대신하여 application 내에서 DB에 편히 연결 가능케 함
4. SqlAlchemy & SQLite
- SqlAlchemy
- 파이썬용 sql 도구 및 orm 라이브러리
- 자체적으로 스키마를 생성하지 않기에 application 코드나 DB 시스템에 간섭하지 않는다.
- 파이썬 형태의 SQL Alchemy 표현 언어를 SQL로 변환해준다.
- SQLite
- RDBMS
- 파이썬 표준 라이브러리에 내장되어 있으므로 별도의 라이브러리로서 설치가 필요하진 않는다.
- 주로 작은 프로젝트나 예제 돌려보기에서 사용하기에 좋다!
5. CRUD 실습
(1) 의존성 설치 (pipenv 환경에서 진행하였음)
pipenv install databases
pipenv install sqlalchemy
pipenv install fastapi
pipenv install aiosqlite
pipenv install uvicorn
(2) router 만들기 (router/note_router.py)
- router란?
- ⭐️ ⭐️ 길잡이 : 웹 application으로 들어오는 요청을 어떻게 처리할지 결정하는 길잡이 역할을 한다. (like spring mvc의 controller, 레스토랑의 서버처럼)
- ex) Pose /notes/ 요청에 대해 create_note_api 함수를 호출!
- ⭐️ ⭐️ 길잡이 : 웹 application으로 들어오는 요청을 어떻게 처리할지 결정하는 길잡이 역할을 한다. (like spring mvc의 controller, 레스토랑의 서버처럼)
- awiat의 역할
- ⭐️ FastAPI는 비동기적인 프로그래밍을 기반으로 한다. 그렇기에 비동기적인 작업을 위해 await 키워드 사용
- 비동기 함수는 async def 로 작성됨
- FastAPI 메서드의 매개변수로 request body가 사용되는 경우
- ⭐️ ⭐️ pydantic 모델을 사용해 데이터 유효성 검사를 수행한다.
- ⭐️ 메서드 속 로직이 동작하기 전에 매개변수 데이터의 유효성 검사를 할 수 있다는 것이 큰 장점이다.
- request body : 서버로 전송되는 데이터로 객체 형태이다.
- ex) 아래 코드에서 확인할 수 있는 dto: CreateNoteDTO가 이에 해당한다.
- note_router.py
from typing import List
from fastapi import APIRouter
from fastapiStudyHub.app.model.model import NoteResponse
from fastapiStudyHub.app.router.dto.dto import CreateNoteDTO, UpdateNoteDTO
from fastapiStudyHub.app.service.note_service import create_note, read_notes, update_note, delete_note
note_router = APIRouter()
@note_router.post("/notes/", response_model=NoteResponse)
async def create_note_api(dto: CreateNoteDTO):
return await create_note(
text = dto.text,
completed = dto.completed
)
@note_router.get("/notes/", response_model=List[NoteResponse])
async def read_notes_api():
return await read_notes()
@note_router.put("/notes/{note_id}", response_model=NoteResponse)
async def update_note_api(note_id: int, dto: UpdateNoteDTO):
return await update_note(
note_id = note_id,
text = dto.text,
completed = dto.completed
)
@note_router.delete("/notes/{note_id}")
async def delete_note_api(note_id: int):
return await delete_note(note_id)
- router/dto/dto.py
- ⭐️ 역할 분리, 가독성 및 유지보수성을 위해 dto를 나누었다.
- ⭐️ 또한 CreateNoteDTO의 경우 text, completed 필드가 필수적이지만 UpdateNoteDTO는 특정 필드만 수정하는 경우도 있기에 무조건적으로 피루적이라고 할 순 없다.
from pydantic import BaseModel
class CreateNoteDTO(BaseModel):
text: str
completed: bool
class UpdateNoteDTO(BaseModel):
text: str
completed: bool
(3) model 만들기 (model/model.py)
- BaseModel
- Pydantic의 BaseModel은 Python 데이터 클래스로, 다음의 주요 기능들을 수행
- ⭐️ 필드 정의 및 검증
- BaseModel을 상속 받아 클래스를 정의할 때, 데이터 구조와 타입을 정의
- 각 필드는 pydantic에 의해 자동으로 검증됨
- 데이터 직렬화 및 역직렬화
- python 객체를 JSON과 같은 형식으로 직렬화할 수 있고, 반대로 역직렬화할 수도 있다.
- 기본값 설정 기능
- 필드에 기본값을 설정할 수 있다.
- 유효성 검사
- @validator 데코레이터를 사용해 특정 필드에 대한 유효성 검사를 추가할 수 있다.
- ⭐️ 필드 정의 및 검증
- Pydantic의 BaseModel은 Python 데이터 클래스로, 다음의 주요 기능들을 수행
from pydantic import BaseModel, ValidationError
# 입력 데이터 모델
class NoteIn(BaseModel):
text: str
completed: bool
# 응답 데이터 모델
class NoteResponse(BaseModel):
id: int
text: str
completed: bool
# 1. 필드값 검증 예시
try:
note = NoteIn(text="Buy milk", completed="yes") # "yes"는 bool 타입이 아니므로 오류 발생
except ValidationError as e:
print(e)
# 2. 직렬화와 역직렬화
note = NoteResponse(id=1, text="Buy milk", completed=False)
print(note.dict()) # {'id': 1, 'text': 'Buy milk', 'completed': False}
print(note.json()) # '{"id": 1, "text": "Buy milk", "completed": false}'
# 3. 기본값 설정
from typing import Optional
class NoteIn(BaseModel):
text: str
completed: bool = False # 기본값 설정
# 4. 유효성 검사
class NoteIn(BaseModel):
text: str
completed: bool
@validator('text')
def check_text_length(cls, v):
if len(v) < 3:
raise ValueError('text must be at least 3 characters long')
return v
- model.py
from pydantic import BaseModel
# 입력받아서 테이블에 저장될 클래스
class NoteIn(BaseModel):
text: str
completed: bool
# 데이터 검색 등의 요청을 받았을 때 response로 반환될 클래스
class NoteResponse(BaseModel):
id: int
text: str
completed: bool
4. 서비스 만들기 (service/note_service.py)
- ⭐️ 반환 타입 힌트
- 함수가 어떤 타입의 데이터를 반환하는지 명시적으로 알려줌으로써 코드의 가독성을 높임
- ex) 아래 코드의 read_notes 메서드는 List [Note]를 반환해주는 것으로 명시되어있군!!
- note_service.py
from typing import List
from fastapiStudyHub.app.model.model import NoteIn, NoteResponse
from fastapiStudyHub.app.repository.repository import NoteRepository
async def create_note(text: str, completed: bool):
noteResponse = NoteIn(
text=text,
completed=completed
)
return await NoteRepository().create(
note = noteResponse
)
async def read_notes() -> List[NoteResponse]:
return await NoteRepository().read_all()
async def update_note(note_id: int, text: str, completed: bool):
note = NoteIn(
text=text,
completed=completed
)
return await NoteRepository().update(note_id, note)
async def delete_note(note_id: int):
return await NoteRepository().delete(note_id)
5. 레포지토리 만들기 (repository/repository.py)
- ⭐️ self의 역할과 필요성
- self는 메서드가 호출될 때 해당 메서드가 속한 객체(즉, 클래스의 인스턴스)를 가리킨다.
- 이를 통해 인스턴스의 속성에 접근하거나 다른 메서들을 호출할 수 있다.
- 객체지향 프로그래밍의 패턴을 따르기 위해 필요함
- ⭐️ ** : 언패킹 연산자는 사전을 개별 키-값 쌍으로 분해하여 함수 호출 시 매개변수로 전달하거나 다른 사전에 병합할 때 사용된다.
- NoteIn의 경우 id값이 없는 반면 NoteResponse는 있기에 언패킹해주었다.
- repository.py
from typing import List
from fastapiStudyHub.app.database.db import database, notes
from fastapiStudyHub.app.model.model import NoteIn, NoteResponse
class NoteRepository:
## 1. CREATE
async def create(self, note: NoteIn):
query = notes.insert().values(text=note.text, completed=note.completed)
last_record_id = await database.execute(query)
return {**note.dict(), "id": last_record_id}
# **note.dict(): Python unpack 문법
# note에 "id" key를 추가하여 response로 반환!
## 2. READ
# /notes에 GET 요청을 보내면 현재 테이블의 모든 데이터를 반환!
async def read_all(self) -> List[NoteResponse]:
query = notes.select()
return await database.fetch_all(query)
# return type: List[Note]
## 3. UPDATE
async def update(self, note_id: int, note: NoteIn):
query = notes.update().where(notes.c.id == note_id).values(text=note.text, completed=note.completed)
await database.execute(query)
return {**note.dict(), "id": note_id}
## 4. DELETE
async def delete(self, note_id: int):
query = notes.delete().where(notes.c.id == note_id)
await database.execute(query)
return {"message": "Note deleted successfully!"}
6. 데이터베이스 및 테이블 설정 (database/db.py)
- ⭐️ 메타데이터 : DB 테이블 관련 정보들을 정의하고 보유하는 객체
- 주요 역할
- 테이블 정의 : 테이블의 이름, 열의 이름, 데이터 타입, 기본키, 외래키 등을 명시 가능
- ORM과의 연결 : ORM 클래스는 메타데이터를 기반으로 DB 스키마와 매핑됨
- 쿼리 생성 : SQLAlchemy는 메타데이터를 기반으로 sql 쿼리를 생성함
- 주요 역할
- ⭐️ 스키마 : DB의 구조와 제약조건에 관해 전반적인 명세(속성, 개체, 관계, 제약조건 등)를 기술한 것
- ⭐️ 엔진 : DB와의 연결을 관리하는 핵심 객체
- db.py
import databases
import sqlalchemy
# SQLAlchemy specific code, as with any other app
DATABASE_URL = "sqlite:///./sqlite_crud.db" # database의 url
# 만약 PostgreSQL같은 다른 SQL을 사용한다면 다른 DATABASE_URL을 사용해야 함!
# DATABASE_URL = "postgresql://user:password@postgresserver/db"
# DATABASE_URL에 database 객체를 생성
database = databases.Database(DATABASE_URL)
# SQLAlchemy로부터 메타데이터 생성
metadata = sqlalchemy.MetaData()
# notes 테이블 생성
notes = sqlalchemy.Table(
"notes",
metadata,
sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),
sqlalchemy.Column("text", sqlalchemy.String),
sqlalchemy.Column("completed", sqlalchemy.Boolean),
)
# SQLAlchemy 엔진 생성
## SQLite를 DB로 사용하자
## SQLite는 기본적으로 멀티 스레드 환경에서 database 접근을 허용하지 않기에 멀티 스레드 접근을 허용하도록 설정
engine = sqlalchemy.create_engine(
DATABASE_URL, connect_args={"check_same_thread": False}
)
## metadata 객체가 포함된 모든 테이블들을 실제 DB에 생성. 즉, notes 테이블을 DB에 생성
metadata.create_all(engine)
'Backend > FastAPI' 카테고리의 다른 글
가상환경 pipenv와 팀 프로젝트 시 pipenv 설정 방법 (1) | 2024.06.24 |
---|