
-- 데이터베이스 생성
CREATE DATABASE mydb;
-- 테이블 생성
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100) UNIQUE,
created_at TIMESTAMP DEFAULT NOW()
);
-- 데이터 삽입
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
-- 조회
SELECT * FROM users;
-- 수정
UPDATE users SET name = 'Bob' WHERE id = 1;
-- 삭제
DELETE FROM users WHERE id = 1;
WITH cte_name AS (
SELECT * FROM users WHERE age >= 20
)
SELECT name, email FROM cte_name WHERE email LIKE '%gmail.com';
WITH RECURSIVE subordinates AS (
SELECT id, name, manager_id
FROM employees
WHERE id = 1 -- CEO부터 시작
UNION ALL
SELECT e.id, e.name, e.manager_id
FROM employees e
INNER JOIN subordinates s ON e.manager_id = s.id
)
SELECT * FROM subordinates;
CREATE TABLE users (
id SERIAL PRIMARY KEY,
data JSONB
);
INSERT INTO users (data) VALUES
('{"name": "Alice", "age": 25, "skills": ["Python", "SQL"]}'),
('{"name": "Bob", "age": 30, "skills": ["Go", "Rust"]}');
-- 조회
SELECT
data->>'name' AS name,
data->>'age' AS age
FROM users
WHERE data->>'age' = '25';
-- 내부조건 조회
SELECT * FROM users
WHERE data->'skills' ? 'Python';
-- 인덱싱
CREATE INDEX idx_users_data ON users USING gin (data);
SELECT
name,
department,
salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank
FROM employees;
| name | department | salary | dept_rank |
|---|---|---|---|
| Alice | IT | 8000 | 1 |
| Bob | IT | 6000 | 2 |
| Carol | HR | 7000 | 1 |
| Dave | HR | 6500 | 2 |
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- 혹은 ROLLBACK;
“읽기 작업(SELECT)이 쓰기(UPDATE/DELETE)를 블로킹하지 않는다”
내부 원리
CREATE INDEX idx_users_name ON users(name);
| 인덱스 타입 | 설명 | 예시 |
|---|---|---|
| BTREE | 기본 인덱스 (범위 검색 가능) | CREATE INDEX ... USING btree |
| HASH | 동등 비교용 (정확히 일치할 때) | USING hash |
| GIN | JSONB, 배열 검색용 | USING gin |
| GiST | 공간, 유사도 검색 | USING gist |
| BRIN | 대용량, 순차적 데이터에 효율 | USING brin |
fastapi_postgres_project/
├── app/
│ ├── main.py
│ ├── models.py
│ ├── database.py
│ └── routers/
│ └── users.py
├── .env
├── pyproject.toml / requirements.txt
└── Dockerfile (선택)
poetry add fastapi uvicorn sqlalchemy psycopg2-binary python-dotenvpip install fastapi uvicorn sqlalchemy psycopg2-binary python-dotenv

fastapi_tortoise_project/
├── app/
│ ├── main.py
│ ├── models.py
│ ├── schemas.py
│ ├── routers/
│ │ └── users.py
│ └── core/
│ └── config.py
├── .env
└── pyproject.toml / requirements.txt
poetry add fastapi uvicorn tortoise-orm aerich python-dotenvDB_URL=postgres://admin:1234@localhost:5432/fastapi_db# app/core/config.py
from dotenv import load_dotenv
import os
load_dotenv()
DB_URL = os.getenv("DB_URL")
# app/models.py
from tortoise import fields
from tortoise.models import Model
class User(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=50)
email = fields.CharField(max_length=100, unique=True)
created_at = fields.DatetimeField(auto_now_add=True)
def __str__(self):
return self.name
# app/schemas.py
from pydantic import BaseModel
class UserIn(BaseModel):
name: str
email: str
class UserOut(BaseModel):
id: int
name: str
email: str
class Config:
orm_mode = True
# app/main.py
from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise
from .core.config import DB_URL
from .routers import users
app = FastAPI(title="FastAPI + PostgreSQL + Tortoise ORM")
# 라우터 등록
app.include_router(users.router)
# Tortoise ORM 초기화
register_tortoise(
app,
db_url=DB_URL,
modules={"models": ["app.models"]},
generate_schemas=True, # 앱 시작 시 테이블 자동 생성
add_exception_handlers=True,
)
@app.get("/")
async def root():
return {"message": "Tortoise ORM 연결 성공!"}
# app/routers/users.py
from fastapi import APIRouter, HTTPException
from app import models, schemas
router = APIRouter(prefix="/users", tags=["users"])
@router.post("/", response_model=schemas.UserOut)
async def create_user(user: schemas.UserIn):
existing = await models.User.filter(email=user.email).first()
if existing:
raise HTTPException(status_code=400, detail="이미 존재하는 이메일입니다.")
new_user = await models.User.create(**user.dict())
return new_user
@router.get("/", response_model=list[schemas.UserOut])
async def get_users():
return await models.User.all()
초기 설정: aerich init -t app.main.TORTOISE_ORM
마이그레이션 파일 생성: aerich init-db
모델 변경 하려면: aerich migrate / aerich upgrade
docker run --name postgres-container -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=1234 -e POSTGRES_DB=mydb -p 5432:5432 -d postgres:16| 옵션 | 의미 |
|---|---|
--name postgres-container | 컨테이너 이름 |
-e POSTGRES_USER=admin | DB 사용자 이름 |
-e POSTGRES_PASSWORD=1234 | 비밀번호 |
-e POSTGRES_DB=mydb | 생성할 기본 DB 이름 |
-p 5432:5432 | 포트 매핑 (로컬:도커) |
-d postgres:16 | 백그라운드 실행, PostgreSQL 16버전 사용 |

\dt = 테이블 목록 보기 \q = 종료docker exec -it postgres psql -U postgres| 명령어 | 기능 |
|---|---|
\l | DB 목록 보기 |
\c DB명 | DB 접속 |
\dt | 테이블 목록 |
\d 테이블명 | 테이블 구조 보기 |
\q | psql 종료 |
\t | 출력 포맷 토글 (on/off) |
