- Published on
- •👁️
FastAPI 설치 및 사용
- Authors

- Name
- River
1. FastAPI 시작하기
python 가상 환경
- 가상 환경 만들기
$ python -m venv .venv- 현재 위치에 .venv 라는 디렉토리가 생긴다.
- 가상 환경 활성화 / 비활성화
PS C:\Users\river\git\fastapi-postgre> .venv/Scripts/activate (.venv) PS C:\Users\river\git\fastapi-postgre> pip list Package Version ------- ------- pip 24.2(.venv) PS C:\Users\river\git\fastapi-postgre> .venv/Scripts/deactivate- 활성화를 해줘야 적용이 된다.
- Python에서 가상환경을 사용하는 이유
- Java 기반 Spring Boot를 사용할 때와 다르게 Python은 기본적으로 시스템 전역에 패키지를 설치한다.
- 여러 프로젝트를 작업하는 경우, 각 프로젝트 마다 다른 버전의 패키지를 사용해야 한다면 문제가 생긴다.
- 각 프로젝트 마다 독립적인 환경을 제공하는 것
fastapi 설치
- fastapi 모듈 설치하기
(.venv) PS C:\Users\river\git\fastapi-postgre> pip install fastapi ... (.venv) PS C:\Users\river\git\fastapi-postgre> pip list Package Version ----------------- -------- annotated-types 0.7.0 anyio 4.9.0 fastapi 0.115.12 idna 3.10 pip 24.2 pydantic 2.11.4 pydantic_core 2.33.2 sniffio 1.3.1 starlette 0.46.2 typing_extensions 4.13.2 typing-inspection 0.4.0
- uvicorn 설치하기
(.venv) PS C:\Users\river\git\fastapi-postgre> pip install uvicorn ... (.venv) PS C:\Users\river\git\fastapi-postgre> pip list Package Version ----------------- -------- annotated-types 0.7.0 anyio 4.9.0 click 8.1.8 colorama 0.4.6 fastapi 0.115.12 h11 0.16.0 idna 3.10 pip 24.2 pydantic 2.11.4 pydantic_core 2.33.2 sniffio 1.3.1 starlette 0.46.2 typing_extensions 4.13.2 typing-inspection 0.4.0 uvicorn 0.34.2 (.venv) PS C:\Users\river\git\fastapi-postgre>
- 패키지 설정 저장
(.venv) PS C:\Users\river\git\fastapi-postgre> pip freeze > requirements.txt (.venv) PS C:\Users\river\git\fastapi-postgre> code requirements.txtannotated-types==0.7.0 anyio==4.9.0 click==8.1.8 colorama==0.4.6 fastapi==0.115.12 h11==0.16.0 idna==3.10 pydantic==2.11.4 pydantic_core==2.33.2 sniffio==1.3.1 starlette==0.46.2 typing-inspection==0.4.0 typing_extensions==4.13.2 uvicorn==0.34.2- 이렇게 하는 이유는
- 사용하는 의존성을 문서화 및 버전 관리
- 환경 복제
- 배포 준비
requirements.txt는 Spring Boot의build.gradle파일과 유사한 역할을 하는 것이다.
- 이렇게 하는 이유는
간단한 API 서버 생성하기
루트 디렉토리에
main.py생성from fastapi import FastAPI from typing import Union app = FastAPI() @app.get("/") def read_root(): return {"Hello": "World"} @app.get("/items/{item_id}") def read_item(item_id: int, q: Union[str, None] = None): return {"item_id": item_id}(.venv) PS C:\Users\river\git\fastapi-postgre> uvicorn main:app --reload-- reload: 소스 파일 수정 시 다시 로딩해주는 옵션
루트 디렉토리에
run.bat이라는 파일 생성uvicorn main:app --reload- 간단하게 입력하기 위해서
C:\Users\river\git\fastapi-postgre>uvicorn main:app --reload INFO: Will watch for changes in these directories: ['C:\\Users\\river\\git\\fastapi-postgre'] INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: Started reloader process [40632] using StatReload INFO: Started server process [119752] INFO: Waiting for application startup. INFO: Application startup complete.- 서버 실행됨
- 확인해보면 json으로 출력되는 것을 확인할 수 있다.
- http://localhost:8000/items/1?q=Hi
{ "item_id": 1, "q": "Hi" }
- 간단하게 입력하기 위해서
API 문서화 : Swagger UI
- 따로 설정할 필요 없이 Swagger UI를 바로 사용할 수 있다.

API 문서화 : ReDoc
- http://localhost:8000/redoc
- 마찬가지로 따로 설정할 필요 없다.

2. 컨트롤러 확장하기
기존
main.pyfrom fastapi import FastAPI from typing import Union app = FastAPI() @app.get("/") def read_root(): return {"Hello": "World"} @app.get("/items/{item_id}") def read_item(item_id: int, q: Union[str, None] = None): return {"item_id": item_id}
- 따로 API를 컨트롤러(router)로 분리하기 :
items.py# controller/items.py from typing import Union from fastapi import APIRouter router = APIRouter( prefix="/items", tags=["items"], responses={404: {"description": "Not Fount"}}, ) @router.get("/{item_id}") def read_item(item_id: int, q: Union[str, None] = None): return {"item_id": item_id, "q": q}- APIRouter를 import 한다.
- router를 만들어준다.
- prefix 설정은 Spring Boot의
@RequestMapping("/api/v1/animals")과 같다. - tags 설정은
- responses 설정은
- prefix 설정은 Spring Boot의
- 기존의
@app.get("/items/{item_id}")를@router.get("/{item_id}")로 전환
main.py에서items.py를 추가하여 사용한다.from fastapi import FastAPI from controller import items app = FastAPI() app.include_router(items.router) @app.get("/") def read_root(): return {"Hello": "World"}
새로운 컨트롤러 추가하기
users.py생성# controller/users.py from fastapi import APIRouter router = APIRouter( prefix="/users", tags=["users"], responses={404: {"description": "Not Fount"}}, ) @router.get("/{user_id}") def read_user(user_id: int): return {"user_id": user_id}from fastapi import APIRouterAPIRouter는 FastAPI에서 라우터를 구성하기 위한 모듈화 도구- Spring Boot의
@RestController
router = APIRouter(...)- 실제로 API 경로를 등록할 "라우터"
prefix="/users"- Spring Boot의
@RequestMapping("/users")와 동일
- Spring Boot의
tags=["users"]- OpenAPI(Swagger) 문서에서 이 라우터를
"users"그룹으로 묶기
- OpenAPI(Swagger) 문서에서 이 라우터를
responses={404: {"description": "Not Found"}}- 자동 문서화 시, 404 상태코드에 대한 설명을 미리 지정하는 것
- 단순 설명하는 용도
@router.get("/{user_id}")- Spring Boot의
@GetMapping("/{userId}")
- Spring Boot의
main.py에users.py를 추가하기from fastapi import FastAPI from controller import items, users app = FastAPI() app.include_router(items.router) app.include_router(users.router) @app.get("/") def read_root(): return {"Hello": "World"}
3. DB(PostgreSQL) 붙이기
- PostgreSQL 설치 및 실행하기
'PostgreSQL 설치 및 사용' 게시글로 이동
- psycodg 설치하기
- PostgreSQL용 Python DB 드라이버 (Java의 JDBC 드라이버)
- Spring Boot + JPA + MySQL이라면
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' runtimeOnly 'com.mysql:mysql-connector-j' // JDBC 드라이버
- Spring Boot + JPA + MySQL이라면
- SQLAlchemy는 ORM만 제공하고, 실제 DB 연결은 하지 않기 때문에 psycodg가 필요하다.
(.venv) PS C:\Users\river\git\fastapi-postgre> pip install "psycopg[binary,pool]" ... (.venv) PS C:\Users\river\git\fastapi-postgre> pip list Package Version ----------------- -------- annotated-types 0.7.0 anyio 4.9.0 click 8.1.8 colorama 0.4.6 fastapi 0.115.12 h11 0.16.0 idna 3.10 pip 24.2 psycopg 3.2.8 psycopg-binary 3.2.8 psycopg-pool 3.2.6 pydantic 2.11.4 pydantic_core 2.33.2 sniffio 1.3.1 starlette 0.46.2 typing_extensions 4.13.2 typing-inspection 0.4.0 tzdata 2025.2 uvicorn 0.34.2
- PostgreSQL용 Python DB 드라이버 (Java의 JDBC 드라이버)
- DB 연결을 위한
config.py생성하기# config/config.py PGSQL_TEST_DATABASE_STRING = "host=127.0.0.1 dbname=test_db user=test_user password=test123 port=5432" PGSQL_TEST_POOL_MIN_SIZE = 10 PGSQL_TEST_POOL_MAX_SIZE = 10 PGSQL_TEST_POOL_MAX_IDLE = 60- Spring Boot + JPA 사용 시, 기본적으로 HikariCP 커넥션 풀이 자동으로 설정
- 그와 달리 FastAPI에서는 Connection Pool을 직접 설정을 해줘야 성능 저하가 없다.
- model 생성하기 (
pgsql_test.py)# model/pgsql_test.py import psycopg import psycopg_pool from config import config pool_default = psycopg_pool.ConnectionPool( config.PGSQL_TEST_DATABASE_STRING, min_size=config.PGSQL_TEST_POOL_MIN_SIZE, max_size=config.PGSQL_TEST_POOL_MAX_SIZE, max_idle=config.PGSQL_TEST_POOL_MAX_IDLE ) def list_admin(): with pool_default.connection() as conn: cur = conn.cursor(row_factory=psycopg.rows.dict_row) try: results = cur.execute("SELECT * FROM TB_ADMIN").fetchall() except psycopg.OperationalError as err: print(f'Error querying: {err}') except psycopg.ProgrammingError as err: print('Database error via psycopg. %s', err) results = False except psycopg.IntegrityError as err: print('PostgreSQL integrity error via psycopg. %s', err) results = False return resultsimport psycopg- psycopg3 라이브러리의 핵심 모듈로, PostgreSQL 드라이버로 직접 연결 및 쿼리 실행할 때 사용
import psycopg_pool- DB 커넥션을 매번 새로 만들지 않고, 재사용을 통해 성능을 향상시키는 모듈 (고속 커넥션 풀 모듈)
- psycopg3에서 제공하는 경량 커넥션 풀(Pool)
with pool_default.connection() as conn:psycopg_pool.ConnectionPool에서 Connection 하나 획득하여 conn이라고 명명- with 문을 사용하여 Connection 자동 반납 (java의 try-with-resources 문)
cur = conn.cursor(row_factory=psycopg.rows.dict_row)- 커서를 생성
- 커서(
cursor)란 DB Connection을 통해 쿼리를 실행할 수 있게 해주는 객체 - Java의 경우
PreparedStatement,ResultSet cursor.execute("SQL")은PreparedStatement.executeQuery()와 매우 유사
- 커서(
row_factory=psycopg.rows.dict_row는row_factory를dict_row로 설정하여 반환 결과를 딕셔너리(dict) 형태로 바꿔주는 설정- 컬럼명이 key 값이다.
- 딕셔너리(dict) 자료형은 Java의
Map<key, value>와 매우 유사한 자료 구조
- 커서를 생성
cur.execute("SQL문").fetchall()- SQL 문장을 실행해서 결과를 전부 가져온다.
fetchall()은 결과 전체를 리스트 형태로 가져오는 메서드fetchone()fetchmany(n)(단, 일부)
- 예외 처리
OperationalError- 연결 문제나 DB 서버 장애 등ProgrammingError- SQL 문법 오류 등IntegrityError- 제약 조건 위반 (PK 중복, NULL 제한 등)
- 새로운 controller 생성 (
admins.py)# controller/admins.py from fastapi import APIRouter from model import pgsql_test router = APIRouter( prefix="/admins", tags=["admins"], responses={404: {"description": "Not Fount"}}, ) @router.get("/list") def list_admin(): results = pgsql_test.list_admin() return results- FastAPI는 내부적으로
dict를 자동으로 JSON 응답으로 직렬화(serialize) 해준다.- FastAPI는 함수에서
dict,list,Pydantic 모델등을 반환하면 자동으로 JSON으로 직렬화해서 응답
- FastAPI는 함수에서
- 그냥
dict만 return 하면 된다.
- FastAPI는 내부적으로
main.py에 admins.py 추가하기
from fastapi import FastAPI from controller import items, users, admins app = FastAPI() app.include_router(items.router) app.include_router(users.router) app.include_router(admins.router) @app.get("/") def read_root(): return {"Hello": "World"}- http://localhost:8000/admins/list
[ { "admin_no": 1, "login_id": "honggildong", "passwd": "1234", "nick": "HONG", "email": "hgd@gmail.com" }, { "admin_no": 2, "login_id": "jangnara", "passwd": "1234", "nick": "JANG", "email": "jnr@gmail.com" } ]
- http://localhost:8000/admins/list
4. stored procedure 사용하기
- Stored Procedure(저장 프로시저)는 데이터베이스에 미리 저장된 SQL 코드 블록(함수)으로 직접 SQL을 보내는 대신 로직을 실행하게 하는 것
pgsql_test.py수정# model/pgsql_test.py import psycopg import psycopg_pool from config import config pool_default = psycopg_pool.ConnectionPool( config.PGSQL_TEST_DATABASE_STRING, min_size=config.PGSQL_TEST_POOL_MIN_SIZE, max_size=config.PGSQL_TEST_POOL_MAX_SIZE, max_idle=config.PGSQL_TEST_POOL_MAX_IDLE ) def list_admin(): with pool_default.connection() as conn: cur = conn.cursor(row_factory=psycopg.rows.dict_row) try: cur.execute("CALL SP_L_ADMIN('out1')") results = cur.execute("FETCH ALL FROM out1").fetchall() conn.commit() except psycopg.OperationalError as err: print(f'Error querying: {err}') except psycopg.ProgrammingError as err: print('Database error via psycopg. %s', err) results = False except psycopg.IntegrityError as err: print('PostgreSQL integrity error via psycopg. %s', err) results = False return results # def list_admin(): # with pool_default.connection() as conn: # cur = conn.cursor(row_factory=psycopg.rows.dict_row) # try: # results = cur.execute("SELECT * FROM TB_ADMIN").fetchall() # except psycopg.OperationalError as err: # print(f'Error querying: {err}') # except psycopg.ProgrammingError as err: # print('Database error via psycopg. %s', err) # results = False # except psycopg.IntegrityError as err: # print('PostgreSQL integrity error via psycopg. %s', err) # results = False # return resultscur.execute("CALL SP_L_ADMIN('out1')")results = cur.execute("FETCH ALL FROM out1").fetchall()conn.commit()- 동일 결과 출력한다.