# AutonetSellCar 트러블슈팅 가이드 이 문서는 배포 및 운영 중 발생한 문제와 해결 방법을 정리합니다. --- ## 2024-12-30: 운영서버 이미지 표시 문제 ### 증상 - 개발서버(192.168.0.204)에서는 이미지가 정상 표시 - 운영서버(192.168.0.202)에서 외부 PC로 접속 시 이미지가 표시되지 않음 - 브라우저 개발자 도구에서 이미지 요청 URL이 `http://localhost:8000/...`으로 되어 있음 ### 원인 분석 #### 1차 원인: 서버2 소스 코드 미동기화 **문제**: 서버2의 프론트엔드 소스 코드가 개발서버와 동기화되지 않아 오래된 코드가 배포됨 | 위치 | 코드 | |------|------| | 서버4 (개발) | `process.env.NEXT_PUBLIC_API_URL \|\| 'http://localhost:8000'` | | 서버2 (운영) | `'http://localhost:8000'` (하드코딩) | **영향받은 파일**: - `/admin/hero-banners/page.tsx` - `/admin/cars/page.tsx` - 기타 관리자 페이지 #### 2차 원인: 차량 상세 페이지 특수 패턴 **문제**: `/cars/[id]/page.tsx`에서 다른 방식으로 localhost가 하드코딩됨 ```typescript // 문제 코드 const getImageUrl = (url: string | undefined): string => { if (!url) return ''; if (url.startsWith('http://') || url.startsWith('https://')) { return url; } // 포트만 동적으로 추출하고 localhost는 하드코딩 const backendPort = process.env.NEXT_PUBLIC_API_URL?.includes('8001') ? 8001 : 8000; return `http://localhost:${backendPort}${url}`; }; ``` ### 해결 방법 #### Step 1: 소스 코드 동기화 ```bash # 서버4 (Windows)에서 실행 scp -r D:/Workspace/claudeCode/AutonetSellCar.com/frontend/src damon@192.168.0.202:/opt/autonet/production/frontend/ ``` #### Step 2: 차량 상세 페이지 수정 ```typescript // 수정된 코드 (/cars/[id]/page.tsx) const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; const getImageUrl = (url: string | undefined): string => { if (!url) return ''; if (url.startsWith('http://') || url.startsWith('https://')) { return url; } return `${API_BASE_URL}${url}`; }; ``` #### Step 3: Docker 이미지 재빌드 ```bash # 서버2에서 실행 cd /opt/autonet/production/frontend # 캐시 없이 재빌드 (중요!) docker build --no-cache -t autonet-frontend-v2 . # 컨테이너 교체 docker stop autonet-frontend docker rm autonet-frontend docker run -d --name autonet-frontend -p 3000:3000 autonet-frontend-v2 ``` #### Step 4: 확인 ```bash # localhost:8000이 없는지 확인 docker exec autonet-frontend sh -c "grep -l 'localhost:8000' /app/.next/static/chunks/app/**/*.js 2>/dev/null || echo 'No localhost:8000 found'" # 올바른 URL이 있는지 확인 docker exec autonet-frontend sh -c "grep -o '192.168.0.202:8000' /app/.next/static/chunks/app/**/*.js 2>/dev/null | wc -l" ``` ### 핵심 개념: Next.js 환경변수 > **중요**: Next.js의 `NEXT_PUBLIC_*` 환경변수는 **빌드 시점**에 JavaScript 번들에 포함됩니다. | 시점 | 설명 | |------|------| | 빌드 시점 | `NEXT_PUBLIC_*` 변수 값이 JS 코드에 직접 삽입됨 | | 런타임 | 이미 삽입된 값이 사용됨 (변경 불가) | 따라서: - `.env.production` 수정 후 반드시 **재빌드** 필요 - Docker 환경에서는 Dockerfile에 `ARG`/`ENV` 설정 필요 ```dockerfile # Dockerfile 예시 (builder 단계) ARG NEXT_PUBLIC_API_URL=http://192.168.0.202:8000 ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} RUN npm run build ``` --- ## 올바른 이미지 URL 패턴 모든 프론트엔드 파일에서 이미지 URL을 생성할 때 다음 패턴을 사용해야 합니다: ```typescript // 올바른 패턴 const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; const getImageUrl = (url: string): string => { if (!url) return ''; if (url.startsWith('http://') || url.startsWith('https://')) { return url; } return `${API_BASE_URL}${url}`; }; ``` **잘못된 패턴 (사용 금지)**: ```typescript // 하드코딩 - 절대 사용 금지! return `http://localhost:8000${url}`; // 포트만 추출 - 사용 금지! const port = process.env.NEXT_PUBLIC_API_URL?.includes('8001') ? 8001 : 8000; return `http://localhost:${port}${url}`; ``` --- ## 디버깅 명령어 모음 ### 프론트엔드 환경변수 확인 ```bash # 컨테이너 환경변수 docker exec autonet-frontend printenv | grep API # .env.production 파일 cat /opt/autonet/production/frontend/.env.production # Dockerfile ARG/ENV 확인 grep -E "(ARG|ENV).*NEXT_PUBLIC" /opt/autonet/production/frontend/Dockerfile ``` ### 빌드된 JS 파일 검사 ```bash # localhost:8000 검색 docker exec autonet-frontend sh -c "grep -r 'localhost:8000' /app/.next/static/chunks/ | head -5" # 올바른 URL 검색 docker exec autonet-frontend sh -c "grep -r '192.168.0.202:8000' /app/.next/static/chunks/ | head -5" ``` ### 이미지 직접 접근 테스트 ```bash # 백엔드에서 이미지 직접 접근 curl -I http://192.168.0.202:8000/uploads/cars/1/image_0.jpg # API 응답에서 이미지 URL 확인 curl -s http://192.168.0.202:8000/api/cars/1 | jq '.images' ``` ### 컨테이너 상태 확인 ```bash docker ps -a | grep autonet docker logs autonet-frontend --tail 20 docker logs autonet-backend --tail 20 ``` --- ## 예방 조치 ### 1. 코드 동기화 자동화 개발 완료 후 운영 서버 배포 시 항상 최신 코드를 동기화: ```bash # 배포 전 동기화 스크립트 scp -r frontend/src damon@192.168.0.202:/opt/autonet/production/frontend/ ``` ### 2. 빌드 전 환경변수 확인 ```bash # 빌드 전 확인 사항 echo "=== .env.production ===" cat /opt/autonet/production/frontend/.env.production echo "=== Dockerfile ENV ===" grep "NEXT_PUBLIC" /opt/autonet/production/frontend/Dockerfile ``` ### 3. 빌드 후 검증 ```bash # 빌드 후 localhost 하드코딩 검사 docker exec autonet-frontend sh -c "grep -r 'localhost:8000' /app/.next/static/chunks/ && echo 'ERROR: localhost found!' || echo 'OK: No localhost hardcoding'" ``` --- ## 체크리스트 배포 시 확인해야 할 항목: - [ ] 서버2 소스 코드가 최신인가? - [ ] `.env.production`에 올바른 URL이 설정되어 있는가? - [ ] Dockerfile에 `ARG`/`ENV`가 설정되어 있는가? - [ ] `--no-cache` 옵션으로 빌드했는가? - [ ] 빌드된 JS에 `localhost:8000`이 없는가? - [ ] 외부 PC에서 이미지가 정상 표시되는가? --- --- ## 2026-01-02: 반복되는 배포 오류 종합 (CRITICAL) ### 문제 1: DB_NAME 오류 (`mongolcar` vs `autonet`) **증상**: - 사이트에 차량/배너 없음 - API가 빈 배열 `[]` 반환 - 로그인은 되지만 데이터 없음 **원인**: - 수동 `docker run` 시 DB_NAME을 `mongolcar`로 잘못 입력 - PostgreSQL에 `mongolcar`와 `autonet` 두 DB가 존재 - `mongolcar`는 비어있고, 실제 데이터는 `autonet`에 있음 **확인 방법**: ```bash # 현재 연결된 DB 확인 ssh server2 "docker exec autonet-backend python -c \"from app.config import get_settings; print('DB_NAME:', get_settings().DB_NAME)\"" # DB별 데이터 확인 ssh server1 "docker exec postgres-primary psql -U admin -d autonet -c 'SELECT COUNT(*) FROM cars;'" ssh server1 "docker exec postgres-primary psql -U admin -d mongolcar -c 'SELECT COUNT(*) FROM cars;'" ``` **해결**: - `DB_NAME=autonet` 확인 후 컨테이너 재시작 - CLAUDE.md의 표준 명령어 복사해서 사용 --- ### 문제 2: 비밀번호 `@` 파싱 오류 **증상**: - `could not translate host name "1122@192.168.0.201"` - 백엔드 컨테이너 계속 재시작 **원인**: - `DB_PASSWORD=roskfl@1122`의 `@`가 URL에서 호스트 구분자로 인식됨 - `config.py`에 `quote_plus()` URL 인코딩이 없거나, Docker 이미지가 오래됨 **확인 방법**: ```bash # config.py에 quote_plus 있는지 확인 ssh server2 "docker exec autonet-backend cat /app/app/config.py | grep quote_plus" ``` **해결**: 1. `config.py`에 `quote_plus()` 적용 확인 2. Docker 이미지 재빌드: `docker build --no-cache -t production-backend ./backend` --- ### 문제 3: 이미지 404 (잘못된 uploads 경로) **증상**: - `/uploads/cars/40/image_0.jpg` → 404 - Hero 배너에 기본 이미지만 표시 **원인**: - Production 컨테이너가 잘못된 경로 마운트: `/home/damon/mongolcar/data/uploads` (비어있음!) - 실제 이미지는 `/opt/autonet/production/backend/uploads`에 있음 **확인 방법**: ```bash # 올바른 경로 (파일 있어야 함) ssh server2 "ls /opt/autonet/production/backend/uploads/cars/ | head -5" # 잘못된 경로 (비어있음!) ssh server2 "ls /home/damon/mongolcar/data/uploads/cars/ 2>/dev/null || echo 'Empty or not exists'" ``` **해결**: - 볼륨 마운트를 `-v /opt/autonet/production/backend/uploads:/app/uploads`로 변경 - CLAUDE.md의 표준 명령어 사용 --- ### 문제 4: Mixed Content (HTTPS/HTTP) **증상**: - 브라우저 콘솔에 Mixed Content 오류 - API 요청 실패 - 사이트에 기본 이미지만 표시 **원인**: - Frontend가 `NEXT_PUBLIC_API_URL=http://192.168.0.202:8000`으로 빌드됨 - HTTPS 페이지에서 HTTP API 호출 시 브라우저가 차단 **확인 방법**: ```bash # 빌드된 JS에서 API URL 확인 ssh server2 "docker exec autonet-frontend grep -r '192.168.0.202:8000' /app/.next/static/chunks/ | head -1" ``` **해결**: ```bash # Frontend 빌드 전 필수! ssh server2 "echo 'NEXT_PUBLIC_API_URL=https://autonetsellcar.com' > /home/damon/mongolcar/frontend/.env.production" ssh server2 "cd /home/damon/mongolcar/frontend && docker build --no-cache -t production-frontend ." ``` --- ### 문제 5: .env 파일이 디렉토리로 생성됨 **증상**: - 로그인 실패 - `(sqlite3.OperationalError) no such column` 에러 - 환경변수 로드 실패 **원인**: - docker-compose 볼륨 마운트 시 `.env` 파일이 없으면 디렉토리로 생성됨 **확인 방법**: ```bash ssh server2 "ls -la /opt/autonet/production/backend/.env" # 'd'로 시작하면 디렉토리 (문제!) ``` **해결**: ```bash # 디렉토리면 삭제 후 파일로 재생성 ssh server2 "rm -rf /opt/autonet/production/backend/.env" ssh server2 "cat > /opt/autonet/production/backend/.env << 'EOF' USE_SQLITE=False DB_HOST=192.168.0.201 DB_PORT=5432 DB_NAME=autonet DB_USER=admin DB_PASSWORD=roskfl@1122 EOF" ``` --- ## 재발 방지 체크리스트 배포 시 반드시 확인: - [ ] DB_NAME이 `autonet`인가? (절대 `mongolcar` 아님!) - [ ] Uploads 경로가 `/opt/autonet/production/backend/uploads`인가? - [ ] Frontend `.env.production`에 `https://autonetsellcar.com`이 설정되어 있는가? - [ ] `--no-cache`로 이미지를 빌드했는가? - [ ] 배포 후 `curl https://autonetsellcar.com/api/hero-banners/`로 데이터 확인했는가? --- ## 변경 이력 | 날짜 | 문제 | 해결 | |------|------|------| | 2026-01-02 | DB_NAME 오류 (mongolcar vs autonet) | DB_NAME=autonet 확인, 표준 명령어 사용 | | 2026-01-02 | 비밀번호 @ 파싱 오류 | quote_plus() URL 인코딩 적용 | | 2026-01-02 | 이미지 404 (잘못된 uploads 경로) | /opt/autonet/production/backend/uploads 사용 | | 2026-01-02 | Mixed Content 오류 | NEXT_PUBLIC_API_URL=https://autonetsellcar.com | | 2024-12-30 | 운영서버 이미지 미표시 (배너) | 소스 코드 동기화 + Docker 재빌드 | | 2024-12-30 | 차량 상세 이미지 미표시 | `getImageUrl()` 함수 수정 |