λ³Έ λ¬Έμλ kiwoom-rest-trade λΌμ΄λΈλ¬λ¦¬λ₯Ό κ°λ°νκΈ° μν΄ μ°¨μΈλ μ΄κ³ μ νμ΄μ¬ ν¨ν€μ§ λ§€λμ μΈ uvλ₯Ό μ¬μ©νμ¬ κ°λ° νκ²½μ ꡬμΆνκ³ , μ μ λΆμ λ° ν
μ€νΈ λꡬλ₯Ό μ€μ νλ κ°μ΄λμ
λλ€.
>=3.10 (μ΅μ λΉλκΈ° λ¬Έλ² λ° ν¨ν΄ λ§€μΉ νμ©μ μν΄ Python 3.10 μ΄μ κΆμ₯)uv (Rust κΈ°λ°μ μ΄κ³ μ μ¬μΈμ νμ΄μ¬ ν¨ν€μ§ λ° νλ‘μ νΈ λ§€λμ )Ruff (λ§€μ° λΉ λ₯Έ μλμ λ€μν λ¦°νΈ λ£°μ
ν΅ν© μ 곡)Mypy (μ격ν νμ
체ν¬λ₯Ό ν΅ν λ²κ·Έ λ°©μ§)Pytest + pytest-asyncio (λΉλκΈ° μ λ/ν΅ν© ν
μ€νΈ μ΅μ ν)uv κΈ°λ°)uvλ νμ΄μ¬ μ€μΉλΆν° κ°μνκ²½ μμ±, μμ‘΄μ± λκΈ°νκΉμ§ λ²κ°μ²λΌ λΉ λ₯΄κ² μ²λ¦¬ν©λλ€.
uv μ€μΉ (μ€μΉλμ΄ μμ§ μμ κ²½μ°)# Windows (PowerShell)
irm https://astral.sh/uv/install.ps1 | iex
# Mac / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
uvλ₯Ό μ¬μ©νλ©΄ νλ‘μ νΈ ν΄λμ λ°λ‘ κ°μνκ²½μ μμ±νκ³ , μλμΌλ‘ νμ΄μ¬ λ²μ κΉμ§ λ€μ΄λ‘λνμ¬ μ μ©ν΄ μ€λλ€.
# νλ‘μ νΈ ν΄λ λ΄λΆμ νμ΄μ¬ 3.10 κΈ°λ° κ°μνκ²½(.venv) μμ±
uv venv --python 3.10
# κ°μνκ²½ νμ±ν (Windows PowerShell)
.venv\Scripts\activate
# κ°μνκ²½ νμ±ν (Mac / Linux)
source .venv/bin/activate
uv.lockκ³Ό pyproject.tomlμ κΈ°μ¬λ λͺ¨λ κ°λ°/λ°νμ ν¨ν€μ§λ₯Ό μ΄κ³ μμΌλ‘ μ€μΉν©λλ€.
uv sync
uvλ νΉμ λꡬ μ μ© ν¬λ§· λμ , νμ΄μ¬ 곡μ νμ€ μ€νμΈ PEP 621 κΈ°λ°μ pyproject.tomlμ κΈ°λ³ΈμΌλ‘ μ¬μ©ν©λλ€. λΉλ λ°±μλλ‘λ κ°λ³κ³ νλμ μΈ hatchlingμ κΆμ₯ν©λλ€.
[project]
name = "kiwoom-rest-trade"
version = "0.1.0"
description = "ν€μμ¦κΆ μ ν REST/WebSocket API λΉλκΈ° νμ΄μ¬ SDK"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "MIT" }
authors = [
{ name = "Your Name", email = "your.email@example.com" }
]
dependencies = [
"httpx[http2]>=0.27.0",
"websockets>=12.0",
"pydantic>=2.6.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
# κ°λ°μ© μμ‘΄μ± μ μ (uv μ μ© dependency-groups λλ κΈ°λ³Έ optional-dependencies)
[dependency-groups]
dev = [
"pytest>=8.0.0",
"pytest-asyncio>=0.23.0",
"ruff>=0.2.0",
"mypy>=1.8.0",
]
# Ruff μ€μ (λ¦°ν° & ν¬λ§·ν°)
[tool.ruff]
target-version = "py310"
line-length = 100
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort (μν¬νΈ μ λ ¬)
"B", # flake8-bugbear (μ μ¬μ λ²κ·Έ)
"RUF", # Ruff κ³ μ κ·μΉ
]
# Mypy μ€μ (νμ
체ν¬)
[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true # μ격ν νμ
μ μ κ°μ
disallow_incomplete_defs = true
ignore_missing_imports = true
# Pytest μ€μ
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
# Hatch λΉλ λμ ν¨ν€μ§ κ²½λ‘ λͺ
μ
[tool.hatch.build.targets.wheel]
packages = ["kiwoom"]
uvλ₯Ό μ¬μ©νλ©΄ κ°μνκ²½ λ΄μ μ€μΉλ μ€ν λꡬλ€μ uv runμ ν΅ν΄ 격리λμ΄ μμ νκ³ λΉ λ₯΄κ² μ€νν μ μμ΅λλ€.
# μ½λ κ²μ¬ λ° μλ μμ (Auto-fix)
uv run ruff check . --fix
# μ½λ ν¬λ§·ν
uv run ruff format .
uv run mypy kiwoom
uv run pytest
tests/test_client.py)import pytest
from kiwoom import AsyncKiwoomClient
@pytest.mark.asyncio
async def test_get_balance(mock_kiwoom_server):
client = AsyncKiwoomClient(appkey="test_key", secretkey="test_secret")
balance = await client.domestic.get_balance(acnt_no="1234567890")
assert balance.acnt_no == "1234567890"
assert balance.tot_evlu_amt > 0
---
## 6. ν¨ν€μ§ λΉλ λ° λ°°ν¬ λ°©λ² (Packaging & Publishing)
λ³Έ λΌμ΄λΈλ¬λ¦¬λ `uv`μ `hatchling` λΉλ λ°±μλλ₯Ό νμ€μΌλ‘ μ±ννκ³ μμΌλ―λ‘, λΉλ λ° PyPI μ
λ‘λ κ³Όμ λν λ¨μνκ³ μ§κ΄μ μ
λλ€.
### 6.1. λ°°ν¬ ν¨ν€μ§ λΉλ (Build)
`pyproject.toml` μ€νμ λ°λΌ ν (whl)κ³Ό μμ€ λ°°ν¬ν(tar.gz)μ μ»΄νμΌ λ° μμ±ν©λλ€.
```bash
# dist/ ν΄λ νμμ λΉλ μν°ν©νΈ μμ±
uv build
λΉλλ ν¨ν€μ§ νμΌμ PyPI(Python Package Index) 곡μ μ μ₯μμ μ λ‘λ(λ°°ν¬)ν©λλ€.
# 곡μ PyPI λ°°ν¬ μ€ν (PyPI API Token νμ)
uv publish
uv publish --publish-url https://test.pypi.org/legacy/
```