chore: [deploy] standalone 모드 빌드/배포 전환
- next.config.ts에 output: 'standalone' 추가 - deploy.sh를 standalone 기반으로 개편 (PM2 → node server.js) - 서버 빌드 제거, 로컬 빌드 후 rsync 배포 방식
This commit is contained in:
117
deploy.sh
117
deploy.sh
@@ -1,8 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# SAM React 배포 스크립트
|
# SAM React 배포 스크립트 (standalone 모드)
|
||||||
# 사용법: ./deploy.sh [dev|prod]
|
# 사용법: ./deploy.sh [dev|prod]
|
||||||
#
|
#
|
||||||
|
# standalone 빌드: node_modules 없이 server.js 단독 실행 가능
|
||||||
|
# 서버 메모리 절약 + 빌드 시간 로컬 활용
|
||||||
|
#
|
||||||
|
|
||||||
set -e # 에러 발생 시 중단
|
set -e # 에러 발생 시 중단
|
||||||
|
|
||||||
@@ -10,25 +13,24 @@ set -e # 에러 발생 시 중단
|
|||||||
# 설정
|
# 설정
|
||||||
# ===========================================
|
# ===========================================
|
||||||
ENV="${1:-dev}"
|
ENV="${1:-dev}"
|
||||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
BUILD_FILE="next-standalone.tar.gz"
|
||||||
BUILD_FILE="next-build.tar.gz"
|
|
||||||
|
|
||||||
# 개발 서버 설정
|
# 개발 서버 설정
|
||||||
DEV_SSH="hskwon@114.203.209.83"
|
DEV_SSH="hskwon@114.203.209.83"
|
||||||
DEV_PATH="/home/webservice/react"
|
DEV_PATH="/home/webservice/react"
|
||||||
DEV_PM2="sam-react"
|
DEV_PORT="3001"
|
||||||
|
|
||||||
# 운영 서버 설정 (추후 설정)
|
# 운영 서버 설정 (추후 설정)
|
||||||
# PROD_SSH="user@prod-server"
|
# PROD_SSH="user@prod-server"
|
||||||
# PROD_PATH="/var/www/react"
|
# PROD_PATH="/var/www/react"
|
||||||
# PROD_PM2="sam-react-prod"
|
# PROD_PORT="3000"
|
||||||
|
|
||||||
# 환경별 설정 선택
|
# 환경별 설정 선택
|
||||||
case $ENV in
|
case $ENV in
|
||||||
dev)
|
dev)
|
||||||
SSH_TARGET=$DEV_SSH
|
SSH_TARGET=$DEV_SSH
|
||||||
REMOTE_PATH=$DEV_PATH
|
REMOTE_PATH=$DEV_PATH
|
||||||
PM2_APP=$DEV_PM2
|
APP_PORT=$DEV_PORT
|
||||||
;;
|
;;
|
||||||
prod)
|
prod)
|
||||||
echo "❌ 운영 환경은 아직 설정되지 않았습니다."
|
echo "❌ 운영 환경은 아직 설정되지 않았습니다."
|
||||||
@@ -47,7 +49,7 @@ esac
|
|||||||
log() {
|
log() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "🚀 $1"
|
echo "$1"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,16 +62,16 @@ error() {
|
|||||||
# ===========================================
|
# ===========================================
|
||||||
# 1. 빌드
|
# 1. 빌드
|
||||||
# ===========================================
|
# ===========================================
|
||||||
log "Step 1/5: 빌드 시작"
|
log "Step 1/5: 로컬 빌드"
|
||||||
|
|
||||||
# .env.local 백업
|
# .env.local 백업 (.env.production 으로 빌드하기 위해)
|
||||||
if [ -f .env.local ]; then
|
if [ -f .env.local ]; then
|
||||||
echo "📦 .env.local 백업..."
|
echo " .env.local 백업..."
|
||||||
mv .env.local .env.local.bak
|
mv .env.local .env.local.bak
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 빌드 실행
|
# 빌드 실행
|
||||||
echo "🔨 npm run build..."
|
echo " npm run build..."
|
||||||
npm run build || {
|
npm run build || {
|
||||||
# 빌드 실패 시 .env.local 복원
|
# 빌드 실패 시 .env.local 복원
|
||||||
if [ -f .env.local.bak ]; then
|
if [ -f .env.local.bak ]; then
|
||||||
@@ -80,68 +82,107 @@ npm run build || {
|
|||||||
|
|
||||||
# .env.local 복원
|
# .env.local 복원
|
||||||
if [ -f .env.local.bak ]; then
|
if [ -f .env.local.bak ]; then
|
||||||
echo "📦 .env.local 복원..."
|
echo " .env.local 복원..."
|
||||||
mv .env.local.bak .env.local
|
mv .env.local.bak .env.local
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✅ 빌드 완료"
|
# standalone 빌드 확인
|
||||||
|
if [ ! -f .next/standalone/server.js ]; then
|
||||||
|
error "standalone 빌드 결과물이 없습니다. next.config.ts에 output: 'standalone' 설정을 확인하세요."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " 빌드 완료"
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# 2. 압축
|
# 2. 압축 (standalone + static + public)
|
||||||
# ===========================================
|
# ===========================================
|
||||||
log "Step 2/5: 압축 시작"
|
log "Step 2/5: 압축"
|
||||||
|
|
||||||
# 기존 압축 파일 삭제
|
|
||||||
rm -f $BUILD_FILE
|
rm -f $BUILD_FILE
|
||||||
|
|
||||||
# .next 폴더 압축 (캐시 제외)
|
# standalone 결과물 + static 파일 + public 폴더 압축
|
||||||
echo "📦 .next 폴더 압축 중..."
|
echo " standalone 빌드 압축 중..."
|
||||||
COPYFILE_DISABLE=1 tar --exclude='.next/cache' -czf $BUILD_FILE .next
|
COPYFILE_DISABLE=1 tar -czf $BUILD_FILE \
|
||||||
|
.next/standalone \
|
||||||
|
.next/static \
|
||||||
|
public
|
||||||
|
|
||||||
# 파일 크기 확인
|
|
||||||
FILE_SIZE=$(ls -lh $BUILD_FILE | awk '{print $5}')
|
FILE_SIZE=$(ls -lh $BUILD_FILE | awk '{print $5}')
|
||||||
echo "✅ 압축 완료: $BUILD_FILE ($FILE_SIZE)"
|
echo " 압축 완료: $BUILD_FILE ($FILE_SIZE)"
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# 3. 업로드
|
# 3. 업로드
|
||||||
# ===========================================
|
# ===========================================
|
||||||
log "Step 3/5: 서버 업로드"
|
log "Step 3/5: 서버 업로드"
|
||||||
|
|
||||||
echo "📤 $SSH_TARGET:$REMOTE_PATH 로 업로드 중..."
|
echo " $SSH_TARGET:$REMOTE_PATH 로 업로드 중..."
|
||||||
scp $BUILD_FILE $SSH_TARGET:$REMOTE_PATH/
|
scp $BUILD_FILE $SSH_TARGET:$REMOTE_PATH/
|
||||||
|
|
||||||
echo "✅ 업로드 완료"
|
echo " 업로드 완료"
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# 4. 원격 배포 실행
|
# 4. 원격 배포 실행
|
||||||
# ===========================================
|
# ===========================================
|
||||||
log "Step 4/5: 원격 배포 실행"
|
log "Step 4/5: 서버 배포"
|
||||||
|
|
||||||
echo "🔧 서버에서 배포 스크립트 실행 중..."
|
echo " 서버에서 배포 실행 중..."
|
||||||
ssh $SSH_TARGET << EOF
|
ssh $SSH_TARGET << REMOTE_EOF
|
||||||
|
set -e
|
||||||
cd $REMOTE_PATH
|
cd $REMOTE_PATH
|
||||||
|
|
||||||
echo "🗑️ 기존 .next 폴더 삭제..."
|
echo " 기존 프로세스 종료..."
|
||||||
rm -rf .next
|
lsof -ti:$APP_PORT | xargs kill 2>/dev/null || true
|
||||||
|
sleep 2
|
||||||
|
|
||||||
echo "📦 압축 해제 중..."
|
echo " 기존 .next 백업..."
|
||||||
|
rm -rf .next.bak
|
||||||
|
mv .next .next.bak 2>/dev/null || true
|
||||||
|
|
||||||
|
echo " 압축 해제..."
|
||||||
tar xzf $BUILD_FILE
|
tar xzf $BUILD_FILE
|
||||||
|
|
||||||
echo "🔄 PM2 재시작..."
|
echo " standalone 배치..."
|
||||||
pm2 restart $PM2_APP
|
# static 파일을 standalone 안으로 복사
|
||||||
|
cp -r .next/static .next/standalone/.next/static
|
||||||
|
|
||||||
echo "🧹 압축 파일 정리..."
|
# public 폴더를 standalone 안으로 복사
|
||||||
|
cp -r public .next/standalone/public
|
||||||
|
|
||||||
|
# .env.production 복사 (서버 환경변수)
|
||||||
|
cp .env.production .next/standalone/.env.production 2>/dev/null || true
|
||||||
|
|
||||||
|
echo " 앱 시작..."
|
||||||
|
cd .next/standalone
|
||||||
|
PORT=$APP_PORT HOSTNAME=0.0.0.0 nohup node server.js > /tmp/sam-react.log 2>&1 &
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 실행 확인
|
||||||
|
if lsof -ti:$APP_PORT > /dev/null 2>&1; then
|
||||||
|
echo " 포트 $APP_PORT 에서 정상 실행 중"
|
||||||
|
else
|
||||||
|
echo " 앱 시작 실패! 로그:"
|
||||||
|
tail -20 /tmp/sam-react.log
|
||||||
|
# 롤백
|
||||||
|
echo " 롤백 중..."
|
||||||
|
cd $REMOTE_PATH
|
||||||
|
rm -rf .next
|
||||||
|
mv .next.bak .next 2>/dev/null || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 정리
|
||||||
|
cd $REMOTE_PATH
|
||||||
rm -f $BUILD_FILE
|
rm -f $BUILD_FILE
|
||||||
|
rm -rf .next.bak
|
||||||
|
|
||||||
echo "✅ 서버 배포 완료"
|
echo " 서버 배포 완료"
|
||||||
EOF
|
REMOTE_EOF
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
# 5. 정리
|
# 5. 정리
|
||||||
# ===========================================
|
# ===========================================
|
||||||
log "Step 5/5: 로컬 정리"
|
log "Step 5/5: 로컬 정리"
|
||||||
|
|
||||||
echo "🧹 로컬 압축 파일 삭제..."
|
|
||||||
rm -f $BUILD_FILE
|
rm -f $BUILD_FILE
|
||||||
|
|
||||||
# ===========================================
|
# ===========================================
|
||||||
@@ -149,10 +190,12 @@ rm -f $BUILD_FILE
|
|||||||
# ===========================================
|
# ===========================================
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "🎉 배포 완료!"
|
echo "배포 완료!"
|
||||||
echo "=========================================="
|
echo "=========================================="
|
||||||
echo "환경: $ENV"
|
echo "환경: $ENV"
|
||||||
echo "서버: $SSH_TARGET"
|
echo "서버: $SSH_TARGET"
|
||||||
echo "경로: $REMOTE_PATH"
|
echo "경로: $REMOTE_PATH"
|
||||||
|
echo "포트: $APP_PORT"
|
||||||
echo "시간: $(date '+%Y-%m-%d %H:%M:%S')"
|
echo "시간: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
echo "=========================================="
|
echo "로그: ssh $SSH_TARGET 'tail -f /tmp/sam-react.log'"
|
||||||
|
echo "=========================================="
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import createNextIntlPlugin from 'next-intl/plugin';
|
|||||||
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
|
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
|
output: 'standalone', // 로컬 빌드 → 서버 배포용 (node_modules 없이 실행 가능)
|
||||||
reactStrictMode: false, // 🧪 TEST: Strict Mode 비활성화로 중복 요청 테스트
|
reactStrictMode: false, // 🧪 TEST: Strict Mode 비활성화로 중복 요청 테스트
|
||||||
turbopack: {}, // ✅ CRITICAL: Next.js 15 + next-intl compatibility
|
turbopack: {}, // ✅ CRITICAL: Next.js 15 + next-intl compatibility
|
||||||
serverExternalPackages: ['puppeteer'], // PDF 생성용 - Webpack 번들 제외
|
serverExternalPackages: ['puppeteer'], // PDF 생성용 - Webpack 번들 제외
|
||||||
|
|||||||
Reference in New Issue
Block a user