# 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
- **상태: ✅ 구현 완료 및 정상 작동 중**
- 모든 오류 해결 완료