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