Skip to content

Commit 53bc454

Browse files
authored
refactor: uv migration, clean architecture (#26)
* refactor: uv migration, apply ruff * refactor: 클린아키텍처 도입
1 parent fd6dfac commit 53bc454

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1941
-1265
lines changed

.vscode/settings.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python3",
3+
"python.analysis.extraPaths": [
4+
"${workspaceFolder}/.venv/lib/python3.13/site-packages"
5+
],
6+
"python.analysis.typeCheckingMode": "basic"
7+
}

Dockerfile

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ FROM python:3.10
22

33
RUN apt-get update && apt-get install -y vim && apt-get install -y less
44

5-
RUN pip install poetry
6-
7-
# 가상환경 생성하지 않음
8-
RUN poetry config virtualenvs.create false
5+
RUN pip install uv
96

107
WORKDIR /satto
118

12-
COPY pyproject.toml poetry.lock ./
9+
COPY pyproject.toml uv.lock ./
1310

14-
RUN poetry lock && poetry install --no-root
11+
RUN uv sync --no-install-project --no-dev
1512

1613
COPY ./src /satto/src

docker-compose.dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ services:
2727
- ${WRITABLE_DIR}/logs/app:/data/logs/app
2828
command: >
2929
sh -c "
30-
gunicorn src.main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
30+
uv run gunicorn src.main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
3131
"
3232
ports:
3333
- "8000:8000"

docker-compose.local.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ services:
3636
- ${WRITABLE_DIR}/logs/app:/data/logs/app
3737
command: >
3838
sh -c "
39-
uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload
39+
uv run uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload
4040
"
4141
ports:
4242
- "8000:8000"

poetry.lock

Lines changed: 0 additions & 1082 deletions
This file was deleted.

pyproject.toml

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ description = ""
55
authors = [
66
{name = "Gukhee Jo", email = "[email protected]"}
77
]
8-
readme = "README.md"
9-
requires-python = "^3.13"
8+
requires-python = ">=3.13"
109
dependencies = [
1110
"fastapi (>=0.116.1,<0.117.0)",
1211
"sqlalchemy (>=2.0.41,<3.0.0)",
@@ -24,9 +23,45 @@ dependencies = [
2423
"apscheduler (>=3.11.0,<4.0.0)"
2524
]
2625

27-
[tool.poetry]
28-
package-mode = false
29-
3026
[build-system]
31-
requires = ["poetry-core>=2.0.0,<3.0.0"]
32-
build-backend = "poetry.core.masonry.api"
27+
requires = ["hatchling"]
28+
build-backend = "hatchling.build"
29+
30+
[tool.hatch.build.targets.wheel]
31+
packages = ["src"]
32+
33+
[dependency-groups]
34+
dev = [
35+
"mypy>=1.19.1",
36+
"ruff>=0.14.9",
37+
]
38+
39+
[tool.ruff]
40+
target-version = "py313"
41+
line-length = 80
42+
43+
[tool.ruff.lint]
44+
select = [
45+
"E", # pycodestyle errors
46+
"F", # pyflakes
47+
"I", # isort
48+
]
49+
ignore = [
50+
"E501", # line too long (handled by formatter)
51+
]
52+
exclude = [
53+
".git",
54+
"__pycache__",
55+
".venv",
56+
"venv",
57+
"migrations",
58+
"mysql_data",
59+
]
60+
61+
[tool.mypy]
62+
python_version = "3.13"
63+
strict = false
64+
warn_unused_configs = true
65+
disallow_untyped_defs = true
66+
disallow_any_unimported = false
67+
warn_return_any = true

src/common/dependencies.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,49 @@
1-
from fastapi import Request
1+
from fastapi import Depends, Request
22
from sqlalchemy.ext.asyncio import AsyncSession
33

4+
from src.fortune.application.service import FortuneService
5+
from src.fortune.domain.interfaces import IFortuneRepository
6+
from src.fortune.infrastructure.repository import FortuneRepository
7+
from src.lotto.application.service import LottoService
8+
from src.lotto.domain.interfaces import ILottoRepository
9+
from src.lotto.infrastructure.repository import LottoRepository
10+
from src.users.application.service import UserService
11+
from src.users.domain.interfaces import IUserRepository
12+
from src.users.infrastructure.repository import UserRepository
13+
414

515
def get_db_session(request: Request) -> AsyncSession:
16+
"""DB 세션 의존성 주입 함수"""
617
return request.state.db_session
18+
19+
20+
def get_lotto_service(
21+
session: AsyncSession = Depends(get_db_session),
22+
) -> LottoService:
23+
"""로또 서비스 의존성 주입 함수"""
24+
lotto_repository: ILottoRepository = LottoRepository(session=session)
25+
user_repository: IUserRepository = UserRepository(session=session)
26+
return LottoService(
27+
lotto_repository=lotto_repository,
28+
user_repository=user_repository,
29+
)
30+
31+
32+
def get_fortune_service(
33+
session: AsyncSession = Depends(get_db_session),
34+
) -> FortuneService:
35+
"""운세 서비스 의존성 주입 함수"""
36+
fortune_repository: IFortuneRepository = FortuneRepository(session=session)
37+
user_repository: IUserRepository = UserRepository(session=session)
38+
return FortuneService(
39+
fortune_repository=fortune_repository,
40+
user_repository=user_repository,
41+
)
42+
43+
44+
def get_user_service(
45+
session: AsyncSession = Depends(get_db_session),
46+
) -> UserService:
47+
"""사용자 서비스 의존성 주입 함수"""
48+
user_repository: IUserRepository = UserRepository(session=session)
49+
return UserService(user_repository=user_repository)

src/common/scheduler/tasks.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11
from src.common.logger import logger
22
from src.config.config import db_config
33
from src.config.database import Mysql
4-
from src.lotto.repository import LottoRepository
5-
from src.lotto.service import LottoService
4+
from src.lotto.application.service import LottoService
5+
from src.lotto.infrastructure.repository import LottoRepository
6+
from src.users.infrastructure.repository import UserRepository
67

78

89
async def update_next_lotto_draw():
910
"""다음 회차 로또 데이터를 동행복권 API에서 가져와서 데이터베이스에 저장합니다."""
1011
db = Mysql(db_config)
11-
12+
1213
try:
1314
async with db.session() as session:
14-
repository = LottoRepository(session)
15-
lotto_service = LottoService(repository)
15+
lotto_repository = LottoRepository(session)
16+
user_repository = UserRepository(session)
17+
lotto_service = LottoService(
18+
lotto_repository=lotto_repository,
19+
user_repository=user_repository,
20+
)
1621
success = await lotto_service.update_next_lotto_draw()
1722
await session.commit()
1823
if success:
1924
logger.info("로또 데이터 업데이트가 성공적으로 완료되었습니다.")
2025
else:
2126
logger.warning("로또 데이터 업데이트가 실패했습니다.")
22-
27+
2328
except Exception as e:
2429
logger.error(f"로또 데이터 업데이트 중 오류 발생: {e}")
2530
finally:

src/fortune/api/__init__.py

Whitespace-only changes.
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
# src/fortune/router.py
1+
# src/fortune/api/router.py
22
from datetime import date
33
from typing import Optional
44
from fastapi import APIRouter, Depends, Query, Path, HTTPException, status
5-
from src.fortune.entities.schemas import (
5+
from src.common.dependencies import get_fortune_service
6+
from src.fortune.api.schemas import (
67
DailyFortuneResource,
78
DailyFortuneResourceCreate,
89
DailyFortuneResourceUpdate,
910
DailyFortuneResourceList,
1011
)
11-
from src.fortune.service import FortuneService
12-
from src.fortune.entities.enums import FortuneType
12+
from src.fortune.application.service import FortuneService
13+
from src.fortune.domain.entities.enums import FortuneType
1314

1415
fortune_router = APIRouter(prefix="/admin/fortune", tags=["fortune-admin"])
1516

@@ -23,7 +24,7 @@ async def list_fortune_resources(
2324
limit: int = Query(10, ge=1, le=100, description="한 페이지 크기"),
2425
publish_date: Optional[date] = Query(None, description="발행일 필터"),
2526
fortune_type: Optional[FortuneType] = Query(None, description="유형 필터"),
26-
service: FortuneService = Depends(),
27+
service: FortuneService = Depends(get_fortune_service),
2728
):
2829
return await service.list_fortunes(cursor, limit, publish_date, fortune_type)
2930

@@ -36,7 +37,7 @@ async def list_fortune_resources(
3637
)
3738
async def create_fortune_resource(
3839
body: DailyFortuneResourceCreate,
39-
service: FortuneService = Depends(),
40+
service: FortuneService = Depends(get_fortune_service),
4041
):
4142
return await service.create_fortune(body)
4243

@@ -49,7 +50,7 @@ async def create_fortune_resource(
4950
async def update_fortune_resource(
5051
body: DailyFortuneResourceUpdate,
5152
resource_id: int = Path(..., description="운세 리소스 ID"),
52-
service: FortuneService = Depends(),
53+
service: FortuneService = Depends(get_fortune_service),
5354
):
5455
return await service.update_fortune(resource_id, body)
5556

@@ -61,7 +62,7 @@ async def update_fortune_resource(
6162
)
6263
async def delete_fortune_resource(
6364
resource_id: int = Path(..., description="운세 리소스 ID"),
64-
service: FortuneService = Depends(),
65+
service: FortuneService = Depends(get_fortune_service),
6566
):
6667
await service.delete_fortune(resource_id)
6768
# 204 No Content

0 commit comments

Comments
 (0)