pgvectorλ₯Ό νμ©ν μλ§¨ν± κ²μ κΈ°λ° κΏ μΌκΈ° μ ν리μΌμ΄μ μ λλ€. μ¬μ©μμ κΏ λ΄μ©μ 벑ν°λ‘ λ³ννμ¬ μ μ₯νκ³ , μ μ¬ν κΏμ κ²μν μ μμ΅λλ€.
- κΏ λ΄μ©κ³Ό κ°μ μ κΈ°λ‘
- μλ§¨ν± κ²μμ ν΅ν μ μ¬ν κΏ μ°ΎκΈ°
- μ¬μ©μλ³ κΏ κ΄λ¦¬
- μ½μ¬μΈ 거리 κΈ°λ° μ μ¬λ μΈ‘μ
- Backend: FastAPI, Python 3.13
- Database: PostgreSQL 18 + pgvector
- Vector Model: sentence-transformers (all-MiniLM-L6-v2, 384μ°¨μ)
- Container: Docker, Docker Compose
- Package Manager: uv
βββββββββββββββ ββββββββββββββββ βββββββββββββββββββ
β index.html ββββββΆβ FastAPI ββββββΆβ PostgreSQL β
β (Frontend) β β (Backend) β β + pgvector β
βββββββββββββββ ββββββββββββββββ βββββββββββββββββββ
β
βΌ
ββββββββββββββββ
β Sentence β
β Transformers β
ββββββββββββββββ
.
βββ Dockerfile # λ©ν° μ€ν
μ΄μ§ λΉλ μ€μ
βββ docker-compose.yaml # μλΉμ€ μ€μΌμ€νΈλ μ΄μ
βββ database.py # DB μ°κ²° λ° μ΄κΈ°ν
βββ main.py # FastAPI μλν¬μΈνΈ
βββ vectorizer.py # ν
μ€νΈβλ²‘ν° λ³ν
βββ index.html # μΉ UI
βββ examples/ # μ¬μ© μμ
β βββ vector_demo.py # λ²‘ν° μμ± λ°λͺ¨
βββ tests/ # ν
μ€νΈ μ½λ
β βββ test_main.py # API μλν¬μΈνΈ ν
μ€νΈ
βββ .github/ # CI/CD μν¬νλ‘μ°
β βββ workflows/
β βββ ci.yml # μ§μμ ν΅ν©
β βββ cd.yml # μ§μμ λ°°ν¬ (μλ)
βββ pyproject.toml # νλ‘μ νΈ μμ‘΄μ±
βββ uv.lock # μμ‘΄μ± λ½ νμΌ
- Docker & Docker Compose
- Python 3.13+ (λ‘컬 κ°λ° μ)
# λΉλ λ° μ€ν
docker compose up -d
# λ‘κ·Έ νμΈ
docker compose logs -f app
# μ’
λ£
docker compose downμ ν리μΌμ΄μ
μ http://localhost:8000μμ μ€νλ©λλ€.
μ°Έκ³ : PostgreSQL 18μ /var/lib/postgresqlμ λ°μ΄ν°λ₯Ό μ μ₯ν©λλ€. μ΄μ λ²μ μμ μ
κ·Έλ μ΄λνλ κ²½μ° docker compose down -vλ‘ κΈ°μ‘΄ λ³Όλ₯¨μ μμ ν΄μΌ ν μ μμ΅λλ€.
# uvλ‘ μμ‘΄μ± μ€μΉ
uv sync
# κ°λ° μλ² μ€ν
uv run uvicorn main:app --reloadλ²‘ν° μμ± κ³Όμ μ νμΈνλ €λ©΄:
python examples/vector_demo.pyμ΄ μ€ν¬λ¦½νΈλ sentence-transformers λͺ¨λΈμ μ¬μ©νμ¬ ν
μ€νΈλ₯Ό 384μ°¨μ 벑ν°λ‘ λ³ννλ κ³Όμ μ 보μ¬μ€λλ€.
POST /add_dream/
Content-Type: application/json
{
"dream_text": "μΉκ΅¬μ ν¨κ» μ¬ννλ κΏ",
"dream_feeling": "μ¦κ±°μ λ€",
"user_id": 1
}GET /search_dreams/?query=μΉκ΅¬&user_id=1μλ΅:
{
"results": [
{
"dream_text": "μΉκ΅¬μ ν¨κ» μ¬ννλ κΏ",
"distance": 0.123
}
]
}CREATE TABLE dream_records (
id SERIAL PRIMARY KEY,
dream_text TEXT NOT NULL,
dream_vector vector(384),
user_id INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- HNSW μΈλ±μ€ (μ½μ¬μΈ 거리)
CREATE INDEX dream_vector_idx
ON dream_records
USING hnsw (dream_vector vector_cosine_ops)
WITH (m = 16, ef_construction = 64);- HNSW μΈλ±μ€: λΉ λ₯Έ κ·Όμ¬ μ΅κ·Όμ μ΄μ κ²μ
- λ©ν° μ€ν μ΄μ§ λΉλ: μ΅μ’ μ΄λ―Έμ§ ν¬κΈ° μ΅μν (2.24GB)
- λ μ΄μ΄ μΊμ±: λΉ λ₯Έ μ¬λΉλ (μ½λ λ³κ²½ μ ~10μ΄, 첫 λΉλ μ ~50λΆ)
- uv ν¨ν€μ§ λ§€λμ : pip λλΉ 10λ°° μ΄μ λΉ λ₯Έ μμ‘΄μ± μ€μΉ
μ΄ νλ‘μ νΈλ λμ©λ ML μμ‘΄μ±(PyTorch, sentence-transformers)μΌλ‘ μΈν΄ GitHub Actionsμμ μλ λ°°ν¬κ° μ νλ©λλ€. λ‘컬μμ λΉλνκ³ λ°°ν¬νλ κ²μ κΆμ₯ν©λλ€.
# 1. λ‘컬μμ λΉλ
docker compose build
# 2. Docker Hubμ νκ·Έ λ° νΈμ
docker tag pgvector-app:latest your-username/dream-diary:latest
docker push your-username/dream-diary:latest
# 3. νλ‘λμ
μλ²μμ λ°°ν¬
docker pull your-username/dream-diary:latest
docker compose up -d- CI (μ§μμ ν΅ν©): λͺ¨λ νΈμμ PRμ λν΄ μλμΌλ‘ ν μ€νΈ λ° λΉλ κ²μ¦ μ€ν
- CD (μ§μμ λ°°ν¬): μλ νΈλ¦¬κ±° λ°©μμΌλ‘ μ€μ (GitHub Actionsμ λμ€ν¬ κ³΅κ° μ μ½)
| λ³μ | κΈ°λ³Έκ° | μ€λͺ |
|---|---|---|
DB_HOST |
localhost |
PostgreSQL νΈμ€νΈ |
DB_NAME |
dream_db |
λ°μ΄ν°λ² μ΄μ€ μ΄λ¦ |
DB_USER |
user |
λ°μ΄ν°λ² μ΄μ€ μ¬μ©μ |
DB_PASSWORD |
password |
λ°μ΄ν°λ² μ΄μ€ λΉλ°λ²νΈ |
# --no-cache μ¬μ©νμ§ λ§ κ²
docker compose build# νμ₯ νμΈ
docker compose exec db psql -U user -d dream_db -c "\dx"
# ν
μ΄λΈ νμΈ
docker compose exec db psql -U user -d dream_db -c "\d dream_records"PostgreSQL 18 μ΄λ―Έμ§λ‘ μ κ·Έλ μ΄λ μ λ€μ μ€λ₯κ° λ°μν μ μμ΅λλ€:
Error: in 18+, these Docker images are configured to store database data...
ν΄κ²° λ°©λ²:
# κΈ°μ‘΄ λ³Όλ₯¨ μμ ν μ¬μμ
docker compose down -v
docker compose up -dμ°Έκ³ : PostgreSQL 18μ /var/lib/postgresqlμ λ°μ΄ν°λ₯Ό μ μ₯ν©λλ€. μ΄μ λ²μ μμ μ
κ·Έλ μ΄λνλ κ²½μ° λ³Όλ₯¨ λ§μ΄νΈ κ²½λ‘λ₯Ό νμΈνμΈμ.
μ±μ΄ λ°μ΄ν°λ² μ΄μ€μ μ°κ²°νμ§ λͺ»νλ κ²½μ°:
docker compose ps # dbκ° "Healthy" μνμΈμ§ νμΈ
docker compose logs db # λ°μ΄ν°λ² μ΄μ€ λ‘κ·Έ νμΈν¬μ€μ²΄ν¬κ° μ€μ λμ΄ μμ΄ λ°μ΄ν°λ² μ΄μ€κ° μ€λΉλ ν μ±μ΄ μμλ©λλ€.