# AutonetSellCar.com 개발 가이드 이 문서는 Claude Code 세션에서 반드시 읽고 참고해야 하는 중요한 정보입니다. > **배포 시 반드시 [DEPLOYMENT.md](./DEPLOYMENT.md)도 함께 확인하세요!** > - DB 스키마 변경 시 수동으로 ALTER TABLE 필요 > - 배포 명령어 및 롤백 방법 안내 --- # 🖥️ 서버 인프라 요약 ## 서버 구성 | 서버 | IP | 역할 | 주요 서비스 | |------|-----|------|-------------| | **Server1** | 192.168.0.201 | Master | nginx-proxy-manager, PostgreSQL, Redis, Portainer, Grafana | | **Server2** | 192.168.0.202 | Worker | AutonetSellCar (Production/Staging) | | **Server3** | 192.168.0.203 | Worker | Grantech, Nextcloud (cloud.grantech.kr) | ## 리버스 프록시 (nginx-proxy-manager) - **관리 UI**: http://server1:81 (또는 http://192.168.0.201:81) - **모든 도메인 SSL 인증서**는 nginx-proxy-manager에서 관리 - **새 도메인 추가 시** → nginx-proxy-manager에서 Proxy Host 추가 ### 등록된 도메인 | 도메인 | Forward Host | Forward Port | |--------|--------------|--------------| | autonetsellcar.com | 192.168.0.202 | 3000 | | staging.autonetsellcar.com | 192.168.0.202 | 3001 | | grantech.kr | 192.168.0.203 | 3001 | | cloud.grantech.kr | 192.168.0.203 | 8080 | ## 데이터베이스 | DB 이름 | 용도 | 서버 | |---------|------|------| | `autonet` | AutonetSellCar Production | Server1 (PostgreSQL) | | `autonet_staging` | AutonetSellCar Staging | Server1 (PostgreSQL) | | `grantech` | Grantech 사이트 | Server1 (PostgreSQL) | ## Docker 컨테이너 관리 ### Server2 (AutonetSellCar) ```bash # 위치: /opt/autonet/staging 또는 /opt/autonet/production docker compose -f docker-compose.staging.yml ps # 스테이징 상태 docker compose -f docker-compose.production.yml ps # 프로덕션 상태 ``` ### Server3 (Grantech, Nextcloud) ```bash # Grantech (위치: /home/damon/sites/grantech) ssh server3 "cd /home/damon/sites/grantech && docker compose ps" ssh server3 "cd /home/damon/sites/grantech && docker compose logs -f" ssh server3 "cd /home/damon/sites/grantech && docker compose restart" # Nextcloud ssh server3 "docker ps | grep nextcloud" ``` | 컨테이너 | 포트 | 설명 | |----------|------|------| | grantech-frontend | 3001 | Next.js (grantech.kr) | | grantech-backend | 8001 | FastAPI (api.grantech.kr) | | nextcloud | 8080 | Nextcloud (cloud.grantech.kr) | > **상세 인프라 문서**: [SERVER_INFRASTRUCTURE_PLAN.md](./SERVER_INFRASTRUCTURE_PLAN.md) --- # ⛔ CRITICAL: 배포 전 필수 확인사항 (DO NOT SKIP!) ## 🚨 Production 환경 고정값 (절대 변경 금지!) | 항목 | 올바른 값 | 잘못된 예 | |------|----------|----------| | **DB_NAME** | `autonet` | ~~mongolcar~~, ~~autonet_db~~ | | **DB_PASSWORD** | `roskfl@1122` (@ 포함, URL 인코딩 필요) | - | | **API_URL (Frontend)** | `https://autonetsellcar.com` | ~~http://192.168.0.202:8000~~ | | **Uploads 경로** | `/opt/autonet/production/backend/uploads` | ~~`/home/damon/mongolcar/data/uploads`~~ | | **배포 디렉토리** | `/opt/autonet/production` | ~~`/home/damon/mongolcar`~~ | ## 🚫 절대 하지 말 것 1. **수동 `docker run`에서 환경변수 직접 타이핑 금지** - [PRODUCTION_VALUES.md](./PRODUCTION_VALUES.md)에서 복사만 허용 2. **DB_NAME 직접 타이핑 금지** - 반드시 `autonet` 복사/붙여넣기 3. **`.env` 파일 덮어쓰기 금지** - 서버의 기존 `.env` 보존 4. **`/home/damon/mongolcar/data/uploads` 사용 금지** - 비어있는 잘못된 경로 ## ✅ 배포 전 필수 검증 명령어 ```bash # 1. DB 이름 확인 (반드시 autonet이어야 함!) ssh server1 "docker exec postgres-primary psql -U admin -d autonet -c 'SELECT COUNT(*) FROM cars;'" # 2. Uploads 경로에 파일 있는지 확인 ssh server2 "ls /opt/autonet/production/backend/uploads/cars/ | head -5" # 3. 배포 후 API 확인 curl -s https://autonetsellcar.com/api/hero-banners/ | head -c 100 ``` ## 📋 표준 컨테이너 실행 명령어 (복사해서 사용!) **⚠️ 아래 명령어를 그대로 복사해서 사용하세요. 절대 직접 타이핑하지 마세요!** ### Backend 실행 ```bash ssh server2 "docker stop autonet-backend 2>/dev/null; docker rm autonet-backend 2>/dev/null; docker run -d --name autonet-backend --restart unless-stopped -p 8000:8000 -e USE_SQLITE=False -e DB_HOST=192.168.0.201 -e DB_PORT=5432 -e DB_NAME=autonet -e DB_USER=admin -e 'DB_PASSWORD=roskfl@1122' -e REDIS_HOST=192.168.0.201 -e REDIS_PORT=6379 -e 'REDIS_PASSWORD=roskfl@1122' -e SECRET_KEY=YourSuperSecretKeyForJWT123! -e AGENT_API_KEY=AgentApiKey123! -v /opt/autonet/production/backend/uploads:/app/uploads --network mongolcar-network production-backend" ``` ### Frontend 빌드 전 (.env.production 설정) ```bash ssh server2 "echo 'NEXT_PUBLIC_API_URL=https://autonetsellcar.com' > /home/damon/mongolcar/frontend/.env.production" ``` ### Frontend 실행 ```bash ssh server2 "docker stop autonet-frontend 2>/dev/null; docker rm autonet-frontend 2>/dev/null; docker run -d --name autonet-frontend --restart unless-stopped -p 3000:3000 --network mongolcar-network production-frontend" ``` --- ## 🚀 배포 워크플로우 (필수!) ### 서버 구성 | 환경 | URL | 용도 | |------|-----|------| | **스테이징** | https://staging.autonetsellcar.com | 테스트용 (먼저 배포) | | **프로덕션** | https://autonetsellcar.com | 실서비스 (테스트 후 배포) | ### CI/CD 자동 배포 흐름 (2025-01-04 적용) ``` ┌─────────────────────────────────────────────────────────────┐ │ 1. 로컬 PC에서 코드 수정 │ │ D:\Workspace\claudeCode\AutonetSellCar.com\ │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 2. Git 커밋 및 푸시 → ★ 스테이징 자동 배포! │ │ git add . │ │ git commit -m "변경 내용" │ │ git push staging main │ │ → post-receive hook이 자동으로 스테이징 빌드/배포 │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 3. 스테이징 테스트 │ │ https://staging.autonetsellcar.com 에서 충분히 테스트 │ └─────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────┐ │ 4. 프로덕션 배포 (deploy.sh promote) │ │ ssh server2 "/opt/autonet/scripts/deploy.sh promote" │ │ → 스테이징 소스를 프로덕션으로 복사 및 빌드/배포 │ └─────────────────────────────────────────────────────────────┘ ``` ### ⚠️ Claude Code 작업 규칙 (중요!) **반드시 CI/CD 프로세스를 따르세요:** 1. **로컬에서만 코드 수정** - 서버 파일 직접 수정 금지! 2. **git push staging main** - 스테이징 자동 배포 3. **테스트 후 promote** - 프로덕션 배포 4. **DB 스키마 변경 시** - ALTER TABLE 명령어 별도 실행 필요 (아래 참조) ```bash # ❌ 잘못된 방식 (절대 금지!) ssh server2 "vim /opt/autonet/production/backend/app/main.py" # 서버 직접 수정 scp backend/app/main.py server2:/opt/autonet/production/backend/app/ # 수동 복사 # ✅ 올바른 방식 # 1. 로컬에서 수정 # 2. git add . && git commit -m "변경 내용" && git push staging main # 3. 스테이징 테스트 # 4. ssh server2 "/opt/autonet/scripts/deploy.sh promote" ``` ### Git 저장소 정보 | 항목 | 값 | |------|-----| | 원격 저장소 | `ssh://damon@192.168.0.202/opt/autonet/git/autonet.git` | | 브랜치 | `main` | | 원격 이름 | `staging` | ### CI/CD 배포 명령어 ```bash # ★ 스테이징 배포 (자동) - git push만 하면 됨! git add . git commit -m "변경 내용 설명" git push staging main # → post-receive hook이 자동으로: # 1. /opt/autonet/staging/ 에 소스 체크아웃 # 2. docker compose로 frontend/backend 빌드 및 재시작 # ★ 프로덕션 배포 (수동 promote) ssh server2 "/opt/autonet/scripts/deploy.sh promote" # → staging 소스를 production으로 복사 후 빌드/배포 # 상태 확인 ssh server2 "/opt/autonet/scripts/deploy.sh status" # 롤백 (문제 발생 시) ssh server2 "/opt/autonet/scripts/deploy.sh rollback" ``` ### 수동 배포 (비상시에만 사용) ```bash # 스테이징 수동 빌드 ssh server2 "cd /opt/autonet/staging && docker compose -f docker-compose.staging.yml build --no-cache && docker compose -f docker-compose.staging.yml up -d" # 프로덕션 수동 빌드 ssh server2 "cd /opt/autonet/production && docker compose -f docker-compose.production.yml build --no-cache && docker compose -f docker-compose.production.yml up -d" ``` --- ## Staging vs Production 환경 분리 ### 데이터베이스 분리 | 환경 | DB 이름 | 용도 | |------|---------|------| | **Production** | `autonet` | 실서비스 데이터 | | **Staging** | `autonet_staging` | 테스트용 데이터 (안전한 테스트) | **DB 서버**: PostgreSQL on `192.168.0.201:5432` ### Staging 환경 설정 **Backend .env** (`/opt/autonet/staging/backend/.env`): ```env DB_NAME=autonet_staging # Production은 autonet ``` **docker-compose.staging.yml**: - Frontend: `NEXT_PUBLIC_API_URL=https://staging.autonetsellcar.com` (build arg) - Backend: `.env` 파일을 볼륨 마운트로 연결 ### 소스 코드 동기화 (CI/CD로 자동화됨) **✅ CI/CD 적용 후**: `git push staging main`만 하면 자동으로 스테이징에 배포됩니다. ```bash # 스테이징 배포 (자동) git push staging main # post-receive hook이 자동으로 staging에 체크아웃 및 빌드 # 프로덕션 배포 (promote 명령) ssh server2 "/opt/autonet/scripts/deploy.sh promote" # staging → production 복사 및 빌드 ``` **수동 동기화 (비상시에만)**: ```bash # Production → Staging 소스 동기화 (일반적으로 필요 없음) ssh damon@192.168.0.202 "rsync -av --exclude='node_modules' --exclude='.next' /opt/autonet/production/frontend/ /opt/autonet/staging/frontend/" ``` ### Staging DB 초기 데이터 복사 ```bash # Production → Staging 데이터 복사 (psql 사용) PGPASSWORD='roskfl@1122' pg_dump -h 192.168.0.201 -U admin -d autonet --data-only -t users -t cars -t car_images -t hero_banners | \ PGPASSWORD='roskfl@1122' psql -h 192.168.0.201 -U admin -d autonet_staging ``` ### ⚠️ DB 스키마 변경 시 (중요!) **SQLAlchemy 모델 변경만으로는 DB가 자동 업데이트되지 않습니다!** 코드에서 새 컬럼을 추가하면, 반드시 수동으로 ALTER TABLE을 실행해야 합니다: ```bash # 1. 먼저 Staging DB에 적용하여 테스트 ssh server1 "docker exec -it postgres-primary psql -U admin -d autonet_staging -c \" ALTER TABLE users ADD COLUMN IF NOT EXISTS new_column VARCHAR(255) DEFAULT ''; \"" # 2. 테스트 후 Production DB에 적용 ssh server1 "docker exec -it postgres-primary psql -U admin -d autonet -c \" ALTER TABLE users ADD COLUMN IF NOT EXISTS new_column VARCHAR(255) DEFAULT ''; \"" ``` **DB 스키마 변경 체크리스트**: 1. `backend/app/models/` 에서 모델 수정 2. Staging DB에 ALTER TABLE 실행 3. git push staging main (코드 배포) 4. Staging에서 테스트 5. Production DB에 ALTER TABLE 실행 6. deploy.sh promote (프로덕션 배포) **최근 추가된 컬럼 예시** (2025-01-04): ```sql -- users 테이블 ALTER TABLE users ADD COLUMN IF NOT EXISTS cash_cc_balance FLOAT DEFAULT 0; -- system_settings 테이블 ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS marketing_enabled BOOLEAN DEFAULT false; ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS marketing_telegram_enabled BOOLEAN DEFAULT false; ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS marketing_telegram_bot_token VARCHAR(255) DEFAULT ''; ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS marketing_telegram_channel_id VARCHAR(255) DEFAULT ''; ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS marketing_telegram_message_template TEXT DEFAULT ''; ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS marketing_facebook_enabled BOOLEAN DEFAULT false; ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS marketing_instagram_enabled BOOLEAN DEFAULT false; ALTER TABLE system_settings ADD COLUMN IF NOT EXISTS marketing_auto_post_on_banner BOOLEAN DEFAULT false; ``` --- ## 1. 프로젝트 구조 ``` AutonetSellCar.com/ ├── backend/ # FastAPI 백엔드 │ ├── app/ │ │ ├── api/ # API 라우터 │ │ ├── models/ # SQLAlchemy 모델 │ │ ├── services/ # 비즈니스 로직 │ │ └── database.py # DB 연결 │ ├── uploads/ # 업로드 파일 저장 │ │ └── performance_checks/ # 성능점검표 PDF │ ├── autonet.db # ★ 실제 사용하는 DB (이것만 사용!) │ └── venv/ # Python 가상환경 ├── frontend/ # Next.js 프론트엔드 │ └── src/ │ ├── app/ # 페이지 │ └── lib/api.ts # API 함수 └── restart-dev.bat # 개발 서버 재시작 스크립트 ``` --- ## 2. 중요: DB 파일 관리 ### ★★★ 반드시 확인 ★★★ **실제 사용하는 DB 파일**: `backend/autonet.db` (이것만!) 과거에 테스트용으로 생성된 빈 DB 파일들이 있을 수 있습니다: - `autonet.db` (루트) - 사용하지 않음 - `autonetsellcar.db` - 사용하지 않음 - `car_platform.db` - 사용하지 않음 **이런 파일들이 발견되면 삭제해야 합니다.** ```python # DB 파일 확인 스크립트 from pathlib import Path base = Path(r'D:\Workspace\claudeCode\AutonetSellCar.com') for db in base.rglob('*.db'): if 'venv' not in str(db) and 'node_modules' not in str(db): print(f"{db}: {db.stat().st_size / 1024:.1f} KB") ``` --- ## 3. 성능점검표 PDF 시스템 ### 3.1 PDF 생성 흐름 1. 관리자가 Carmodoo에서 차량 검색 2. 차량 가져오기(import) 시 `check_num` (성능점검번호) 추출 3. `capture_performance_check_pdf()` 함수로 PDF 캡처 시도 4. 성공 시 `car_performance_checks.pdf_path`에 경로 저장 5. 프론트엔드에서 `pdf_path`가 있으면 PDF 보기 버튼 표시 ### 3.2 PDF 버튼이 안 보이는 경우 **원인**: `pdf_path`가 NULL인 경우 **해결 방법**: 1. 관리자 > Hero Banners 페이지에서 "PDF 재시도" 버튼 클릭 2. 또는 API 직접 호출: `POST /api/carmodoo/admin/retry-all-failed-pdfs` ### 3.3 PDF 관련 파일들 - `backend/app/services/pdf_service.py` - PDF 캡처 로직 (3회 자동 재시도) - `backend/app/api/carmodoo.py` - PDF 관련 API 엔드포인트 - `frontend/src/app/cars/[id]/page.tsx` - PDF 보기 버튼 (라인 605 근처) ```javascript // PDF 버튼 표시 조건 (page.tsx) {performanceCheck.data?.pdf_path && ( // PDF 버튼 렌더링 )} ``` --- ## 4. check_num (성능점검번호) 처리 ### 4.1 check_num 추출 위치 Carmodoo HTML에서 `checkNum`은 **주석 처리된 부분**에 있음: ```html ``` ### 4.2 파싱 로직 (carmodoo.py) ```python # 전체 row HTML에서 checkNum 추출 (주석 포함) row_html = etree.tostring(row, encoding='unicode') check_match = re.search(r'checkNum=(\d+)', row_html) if check_match: check_num = check_match.group(1) ``` ### 4.3 check_num 관련 모델/인터페이스 **백엔드** (`carmodoo.py`): ```python class CarmodooSearchResultItem(BaseModel): # ... 기타 필드 ... check_num: Optional[str] = None # 성능점검번호 ``` **프론트엔드** (`api.ts`): ```typescript export interface CarmodooSearchResult { // ... 기타 필드 ... check_num?: string; // 성능점검번호 } ``` --- ## 5. 서버 재시작 관련 ### 5.1 uvicorn 캐시 문제 **증상**: 코드 수정 후에도 API 스키마에 변경사항이 반영되지 않음 **원인**: - `--reload` 옵션이 있어도 일부 변경사항이 적용되지 않는 경우 있음 - `__pycache__` 캐시 문제 **해결 방법**: ```powershell # 1. __pycache__ 삭제 Get-ChildItem -Path 'backend' -Recurse -Directory -Filter '__pycache__' | Remove-Item -Recurse -Force # 2. 서버 완전 재시작 # 방법 A: restart-dev.bat 실행 # 방법 B: 수동으로 uvicorn 프로세스 종료 후 재시작 ``` ### 5.2 포트 확인 ```cmd netstat -ano | findstr :8000 netstat -ano | findstr :3000 ``` --- ## 6. 관리자 기능 ### 6.1 Hero Banners 페이지 (/admin/hero-banners) **차량 검색 필터**: - 제조사, 모델, 등급 - 연식 (From ~ To) - 연료, 배기량, 주행거리 **PDF 관리**: - "PDF 재시도" 버튼 - PDF 없는 모든 차량에 대해 PDF 재생성 ### 6.2 관리자 API 엔드포인트 | 엔드포인트 | 설명 | |-----------|------| | `GET /api/carmodoo/admin/pdf-failures` | PDF 생성 실패 목록 | | `POST /api/carmodoo/admin/retry-all-failed-pdfs` | 전체 PDF 재시도 | | `POST /api/carmodoo/regenerate-pdf/{car_id}` | 특정 차량 PDF 재생성 | --- ## 7. 자주 발생하는 문제와 해결 ### 7.1 "PDF 버튼이 안 보여요" **체크리스트**: 1. 로그인 상태 확인 (로그인 사용자만 PDF 접근 가능) 2. DB에서 해당 차량의 `pdf_path` 확인 ```sql SELECT car_id, check_number, pdf_path FROM car_performance_checks WHERE car_id = ?; ``` 3. `pdf_path`가 NULL이면 → PDF 재생성 필요 4. PDF 파일이 실제로 존재하는지 확인: `backend/uploads/performance_checks/` ### 7.2 "코드 수정했는데 적용이 안 돼요" 1. `__pycache__` 삭제 2. uvicorn 서버 완전 재시작 3. 브라우저 캐시 클리어 (Ctrl+Shift+R) ### 7.3 "검색 결과가 안 나와요" 1. 캐시 만료 확인: `carmodoo_search_cache` 테이블 2. 제조사/모델 코드 확인 (예: 기아=2, K5=38) 3. 연도 범위 확인 --- ## 8. 개발 환경 시작 ```bash # 방법 1: 배치 파일 사용 restart-dev.bat # 방법 2: 수동 시작 # 터미널 1 (백엔드) cd backend venv\Scripts\activate python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 # 터미널 2 (프론트엔드) cd frontend npm run dev ``` --- ## 9. 유용한 디버깅 명령어 ```python # DB 상태 확인 import sqlite3 conn = sqlite3.connect('backend/autonet.db') cursor = conn.cursor() # 성능점검 데이터 확인 cursor.execute('SELECT car_id, check_number, pdf_path FROM car_performance_checks') for row in cursor.fetchall(): print(row) # PDF 파일 존재 확인 from pathlib import Path pdf_dir = Path('backend/uploads/performance_checks') for pdf in pdf_dir.glob('*.pdf'): print(f"{pdf.name}: {pdf.stat().st_size / 1024:.1f} KB") ``` --- ## 10. 환율 시스템 ### 10.1 개요 한국수출입은행 API에서 실시간 환율을 가져와 모든 가격 표시에 적용합니다. - **갱신 주기**: 매일 11:30 AM (한국수출입은행 고시 시간) - **캐싱**: 30분간 캐시 유지 - **폴백**: API 실패 시 기본값 사용 ### 10.2 환율 API 엔드포인트 | 엔드포인트 | 설명 | |-----------|------| | `GET /api/exchange-rate` | 전체 환율 정보 (상세) | | `GET /api/exchange-rate/simple` | 간단한 환율 정보 (USD, EUR, JPY 등) | **응답 예시** (`/api/exchange-rate/simple`): ```json { "USD": {"rate": 1483.4, "symbol": "$", "name": "미국 달러"}, "EUR": {"rate": 1749.74, "symbol": "€", "name": "유로"}, "JPY": {"rate": 9.5001, "symbol": "¥", "name": "일본 옌"} } ``` ### 10.3 프론트엔드 환율 Store **파일**: `frontend/src/lib/exchangeRateStore.ts` ```typescript // 환율 가져오기 const store = useExchangeRateStore.getState(); const usdRate = store.rates.USD?.rate || 1483; // 폴백 값 // KRW → USD 변환 const usdAmount = krwAmount / usdRate; // USD → KRW 변환 const krwAmount = usdAmount * usdRate; ``` **기본값 (API 실패 시)**: | 통화 | rate (1 단위당 KRW) | |------|---------------------| | USD | 1483 | | EUR | 1750 | | JPY | 9.5 | | CNY | 203 | | MNT | 0.43 | | RUB | 14.5 | ### 10.4 환율 적용 파일들 | 파일 | 용도 | |------|------| | `frontend/src/lib/exchangeRateStore.ts` | Zustand 환율 스토어 | | `frontend/src/lib/i18n.ts` | `formatPriceWithCurrency()` 함수 | | `frontend/src/app/exchange-rate/page.tsx` | 환율 정보 페이지 | | `frontend/src/app/cost/page.tsx` | 비용 계산기 | | `frontend/src/components/SearchFilters.tsx` | 검색 필터 가격 표시 | | `frontend/src/app/admin/purchased/page.tsx` | 구매 차량 관리 | ### 10.5 주의사항 **하드코딩된 환율 사용 금지!** ```typescript // 잘못된 예 const usd = krwAmount * 0.00069; // 하드코딩 X const krw = usdAmount * 1333; // 하드코딩 X // 올바른 예 const usdRate = useExchangeRateStore.getState().rates.USD?.rate || 1483; const usd = krwAmount / usdRate; const krw = usdAmount * usdRate; ``` ### 10.6 환율 디버깅 ```bash # API 테스트 curl http://localhost:8000/api/exchange-rate/simple # DB 환율 데이터 확인 sqlite3 backend/autonet.db "SELECT * FROM exchange_rates ORDER BY updated_at DESC LIMIT 5;" ``` --- ## 11. 다국어 번역 시스템 ### 11.1 개요 차량 정보(차량명, 연료, 변속기, 색상 등)를 사용자 선택 언어로 번역합니다. - **지원 언어**: 한국어(ko), 영어(en), 몽골어(mn), 러시아어(ru) - **기본값**: 해당 언어 번역이 없으면 영어(en)로 대체 ### 11.2 번역 관련 파일들 | 파일 | 역할 | |------|------| | `frontend/src/lib/i18n.ts` | 정적 번역 사전 (`CAR_TRANSLATIONS`), `translateCarName()` 함수 | | `frontend/src/lib/useTranslate.ts` | `translate()` 훅 - API + 정적 번역 폴백 | ### 11.3 `CAR_TRANSLATIONS` 구조 ```typescript const CAR_TRANSLATIONS: Record> = { // 연료 타입 '휘발유': { ko: '휘발유', en: 'Gasoline', mn: 'Бензин', ru: 'Бензин' }, '경유': { ko: '경유', en: 'Diesel', mn: 'Дизель', ru: 'Дизель' }, // 변속기 '오토': { ko: '오토', en: 'Auto', mn: 'Авто', ru: 'Авто' }, '수동': { ko: '수동', en: 'Manual', mn: 'Механик', ru: 'Механика' }, // 제조사 'KG모빌리티(쌍용)': { ko: 'KG모빌리티(쌍용)', en: 'KG Mobility (SsangYong)', ... }, // 모델 '렉스턴 스포츠': { ko: '렉스턴 스포츠', en: 'Rexton Sports', ... }, // 색상 '흰색': { ko: '흰색', en: 'White', mn: 'Цагаан', ru: 'Белый' }, }; ``` ### 11.4 번역 함수 사용법 **방법 1: `useTranslate` 훅** (권장) ```tsx import { useTranslate } from '@/lib/useTranslate'; function Component() { const { translate } = useTranslate(); return {translate(car.fuel)}; // '휘발유' → 'Gasoline' } ``` **방법 2: `translateCarName` 함수** (직접 호출) ```tsx import { translateCarName } from '@/lib/i18n'; const translatedFuel = translateCarName(car.fuel, language); // '경유' → 'Diesel' ``` ### 11.5 새 번역 추가하기 `CAR_TRANSLATIONS`에 새 항목 추가: ```typescript '새로운용어': { ko: '새로운용어', en: 'New Term', mn: 'Шинэ нэр', ru: 'Новый термин' }, ``` **주의**: 긴 문자열을 먼저 매칭하기 위해 `SORTED_CAR_KEYS`가 자동으로 정렬됨 ### 11.6 번역이 안 되는 경우 1. **`CAR_TRANSLATIONS`에 해당 용어가 없음** → 새 항목 추가 2. **프론트엔드 리빌드 필요** → `npm run dev` 재시작 또는 `.next` 폴더 삭제 3. **`translate()` 함수 미사용** → 페이지에서 직접 값 출력 중 (수정 필요) 4. **localStorage에 'ko' 언어 저장됨** → 일반 유저는 한국어 선택 불가지만 localStorage에 남아있으면 번역 스킵됨 - `LanguageSelector.tsx`에서 자동으로 영어로 리셋하도록 수정됨 ### 11.7 언어 선택 제한 - **관리자(is_admin=true)**: 한국어, 영어, 몽골어, 러시아어 모두 선택 가능 - **일반 유저**: 영어, 몽골어, 러시아어만 선택 가능 (한국어 숨김) - 파일: `frontend/src/components/LanguageSelector.tsx` --- ## 12. CC (Car Credit) 시스템 ### 12.1 CC란? - 플랫폼 내 가상 화폐 - **차량 추천 서비스**에 사용 (1 CC = N대 추천, 관리자 설정 가능) - 신규 가입 시 1 CC 지급 - **관리자 설정**: `/admin/settings`에서 `Cars per CC` 값으로 1 CC당 추천 대수 조절 가능 (기본값: 3대) ### 12.2 CC 충전 패키지 | 충전 금액 | 받는 CC | 추천 가능 차량 (기본 3대/CC) | 할인율 | |-----------|---------|----------------|--------| | $10 | 10 CC | 30대 | - | | $27 | 30 CC | 90대 | 10% | | $40 | 50 CC | 150대 | 20% | > **참고**: 추천 가능 차량 수는 관리자 설정의 `Cars per CC` 값에 따라 변동됩니다. ### 12.3 결제 수단 | 수단 | 대상 | 처리 방식 | |------|------|-----------| | **Stripe** | 몽골 사용자 | Visa/Mastercard 자동 결제 | | **몽골 파트너 계좌** | 러시아 사용자 | 수동 충전 요청 → 관리자 승인 | ### 12.4 Stripe 설정 **환경변수** (backend/.env): ```env STRIPE_SECRET_KEY=sk_test_... # Stripe 비밀키 STRIPE_PUBLISHABLE_KEY=pk_test_... # Stripe 공개키 STRIPE_WEBHOOK_SECRET=whsec_... # Webhook 시크릿 STRIPE_SUCCESS_URL=https://yourdomain.com/cc/success STRIPE_CANCEL_URL=https://yourdomain.com/cc ``` **API 엔드포인트**: | 엔드포인트 | 설명 | |-----------|------| | `GET /api/cc/packages` | CC 패키지 목록 | | `POST /api/cc/create-checkout-session` | Stripe 결제 세션 생성 | | `POST /api/cc/webhook` | Stripe Webhook 수신 | | `GET /api/cc/checkout-success` | 결제 완료 확인 | | `POST /api/cc/manual-request` | 수동 충전 요청 (러시아용) | **Stripe Webhook 설정**: 1. Stripe Dashboard → Webhooks 2. 엔드포인트 추가: `https://yourdomain.com/api/cc/webhook` 3. 이벤트 선택: `checkout.session.completed`, `checkout.session.expired` ### 12.5 CC vs 차량 열람 > **중요**: CC는 추천 서비스에만 사용됩니다! | 기능 | 필요 조건 | |------|-----------| | 차량 정보 열람 (이미지, 딜러, 성능점검표) | 로그인만 하면 무료 | | 배너 차량 열람 | 비로그인도 무료 | | 차량 추천 서비스 | **CC 필요** (1 CC = N대, 설정에 따름) | --- ## 13. 상세사양조회 시스템 (AUTOBEGINS) ### 13.1 개요 Carmodoo 딜러 포탈의 AUTOBEGINS 서비스를 통해 차량번호 기반 상세사양을 조회합니다. - **데이터 소스**: AUTOBEGINS (api.autobegins.com) via Carmodoo iframe - **조회 방식**: Playwright 브라우저 자동화 - **저장 시점**: 배너 등록(import) 시 자동 저장 ### 13.2 조회 가능 정보 | 카테고리 | 정보 | |----------|------| | 기본 정보 | 제조사, 모델명, 등급, 연식 | | 엔진/구동 | 배기량, 연료, 변속기, 구동방식 | | 성능 | 최대출력, 최대토크, 연비 | | 차체 | 차체형식, 도어수, 승차정원, 전장/전폭/전고/휠베이스 | | 옵션 | 안전옵션, 편의옵션, 외장옵션, 내장옵션 | | 가격 | 출고가, 기본가, 옵션가 | ### 13.3 관련 파일 | 파일 | 역할 | |------|------| | `backend/app/services/spec_service.py` | Playwright 기반 사양 조회 | | `backend/app/models/car_specification.py` | CarSpecification 모델 | | `backend/app/api/carmodoo.py` | `/specifications/{car_number}`, `/car/{car_id}/specifications` API | ### 13.4 딜러 상세설명 (dealer_description) - `cars.dealer_description` 컬럼에 저장 - Carmodoo 상세페이지에서 딜러가 작성한 설명 추출 - 차량 상세 페이지에서 amber 배경으로 표시 (로그인 사용자) **추출 방식 (2024-12-25 개선)**: 1. 검색 결과에서 `car_key` 추출 (`dealerCarviewPopup('...')` 패턴) 2. `dealerCarView.html?key=` URL로 상세 페이지 접근 3. `

상세설명

...
` 파싱 ```python # carmodoo.py - 딜러 설명 추출 (car_key 기반) async def get_car_detail(self, car_no: str, car_key: str = "") -> dict: if car_key: url = f"{CARMODOO_BASE_URL}/car/dealerCarView.html" params = {"key": car_key, "tabStart": "1"} # EUC-KR 디코딩 후 상세설명 추출 #
...
설명
``` **관련 필드**: - `CarmodooSearchResultItem.car_key` - 검색 결과에 포함 - `AdminSearchResultItem.car_key` - 관리자 검색 결과에 포함 - `ImportCarRequest.car_key` - Import 요청에 포함 --- ## 14. Quote Request 시스템 (차량 추천 요청) ### 14.1 개요 사용자가 원하는 차량 조건을 입력하면 관리자가 맞춤 차량을 추천해주는 서비스입니다. - **비용**: 1 CC per request - **응답 시간**: 24시간 이내 추천 - **페이지**: `/vehicle-request` ### 14.2 요청 조건 | 필드 | 필수 | 설명 | |------|------|------| | 제조사 | ★ | 기아, 현대 등 | | 모델 | ★ | K5, 소나타 등 | | 등급 | - | 선택 사항 | | 연식 범위 | - | 2020 ~ 2024 | | 주행거리 | - | 만km 단위 | | 연료 | - | 휘발유, 경유, 하이브리드 등 | | 배기량 | - | 1000cc ~ 5000cc | ### 14.3 CC 결제 흐름 1. 사용자가 조건 입력 2. 제출 시 CC 잔액 확인 (1 CC 필요) 3. 잔액 부족 시 `/cc` 페이지로 안내 4. CC 차감 후 요청 생성 5. `vehicle_requests.cc_paid` 컬럼에 기록 ### 14.4 관련 파일 | 파일 | 역할 | |------|------| | `backend/app/api/vehicle_requests.py` | 요청 생성 API (CC 차감) | | `backend/app/models/vehicle_request.py` | VehicleRequest 모델 (cc_paid 컬럼) | | `frontend/src/app/vehicle-request/page.tsx` | 요청 폼 UI (CC 안내) | --- ## 15. 현지딜러 시스템 ### 15.1 딜러 등급 및 수수료 몽골 마진(5%)에서 딜러 수수료 지급: | 등급 | 조건 | 수수료율 | |------|------|----------| | 일반 (Standard) | 기본 | 3.0% | | 실버 (Silver) | 10건+ 판매 | 3.5% | | 골드 (Gold) | 30건+ 판매 | 4.0% | | 플래티넘 (Platinum) | 100건+ 판매 | 4.5% | ### 15.2 레퍼럴 시스템 - 딜러가 고객에게 추천 코드 제공 - 고객이 차량 구매 시 딜러에게 수수료 지급 - **1단계 직접 추천만 인정** (다단계 아님) --- ## 16. 딜러 설명 번역 시스템 ### 16.1 개요 딜러 설명(dealer_description)을 Azure Translator API를 사용하여 다국어로 번역합니다. - **번역 API**: Microsoft Azure Translator (한국어 → 영어/몽골어/러시아어 직접 지원) - **무료 한도**: 월 200만 글자 - **저장 시점**: Import 시 자동 번역, 관리자가 확인 후 배너/추천 전송 ### 16.2 DB 필드 | 컬럼 | 설명 | |------|------| | `cars.dealer_description` | 한국어 원문 | | `cars.dealer_description_en` | 영어 번역 | | `cars.dealer_description_mn` | 몽골어 번역 | | `cars.dealer_description_ru` | 러시아어 번역 | ### 16.3 환경 변수 ```env # Azure Translator API AZURE_TRANSLATOR_KEY=your_api_key AZURE_TRANSLATOR_REGION=koreacentral ``` ### 16.4 관련 파일 | 파일 | 역할 | |------|------| | `backend/app/services/translation_service.py` | Azure Translator 연동 | | `backend/app/api/carmodoo.py` | 번역 관리 API 엔드포인트 | | `frontend/src/app/admin/dealer-translations/page.tsx` | 관리자 번역 확인/수정 UI | | `frontend/src/app/cars/[id]/page.tsx` | 사용자 페이지 번역 표시 | ### 16.5 관리자 API 엔드포인트 | 엔드포인트 | 설명 | |-----------|------| | `GET /api/carmodoo/car/{car_id}/translations` | 차량 번역 조회 | | `PUT /api/carmodoo/car/{car_id}/translations` | 차량 번역 수정 | | `POST /api/carmodoo/car/{car_id}/translations/regenerate` | 번역 재생성 | | `GET /api/carmodoo/admin/untranslated-cars` | 미번역 차량 목록 | | `POST /api/carmodoo/admin/translate-all-pending` | 일괄 번역 | ### 16.6 번역 흐름 ``` Import 시 자동 번역 (Azure API) ↓ 관리자 확인 (/admin/dealer-translations) ↓ 필요시 수정 또는 재번역 ↓ 배너 등록 / 추천 전송 ↓ 사용자 페이지에서 한국어 원문 + 선택언어 번역 표시 ``` --- ## 17. 변경 이력 | 날짜 | 변경 내용 | |------|----------| | 2025-01-04 | **CI/CD 자동 배포 시스템 구축**: git push → 스테이징 자동 배포, deploy.sh promote → 프로덕션 배포 | | 2025-01-04 | **docker-compose 통합**: 개별 docker run 대신 docker-compose.staging.yml/production.yml 사용 | | 2025-01-04 | **CLAUDE.md CI/CD 워크플로우 문서화**: 서버 직접 수정 금지, DB 스키마 변경 프로세스 추가 | | 2025-01-03 | **Staging DB 분리**: `autonet_staging` DB 생성, Production/Staging 환경 완전 분리 | | 2025-01-03 | **My Vehicles 기능**: 직접 구매 차량을 My Request 페이지에 표시 | | 2024-12-31 | **Admin Settings 기능 추가**: Show Dealer Comment 토글, Korean Domestic + Export Customs 금액 설정 | | 2024-12-31 | Cost 페이지에서 국내비용+수출통관비용 동적 적용 (settings API 연동) | | 2024-12-31 | **Visitor Stats 국가별 통계 강화**: 국기 이모지 추가, 전용 Country Stats 카드 추가 | | 2024-12-31 | Hero Banners API 라우트 순서 수정 (422 에러 해결) | | 2024-12-31 | Banner Toggle 로직 수정 (HeroBanner 테이블 기준으로 변경) | | 2024-12-27 | **딜러 설명 번역 시스템 추가**: Azure Translator API 연동, 한국어→영어/몽골어/러시아어 직접 번역 | | 2024-12-27 | 관리자 번역 관리 페이지 추가 (`/admin/dealer-translations`) | | 2024-12-27 | DB 스키마 확장: `dealer_description_en/mn/ru` 컬럼 추가 | | 2024-12-25 | **딜러 상세설명 추출 방식 개선**: car_key 기반 dealerCarView.html 사용 (기존 carPopView.html 404 문제 해결) | | 2024-12-25 | 검색 결과에 `car_key` 필드 추가 (CarmodooSearchResultItem, AdminSearchResultItem) | | 2024-12-25 | Import 시 car_key 전달하여 딜러 설명 자동 추출 | | 2024-12-25 | 상세사양조회 시스템 추가 (AUTOBEGINS, spec_service.py, CarSpecification 모델) | | 2024-12-25 | 딜러 상세설명 필드 추가 (cars.dealer_description) | | 2024-12-25 | Quote Request 1CC 결제 시스템 추가 (vehicle_requests.cc_paid) | | 2024-12-25 | CC당 추천 대수 관리자 설정 추가 (cars_per_cc, 기본값 3대) | | 2024-12-25 | 차량 검색 시 연료 조건 필터링 버그 수정 | | 2024-12-24 | Stripe 결제 연동 (CC 충전 패키지, Checkout Session, Webhook) | | 2024-12-24 | CC 시스템 변경 (추천 서비스 기반, 차량열람 무료화) | | 2024-12-24 | 언어 자동 리셋 버그 수정 (localStorage 'ko' 문제) | | 2024-12-24 | 다국어 번역 시스템 개선 (연료/변속기/색상/차량명) | | 2024-12-24 | 환율 시스템 동적 적용 (하드코딩 제거, API 연동) | | 2024-12-24 | PDF 재시도 로직 추가 (3회 자동 재시도) | | 2024-12-24 | 빈 DB 파일 정리 | | 2024-12-24 | 관리자 PDF 재시도 UI/API 추가 | | 2024-12-24 | Hero Banners 검색에 주행거리 필터 추가 | --- **이 문서는 새 세션 시작 시 반드시 읽어주세요!**