Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cb3dd19
feat: ์œ ์‚ฌ๋„ torch ์‚ญ์ œ ํ›„ onnx ์ถ”๊ฐ€
thkim7 Sep 11, 2025
72002de
feat: ์œ ์‚ฌ๋„ torch ์‚ญ์ œ ํ›„ onnx ์ถ”๊ฐ€
thkim7 Sep 11, 2025
99e2f99
chore: onnx ๋ชจ๋ธ ์ €์žฅ์šฉ ๋ณผ๋ฅจ docker-composer์— ์ถ”๊ฐ€
thkim7 Sep 11, 2025
3f2a398
Merge branch 'develop' into feature/onnx
thkim7 Sep 11, 2025
cab225b
chore: poetry.lock ์ถ”๊ฐ€
thkim7 Sep 12, 2025
c5ac0ab
chore: poetry.lock ์ถ”๊ฐ€
thkim7 Sep 12, 2025
000f524
chore: poetry run black & ci-python ์›์ƒ ๋ณต๊ตฌ
thkim7 Sep 12, 2025
78c43d8
chore: ci-python ๋จธ์ง€ ํ…Œ์ŠคํŠธ ์ถ”๊ฐ€
thkim7 Sep 12, 2025
219a5b6
Merge pull request #87 from Kernel180-BE12/feature/onnx
thkim7 Sep 12, 2025
be53bb4
typeHandler๊ฐ€ ๋“ฑ๋ก๋˜์ง€ ์•Š๋Š” ๋ฒ„๊ทธ (#88)
can019 Sep 13, 2025
5579b7c
๊ธฐ๋ณธ์ ์ธ global exception handling (#90)
can019 Sep 13, 2025
34c22dc
Caddyfile copy ์ถ”๊ฐ€ (#89)
can019 Sep 13, 2025
1f38926
refactor: ํ•„์š”์—†๋Š” API url ์‚ญ์ œ
dktkf Sep 14, 2025
d60939f
refactor: 2์ฐจ ๋งˆ์ผ์Šคํ†ค์„์œ„ํ•œ ๊ณตํ†ต request๋ฐ response ์ฃผ์„
dktkf Sep 14, 2025
e8c83c3
refactor:
dktkf Sep 14, 2025
b8e3aaa
Merge pull request #92 from Kernel180-BE12/feature/milestone_patch
kakusiA Sep 14, 2025
959adf4
Workflow dummy data insert sql (alpha) (#93)
can019 Sep 15, 2025
7a147bd
chore: v0.0.4 erd ์ ์šฉ
can019 Sep 15, 2025
1050dd3
Loki, Grafana ๋กœ์ปฌ ์„ธํŒ… ๋ฐ log ์ฒ˜๋ฆฌ (#95)
can019 Sep 15, 2025
91db5c3
๋กœ๊ทธ์•„์›ƒ api (#96)
bwnfo3 Sep 15, 2025
e36e407
Grafana Loki ๋กœ๊น… (develop) (#97)
can019 Sep 15, 2025
71f3b5b
Pagination dto ๋ฐ ๊ณตํ†ต service (#94)
can019 Sep 15, 2025
d766df4
feat: crawling_util ํ•œ๊ฐœ๋กœ ํ•ฉ์นจ
thkim7 Sep 15, 2025
5a9dd2b
Merge branch 'develop' of https://github.com/Kernel180-BE12/Final-4teโ€ฆ
thkim7 Sep 16, 2025
1106b30
Merge branch 'develop' of https://github.com/Kernel180-BE12/Final-4teโ€ฆ
thkim7 Sep 16, 2025
a3f4bb7
๋ธ”๋กœ๊ทธ ์ž๋™ํ™” ๋ฐฐ์น˜ Job ๋ฐ Tasklet ์„ธํŒ… (#91)
jihukimme Sep 16, 2025
1acd33c
chore: ์ฝ”๋“œ์— ์žˆ๋Š” job_id ์ „๋ถ€ ์ œ๊ฑฐ
thkim7 Sep 16, 2025
5700b77
chore: poetry run black .
thkim7 Sep 16, 2025
2172cde
Loki e2e test support (#99)
can019 Sep 16, 2025
edcbbce
Merge pull request #98 from Kernel180-BE12/feature/crawl_service_refaโ€ฆ
thkim7 Sep 16, 2025
c4759de
Merge branch 'develop' of https://github.com/Kernel180-BE12/Final-4teโ€ฆ
thkim7 Sep 16, 2025
af62f52
feat: ์˜ˆ์‹œ ๋ฐ์ดํ„ฐ๋กœ GPTํ•œํ…Œ ์ฝ˜ํ…์ธ  ์ƒ์„ฑ ํ›„ blogger์— ์—…๋กœ๋“œ ์„ฑ๊ณต
thkim7 Sep 16, 2025
64c78c7
feat: rag๋กœ ์ฝ˜ํ…์ธ  ์ƒ์„ฑ ํ›„ blogger ํฌ์ŠคํŒ… ํ…Œ์ŠคํŠธ ํ†ต๊ณผ
thkim7 Sep 16, 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
2 changes: 2 additions & 0 deletions .github/workflows/ci-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: CI (Python/FastAPI)

on:
push:
branches:
- feature/onnx
tags:
- 'pre-processing-v*'
pull_request:
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/deploy-java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ jobs:
target: "~/app/docker/production/"
overwrite: true

- name: Copy Caddyfile to EC2
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SERVER_HOST }}
username: ubuntu
key: ${{ secrets.SERVER_SSH_KEY }}
source: "docker/production/Caddyfile"
target: "~/app/docker/production/"
overwrite: true

- name: Deploy on EC2
uses: appleboy/ssh-action@v1.0.3
with:
Expand Down
23 changes: 7 additions & 16 deletions apps/pre-processing-service/app/api/endpoints/blog.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@
from ...model.schemas import *
from app.service.blog.tistory_blog_post_service import TistoryBlogPostService
from app.service.blog.naver_blog_post_service import NaverBlogPostService
from ...service.blog.blogger_blog_post_service import BloggerBlogPostService
from ...service.blog.blogger_blog_post_adapter import (
BloggerBlogPostAdapter,
) # ์ˆ˜์ •๋œ import

router = APIRouter()


@router.get("/", summary="๋ธ”๋กœ๊ทธ API ์ƒํƒœ ํ™•์ธ")
async def root():
return {"message": "blog API"}


@router.post(
"/rag/create",
response_model=ResponseBlogCreate,
Expand Down Expand Up @@ -49,9 +46,7 @@ async def publish(request: RequestBlogPublish):
raise CustomException(
"๋„ค์ด๋ฒ„ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ…์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.", status_code=500
)
return ResponseBlogPublish(
job_id=1, schedule_id=1, schedule_his_id=1, status="200", metadata=result
)
return ResponseBlogPublish(status="success", metadata=result)

elif request.tag == "tistory":
tistory_service = TistoryBlogPostService()
Expand All @@ -66,12 +61,10 @@ async def publish(request: RequestBlogPublish):
"ํ‹ฐ์Šคํ† ๋ฆฌ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ…์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.", status_code=500
)

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

elif request.tag == "blogger":
blogger_service = BloggerBlogPostService()
blogger_service = BloggerBlogPostAdapter() # ์ˆ˜์ •: Adapter ์‚ฌ์šฉ
result = blogger_service.post_content(
title=request.post_title,
content=request.post_content,
Expand All @@ -83,6 +76,4 @@ async def publish(request: RequestBlogPublish):
"๋ธ”๋กœ๊ฑฐ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ…์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.", status_code=500
)

return ResponseBlogPublish(
job_id=1, schedule_id=1, schedule_his_id=1, status="200", metadata=result
)
return ResponseBlogPublish(status="success", metadata=result)
8 changes: 1 addition & 7 deletions apps/pre-processing-service/app/api/endpoints/keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@
router = APIRouter()


@router.get("/", summary="ํ‚ค์›Œ๋“œ API ์ƒํƒœ ํ™•์ธ")
@router.get("/")
async def root():
"""
ํ‚ค์›Œ๋“œ API๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธ
"""
return {"message": "keyword API"}


Expand All @@ -23,9 +20,6 @@ async def search(request: RequestNaverSearch):

์š”์ฒญ ์˜ˆ์‹œ:
{
"job_id": 1,
"schedule_id": 1,
"schedule_his_id": 1,
"tag": "naver",
"category": "50000000",
"start_date": "2025-09-01",
Expand Down
53 changes: 23 additions & 30 deletions apps/pre-processing-service/app/api/endpoints/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from ...service.crawl_service import CrawlService
from ...service.search_service import SearchService
from ...service.match_service import MatchService
from ...service.similarity_service import SimilarityService


# from ...service.similarity_service import SimilarityService
Expand All @@ -16,14 +17,6 @@
router = APIRouter()


@router.get("/", summary="์ƒํ’ˆ API ์ƒํƒœ ํ™•์ธ")
async def root():
"""
์ƒํ’ˆ API ์„œ๋ฒ„ ์ƒํƒœ ํ™•์ธ์šฉ ์—”๋“œํฌ์ธํŠธ
"""
return {"message": "product API"}


@router.post("/search", response_model=ResponseSadaguSearch, summary="์ƒํ’ˆ ๊ฒ€์ƒ‰")
async def search(request: RequestSadaguSearch):
"""
Expand Down Expand Up @@ -62,33 +55,33 @@ async def match(request: RequestSadaguMatch):
raise HTTPException(status_code=500, detail=str(e))


# @router.post(
# "/similarity", response_model=ResponseSadaguSimilarity, summary="์ƒํ’ˆ ์œ ์‚ฌ๋„ ๋ถ„์„"
# )
# async def similarity(request: RequestSadaguSimilarity):
# """
# ๋งค์นญ๋œ ์ƒํ’ˆ๋“ค ์ค‘ ํ‚ค์›Œ๋“œ์™€์˜ ์œ ์‚ฌ๋„๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ์ตœ์ ์˜ ์ƒํ’ˆ์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
# """
# try:
# similarity_service = SimilarityService()
# result = similarity_service.select_product_by_similarity(request)
#
# if not result:
# raise CustomException(
# 500, "์œ ์‚ฌ๋„ ๋ถ„์„์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.", "SIMILARITY_FAILED"
# )
#
# return result
# except InvalidItemDataException as e:
# raise HTTPException(status_code=e.status_code, detail=e.detail)
# except Exception as e:
# raise HTTPException(status_code=500, detail=str(e))
@router.post(
"/similarity", response_model=ResponseSadaguSimilarity, summary="์ƒํ’ˆ ์œ ์‚ฌ๋„ ๋ถ„์„"
)
async def similarity(request: RequestSadaguSimilarity):
"""
๋งค์นญ๋œ ์ƒํ’ˆ๋“ค ์ค‘ ํ‚ค์›Œ๋“œ์™€์˜ ์œ ์‚ฌ๋„๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ์ตœ์ ์˜ ์ƒํ’ˆ์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
"""
try:
similarity_service = SimilarityService()
result = similarity_service.select_product_by_similarity(request)

if not result:
raise CustomException(
500, "์œ ์‚ฌ๋„ ๋ถ„์„์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.", "SIMILARITY_FAILED"
)

return result
except InvalidItemDataException as e:
raise HTTPException(status_code=e.status_code, detail=e.detail)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))


@router.post(
"/crawl", response_model=ResponseSadaguCrawl, summary="์ƒํ’ˆ ์ƒ์„ธ ์ •๋ณด ํฌ๋กค๋ง"
)
async def crawl(request: Request, body: RequestSadaguCrawl):
async def crawl(body: RequestSadaguCrawl):
"""
์ƒํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€๋ฅผ ํฌ๋กค๋งํ•˜์—ฌ ์ƒ์„ธ ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.
"""
Expand Down
18 changes: 4 additions & 14 deletions apps/pre-processing-service/app/api/endpoints/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@
router = APIRouter()


@router.get("/")
async def root():
return {"message": "ํ…Œ์ŠคํŠธ API"}


@router.get("/hello/{name}", tags=["hello"])
# @log_api_call
async def say_hello(name: str):
Expand Down Expand Up @@ -67,19 +62,14 @@ def with_meta(data: Mapping[str, Any], meta: Mapping[str, Any]) -> Dict[str, Any

@router.get("/tester", response_model=None)
async def processing_tester():
meta = {
"job_id": 1,
"schedule_id": 1,
"schedule_his_id": 1, # โœ… ํƒ€์ดํฌ ์ˆ˜์ •
}
request_dict = {
"tag": "naver",
"category": "50000000",
"start_date": "2025-09-01",
"end_date": "2025-09-02",
}
# ๋„ค์ด๋ฒ„ ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰
naver_request = RequestNaverSearch(**with_meta(meta, request_dict))
naver_request = RequestNaverSearch(**with_meta(request_dict))
response_data = await keyword_search(naver_request)
keyword = response_data.get("keyword")
loguru.logger.info(keyword)
Expand All @@ -89,21 +79,21 @@ async def processing_tester():
}

# ์‹ธ๋‹ค๊ตฌ ์ƒํ’ˆ ๊ฒ€์ƒ‰
sadagu_request = RequestSadaguSearch(**with_meta(meta, keyword))
sadagu_request = RequestSadaguSearch(**with_meta(keyword))
search_service = SearchService()
keyword_result = await search_service.search_products(sadagu_request)
loguru.logger.info(keyword_result)

# ์‹ธ๋‹ค๊ตฌ ์ƒํ’ˆ ๋งค์น˜
keyword["search_results"] = keyword_result.get("search_results")
keyword_match_request = RequestSadaguMatch(**with_meta(meta, keyword))
keyword_match_request = RequestSadaguMatch(**with_meta(keyword))
match_service = MatchService()
keyword_match_response = match_service.match_products(keyword_match_request)
loguru.logger.info(keyword_match_response)

# ์‹ธ๋‹ค๊ตฌ ์ƒํ’ˆ ์œ ์‚ฌ๋„ ๋ถ„์„
keyword["matched_products"] = keyword_match_response.get("matched_products")
keyword_similarity_request = RequestSadaguSimilarity(**with_meta(meta, keyword))
keyword_similarity_request = RequestSadaguSimilarity(**with_meta(keyword))
# similarity_service = SimilarityService()
# keyword_similarity_response = similarity_service.select_product_by_similarity(
# keyword_similarity_request
Expand Down
3 changes: 3 additions & 0 deletions apps/pre-processing-service/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ class BaseSettingsConfig(BaseSettings):
# MeCab ์‚ฌ์ „ ๊ฒฝ๋กœ (์ž๋™ ๊ฐ์ง€)
mecab_path: Optional[str] = None

# ํ…Œ์ŠคํŠธ/์ถ”๊ฐ€์šฉ ํ•„๋“œ
openai_api_key: Optional[str] = None # << ์ด ๋ถ€๋ถ„ ์ถ”๊ฐ€

def __init__(self, **kwargs):
super().__init__(**kwargs)

Expand Down
20 changes: 2 additions & 18 deletions apps/pre-processing-service/app/model/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,13 @@

# ๊ธฐ๋ณธ ์š”์ฒญ
class RequestBase(BaseModel):
job_id: int = Field(
..., title="์ž‘์—… ID", description="ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์ž‘์—…์˜ ๊ณ ์œ  ์‹๋ณ„์ž"
)
schedule_id: int = Field(
..., title="์Šค์ผ€์ค„ ID", description="์˜ˆ์•ฝ๋œ ์Šค์ผ€์ค„์˜ ๊ณ ์œ  ์‹๋ณ„์ž"
)
schedule_his_id: Optional[int] = Field(
None, title="์Šค์ผ€์ค„ ํžˆ์Šคํ† ๋ฆฌ ID", description="์Šค์ผ€์ค„ ์‹คํ–‰ ์ด๋ ฅ์˜ ๊ณ ์œ  ์‹๋ณ„์ž"
)
pass


# ๊ธฐ๋ณธ ์‘๋‹ต
class ResponseBase(BaseModel):
job_id: int = Field(
..., title="์ž‘์—… ID", description="ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์ž‘์—…์˜ ๊ณ ์œ  ์‹๋ณ„์ž"
)
schedule_id: int = Field(
..., title="์Šค์ผ€์ค„ ID", description="์˜ˆ์•ฝ๋œ ์Šค์ผ€์ค„์˜ ๊ณ ์œ  ์‹๋ณ„์ž"
)
schedule_his_id: Optional[int] = Field(
None, title="์Šค์ผ€์ค„ ํžˆ์Šคํ† ๋ฆฌ ID", description="์Šค์ผ€์ค„ ์‹คํ–‰ ์ด๋ ฅ์˜ ๊ณ ์œ  ์‹๋ณ„์ž"
)
status: str = Field(..., title="์ƒํƒœ", description="์š”์ฒญ ์ฒ˜๋ฆฌ ์ƒํƒœ")
pass


# ๋„ค์ด๋ฒ„ ํ‚ค์›Œ๋“œ ์ถ”์ถœ
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC, abstractmethod
from typing import Dict, List, Optional
from typing import Dict

from app.utils.crawling_util import CrawlingUtil
from app.errors.BlogPostingException import *
Expand All @@ -11,51 +11,39 @@ class BaseBlogPostService(ABC):
๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ… ์„œ๋น„์Šค ์ถ”์ƒ ํด๋ž˜์Šค
"""

def __init__(self, config_file="blog_config.json"):
"""๊ณตํ†ต ์ดˆ๊ธฐํ™” ๋กœ์ง"""
# Selenium ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค๋ฅผ ์œ„ํ•œ ์ดˆ๊ธฐํ™”
if self._requires_webdriver():
def __init__(self, use_webdriver=True):
"""
๊ณตํ†ต ์ดˆ๊ธฐํ™” ๋กœ์ง
:param use_webdriver: ์›น๋“œ๋ผ์ด๋ฒ„ ์‚ฌ์šฉ ์—ฌ๋ถ€ (API ์„œ๋น„์Šค์˜ ๊ฒฝ์šฐ False)
"""
self.use_webdriver = use_webdriver

if self.use_webdriver:
try:
self.crawling_service = CrawlingUtil()
# ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŒ…์šฉ ์„ค์ •์œผ๋กœ ์ดˆ๊ธฐํ™”
self.crawling_service = CrawlingUtil(
headless=False, # ๋„ค์ด๋ฒ„ ํƒ์ง€ ์šฐํšŒ๋ฅผ ์œ„ํ•ด headless ๋น„ํ™œ์„ฑํ™”
for_blog_posting=True,
)
self.web_driver = self.crawling_service.get_driver()
self.wait_driver = self.crawling_service.get_wait()
except Exception:
raise WebDriverConnectionException()
else:
# API ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค์˜ ๊ฒฝ์šฐ WebDriver๊ฐ€ ํ•„์š” ์—†์Œ
self.crawling_service = None
self.web_driver = None
self.wait_driver = None

# API ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค๋ฅผ ์œ„ํ•œ ์ดˆ๊ธฐํ™”
self.config_file = config_file
self.config = {}
self.current_upload_account = None

# API ๊ด€๋ จ ์†์„ฑ๋“ค (์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์„œ๋น„์Šค์—์„œ๋Š” None์œผ๋กœ ์œ ์ง€)
self.blogger_service = None
self.blog_id = None
self.scopes = None

self._load_config()

def _requires_webdriver(self) -> bool:
"""
์„œ๋ธŒํด๋ž˜์Šค์—์„œ WebDriver๊ฐ€ ํ•„์š”ํ•œ์ง€ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜
๊ธฐ๋ณธ๊ฐ’์€ True (Selenium ๊ธฐ๋ฐ˜), API ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค์—์„œ๋Š” False๋กœ ์˜ค๋ฒ„๋ผ์ด๋“œ
"""
return True

@abstractmethod
def _load_config(self) -> None:
"""ํ”Œ๋žซํผ๋ณ„ ์„ค์ • ๋กœ๋“œ"""
pass

@abstractmethod
def _login(self) -> None:
"""
ํ”Œ๋žซํผ๋ณ„ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ (API ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค์˜ ๊ฒฝ์šฐ ์ธ์ฆ์œผ๋กœ ๋Œ€์ฒด)
๊ธฐ๋ณธ ๊ตฌํ˜„์€ ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š์Œ (API ์„œ๋น„์Šค์šฉ)
"""
"""ํ”Œ๋žซํผ๋ณ„ ๋กœ๊ทธ์ธ ๊ตฌํ˜„"""
pass

@abstractmethod
Expand Down Expand Up @@ -83,6 +71,14 @@ def _validate_content(
:param content: ํฌ์ŠคํŠธ ๋‚ด์šฉ
:param tags: ํฌ์ŠคํŠธ ํƒœ๊ทธ ๋ฆฌ์ŠคํŠธ
"""
# if not title or not title.strip():
# raise BlogContentValidationException("title", "์ œ๋ชฉ์ด ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค")
#
# if not content or not content.strip():
# raise BlogContentValidationException("content", "๋‚ด์šฉ์ด ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค")
#
# if tags is None:
# raise BlogContentValidationException("tags", "ํƒœ๊ทธ๊ฐ€ ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค")
pass

def post_content(self, title: str, content: str, tags: List[str] = None) -> Dict:
Expand All @@ -96,7 +92,7 @@ def post_content(self, title: str, content: str, tags: List[str] = None) -> Dict
# 1. ์ฝ˜ํ…์ธ  ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
self._validate_content(title, content, tags)

# 2. ๋กœ๊ทธ์ธ (Selenium ๊ธฐ๋ฐ˜) ๋˜๋Š” ์ธ์ฆ (API ๊ธฐ๋ฐ˜)
# 2. ๋กœ๊ทธ์ธ
self._login()

# 3. ํฌ์ŠคํŠธ ์ž‘์„ฑ ๋ฐ ๋ฐœํ–‰
Expand Down
Loading
Loading