[2026] Node.js 배포 가이드 | PM2, Docker, AWS, Nginx

[2026] Node.js 배포 가이드 | PM2, Docker, AWS, Nginx

이 글의 핵심

Node.js 배포 가이드: PM2, Docker, AWS, Nginx. PM2 (Process Manager)·Docker.

들어가며

배포 체크리스트

로컬에서 node app.js로 돌리던 것을 프로세스 관리(PM2)·리버스 프록시(Nginx)·컨테이너(Docker)까지 올리면, 재시작·로그·환경 분리·무중단 배포 같은 운영 요구를 맞출 수 있습니다. 아래 체크리스트는 “코드만 올리는 것”이 아니라 실행 환경까지 포함했을 때의 최소 점검 항목입니다. 배포 전에는 Node.js 테스트(Jest 등)로 회귀를 막고, GitHub Actions CI/CD로 빌드·테스트를 자동화하는 흐름이 흔합니다. 로컬·스테이징은 Docker Compose, 오케스트레이션 입문은 minikube, C++·네이티브 쪽은 C++ Docker·배포 이미지·C++ GitHub Actions와 같은 언어 무관 패턴을 비교해 보세요. 배포 전 확인사항:

  • 환경 변수 설정
  • 프로덕션 의존성만 설치
  • 에러 로깅 설정
  • 보안 설정 (Helmet, CORS)
  • 데이터베이스 마이그레이션
  • 정적 파일 빌드
  • 테스트 통과
  • 성능 테스트 배포 방식:
  • 전통적 방식: VPS, PM2, Nginx
  • 컨테이너: Docker, Kubernetes
  • 서버리스: AWS Lambda, Vercel
  • PaaS: Heroku, Railway, Render

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

1. PM2 (Process Manager)

설치

# 전역 설치
npm install -g pm2

기본 사용법

아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 앱 시작
pm2 start app.js
# 이름 지정
pm2 start app.js --name "my-app"
# 환경 변수 설정
pm2 start app.js --name "my-app" --env production
# Watch 모드 (파일 변경 시 재시작)
pm2 start app.js --watch
# 인터프리터 지정
pm2 start app.js --interpreter node

클러스터 모드

아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# 클러스터 모드 (멀티 코어 활용)
pm2 start app.js -i max  # CPU 코어 수만큼
# 특정 개수
pm2 start app.js -i 4
# 무중단 재시작
pm2 reload my-app

PM2 명령어

다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 상태 확인
pm2 status
pm2 list
# 로그 확인
pm2 logs
pm2 logs my-app
pm2 logs --lines 100
# 모니터링
pm2 monit
# 재시작
pm2 restart my-app
pm2 restart all
# 중지
pm2 stop my-app
pm2 stop all
# 삭제
pm2 delete my-app
pm2 delete all
# 정보
pm2 info my-app
# 저장 (현재 프로세스 목록)
pm2 save
# 부팅 시 자동 시작
pm2 startup
pm2 save

ecosystem.config.js

다음은 javascript를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ecosystem.config.js
module.exports = {
    apps: [{
        name: 'my-app',
        script: './app.js',
        instances: 'max',
        exec_mode: 'cluster',
        env: {
            NODE_ENV: 'development',
            PORT: 3000
        },
        env_production: {
            NODE_ENV: 'production',
            PORT: 8080
        },
        error_file: './logs/err.log',
        out_file: './logs/out.log',
        log_date_format: 'YYYY-MM-DD HH:mm:ss',
        merge_logs: true,
        max_memory_restart: '500M',
        watch: false,
        ignore_watch: ['node_modules', 'logs'],
        max_restarts: 10,
        min_uptime: '10s'
    }]
};

사용: 아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# 시작
pm2 start ecosystem.config.js
# 프로덕션 환경
pm2 start ecosystem.config.js --env production
# 재시작
pm2 restart ecosystem.config.js

2. Docker

Dockerfile

다음은 dockerfile를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# Dockerfile
FROM node:20-alpine
# 작업 디렉토리
WORKDIR /app
# 의존성 파일 복사
COPY package*.json ./
# 의존성 설치
RUN npm ci --only=production
# 소스 코드 복사
COPY . .
# 포트 노출
EXPOSE 3000
# 헬스체크
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD node healthcheck.js
# 앱 시작
CMD ["node", "app.js"]

.dockerignore

아래 코드는 code를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

node_modules
npm-debug.log
.env
.git
.gitignore
README.md
.vscode
coverage
.DS_Store

Docker 명령어

다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 이미지 빌드
docker build -t my-app:1.0.0 .
# 컨테이너 실행
docker run -d \
    --name my-app \
    -p 3000:3000 \
    -e NODE_ENV=production \
    -e PORT=3000 \
    my-app:1.0.0
# 로그 확인
docker logs my-app
docker logs -f my-app  # 실시간
# 컨테이너 중지/시작
docker stop my-app
docker start my-app
# 컨테이너 재시작
docker restart my-app
# 컨테이너 삭제
docker rm my-app
# 이미지 삭제
docker rmi my-app:1.0.0

Docker Compose

다음은 yaml를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
      - MONGODB_URI=mongodb://mongo:27017/mydb
    depends_on:
      - mongo
    restart: unless-stopped
    volumes:
      - ./logs:/app/logs
  
  mongo:
    image: mongo:7
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db
    restart: unless-stopped
  
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - app
    restart: unless-stopped
volumes:
  mongo-data:

실행: 아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 시작
docker-compose up -d
# 로그
docker-compose logs -f
# 중지
docker-compose down
# 재시작
docker-compose restart
# 빌드 후 시작
docker-compose up -d --build

3. Nginx 리버스 프록시

설치 (Ubuntu)

sudo apt update
sudo apt install nginx

기본 설정

다음은 nginx를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

활성화:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

SSL (Let’s Encrypt)

아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# Certbot 설치
sudo apt install certbot python3-certbot-nginx
# SSL 인증서 발급
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# 자동 갱신 테스트
sudo certbot renew --dry-run

SSL 설정: 다음은 nginx를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

server {
    listen 443 ssl http2;
    server_name yourdomain.com;
    
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    location / {
        proxy_pass http://localhost:3000;
        # ....프록시 설정
    }
}
# HTTP → HTTPS 리다이렉트
server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$server_name$request_uri;
}

로드 밸런싱

다음은 nginx를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# upstream 정의
upstream backend {
    least_conn;  # 연결 수가 적은 서버로
    server localhost:3000;
    server localhost:3001;
    server localhost:3002;
}
server {
    listen 80;
    server_name yourdomain.com;
    
    location / {
        proxy_pass http://backend;
        # ....프록시 설정
    }
}

4. AWS 배포

EC2 배포

1. EC2 인스턴스 생성:

  • Ubuntu Server 선택
  • 보안 그룹: HTTP(80), HTTPS(443), SSH(22) 포트 열기 2. 서버 설정: 다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
# SSH 접속
ssh -i your-key.pem ubuntu@your-ec2-ip
# Node.js 설치
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# Git 설치
sudo apt-get install git
# 프로젝트 클론
git clone https://github.com/yourusername/your-repo.git
cd your-repo
# 의존성 설치
npm ci --only=production
# 환경 변수 설정
nano .env
# PM2로 실행
npm install -g pm2
pm2 start app.js --name "my-app" -i max
pm2 startup
pm2 save
# Nginx 설정
sudo apt install nginx
sudo nano /etc/nginx/sites-available/myapp
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Elastic Beanstalk

설치:

pip install awsebcli

초기화: 다음은 bash를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

eb init
# 환경 생성 및 배포
eb create production
eb deploy
# 로그 확인
eb logs
# 환경 변수 설정
eb setenv NODE_ENV=production PORT=8080
# 상태 확인
eb status
# 종료
eb terminate production

Lambda (서버리스)

아래 코드는 javascript를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// lambda.js
const serverless = require('serverless-http');
const app = require('./app');
module.exports.handler = serverless(app);

serverless.yml: 다음은 yaml를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

service: my-app
provider:
  name: aws
  runtime: nodejs20.x
  region: ap-northeast-2
functions:
  app:
    handler: lambda.handler
    events:
      - http:
          path: /{proxy+}
          method: ANY
          cors: true

배포:

npm install -g serverless
serverless deploy

5. 환경 변수 관리

.env 파일

아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# .env.development
NODE_ENV=development
PORT=3000
MONGODB_URI=mongodb://localhost:27017/mydb-dev
JWT_SECRET=dev-secret
# .env.production
NODE_ENV=production
PORT=8080
MONGODB_URI=mongodb://prod-server:27017/mydb
JWT_SECRET=super-secret-production-key

dotenv 사용

다음은 javascript를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// config.js
require('dotenv').config({
    path: `.env.${process.env.NODE_ENV || 'development'}`
});
const config = {
    nodeEnv: process.env.NODE_ENV || 'development',
    port: process.env.PORT || 3000,
    mongodbUri: process.env.MONGODB_URI,
    jwtSecret: process.env.JWT_SECRET,
    
    isDevelopment: process.env.NODE_ENV === 'development',
    isProduction: process.env.NODE_ENV === 'production',
    isTest: process.env.NODE_ENV === 'test'
};
// 필수 환경 변수 검증
const required = ['MONGODB_URI', 'JWT_SECRET'];
for (const key of required) {
    if (!process.env[key]) {
        throw new Error(`환경 변수 ${key}가 필요합니다`);
    }
}
module.exports = config;

AWS Systems Manager Parameter Store

아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# AWS CLI로 환경 변수 저장
aws ssm put-parameter \
    --name "/myapp/production/JWT_SECRET" \
    --value "your-secret-key" \
    --type "SecureString"

다음은 javascript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// config/aws.js
const AWS = require('aws-sdk');
const ssm = new AWS.SSM({ region: 'ap-northeast-2' });
async function loadConfig() {
    const params = {
        Names: [
            '/myapp/production/JWT_SECRET',
            '/myapp/production/MONGODB_URI'
        ],
        WithDecryption: true
    };
    
    const result = await ssm.getParameters(params).promise();
    
    const config = {};
    result.Parameters.forEach(param => {
        const key = param.Name.split('/').pop();
        config[key] = param.Value;
    });
    
    return config;
}
module.exports = { loadConfig };

6. 로깅

Winston

npm install winston

다음은 javascript를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// logger.js
const winston = require('winston');
const logger = winston.createLogger({
    level: process.env.LOG_LEVEL || 'info',
    format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.errors({ stack: true }),
        winston.format.json()
    ),
    defaultMeta: { service: 'my-app' },
    transports: [
        // 파일 로그
        new winston.transports.File({
            filename: 'logs/error.log',
            level: 'error'
        }),
        new winston.transports.File({
            filename: 'logs/combined.log'
        })
    ]
});
// 개발 환경에서는 콘솔 출력
if (process.env.NODE_ENV !== 'production') {
    logger.add(new winston.transports.Console({
        format: winston.format.combine(
            winston.format.colorize(),
            winston.format.simple()
        )
    }));
}
module.exports = logger;

아래 코드는 javascript를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// app.js
const logger = require('./logger');
logger.info('서버 시작', { port: 3000 });
logger.error('에러 발생', { error: err.message, stack: err.stack });
logger.warn('경고', { memory: process.memoryUsage() });

Morgan (HTTP 로깅)

다음은 javascript를 활용한 상세한 구현 코드입니다. 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

const morgan = require('morgan');
const logger = require('./logger');
// 커스텀 스트림
const stream = {
    write: (message) => {
        logger.info(message.trim());
    }
};
// 프로덕션
if (process.env.NODE_ENV === 'production') {
    app.use(morgan('combined', { stream }));
} else {
    app.use(morgan('dev'));
}

7. 모니터링

PM2 모니터링

아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# 실시간 모니터링
pm2 monit
# 웹 대시보드
pm2 plus

헬스체크

다음은 javascript를 활용한 상세한 구현 코드입니다. 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// healthcheck.js
const http = require('http');
const options = {
    host: 'localhost',
    port: 3000,
    path: '/health',
    timeout: 2000
};
const request = http.request(options, (res) => {
    if (res.statusCode === 200) {
        process.exit(0);
    } else {
        process.exit(1);
    }
});
request.on('error', () => {
    process.exit(1);
});
request.end();

아래 코드는 javascript를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// app.js
// 실행 예제
app.get('/health', (req, res) => {
    res.status(200).json({
        status: 'ok',
        uptime: process.uptime(),
        timestamp: Date.now()
    });
});

메트릭 수집

npm install prom-client

다음은 javascript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

const promClient = require('prom-client');
// 기본 메트릭 수집
const register = new promClient.Registry();
promClient.collectDefaultMetrics({ register });
// 커스텀 메트릭
const httpRequestDuration = new promClient.Histogram({
    name: 'http_request_duration_seconds',
    help: 'HTTP 요청 처리 시간',
    labelNames: ['method', 'route', 'status_code'],
    registers: [register]
});
// 미들웨어
app.use((req, res, next) => {
    const start = Date.now();
    
    res.on('finish', () => {
        const duration = (Date.now() - start) / 1000;
        httpRequestDuration
            .labels(req.method, req.route?.path || req.path, res.statusCode)
            .observe(duration);
    });
    
    next();
});
// 메트릭 엔드포인트
app.get('/metrics', async (req, res) => {
    res.set('Content-Type', register.contentType);
    res.end(await register.metrics());
});

8. CI/CD

GitHub Actions

다음은 yaml를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# .github/workflows/deploy.yml
name: Deploy to Production
on:
  push:
    branches: [ main ]
jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test
      
      - name: Run linter
        run: npm run lint
  
  deploy:
    needs: test
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Deploy to EC2
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ubuntu
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            cd /home/ubuntu/my-app
            git pull origin main
            npm ci --only=production
            pm2 reload ecosystem.config.js --env production

Docker 이미지 빌드 및 배포

다음은 yaml를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# .github/workflows/docker.yml
name: Build and Deploy Docker
on:
  push:
    branches: [ main ]
jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Build and push
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: yourusername/my-app:latest
      
      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            docker pull yourusername/my-app:latest
            docker stop my-app || true
            docker rm my-app || true
            docker run -d \
              --name my-app \
              -p 3000:3000 \
              -e NODE_ENV=production \
              yourusername/my-app:latest

9. 무중단 배포

PM2 무중단 재시작

아래 코드는 bash를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

# reload: 무중단 재시작 (클러스터 모드)
pm2 reload my-app
# gracefulReload: 더 안전한 재시작
pm2 gracefulReload my-app

Graceful Shutdown

다음은 javascript를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// app.js
const express = require('express');
const app = express();
const server = app.listen(3000);
// 진행 중인 요청 추적
let connections = new Set();
server.on('connection', (conn) => {
    connections.add(conn);
    
    conn.on('close', () => {
        connections.delete(conn);
    });
});
// Graceful Shutdown
function gracefulShutdown(signal) {
    console.log(`${signal} 신호 받음. 서버 종료 중...`);
    
    // 새 연결 거부
    server.close(async () => {
        console.log('서버 종료됨');
        
        // 데이터베이스 연결 종료
        await mongoose.connection.close();
        
        process.exit(0);
    });
    
    // 30초 후 강제 종료
    setTimeout(() => {
        console.error('강제 종료');
        process.exit(1);
    }, 30000);
    
    // 기존 연결 종료
    connections.forEach((conn) => {
        conn.end();
        
        setTimeout(() => {
            conn.destroy();
        }, 5000);
    });
}
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

10. 자주 발생하는 문제

문제 1: 포트 충돌

에러:

Error: listen EADDRINUSE: address already in use :::3000

해결: 아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# 프로세스 찾기
lsof -i :3000
netstat -ano | findstr :3000
# 프로세스 종료
kill -9 <PID>
taskkill /PID <PID> /F
# PM2로 관리
pm2 delete all
pm2 start app.js

문제 2: 메모리 누수

증상: 메모리 사용량이 계속 증가 해결: 다음은 javascript를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// PM2 설정
module.exports = {
    apps: [{
        name: 'my-app',
        script: './app.js',
        max_memory_restart: '500M'  // 500MB 초과 시 재시작
    }]
};
// 메모리 모니터링
setInterval(() => {
    const used = process.memoryUsage();
    console.log({
        rss: `${Math.round(used.rss / 1024 / 1024)} MB`,
        heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)} MB`,
        heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)} MB`
    });
}, 60000);

문제 3: 환경 변수 누락

아래 코드는 javascript를 사용한 구현 예제입니다. 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 시작 시 검증
const required = [
    'NODE_ENV',
    'PORT',
    'MONGODB_URI',
    'JWT_SECRET'
];
for (const key of required) {
    if (!process.env[key]) {
        console.error(`환경 변수 ${key}가 설정되지 않았습니다`);
        process.exit(1);
    }
}

11. 실전 팁

배포 스크립트

아래 코드는 json를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

{
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js",
    "test": "jest",
    "lint": "eslint .",
    "build": "npm ci --only=production",
    "deploy": "npm run test && npm run build && pm2 reload ecosystem.config.js"
  }
}

롤백 전략

아래 코드는 bash를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# Git 태그로 버전 관리
git tag v1.0.0
git push origin v1.0.0
# 배포
git checkout v1.0.0
npm ci --only=production
pm2 reload my-app
# 롤백
git checkout v0.9.9
npm ci --only=production
pm2 reload my-app

블루-그린 배포

아래 코드는 nginx를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

# Nginx 설정
upstream backend {
    server localhost:3000;  # Blue (현재)
}
# 배포 시:
# 1. Green 환경에 새 버전 배포 (포트 3001)
# 2. 테스트
# 3. Nginx 설정 변경
upstream backend {
    server localhost:3001;  # Green (새 버전)
}
# 4. Nginx 리로드
# 5. Blue 환경 종료

정리

핵심 요약

  1. PM2: 프로세스 관리, 클러스터 모드, 자동 재시작
  2. Docker: 컨테이너화, 환경 일관성
  3. Nginx: 리버스 프록시, SSL, 로드 밸런싱
  4. AWS: EC2, Elastic Beanstalk, Lambda
  5. CI/CD: GitHub Actions, 자동 배포
  6. 모니터링: 로그, 메트릭, 헬스체크

배포 방식 비교

방식장점단점사용 사례
VPS + PM2완전한 제어관리 부담중소규모
Docker환경 일관성학습 곡선마이크로서비스
PaaS간편함제한적빠른 프로토타입
서버리스자동 확장Cold Start이벤트 기반

배포 체크리스트

코드:

  • 테스트 통과
  • Linter 통과
  • 프로덕션 빌드
  • 의존성 최신화 설정:
  • 환경 변수 설정
  • 데이터베이스 마이그레이션
  • SSL 인증서
  • 방화벽 규칙 모니터링:
  • 로깅 설정
  • 에러 추적
  • 성능 모니터링
  • 알림 설정 보안:
  • 비밀 키 관리
  • HTTPS 적용
  • Rate Limiting
  • 보안 헤더

다음 단계

추천 학습 자료

도구:


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3