Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3ca9498
chore: python CI μΆ”κ°€
kakusiA Sep 9, 2025
bcdabed
chore: μ½”λ“œ ν…ŒμŠ€νŠΈ
kakusiA Sep 9, 2025
904b96b
chore: poetry μ˜μ‘΄μ„± μΆ”κ°€
kakusiA Sep 9, 2025
c067e0d
refactor:μ½”λ“œ ν¬λ©§νŒ…
kakusiA Sep 9, 2025
65718d1
chore: ci ruff제거
kakusiA Sep 9, 2025
1080b5e
chore:
kakusiA Sep 9, 2025
eb16585
chore:
kakusiA Sep 9, 2025
caf48d8
refactor:ν…ŒμŠ€νŠΈμ½”λ“œ url 버그 μˆ˜μ •
kakusiA Sep 9, 2025
1b5805c
chore:docker image add
kakusiA Sep 9, 2025
4b6aa19
chore:poetry ν•„μš”μ—†λŠ” 라이브러리 μ‚­μ œ
kakusiA Sep 9, 2025
888d455
chore:ruff 주석 처리
kakusiA Sep 9, 2025
88792af
chore:νŒ¨ν‚€μ§€ λ‹€μ‹œ μΆ”κ°€
kakusiA Sep 9, 2025
0637263
chore: python CI μ½”λ“œ λ¦¬νŽ™ν† λ§
kakusiA Sep 9, 2025
7b3b1b4
chore: μ½”λ“œλ³€κ²½
kakusiA Sep 9, 2025
0f87d41
chore: CI 파이썬 μ½”λ“œ μˆ˜μ •
kakusiA Sep 9, 2025
fe1a283
chore:CI μ½”λ“œ λ‘€λ°±
kakusiA Sep 9, 2025
c3ab105
chore: 도컀 파일 μˆ˜μ •
kakusiA Sep 9, 2025
4000ccc
chore:
kakusiA Sep 10, 2025
9cb52cd
chore:dockerfile μˆ˜μ •
kakusiA Sep 10, 2025
b9f34da
Merge branch 'develop' into feature/python-ci
kakusiA Sep 10, 2025
7390721
chore: poetry torch λ³€κ²½
kakusiA Sep 10, 2025
e1be0f5
chore: black refactor
kakusiA Sep 10, 2025
c273fdf
chore: pyhton CI μ„€μ • μ™„λ£Œ
kakusiA Sep 10, 2025
40b923f
Merge branch 'develop' into feature/python-ci
kakusiA Sep 10, 2025
955aa11
chore:μ½”λ“œ ν¬λ©§νŒ…
kakusiA Sep 10, 2025
2d610d2
chore:poetryνŒ¨ν‚€μ§€ λ³€κ²½
kakusiA Sep 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions .github/workflows/ci-python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
name: CI (Python/FastAPI)

on:
push:
branches:
- main
paths:
- "apps/pre-processing-service/**" # Python μ„œλΉ„μŠ€ 경둜
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- main
- develop
- release/**
paths:
- "apps/pre-processing-service/**" # Python μ„œλΉ„μŠ€ 경둜

permissions:
contents: read
packages: write
security-events: write
checks: write
pull-requests: write

jobs:
lint:
if: github.event.pull_request.draft == false
name: Lint & Format Check
runs-on: ubuntu-latest

defaults:
run:
working-directory: apps/pre-processing-service

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install Poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true

- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v4
with:
path: apps/pre-processing-service/.venv
key: venv-${{ runner.os }}-${{ hashFiles('apps/pre-processing-service/poetry.lock') }}

- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root

- name: Run Formatter Check (Black)
run: poetry run black --check .

# - name: Run Linter (Ruff)
# run: poetry run ruff check .

test:
name: Run Tests
runs-on: ubuntu-latest
needs: lint

defaults:
run:
working-directory: apps/pre-processing-service

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install Poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
installer-parallel: true

- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v4
with:
path: apps/pre-processing-service/.venv
key: venv-${{ runner.os }}-${{ hashFiles('apps/pre-processing-service/poetry.lock') }}

- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root

- name: Run tests with Pytest
env:
DB_HOST: localhost
DB_PORT: 3306
DB_USER: test_user
DB_PASS: test_pass
DB_NAME: test_db
ENV_NAME: test
run: poetry run pytest

build-and-push-docker:
name: Build Docker Image and push to registry
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/feature/python-ci' && github.event_name == 'push'
needs:
- test

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Login to Docker Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set repo lowercase
run: echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ./apps/pre-processing-service # Dockerfile이 μžˆλŠ” 경둜
push: true
tags: |
ghcr.io/${{ env.REPO_LC }}/pre-processing-service:latest
ghcr.io/${{ env.REPO_LC }}/pre-processing-service:${{ github.sha }}

- name: Analyze image layers
run: |
echo "=== Image Layer Analysis ==="
docker history ghcr.io/${{ env.REPO_LC }}/pre-processing-service:latest --human --no-trunc
20 changes: 20 additions & 0 deletions apps/pre-processing-service/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.git
.gitignore
**/__pycache__/
**/*.pyc
**/.pytest_cache/
**/.mypy_cache/
**/.ruff_cache/
**/.venv/
**/node_modules/
**/dist/
**/build/
tests/
docs/
scripts/
.github/
.env
.env.*
*.log
pytest-report.xml
coverage.xml
33 changes: 25 additions & 8 deletions apps/pre-processing-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
# ---- builder ----
FROM python:3.11-slim AS builder
WORKDIR /app

# ν•„μˆ˜ OS νŒ¨ν‚€μ§€
RUN apt-get update && apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*

# Poetry μ„€μΉ˜
RUN curl -sSL https://install.python-poetry.org | python3 -
ENV PATH="/root/.local/bin:$PATH"
RUN poetry config virtualenvs.create false
RUN poetry self add "poetry-plugin-export>=1.7.0"
# λŸ°νƒ€μž„ κ°€μƒν™˜κ²½
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# μ˜μ‘΄μ„± ν•΄κ²° β†’ requirements둜 export β†’ pip둜 μ„€μΉ˜(= λ°˜λ“œμ‹œ /opt/venv에 μ„€μΉ˜λ¨)
COPY pyproject.toml poetry.lock ./
RUN poetry install --no-root
RUN poetry export --without dev -f requirements.txt -o requirements.txt \
&& pip install --no-cache-dir -r requirements.txt

# ---- runtime ----
FROM python:3.11-slim AS final
WORKDIR /app
# site-packages + μ½˜μ†” 슀크립트(gunicorn/uvicorn) ν•¨κ»˜ 볡사
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
COPY ./app ./app
EXPOSE 8000
CMD ["gunicorn", "-w", "2", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000", "app.main:app"]

# /opt/venv 볡사
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# μ•± μ†ŒμŠ€
COPY . .


# (ꢌμž₯ λŒ€μ•ˆ) μ½”λ“œμ—μ„œ uvicorn import μ•ˆ ν•˜κ³  ν”„λ‘œμ„ΈμŠ€ λ§€λ‹ˆμ €λ‘œ μ‹€ν–‰ν•˜λ €λ©΄:
CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "app.main:app", "-b", "0.0.0.0:8000"]
53 changes: 29 additions & 24 deletions apps/pre-processing-service/app/api/endpoints/blog.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,29 @@

router = APIRouter()


@router.get("/", summary="λΈ”λ‘œκ·Έ API μƒνƒœ 확인")
async def root():
return {"message": "blog API"}

@router.post("/rag/create", response_model=ResponseBlogCreate, summary="RAG 기반 λΈ”λ‘œκ·Έ μ½˜ν…μΈ  생성")

@router.post(
"/rag/create",
response_model=ResponseBlogCreate,
summary="RAG 기반 λΈ”λ‘œκ·Έ μ½˜ν…μΈ  생성",
)
async def rag_create(request: RequestBlogCreate):
"""
RAG 기반 λΈ”λ‘œκ·Έ μ½˜ν…μΈ  생성
"""
return {"message": "blog API"}

@router.post("/publish", response_model=ResponseBlogPublish, summary="λΈ”λ‘œκ·Έ μ½˜ν…μΈ  배포 (넀이버/ν‹°μŠ€ν† λ¦¬/λΈ”λ‘œκ±° 지원)")

@router.post(
"/publish",
response_model=ResponseBlogPublish,
summary="λΈ”λ‘œκ·Έ μ½˜ν…μΈ  배포 (넀이버/ν‹°μŠ€ν† λ¦¬/λΈ”λ‘œκ±° 지원)",
)
async def publish(request: RequestBlogPublish):
"""
μƒμ„±λœ λΈ”λ‘œκ·Έ μ½˜ν…μΈ λ₯Ό λ°°ν¬ν•©λ‹ˆλ‹€.
Expand All @@ -31,53 +42,47 @@ async def publish(request: RequestBlogPublish):
result = naver_service.post_content(
title=request.post_title,
content=request.post_content,
tags=request.post_tags
tags=request.post_tags,
)

if not result:
raise CustomException("넀이버 λΈ”λ‘œκ·Έ ν¬μŠ€νŒ…μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.", status_code=500)
raise CustomException(
"넀이버 λΈ”λ‘œκ·Έ ν¬μŠ€νŒ…μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.", status_code=500
)
return ResponseBlogPublish(
job_id= 1,
schedule_id= 1,
schedule_his_id= 1,
status="200",
metadata=result
job_id=1, schedule_id=1, schedule_his_id=1, status="200", metadata=result
)

elif request.tag == "tistory":
tistory_service = TistoryBlogPostService()
result = tistory_service.post_content(
title=request.post_title,
content=request.post_content,
tags=request.post_tags
tags=request.post_tags,
)

if not result:
raise CustomException("ν‹°μŠ€ν† λ¦¬ λΈ”λ‘œκ·Έ ν¬μŠ€νŒ…μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.", status_code=500)
raise CustomException(
"ν‹°μŠ€ν† λ¦¬ λΈ”λ‘œκ·Έ ν¬μŠ€νŒ…μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.", status_code=500
)

return ResponseBlogPublish(
job_id= 1,
schedule_id= 1,
schedule_his_id= 1,
status="200",
metadata=result
job_id=1, schedule_id=1, schedule_his_id=1, status="200", metadata=result
)

elif request.tag == "blogger":
blogger_service = BloggerBlogPostService()
result = blogger_service.post_content(
title=request.post_title,
content=request.post_content,
tags=request.post_tags
tags=request.post_tags,
)

if not result:
raise CustomException("λΈ”λ‘œκ±° λΈ”λ‘œκ·Έ ν¬μŠ€νŒ…μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.", status_code=500)
raise CustomException(
"λΈ”λ‘œκ±° λΈ”λ‘œκ·Έ ν¬μŠ€νŒ…μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.", status_code=500
)

return ResponseBlogPublish(
job_id= 1,
schedule_id= 1,
schedule_his_id= 1,
status="200",
metadata=result
)
job_id=1, schedule_id=1, schedule_his_id=1, status="200", metadata=result
)
10 changes: 8 additions & 2 deletions apps/pre-processing-service/app/api/endpoints/keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ async def root():
return {"message": "keyword API"}


@router.post("/search", response_model=ResponseNaverSearch, summary="넀이버 ν‚€μ›Œλ“œ 검색")
@router.post(
"/search", response_model=ResponseNaverSearch, summary="넀이버 ν‚€μ›Œλ“œ 검색"
)
async def search(request: RequestNaverSearch):
"""
이 μ—”λ“œν¬μΈνŠΈλŠ” JSON μš”μ²­μœΌλ‘œ 넀이버 ν‚€μ›Œλ“œ 검색을 μˆ˜ν–‰ν•©λ‹ˆλ‹€.
Expand All @@ -34,7 +36,11 @@ async def search(request: RequestNaverSearch):
return response_data


@router.post("/ssadagu/validate", response_model=ResponseNaverSearch, summary="사닀ꡬλͺ° ν‚€μ›Œλ“œ 검증")
@router.post(
"/ssadagu/validate",
response_model=ResponseNaverSearch,
summary="사닀ꡬλͺ° ν‚€μ›Œλ“œ 검증",
)
async def ssadagu_validate(request: RequestNaverSearch):
"""
사닀ꡬλͺ° ν‚€μ›Œλ“œ 검증 ν…ŒμŠ€νŠΈμš© μ—”λ“œν¬μΈνŠΈ
Expand Down
18 changes: 14 additions & 4 deletions apps/pre-processing-service/app/api/endpoints/product.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from fastapi import APIRouter, Request, HTTPException
from app.decorators.logging import log_api_call
from ...errors.CustomException import InvalidItemDataException, ItemNotFoundException, CustomException
from ...errors.CustomException import (
InvalidItemDataException,
ItemNotFoundException,
CustomException,
)
from ...service.crawl_service import CrawlService
from ...service.search_service import SearchService
from ...service.match_service import MatchService
Expand Down Expand Up @@ -56,7 +60,9 @@ async def match(request: RequestSadaguMatch):
raise HTTPException(status_code=500, detail=str(e))


@router.post("/similarity", response_model=ResponseSadaguSimilarity, summary="μƒν’ˆ μœ μ‚¬λ„ 뢄석")
@router.post(
"/similarity", response_model=ResponseSadaguSimilarity, summary="μƒν’ˆ μœ μ‚¬λ„ 뢄석"
)
async def similarity(request: RequestSadaguSimilarity):
"""
맀칭된 μƒν’ˆλ“€ 쀑 ν‚€μ›Œλ“œμ™€μ˜ μœ μ‚¬λ„λ₯Ό κ³„μ‚°ν•˜μ—¬ 졜적의 μƒν’ˆμ„ μ„ νƒν•©λ‹ˆλ‹€.
Expand All @@ -66,7 +72,9 @@ async def similarity(request: RequestSadaguSimilarity):
result = similarity_service.select_product_by_similarity(request)

if not result:
raise CustomException(500, "μœ μ‚¬λ„ 뢄석에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.", "SIMILARITY_FAILED")
raise CustomException(
500, "μœ μ‚¬λ„ 뢄석에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.", "SIMILARITY_FAILED"
)

return result
except InvalidItemDataException as e:
Expand All @@ -75,7 +83,9 @@ async def similarity(request: RequestSadaguSimilarity):
raise HTTPException(status_code=500, detail=str(e))


@router.post("/crawl", response_model=ResponseSadaguCrawl, summary="μƒν’ˆ 상세 정보 크둀링")
@router.post(
"/crawl", response_model=ResponseSadaguCrawl, summary="μƒν’ˆ 상세 정보 크둀링"
)
async def crawl(request: Request, body: RequestSadaguCrawl):
"""
μƒν’ˆ 상세 νŽ˜μ΄μ§€λ₯Ό ν¬λ‘€λ§ν•˜μ—¬ 상세 정보λ₯Ό μˆ˜μ§‘ν•©λ‹ˆλ‹€.
Expand Down
Loading
Loading