Files
AutonetSellCar/DEPLOYMENT_GUIDE.md
AutonetSellCar Deploy b1afea79d9 Add SOLD OUT badge and improve deployment docs
- Add SOLD OUT overlay on car detail page image
- Add SOLD OUT badge next to car name
- Add Sold Out status in Admin Cars detail view
- Add soldout field to Car TypeScript interface
- Create PRODUCTION_VALUES.md for deployment reference
- Update CLAUDE.md with CRITICAL deployment section
- Update TROUBLESHOOTING.md with recurring errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-03 09:05:16 +09:00

710 lines
30 KiB
Markdown

# AutonetSellCar.com 배포 가이드
이 문서는 시스템 아키텍처와 코드 수정부터 운영 배포까지의 전체 과정을 설명합니다.
> **중요**: 배포 전 반드시 [PRODUCTION_VALUES.md](./PRODUCTION_VALUES.md)의 값을 확인하세요!
> 반복되는 오류 해결은 [TROUBLESHOOTING.md](./TROUBLESHOOTING.md)를 참고하세요.
---
## 1. 시스템 아키텍처 개요
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ AutonetSellCar.com 시스템 아키텍처 │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────┐
│ 인터넷 │
│ 59.14.158.123 │
└────────┬────────┘
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
autonetsellcar.com autonetsellcar.com autonetsellcar.com
/ /api /uploads
│ │ │
└──────────────────┼──────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 서버1 (192.168.0.201) │
│ 인프라 서버 │
│ ┌─────────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Nginx Proxy │ │ PostgreSQL │ │ Redis │ │ Portainer/Grafana │ │
│ │ Manager │ │ :5432 │ │ :6379 │ │ Prometheus │ │
│ │ :80/:443 │ │ │ │ │ │ │ │
│ │ │ │ DB: autonet │ │ │ │ │ │
│ └────────┬────────┘ └──────┬──────┘ └──────┬──────┘ └─────────────────────┘ │
│ │ │ │ │
└───────────┼──────────────────┼────────────────┼──────────────────────────────────┘
│ │ │
│ └────────┬───────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 서버2 (192.168.0.202) │
│ 애플리케이션 서버 │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Production Environment │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ autonet-frontend │ │ autonet-backend │ │ carmodoo-agent │ │ │
│ │ │ Next.js 14 │───▶│ FastAPI │───▶│ Playwright │ │ │
│ │ │ :3000 │ │ :8000 │ │ PDF/Spec 조회 │ │ │
│ │ └──────────────────┘ └────────┬─────────┘ └──────────────────┘ │ │
│ │ │ │ │
│ │ │ PostgreSQL/Redis │ │
│ │ └──────────────────────────────────────┼───┼──▶ 서버1
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Staging Environment │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ frontend-staging │ │ backend-staging │ │ │
│ │ │ :3001 │ │ :8001 │ │ │
│ │ └──────────────────┘ └──────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 서버4 (개발 PC) │
│ │
│ D:\Workspace\claudeCode\AutonetSellCar.com\ │
│ ├── frontend/ (Next.js) │
│ ├── backend/ (FastAPI) │
│ └── carmodoo-agent/ │
│ │
│ 로컬 테스트: localhost:3000 / localhost:8000 │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
```
---
## 2. 서버별 역할 및 구성
### 서버1 (192.168.0.201) - 인프라 서버
| 서비스 | 포트 | 역할 |
|--------|------|------|
| Nginx Proxy Manager | 80, 443, 81 | 리버스 프록시, SSL 인증서 관리 |
| PostgreSQL | 5432 | 메인 데이터베이스 (autonet) |
| Redis | 6379 | 캐시, 세션 관리 |
| Portainer | 9000 | Docker 관리 UI |
| Prometheus | 9090 | 메트릭 수집 |
| Grafana | 3000 | 모니터링 대시보드 |
### 서버2 (192.168.0.202) - 애플리케이션 서버
| 컨테이너 | 포트 | 역할 |
|----------|------|------|
| autonet-frontend | 3000 | Next.js 프론트엔드 (운영) |
| autonet-backend | 8000 | FastAPI 백엔드 (운영) |
| carmodoo-agent | - | Playwright 기반 PDF/스펙 조회 |
| autonet-frontend-staging | 3001 | 스테이징 프론트엔드 |
| autonet-backend-staging | 8001 | 스테이징 백엔드 |
---
## 3. 도메인 및 라우팅 설정
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Nginx Proxy Manager 설정 │
└─────────────────────────────────────────────────────────────────────────────────┘
autonetsellcar.com (SSL: Let's Encrypt)
├── / ──▶ 192.168.0.202:3000 (Frontend)
├── /api/* ──▶ 192.168.0.202:8000 (Backend)
│ └── Custom Location: /api
│ Forward: http://192.168.0.202:8000
└── /uploads/* ──▶ 192.168.0.202:8000 (Backend Static)
└── Custom Location: /uploads
Forward: http://192.168.0.202:8000
설정 방법:
┌─────────────────────────────────────────────────────────────────────────┐
│ 1. Nginx Proxy Manager (http://192.168.0.201:81) 접속 │
│ 2. Proxy Hosts > autonetsellcar.com 선택 │
│ 3. Custom Locations 탭에서 /api, /uploads 추가 │
│ - Location: /api │
│ - Scheme: http │
│ - Forward Hostname: 192.168.0.202 │
│ - Forward Port: 8000 │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 4. PostgreSQL 설정
### 4.1 서버1 PostgreSQL Docker 컨테이너
```bash
# PostgreSQL 컨테이너 정보 확인
docker exec postgres-primary env | grep POSTGRES
# 출력:
# POSTGRES_USER=admin
# POSTGRES_PASSWORD=roskfl@1122
# POSTGRES_DB=mongolcar (컨테이너 기본 DB, AutonetSellCar는 autonet DB 사용!)
```
### 4.2 데이터베이스 생성
```bash
# autonet 데이터베이스 생성
docker exec -it postgres-primary psql -U admin -d postgres -c "CREATE DATABASE autonet;"
# 비밀번호 재설정 (필요시)
docker exec -it postgres-primary psql -U admin -d postgres -c "ALTER USER admin PASSWORD 'roskfl@1122';"
```
### 4.3 외부 접속 설정 확인
```bash
# pg_hba.conf 확인 (외부 접속 허용)
docker exec postgres-primary cat /var/lib/postgresql/data/pg_hba.conf | grep -v "^#" | grep -v "^$"
# 필수 설정:
# host all all all scram-sha-256
```
### 4.4 백엔드 PostgreSQL 연결 설정
**backend/.env:**
```env
# Database
USE_SQLITE=False
DB_HOST=192.168.0.201
DB_PORT=5432
DB_NAME=autonet
DB_USER=admin
DB_PASSWORD=roskfl@1122
```
**backend/app/config.py:**
```python
@property
def DATABASE_URL(self) -> str:
if self.USE_SQLITE:
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
db_path = os.path.join(base_dir, "autonet.db")
return f"sqlite:///{db_path}"
# URL-encode password for special characters like @ # etc
from urllib.parse import quote_plus
encoded_password = quote_plus(self.DB_PASSWORD)
return f"postgresql://{self.DB_USER}:{encoded_password}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
```
### 4.5 SQLite → PostgreSQL 마이그레이션
```bash
# 개발 서버(서버4)에서 실행
cd D:\Workspace\claudeCode\AutonetSellCar.com\backend
# psycopg2 설치
venv\Scripts\pip.exe install psycopg2-binary
# 마이그레이션 스크립트 실행
venv\Scripts\python.exe migrate_to_postgres.py
```
**마이그레이션 스크립트 주요 기능:**
- PostgreSQL에 테이블 자동 생성 (SQLAlchemy 모델 기반)
- SQLite boolean (0/1) → PostgreSQL boolean (true/false) 변환
- FK 제약조건 임시 비활성화 후 데이터 이전
- Sequence 자동 리셋
---
## 5. Docker Compose 설정
### 5.1 Docker Compose v2 설치 (서버2)
```bash
# Docker 공식 저장소 추가
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg -y
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Docker Compose 플러그인 설치
sudo apt-get update
sudo apt-get install docker-compose-plugin -y
# 버전 확인
docker compose version
```
### 5.2 docker-compose.production.yml
```yaml
services:
frontend:
build: ./frontend
container_name: autonet-frontend
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- NEXT_PUBLIC_API_URL=https://autonetsellcar.com
depends_on:
- backend
networks:
- autonet-production-network
backend:
build: ./backend
container_name: autonet-backend
ports:
- "8000:8000"
environment:
- ENV=production
env_file:
- ./backend/.env
volumes:
- ./backend/uploads:/app/uploads
networks:
- autonet-production-network
carmodoo-agent:
build: ./agent
container_name: carmodoo-agent
volumes:
- ./agent/.env:/app/.env:ro
networks:
- autonet-production-network
networks:
autonet-production-network:
driver: bridge
```
### 5.3 주요 Docker 명령어
```bash
# 전체 시작
docker compose -f docker-compose.production.yml up -d
# 특정 서비스 재빌드 (캐시 없이)
docker compose -f docker-compose.production.yml build backend --no-cache
docker compose -f docker-compose.production.yml up -d backend
# 로그 확인
docker logs autonet-backend --tail 50 -f
# 컨테이너 상태
docker ps
# 전체 중지 및 제거
docker compose -f docker-compose.production.yml down
# 네트워크/볼륨 문제 시
docker rm -f autonet-backend autonet-frontend carmodoo-agent
docker network rm autonet-production-network
docker compose -f docker-compose.production.yml up -d
```
---
## 6. 배포 흐름
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 배포 파이프라인 │
└─────────────────────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 1. 개발 │ ───▶ │ 2. 커밋 │ ───▶ │ 3. 스테이징 │ ───▶ │ 4. 운영 │
│ (서버4) │ │ & Push │ │ 테스트 │ │ 배포 │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
│ │ │ │
▼ ▼ ▼ ▼
코드 수정 git commit Docker 빌드 promote
로컬 테스트 git push staging :3001/:8001 :3000/:8000
```
### Step 1: 개발 (서버4)
```bash
# 로컬 개발 서버 실행
cd backend && venv\Scripts\activate
uvicorn app.main:app --reload --port 8000
cd frontend
npm run dev
# 테스트: http://localhost:3000
```
### Step 2: Git Commit & Push
```bash
git status
git add .
git commit -m "feat: 기능 설명"
git push staging main
```
### Step 3: 스테이징 테스트
```bash
# 서버2 SSH 접속 후
cd /opt/autonet/staging
docker compose -f docker-compose.staging.yml build --no-cache
docker compose -f docker-compose.staging.yml up -d
# 테스트 URL
# Frontend: http://192.168.0.202:3001
# Backend: http://192.168.0.202:8001/docs
```
### Step 4: 운영 배포
```bash
# 서버2에서
cd /opt/autonet/production
docker compose -f docker-compose.production.yml build --no-cache
docker compose -f docker-compose.production.yml up -d
# 또는 스크립트 사용
/opt/autonet/scripts/deploy.sh promote
# 운영 URL
# https://autonetsellcar.com
# https://autonetsellcar.com/api/docs
```
---
## 7. 디렉토리 구조
```
서버2 (/opt/autonet/)
├── git/
│ └── autonet.git/ # Bare Git Repository
│ └── hooks/
│ └── post-receive # 자동 배포 훅
├── staging/ # 스테이징 환경
│ ├── frontend/
│ ├── backend/
│ │ └── .env # 스테이징 환경변수
│ └── docker-compose.staging.yml
├── production/ # 운영 환경
│ ├── frontend/
│ ├── backend/
│ │ ├── .env # 운영 환경변수 (PostgreSQL)
│ │ ├── requirements.txt # psycopg2-binary 포함
│ │ └── app/
│ │ └── config.py # URL 인코딩 적용
│ ├── agent/
│ │ └── .env # Carmodoo 로그인 정보
│ └── docker-compose.production.yml
├── releases/ # 롤백용 백업
│ ├── 20241230_140000/
│ └── 20241230_150000/
└── scripts/
├── deploy-staging.sh
└── deploy.sh
```
---
## 8. 환경변수 설정
### 운영 서버 backend/.env
```env
# Database (PostgreSQL)
USE_SQLITE=False
DB_HOST=192.168.0.201
DB_PORT=5432
DB_NAME=autonet
DB_USER=admin
DB_PASSWORD=roskfl@1122
# Redis
REDIS_HOST=192.168.0.201
REDIS_PORT=6379
REDIS_PASSWORD=
# JWT
SECRET_KEY=your-secret-key-change-in-production
# App
DEBUG=False
# Azure Translator
AZURE_TRANSLATOR_KEY=your-azure-key
AZURE_TRANSLATOR_REGION=southeastasia
# Email (SMTP)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=autonetsellcar@gmail.com
SMTP_PASSWORD=your-app-password
SMTP_FROM_EMAIL=autonetsellcar@gmail.com
SMTP_FROM_NAME=AutonetSellCar
# Verification
VERIFICATION_CODE_EXPIRE_MINUTES=10
EMAIL_VERIFICATION_REQUIRED=True
```
### 운영 서버 agent/.env
```env
# Carmodoo 딜러 포탈 로그인 정보
CARMODOO_USER_ID=01033315258
CARMODOO_PASSWORD=alskfl@1122
# Backend API 연결 (Docker 네트워크 내부)
API_SERVER_URL=http://autonet-backend:8000/api
```
> ⚠️ **중요**: carmodoo-agent는 이 환경변수가 없으면 로그인 실패로 계속 재시작됩니다.
---
## 9. Git 기반 배포가 필요한 이유
### 9.1 문제 상황 (Git 미사용 시)
```
[서버4 - 로컬 개발] [서버2 - 운영 서버]
소스코드 A 소스코드 A
│ │
│ 직접 수정 → 소스코드 B (예: 번역 수정)
│ │
└────── docker build ───────────┘
소스코드 A로 덮어씀
(B 변경사항 사라짐!)
```
**원인**:
- 서버2에서 직접 코드를 수정함
- 서버4에서 `docker build`를 하면 서버4의 소스코드로 컨테이너가 다시 빌드됨
- 서버2에서 수정한 내용이 서버4에는 없으므로 → **변경사항이 사라짐**
### 9.2 해결 방법 (Git 기반 배포)
```
[서버4 - 로컬 개발] [서버2 - 운영 서버]
소스코드 수정
git commit
git push staging ────────────────→ Git Bare Repository
post-receive hook (자동)
소스코드 복사 + docker build
스테이징 컨테이너 실행
검증 후 deploy.sh promote
운영 컨테이너 실행
```
### 9.3 핵심 원칙
| 이전 (문제) | 현재 (해결) |
|------------|------------|
| 서버2에서 직접 수정 | **서버4에서만 수정** |
| 수정 후 바로 docker build | git commit → git push |
| 변경 이력 없음 | Git으로 버전 관리 |
| 롤백 불가 | `deploy.sh rollback`으로 롤백 가능 |
### 9.4 올바른 배포 워크플로우
```bash
# 1. 서버4에서 코드 수정 (로컬 개발)
code frontend/src/app/page.tsx
# 2. 로컬 테스트
npm run dev
# 3. Git 커밋
git add .
git commit -m "fix: 번역 수정"
# 4. 스테이징 배포 (자동 - post-receive hook)
git push staging main
# 5. 스테이징 검증
# http://192.168.0.202:3001 (frontend)
# http://192.168.0.202:8001/docs (backend)
# 6. 운영 승격
ssh damon@192.168.0.202 "/opt/autonet/scripts/deploy.sh promote"
```
> ⚠️ **중요**: 서버2에서 직접 코드를 수정하지 마세요! 모든 변경은 서버4에서 Git을 통해 배포해야 합니다.
---
## 10. 문제 해결
### PostgreSQL 연결 오류
```bash
# 에러: could not translate host name "1122@192.168.0.201"
# 원인: 비밀번호에 @ 문자가 URL 구분자로 인식됨
# 해결: config.py에서 URL 인코딩 적용
from urllib.parse import quote_plus
encoded_password = quote_plus(self.DB_PASSWORD)
```
### Docker 네트워크 충돌
```bash
# 에러: network was found but has incorrect label
docker compose -f docker-compose.production.yml down
docker network rm autonet-production-network
docker compose -f docker-compose.production.yml up -d
```
### 컨테이너 이름 충돌
```bash
# 에러: container name is already in use
docker rm -f autonet-backend autonet-frontend carmodoo-agent
docker compose -f docker-compose.production.yml up -d
```
### psycopg2 모듈 없음
```bash
# requirements.txt에 추가
psycopg2-binary # PostgreSQL production
# 또는 직접 추가
echo "psycopg2-binary" >> /opt/autonet/production/backend/requirements.txt
docker compose -f docker-compose.production.yml build backend --no-cache
```
### Mixed Content 에러
```
# 에러: Mixed Content - HTTPS 페이지에서 HTTP API 호출
# 해결: Frontend 빌드 시 HTTPS API URL 사용
NEXT_PUBLIC_API_URL=https://autonetsellcar.com
```
### carmodoo-agent 계속 재시작
```bash
# 에러 확인
docker logs carmodoo-agent --tail 20
# 에러: User ID and password are required
# 원인: agent/.env 파일 없음 또는 환경변수 미설정
# 해결:
# 1. .env 파일 생성
cat > /opt/autonet/production/agent/.env << 'EOF'
CARMODOO_USER_ID=01033315258
CARMODOO_PASSWORD=alskfl@1122
API_SERVER_URL=http://autonet-backend:8000/api
EOF
# 2. 컨테이너 재생성 (.env 마운트 적용)
docker stop carmodoo-agent && docker rm carmodoo-agent
docker run -d --name carmodoo-agent \
--restart unless-stopped \
--network autonet-production-network \
-v /opt/autonet/production/agent/.env:/app/.env:ro \
production-carmodoo-agent
```
---
## 11. 빠른 참조 명령어
```
╔═══════════════════════════════════════════════════════════════════════════════╗
║ 개발 (서버4) ║
╠═══════════════════════════════════════════════════════════════════════════════╣
║ git status # 변경 파일 확인 ║
║ git add . # 스테이징 ║
║ git commit -m "message" # 커밋 ║
║ git push staging main # 서버2로 푸시 ║
╚═══════════════════════════════════════════════════════════════════════════════╝
╔═══════════════════════════════════════════════════════════════════════════════╗
║ 운영 (서버2) ║
╠═══════════════════════════════════════════════════════════════════════════════╣
║ docker ps # 실행 중인 컨테이너 ║
║ docker logs autonet-backend --tail 50 # 백엔드 로그 ║
║ docker compose -f docker-compose.production.yml up -d # 시작 ║
║ docker compose -f docker-compose.production.yml down # 중지 ║
║ docker compose -f docker-compose.production.yml build --no-cache # 재빌드 ║
╚═══════════════════════════════════════════════════════════════════════════════╝
╔═══════════════════════════════════════════════════════════════════════════════╗
║ PostgreSQL (서버1) ║
╠═══════════════════════════════════════════════════════════════════════════════╣
║ docker exec -it postgres-primary psql -U admin -d autonet # DB 접속 ║
║ \dt # 테이블 목록 ║
║ \q # 종료 ║
╚═══════════════════════════════════════════════════════════════════════════════╝
```
---
## 12. 체크리스트
### 최초 서버 설정
- [ ] 서버1: PostgreSQL 컨테이너 실행 중
- [ ] 서버1: autonet 데이터베이스 생성
- [ ] 서버1: 외부 접속 허용 (pg_hba.conf)
- [ ] 서버1: Nginx Proxy Manager SSL 설정
- [ ] 서버1: Custom Location (/api, /uploads) 설정
- [ ] 서버2: Docker Compose v2 설치
- [ ] 서버2: backend/.env PostgreSQL 설정
- [ ] 서버2: requirements.txt에 psycopg2-binary 추가
- [ ] 서버2: config.py URL 인코딩 적용
### 배포 전
- [ ] 로컬 테스트 완료
- [ ] git status로 변경 파일 확인
- [ ] 커밋 메시지 작성
### 배포 후
- [ ] docker ps로 컨테이너 상태 확인
- [ ] docker logs로 에러 확인
- [ ] https://autonetsellcar.com 접속 테스트
- [ ] API 응답 확인 (/api/docs)
---
**최종 업데이트**: 2024-12-31