- Frontend: Next.js 14 with TypeScript - Backend: FastAPI with SQLAlchemy - Agent: Carmodoo sync agent - Deployment: Docker Compose based staging/production setup - Scripts: Automated deployment with rollback support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
331 lines
7.9 KiB
Markdown
331 lines
7.9 KiB
Markdown
# 사이트 배포 가이드
|
|
|
|
## 개요
|
|
|
|
이 문서는 Next.js Frontend + FastAPI Backend 사이트를 Ubuntu 서버에 배포하는 전체 과정을 설명합니다.
|
|
|
|
---
|
|
|
|
## 배포 순서도
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ 사이트 배포 전체 흐름 │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
1. 사전 준비
|
|
│
|
|
├─→ DNS 설정 (도메인 → 공인 IP)
|
|
│ ├─ example.com → 59.14.158.123
|
|
│ ├─ www.example.com → 59.14.158.123
|
|
│ └─ api.example.com → 59.14.158.123 ★ 중요: API 서브도메인 필수
|
|
│
|
|
├─→ 공유기 포트포워딩
|
|
│ ├─ 80 → 192.168.0.201:80 (NPM)
|
|
│ └─ 443 → 192.168.0.201:443 (NPM)
|
|
│
|
|
└─→ 서버 준비
|
|
├─ Docker 설치
|
|
├─ Node.js 설치
|
|
└─ Python 설치
|
|
|
|
2. 소스코드 전송
|
|
│
|
|
├─→ 압축 (node_modules 제외)
|
|
│ └─ 7z a -tzip project.zip frontend backend -xr!node_modules
|
|
│
|
|
├─→ 전송 (내부망 경유 권장)
|
|
│ └─ scp project.zip damon@192.168.0.203:~/sites/project/
|
|
│
|
|
└─→ 압축 해제
|
|
└─ unzip project.zip
|
|
|
|
3. Backend 배포
|
|
│
|
|
├─→ 가상환경 생성
|
|
│ └─ python3 -m venv venv && source venv/bin/activate
|
|
│
|
|
├─→ 의존성 설치
|
|
│ └─ pip install -r requirements.txt
|
|
│
|
|
├─→ ★★★ CORS 설정 수정 ★★★
|
|
│ └─ config.py의 CORS_ORIGINS에 도메인 추가
|
|
│ - http://example.com
|
|
│ - https://example.com
|
|
│ - http://www.example.com
|
|
│ - https://www.example.com
|
|
│
|
|
├─→ Admin 계정 생성/리셋
|
|
│ └─ python init_admin.py 또는 reset_pw.py 실행
|
|
│
|
|
└─→ 서버 실행
|
|
└─ nohup uvicorn app.main:app --host 0.0.0.0 --port 8001 > ~/logs/backend.log 2>&1 &
|
|
|
|
4. Frontend 배포
|
|
│
|
|
├─→ 의존성 설치
|
|
│ └─ npm install
|
|
│
|
|
├─→ ★★★ 환경변수 설정 ★★★
|
|
│ └─ echo "NEXT_PUBLIC_API_URL=http://api.example.com/api" > .env.local
|
|
│
|
|
├─→ 빌드
|
|
│ └─ npm run build
|
|
│
|
|
└─→ 서버 실행
|
|
└─ nohup sh -c 'PORT=3001 npm start' > ~/logs/frontend.log 2>&1 &
|
|
|
|
5. NPM (Nginx Proxy Manager) 설정
|
|
│
|
|
├─→ Frontend Proxy Host
|
|
│ ├─ Domain: example.com, www.example.com
|
|
│ ├─ Forward: 192.168.0.203:3001
|
|
│ └─ SSL: Let's Encrypt
|
|
│
|
|
└─→ ★★★ API Proxy Host ★★★
|
|
├─ Domain: api.example.com
|
|
├─ Forward: 192.168.0.203:8001
|
|
└─ SSL: Let's Encrypt
|
|
|
|
6. 테스트
|
|
│
|
|
├─→ 사이트 접속: http://example.com
|
|
├─→ API 접속: http://api.example.com/api/health
|
|
└─→ Admin 로그인: http://example.com/admin/login
|
|
```
|
|
|
|
---
|
|
|
|
## 핵심 체크리스트
|
|
|
|
### 배포 전 필수 확인 사항
|
|
|
|
- [ ] DNS에 api 서브도메인 A 레코드 추가했는가?
|
|
- [ ] NPM에 api.도메인 프록시 설정했는가?
|
|
- [ ] Backend CORS_ORIGINS에 도메인 추가했는가?
|
|
- [ ] Frontend .env.local에 API URL 설정했는가?
|
|
- [ ] Admin 계정이 DB에 존재하는가?
|
|
|
|
---
|
|
|
|
## 자주 발생하는 문제와 해결법
|
|
|
|
### 1. 사이트 로딩만 계속됨 (뱅글뱅글)
|
|
|
|
**원인**: Frontend가 API에 연결하지 못함
|
|
|
|
**확인 순서**:
|
|
```bash
|
|
# 1. Backend 실행 확인
|
|
ps aux | grep uvicorn
|
|
|
|
# 2. API 직접 테스트
|
|
curl http://api.example.com/api/health
|
|
|
|
# 3. .env.local 확인
|
|
cat ~/sites/project/frontend/.env.local
|
|
|
|
# 4. 빌드에 환경변수 적용 확인
|
|
grep -r "api.example" ~/sites/project/frontend/.next/ | head -3
|
|
```
|
|
|
|
**해결**:
|
|
```bash
|
|
# .env.local 설정 후 재빌드
|
|
echo "NEXT_PUBLIC_API_URL=http://api.example.com/api" > .env.local
|
|
rm -rf .next
|
|
npm run build
|
|
```
|
|
|
|
---
|
|
|
|
### 2. CORS 에러
|
|
|
|
**원인**: Backend에서 Frontend 도메인을 허용하지 않음
|
|
|
|
**증상**: 브라우저 Network 탭에서 "CORS error" 표시
|
|
|
|
**확인**:
|
|
```bash
|
|
cat ~/sites/project/backend/app/core/config.py | grep CORS
|
|
```
|
|
|
|
**해결**: config.py의 CORS_ORIGINS에 도메인 추가
|
|
```python
|
|
CORS_ORIGINS: list = [
|
|
"http://localhost:3000",
|
|
"http://example.com",
|
|
"https://example.com",
|
|
"http://www.example.com",
|
|
"https://www.example.com"
|
|
]
|
|
```
|
|
|
|
Backend 재시작:
|
|
```bash
|
|
pkill -f uvicorn
|
|
nohup uvicorn app.main:app --host 0.0.0.0 --port 8001 > ~/logs/backend.log 2>&1 &
|
|
```
|
|
|
|
---
|
|
|
|
### 3. 로그인 안 됨
|
|
|
|
**원인**: DB에 admin 계정이 없거나 비밀번호가 다름
|
|
|
|
**확인**:
|
|
```bash
|
|
# API 직접 테스트
|
|
curl -X POST "http://api.example.com/api/auth/login" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"username":"admin","password":"비밀번호"}'
|
|
```
|
|
|
|
**해결**: 비밀번호 리셋 스크립트 실행
|
|
```bash
|
|
cd ~/sites/project/backend
|
|
source venv/bin/activate
|
|
cat > reset_pw.py << 'EOF'
|
|
from app.core.database import SessionLocal
|
|
from app.models.admin import Admin
|
|
from app.core.security import get_password_hash
|
|
db = SessionLocal()
|
|
admin = db.query(Admin).filter(Admin.username == 'admin').first()
|
|
if admin:
|
|
admin.password_hash = get_password_hash('새비밀번호')
|
|
db.commit()
|
|
print('Password reset')
|
|
else:
|
|
print('Admin not found')
|
|
db.close()
|
|
EOF
|
|
python reset_pw.py
|
|
```
|
|
|
|
---
|
|
|
|
### 4. 포트 충돌 (EADDRINUSE)
|
|
|
|
**원인**: 기존 프로세스가 포트를 점유 중
|
|
|
|
**확인**:
|
|
```bash
|
|
sudo netstat -tlnp | grep 3001
|
|
sudo netstat -tlnp | grep 8001
|
|
```
|
|
|
|
**해결**:
|
|
```bash
|
|
# PID 확인 후 종료
|
|
sudo kill -9 <PID>
|
|
|
|
# 또는 프로세스 이름으로 종료
|
|
pkill -f "npm start"
|
|
pkill -f uvicorn
|
|
```
|
|
|
|
---
|
|
|
|
### 5. 500 Internal Server Error (정적 파일)
|
|
|
|
**원인**: 빌드 파일 손상 또는 캐시 문제
|
|
|
|
**해결**:
|
|
```bash
|
|
cd ~/sites/project/frontend
|
|
pkill -f "npm start"
|
|
rm -rf .next
|
|
rm -rf node_modules/.cache
|
|
npm run build
|
|
nohup sh -c 'PORT=3001 npm start' > ~/logs/frontend.log 2>&1 &
|
|
```
|
|
|
|
---
|
|
|
|
## 유용한 명령어 모음
|
|
|
|
### 프로세스 관리
|
|
|
|
```bash
|
|
# 프로세스 확인
|
|
ps aux | grep -E "npm|uvicorn|node"
|
|
|
|
# 포트 사용 확인
|
|
sudo netstat -tlnp | grep -E "3001|8001"
|
|
|
|
# 로그 확인
|
|
tail -f ~/logs/frontend.log
|
|
tail -f ~/logs/backend.log
|
|
```
|
|
|
|
### 서비스 재시작
|
|
|
|
```bash
|
|
# Frontend 재시작
|
|
pkill -f "npm start"
|
|
cd ~/sites/project/frontend
|
|
nohup sh -c 'PORT=3001 npm start' > ~/logs/frontend.log 2>&1 &
|
|
|
|
# Backend 재시작
|
|
pkill -f uvicorn
|
|
cd ~/sites/project/backend
|
|
source venv/bin/activate
|
|
nohup uvicorn app.main:app --host 0.0.0.0 --port 8001 > ~/logs/backend.log 2>&1 &
|
|
```
|
|
|
|
### API 테스트
|
|
|
|
```bash
|
|
# Health check
|
|
curl http://api.example.com/api/health
|
|
|
|
# 로그인 테스트
|
|
curl -X POST "http://api.example.com/api/auth/login" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"username":"admin","password":"password"}'
|
|
```
|
|
|
|
---
|
|
|
|
## 서버 정보
|
|
|
|
### 네트워크 구성
|
|
|
|
| 서버 | 내부 IP | 역할 |
|
|
|------|---------|------|
|
|
| Server1 | 192.168.0.201 | NPM, PostgreSQL, Redis, 모니터링 |
|
|
| Server2 | 192.168.0.202 | MongolCar (autonetsellcar.com) |
|
|
| Server3 | 192.168.0.203 | Grantech, Cylinx |
|
|
| Server4 | 192.168.0.204 | Windows PC (파일 전송 중계) |
|
|
|
|
### 외부 접속
|
|
|
|
| 포트 | 용도 |
|
|
|------|------|
|
|
| 80, 443 | NPM (웹 서비스) |
|
|
| 81 | NPM 관리 페이지 |
|
|
| 201, 202, 203 | SSH (각 서버) |
|
|
|
|
### 도메인
|
|
|
|
| 도메인 | 서비스 |
|
|
|--------|--------|
|
|
| autonetsellcar.com | MongolCar |
|
|
| grantech.kr | Grantech |
|
|
| api.grantech.kr | Grantech API |
|
|
| cylinx.kr | Cylinx (예정) |
|
|
|
|
---
|
|
|
|
## 버전 정보
|
|
|
|
- Node.js: 20.x
|
|
- Python: 3.10
|
|
- Next.js: 16.x
|
|
- FastAPI: Latest
|
|
- Ubuntu: 22.04
|
|
|
|
---
|
|
|
|
*최종 업데이트: 2025-12-05*
|