151 lines
5.3 KiB
Markdown
151 lines
5.3 KiB
Markdown
# skill-publish 서비스 구현 및 오류 해결
|
|
|
|
## 작성일: 2025-09-06
|
|
## 작성자: happybell80
|
|
## 관련: skill-publish (포트 8511), company-x_hompage 코드 이식
|
|
|
|
## 1. 프로젝트 초기 구성
|
|
**목표**: company-x_hompage/src/publishers 코드를 skill-publish로 분리
|
|
- 저장소: https://git.ro-being.com/ivada_Ro-being/skill-publish.git
|
|
- 포트: 8511 (내부 통신용, Nginx 불필요)
|
|
- 서버: 51124
|
|
|
|
## 2. 주요 오류 및 해결
|
|
|
|
### 2.1 pyproject.toml 패키지 경로 오류
|
|
**오류**: `No directory matches the name of your project (skill_publish)`
|
|
**원인**: hatchling이 패키지 경로를 찾지 못함
|
|
**해결**: pyproject.toml에 추가
|
|
```toml
|
|
[tool.hatch.build.targets.wheel]
|
|
packages = ["app"]
|
|
```
|
|
|
|
### 2.2 Docker network 오류
|
|
**오류**: `network ivada-network declared as external, but could not be found`
|
|
**원인**: 외부 네트워크 불필요 (내부 통신만 사용)
|
|
**해결**: docker-compose.yml에서 제거
|
|
- 삭제: `version: '3.8'` (obsolete)
|
|
- 삭제: networks 섹션 전체
|
|
|
|
### 2.3 Gitea Actions SSH 배포 오류
|
|
**오류**: 로컬 경로 접근 시도
|
|
**원인**: skill_news 참고하여 SSH 방식 누락
|
|
**해결**: .gitea/workflows/deploy.yml 수정
|
|
```yaml
|
|
- name: Setup SSH
|
|
run: |
|
|
echo "${{ secrets.SSH_PRIVATE_KEY_51124 }}" > ~/.ssh/deploy_key
|
|
chmod 600 ~/.ssh/deploy_key
|
|
ssh-keyscan -p 51124 -H ${{ secrets.SSH_HOST_51124 }} >> ~/.ssh/known_hosts
|
|
|
|
- name: Deploy to 51124 Server
|
|
run: |
|
|
ssh -p 51124 -i ~/.ssh/deploy_key admin@${{ secrets.SSH_HOST_51124 }} << 'DEPLOY_SCRIPT'
|
|
cd /home/admin/ivada_project/skill-publish
|
|
git pull origin main --rebase
|
|
docker compose down || echo "Container was not running"
|
|
docker compose up -d --build
|
|
DEPLOY_SCRIPT
|
|
```
|
|
|
|
### 2.4 Import 경로 오류 (src → app)
|
|
**오류**: `ImportError: No module named 'src'`
|
|
**원인**: company-x_hompage는 src/, skill-publish는 app/ 구조
|
|
**해결**: 모든 파일에서 일괄 변경
|
|
```bash
|
|
find app/services -name "*.py" -exec sed -i 's/from src\./from app./g' {} \;
|
|
```
|
|
**수정 파일**:
|
|
- app/services/*.py (8개 파일)
|
|
- squarespace_login.py, post_formatter.py, image_uploader.py 등
|
|
|
|
### 2.5 get_logger 함수 누락
|
|
**오류**: `ImportError: cannot import name 'get_logger'`
|
|
**원인**: company-x_hompage와 logger 구조 차이
|
|
**해결**: app/utils/logger.py에 호환성 함수 추가
|
|
```python
|
|
def get_logger(name: str, category: str = 'general') -> logging.Logger:
|
|
return setup_logger(name)
|
|
```
|
|
|
|
### 2.6 login_to_squarespace 함수 누락
|
|
**오류**: `ImportError: cannot import name 'login_to_squarespace'`
|
|
**원인**: publisher_service.py에서 함수형 호출, 원본은 클래스만 존재
|
|
**해결**: app/services/squarespace_login.py에 호환 함수 추가
|
|
```python
|
|
async def login_to_squarespace(page: Page) -> bool:
|
|
login_instance = SquarespaceLogin()
|
|
success, error = await login_instance.login(page)
|
|
return success
|
|
```
|
|
|
|
### 2.7 NewsDataManager 클래스 누락
|
|
**오류**: `NameError: name 'NewsDataManager' is not defined`
|
|
**원인**: company-x_hompage의 collectors 모듈 미포함
|
|
**해결**: app/services/post_formatter.py에서 직접 파일 I/O로 대체
|
|
```python
|
|
# 변경 전
|
|
self.manager = NewsDataManager(keyword)
|
|
unpublished = self.manager.get_unposted_articles(limit=100)
|
|
|
|
# 변경 후
|
|
self.news_file = self.news_dir / f"{self.safe_keyword}_news.json"
|
|
if self.news_file.exists():
|
|
with open(self.news_file, 'r', encoding='utf-8') as f:
|
|
all_news = json.load(f)
|
|
unpublished = [a for a in all_news if not a.get('posted', False)][:100]
|
|
```
|
|
|
|
### 2.8 ImageDownloader 클래스 누락
|
|
**해결**: app/services/image_uploader.py:10에서 주석 처리
|
|
```python
|
|
# from app.image_downloader import ImageDownloader
|
|
```
|
|
|
|
### 2.9 post_formatter.py 들여쓰기 문법 오류
|
|
**오류**: `SyntaxError: invalid syntax at line 85`
|
|
**원인**: if-else 블록 잘못된 중첩
|
|
**해결**: app/services/post_formatter.py:77-84 들여쓰기 수정
|
|
|
|
## 3. 환경변수 설정 (51124 서버)
|
|
```bash
|
|
cat > /home/admin/ivada_project/skill-publish/.env << 'EOF'
|
|
PORT=8511
|
|
SQUARESPACE_EMAIL=실제이메일
|
|
SQUARESPACE_PASSWORD=실제비밀번호
|
|
SQUARESPACE_SITE_URL=https://www.company-x.partners
|
|
HEADLESS_BROWSER=true
|
|
EOF
|
|
```
|
|
|
|
## 4. 최종 구조
|
|
```
|
|
skill-publish/
|
|
├── app/
|
|
│ ├── main.py (FastAPI 앱)
|
|
│ ├── config.py (설정)
|
|
│ ├── services/
|
|
│ │ ├── publisher_service.py (메인 서비스)
|
|
│ │ ├── squarespace_*.py (이식된 코드)
|
|
│ │ └── post_formatter.py (수정됨)
|
|
│ └── utils/
|
|
│ └── logger.py (호환성 추가)
|
|
├── Dockerfile (Playwright 포함)
|
|
├── docker-compose.yml (네트워크 제거)
|
|
└── .gitea/workflows/deploy.yml (SSH 배포)
|
|
```
|
|
|
|
## 5. 교훈
|
|
1. **코드 이식시 import 경로 전면 검토 필수**
|
|
2. **Docker 네트워크는 필요한 경우만 사용**
|
|
3. **Gitea Actions는 SSH 배포 패턴 통일**
|
|
4. **의존성 모듈은 독립적으로 구현 또는 제거**
|
|
5. **환경변수로 하드코딩 제거 (GEMINI_MODEL 등)**
|
|
|
|
## 6. 최종 상태
|
|
**서비스 정상 작동 확인** (2025-09-06)
|
|
- 포트 8511 정상 리스닝
|
|
- Health 상태: healthy
|
|
- **상태: ✅ 구현 완료 및 정상 작동 중**
|
|
- 모든 오류 해결 완료 |