- Document git hook vs manual docker run conflict issue - Add resolution steps for container name conflicts - Reinforce: always use docker-compose for staging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
38 KiB
AutonetSellCar.com 개발 가이드
이 문서는 Claude Code 세션에서 반드시 읽고 참고해야 하는 중요한 정보입니다.
배포 시 반드시 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)
# 위치: /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)
# 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
⛔ CRITICAL: 배포 전 필수 확인사항 (DO NOT SKIP!)
🚨 Production 환경 고정값 (절대 변경 금지!)
| 항목 | 올바른 값 | 잘못된 예 |
|---|---|---|
| DB_NAME | autonet |
|
| DB_PASSWORD | roskfl@1122 (@ 포함, URL 인코딩 필요) |
- |
| API_URL (Frontend) | https://autonetsellcar.com |
|
| Uploads 경로 | /opt/autonet/production/backend/uploads |
/home/damon/mongolcar/data/uploads |
| 배포 디렉토리 | /opt/autonet/production |
/home/damon/mongolcar |
🚫 절대 하지 말 것
- 수동
docker run에서 환경변수 직접 타이핑 금지 - PRODUCTION_VALUES.md에서 복사만 허용 - DB_NAME 직접 타이핑 금지 - 반드시
autonet복사/붙여넣기 .env파일 덮어쓰기 금지 - 서버의 기존.env보존/home/damon/mongolcar/data/uploads사용 금지 - 비어있는 잘못된 경로- 스테이징 배포 시 수동
docker run금지 - 반드시docker compose사용! (아래 참고)
⚠️ 스테이징 배포 주의사항 (2025-01-05 사고 사례)
문제: 수동 docker run으로 스테이징 배포 시 볼륨 마운트 경로를 잘못 지정하여 이미지가 사라짐
원인: docker-compose.staging.yml은 production uploads를 마운트하지만, 수동 명령어에서 staging uploads를 마운트함
# docker-compose.staging.yml (올바른 설정)
volumes:
- /opt/autonet/production/backend/uploads:/app/uploads # production 폴더 공유!
올바른 스테이징 배포 방법:
# 반드시 docker-compose 사용!
ssh server2 "cd /opt/autonet/staging && docker compose -f docker-compose.staging.yml up -d --build"
# 개별 서비스만 재시작
ssh server2 "cd /opt/autonet/staging && docker compose -f docker-compose.staging.yml up -d --build backend-staging"
ssh server2 "cd /opt/autonet/staging && docker compose -f docker-compose.staging.yml up -d --build frontend-staging"
절대 하지 말 것:
# ❌ 이렇게 하면 안됨! 볼륨 경로가 docker-compose와 달라짐
docker run -v /opt/autonet/staging/backend/uploads:/app/uploads ...
⚠️ 수동 배포와 자동 배포(git hook) 충돌 문제 (2025-01-05 추가 사례)
문제: 수동으로 컨테이너를 만든 후 git push하면 자동 배포가 실패함
원인:
- 수동 배포:
docker run --name autonet-frontend-staging ...→ docker-compose가 관리하지 않음 - 자동 배포:
docker compose down→ 수동 컨테이너는 중지되지 않음 - 자동 배포:
docker compose up→ 컨테이너 이름 충돌로 실패
해결 방법: 수동 컨테이너가 있으면 먼저 제거
# 충돌 시 수동 컨테이너 제거 후 docker-compose 재실행
ssh server2 "docker stop autonet-frontend-staging autonet-backend-staging 2>/dev/null; docker rm autonet-frontend-staging autonet-backend-staging 2>/dev/null"
ssh server2 "cd /opt/autonet/staging && docker compose -f docker-compose.staging.yml up -d"
예방책: 스테이징 배포는 항상 docker-compose만 사용하고, 수동 docker run 절대 금지!
✅ 배포 전 필수 검증 명령어
# 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 실행
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 설정)
ssh server2 "echo 'NEXT_PUBLIC_API_URL=https://autonetsellcar.com' > /home/damon/mongolcar/frontend/.env.production"
Frontend 실행
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 프로세스를 따르세요:
- 로컬에서만 코드 수정 - 서버 파일 직접 수정 금지!
- git push staging main - 스테이징 자동 배포
- 테스트 후 promote - 프로덕션 배포
- DB 스키마 변경 시 - ALTER TABLE 명령어 별도 실행 필요 (아래 참조)
# ❌ 잘못된 방식 (절대 금지!)
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 배포 명령어
# ★ 스테이징 배포 (자동) - 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"
수동 배포 (비상시에만 사용)
# 스테이징 수동 빌드
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):
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만 하면 자동으로 스테이징에 배포됩니다.
# 스테이징 배포 (자동)
git push staging main # post-receive hook이 자동으로 staging에 체크아웃 및 빌드
# 프로덕션 배포 (promote 명령)
ssh server2 "/opt/autonet/scripts/deploy.sh promote" # staging → production 복사 및 빌드
수동 동기화 (비상시에만):
# 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 초기 데이터 복사
# 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을 실행해야 합니다:
# 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 스키마 변경 체크리스트:
backend/app/models/에서 모델 수정- Staging DB에 ALTER TABLE 실행
- git push staging main (코드 배포)
- Staging에서 테스트
- Production DB에 ALTER TABLE 실행
- deploy.sh promote (프로덕션 배포)
최근 추가된 컬럼 예시 (2025-01-04):
-- 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- 사용하지 않음
이런 파일들이 발견되면 삭제해야 합니다.
# 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 생성 흐름
- 관리자가 Carmodoo에서 차량 검색
- 차량 가져오기(import) 시
check_num(성능점검번호) 추출 capture_performance_check_pdf()함수로 PDF 캡처 시도- 성공 시
car_performance_checks.pdf_path에 경로 저장 - 프론트엔드에서
pdf_path가 있으면 PDF 보기 버튼 표시
3.2 PDF 버튼이 안 보이는 경우
원인: pdf_path가 NULL인 경우
해결 방법:
- 관리자 > Hero Banners 페이지에서 "PDF 재시도" 버튼 클릭
- 또는 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 근처)
// PDF 버튼 표시 조건 (page.tsx)
{performanceCheck.data?.pdf_path && (
// PDF 버튼 렌더링
)}
4. check_num (성능점검번호) 처리
4.1 check_num 추출 위치
Carmodoo HTML에서 checkNum은 주석 처리된 부분에 있음:
<!-- ... checkNum=9830018360 ... -->
4.2 파싱 로직 (carmodoo.py)
# 전체 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):
class CarmodooSearchResultItem(BaseModel):
# ... 기타 필드 ...
check_num: Optional[str] = None # 성능점검번호
프론트엔드 (api.ts):
export interface CarmodooSearchResult {
// ... 기타 필드 ...
check_num?: string; // 성능점검번호
}
5. 서버 재시작 관련
5.1 uvicorn 캐시 문제
증상: 코드 수정 후에도 API 스키마에 변경사항이 반영되지 않음
원인:
--reload옵션이 있어도 일부 변경사항이 적용되지 않는 경우 있음__pycache__캐시 문제
해결 방법:
# 1. __pycache__ 삭제
Get-ChildItem -Path 'backend' -Recurse -Directory -Filter '__pycache__' | Remove-Item -Recurse -Force
# 2. 서버 완전 재시작
# 방법 A: restart-dev.bat 실행
# 방법 B: 수동으로 uvicorn 프로세스 종료 후 재시작
5.2 포트 확인
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 버튼이 안 보여요"
체크리스트:
- 로그인 상태 확인 (로그인 사용자만 PDF 접근 가능)
- DB에서 해당 차량의
pdf_path확인SELECT car_id, check_number, pdf_path FROM car_performance_checks WHERE car_id = ?; pdf_path가 NULL이면 → PDF 재생성 필요- PDF 파일이 실제로 존재하는지 확인:
backend/uploads/performance_checks/
7.2 "코드 수정했는데 적용이 안 돼요"
__pycache__삭제- uvicorn 서버 완전 재시작
- 브라우저 캐시 클리어 (Ctrl+Shift+R)
7.3 "검색 결과가 안 나와요"
- 캐시 만료 확인:
carmodoo_search_cache테이블 - 제조사/모델 코드 확인 (예: 기아=2, K5=38)
- 연도 범위 확인
8. 개발 환경 시작
# 방법 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. 유용한 디버깅 명령어
# 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):
{
"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
// 환율 가져오기
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 주의사항
하드코딩된 환율 사용 금지!
// 잘못된 예
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 환율 디버깅
# 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 구조
const CAR_TRANSLATIONS: Record<string, Record<string, string>> = {
// 연료 타입
'휘발유': { 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 훅 (권장)
import { useTranslate } from '@/lib/useTranslate';
function Component() {
const { translate } = useTranslate();
return <span>{translate(car.fuel)}</span>; // '휘발유' → 'Gasoline'
}
방법 2: translateCarName 함수 (직접 호출)
import { translateCarName } from '@/lib/i18n';
const translatedFuel = translateCarName(car.fuel, language); // '경유' → 'Diesel'
11.5 새 번역 추가하기
CAR_TRANSLATIONS에 새 항목 추가:
'새로운용어': { ko: '새로운용어', en: 'New Term', mn: 'Шинэ нэр', ru: 'Новый термин' },
주의: 긴 문자열을 먼저 매칭하기 위해 SORTED_CAR_KEYS가 자동으로 정렬됨
11.6 번역이 안 되는 경우
CAR_TRANSLATIONS에 해당 용어가 없음 → 새 항목 추가- 프론트엔드 리빌드 필요 →
npm run dev재시작 또는.next폴더 삭제 translate()함수 미사용 → 페이지에서 직접 값 출력 중 (수정 필요)- 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):
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 설정:
- Stripe Dashboard → Webhooks
- 엔드포인트 추가:
https://yourdomain.com/api/cc/webhook - 이벤트 선택:
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 개선):
- 검색 결과에서
car_key추출 (dealerCarviewPopup('...')패턴) dealerCarView.html?key=<car_key>URL로 상세 페이지 접근<div class="carViewMemoWrap"><h3>상세설명</h3><div class="memo">...</div></div>파싱
# 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 디코딩 후 상세설명 추출
# <div class="carViewMemoWrap">...<div class="memo">설명</div></div>
관련 필드:
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 결제 흐름
- 사용자가 조건 입력
- 제출 시 CC 잔액 확인 (1 CC 필요)
- 잔액 부족 시
/cc페이지로 안내 - CC 차감 후 요청 생성
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 환경 변수
# 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 검색에 주행거리 필터 추가 |
이 문서는 새 세션 시작 시 반드시 읽어주세요!