웹사이트 검색

우분투에서 프로덕션용 Node.js 애플리케이션 설정 방법


소개

Node.js 애플리케이션을 로컬에서 실행하는 것은 첫 번째 단계일 뿐입니다. 이를 프로덕션 환경에 맞게 준비하려면 안정성, 보안, 성능을 요구하는 실제 환경에 맞게 구성해야 합니다. 적절하게 구성된 프로덕션 환경은 앱이 장애에서 복구하고, 트래픽을 안전하게 제공하며, 다운타임 없이 증가하는 수요를 처리할 수 있도록 보장합니다.

이 가이드에서는 Node.js, PM2, Nginx, Let’s Encrypt, UFW를 사용하여 Ubuntu에서 완전한 프로덕션 환경을 구축하게 됩니다. Node.js는 애플리케이션을 실행하고, PM2는 프로세스를 관리하며 자동으로 재시작하며, Nginx는 들어오는 요청을 처리하고 HTTPS를 통해 트래픽을 제공합니다. Let’s Encrypt는 무료 SSL 인증서를 제공하고, UFW는 네트워크 접근을 제어하여 서버를 보호합니다. 이 도구들을 함께 사용하면 신뢰할 수 있고 효율적이며 안전한 애플리케이션 배포를 위한 강력한 기반을 마련할 수 있습니다.

Linux-Console.net 앱 플랫폼을 사용하여 GitHub에서 Node 애플리케이션을 배포하세요. Linux-Console.net이 앱 확장에 집중하도록 하세요.

핵심 요약:

  • PM2와 같은 프로세스 관리자를 사용하세요 Node.js 앱을 지속적으로 실행하고, 충돌 시 자동으로 재시작하며, pm2 startup과 pm2 save를 사용하여 재부팅 시 시작하도록 설정하세요.
  • Nginx를 리버스 프록시로 사용하여 앱 실행 - Node.js를 직접 노출하는 대신 Nginx가 SSL, 정적 자산, 로드 밸런싱을 효율적으로 처리합니다.
  • 항상 Node.js를 프로덕션 환경에서는 localhost (127.0.0.1)에 바인딩하여 직접적인 공개 접근을 방지하고 외부 트래픽은 Nginx에 의존하세요.
  • Let's Encrypt와 Certbot으로 HTTPS 활성화. Nginx가 SSL 종료와 인증서 갱신을 sudo certbot renew --dry-run 명령어로 자동으로 관리하도록 하세요.
  • UFW로 서버를 보호하세요. SSH와 “Nginx Full”(HTTP/HTTPS) 트래픽만 허용하고 나머지는 모두 차단하세요.
  • 환경 변수 또는 dotenv로 불러온 .env 파일을 통해 비밀 정보를 관리하고, 버전 관리에는 포함하지 마세요.
  • Nginx에서 압축 및 캐싱 활성화 (gzip 및 Brotli)하여 로드 속도를 개선하고 대역폭 사용량을 줄이세요.
  • PM2 및 시스템 도구로 모니터링 (pm2 monit, htop 또는 Grafana/Prometheus와 같은 도구와의 통합)하여 성능 문제를 조기에 감지합니다.
  • 정기적으로 업데이트와 감사를 적용하세요 (sudo apt update && sudo apt upgrade, npm audit) 시스템 및 의존성 보안을 유지하기 위해.
  • 권장 모범 사례를 따르세요. 예를 들어, 비루트 사용자로 실행하기, 백업 자동화, 로그 순환, 모든 곳에서 HTTPS 사용 등 안정적이고 안전한 배포를 위해 적용할 수 있습니다.

전제 조건

이 안내서는 다음 사항을 가지고 있다고 가정합니다:

  • Ubuntu 서버 설정은 초기 Ubuntu 서버 설정에서 설명된 대로입니다. sudo 권한이 있는 일반 사용자와 활성화된 방화벽이 있어야 합니다.
  • 도메인 이름이 서버의 공용 IP를 가리키도록 설정되어야 합니다. 이 튜토리얼에서는 전체적으로 도메인 이름 example.com을 사용할 것입니다.
  • Nginx가 설치되었습니다. 자세한 내용은 'Ubuntu에 Nginx를 설치하고 구성하는 방법' 가이드를 참고하세요.
  • Let’s Encrypt 인증서를 사용하여 SSL로 구성된 Nginx. 우분투에서 Let’s Encrypt로 Nginx를 안전하게 설정하는 방법이 이 과정을 안내해 줄 것입니다.

필수 요건을 완료하면, https://example.com/에서 도메인의 기본 플레이스홀더 페이지를 제공하는 서버를 갖게 됩니다.

Node.js 설치 및 확인

이 단계에서는 NodeSource PPA를 사용하여 Node.js를 설치하고 설치가 성공적으로 이루어졌는지 확인합니다. 이 방법은 Node.js의 최신 장기 지원(LTS) 버전과 관련 패키지 관리자 npm을 사용할 수 있도록 보장합니다.

NodeSource PPA를 통해 Node.js 설치

먼저 NodeSource PPA를 설치하여 해당 저장소에 접근할 수 있도록 합니다. 홈 디렉토리에 있는지 확인하고, curl을 사용하여 최신 LTS 버전의 Node.js 설치 스크립트를 다운로드하세요.

cd ~

curl -sL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh

실행하기 전에 nano로 스크립트 내용을 검토할 수 있습니다:

nano nodesource_setup.sh

준비가 되면 sudo와 함께 스크립트를 실행하세요:

sudo bash nodesource_setup.sh

이것은 PPA를 구성에 추가하고 로컬 패키지 캐시를 자동으로 업데이트합니다.

이제 Node.js를 설치할 수 있습니다:

sudo apt install nodejs

설치 여부를 확인하고 Node.js 버전을 확인하려면 다음을 실행하세요:

node -v

다음과 유사한 출력 결과를 볼 수 있을 것입니다:

v24.11.1

npm과 build-essential을 설치하세요

nodejs 패키지는 Node.js 패키지 관리자 npm도 설치합니다. 설치를 확인하고 구성 파일을 초기화하려면 다음 명령을 실행하세요:

npm -v

비슷한 출력 결과를 볼 수 있을 것입니다:

11.6.2

일부 npm 패키지가 작동하려면(예를 들어, 소스에서 코드를 컴파일해야 하는 패키지), build-essential 패키지를 설치해야 합니다:

sudo apt install build-essential

Node.js와 npm이 성공적으로 설치되었으므로 이제 애플리케이션 의존성을 개발하고 관리하는 데 필요한 도구를 갖추게 되었습니다. 다음 섹션에서는 런타임 환경을 테스트하기 위해 간단한 Node.js 애플리케이션을 만들어보겠습니다.

샘플 Node.js 애플리케이션 만들기

간단한 Node.js 애플리케이션을 작성해 모든 HTTP 요청에 대해 "Hello World"를 반환해 보겠습니다. 이 샘플 앱은 Node.js가 올바르게 설치되었는지 확인하고, 애플리케이션이 프로덕션 환경에서 localhost에 안전하게 연결되도록 구성하는 방법을 보여줍니다.

간단한 Node.js 서버를 만들기

홈 디렉토리에 hello.js라는 새 파일을 만드세요:

cd ~

nano hello.js

다음 코드를 파일에 추가하세요:

const http = require('http');

const hostname = 'localhost';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World!\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

작업을 마쳤으면 파일을 저장하고 닫으세요.

이 스크립트는 포트 3000에서 대기하는 간단한 HTTP 서버를 생성하며, 들어오는 모든 요청에 대해 Hello World!로 응답합니다. localhost에 바인딩되어 있기 때문에 동일한 컴퓨터에서의 연결만 허용됩니다.

애플리케이션을 테스트하다

Node.js를 사용하여 애플리케이션을 실행하세요:

node hello.js

다음과 유사한 출력을 확인할 수 있어야 합니다:

Server running at http://localhost:3000/

다른 터미널 세션에서 curl을 사용하여 앱을 테스트하세요:

curl http://localhost:3000

다음과 같은 출력 결과를 확인해야 합니다:

Hello World!

예상한 결과가 나오지 않는다면, Node.js 애플리케이션이 실행 중이며 올바른 주소와 포트에서 수신하도록 설정되어 있는지 확인하세요.

작동이 제대로 되는 것을 확인했으면, 원래 터미널에서 CTRL+C를 눌러 애플리케이션을 종료하여 서버를 중지하세요.

운영 환경에서 앱이 localhost에 바인딩해야 하는 이유

운영 환경에서 Node.js 애플리케이션은 일반적으로 Nginx와 같은 리버스 프록시 뒤에 위치합니다. Node.js 애플리케이션은 내부 주소(예: localhost:3000)에서 대기하며, Nginx가 들어오는 웹 트래픽, TLS 종료 및 라우팅을 처리합니다.

애플리케이션을 localhost에 바인딩하면 여러 가지 보안 및 신뢰성 이점을 제공합니다:

  • 외부 노출 방지: Node.js 프로세스에 직접 접근할 수 있는 유일한 방법은 Nginx입니다.
  • HTTPS 오프로드 활성화: Nginx가 SSL 인증서와 암호화를 관리하여 Node.js의 부담을 덜어줍니다.
  • 방화벽 규칙을 단순화: 방화벽은 HTTP/HTTPS를 허용하면서 모든 내부 애플리케이션 포트에 대한 접근을 차단할 수 있습니다.

이 설정은 귀하의 Node.js 앱이 격리되고 안전하며 Nginx 리버스 프록시를 통해서만 접근할 수 있도록 보장합니다.

PM2 프로세스 관리자를 설치하고 구성하기

이 섹션에서는 Node.js 애플리케이션용 프로덕션 프로세스 관리자 PM2를 설치합니다. PM2를 사용하면 애플리케이션을 계속 실행 상태로 유지하고, 충돌 후 자동으로 재시작하며, 시스템 부팅 시 자동으로 시작되도록 설정할 수 있습니다.

PM2 설치

npm을 사용하여 전역적으로 PM2를 설치하세요:

sudo npm install -g pm2

-g 플래그는 PM2를 시스템 전체에 설치하므로 모든 사용자와 애플리케이션에서 사용할 수 있습니다.

설치가 완료되면, PM2를 사용하여 샘플 애플리케이션을 시작하세요:

pm2 start hello.js

PM2가 앱 관리를 시작했음을 보여주는 출력 테이블이 표시됩니다:

[PM2] Spawning PM2 daemon with pm2_home=/home/user/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /root/nodeapp/hello.js in fork_mode (1 instance)
[PM2] Done.
┌────┬──────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name     │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼──────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ hello    │ default     │ N/A     │ fork    │ 3826     │ 0s     │ 0    │ online    │ 0%       │ 33.3mb   │ root     │ disabled │
└────┴──────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

위에서 언급했듯이, PM2는 자동으로 앱 이름(파일 이름을 기반으로 하며 .js 확장자는 제외)과 PM2 id를 지정합니다. PM2는 또한 프로세스의 PID, 현재 상태 및 메모리 사용량과 같은 다른 정보도 관리합니다.

충돌 시 자동 재시작 및 재부팅 활성화

PM2는 애플리케이션이 충돌하거나 종료되면 자동으로 재시작합니다. 서버가 재부팅될 때 PM2 자체가 자동으로 시작되도록 하려면 다음을 실행하세요:

pm2 startup systemd

PM2는 다음과 비슷한 명령어를 생성합니다(sammy를 자신의 사용자 이름으로 바꿔 사용하세요):

[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/home/sammy/.nvm/versions/node/v24.11.1/bin /home/sammy/.nvm/versions/node/v24.11.1/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

참고: 경로는 사용 중인 Node.js 버전과 사용자 이름에 따라 달라질 수 있습니다.

그 명령어를 실행한 후, PM2가 다음 재부팅 때 앱을 기억하도록 실행 중인 앱을 저장하세요:

pm2 save

모든 설정이 올바르게 되었는지 확인하려면 서비스 상태를 확인하세요:

systemctl status pm2-sammy

참고: systemd에 대한 자세한 개요는 'Systemd Essentials: Working with Services, Units, and the Journal'을 참조하세요.

문제가 발생하면 서버를 재부팅하고 앱이 자동으로 시작되는지 확인하세요.

PM2 에코시스템 파일 사용하기

여러 앱을 실행하거나 다양한 환경을 관리할 준비가 되었을 때, PM2의 ecosystem 파일이 큰 도움이 됩니다. 이 파일은 환경 변수, 포트, 배포 설정 등을 한 곳에서 정의할 수 있는 간단한 구성 파일입니다.

다음 명령을 실행하여 시작 파일을 생성하세요:

pm2 ecosystem

그런 다음 생성된 ecosystem.config.js 파일을 편집하세요:

module.exports = {
  apps: [
    {
      name: 'hello',
      script: 'hello.js',
      env: {
        NODE_ENV: 'development',
        PORT: 3001
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 3000
      }
    }
  ]
};

앱을 프로덕션 모드에서 시작하려면 다음을 사용하세요:

pm2 start ecosystem.config.js --env production

에코시스템 파일을 사용하면 여러 환경을 쉽게 관리하고 환경 변수를 소스 코드 밖에 안전하게 저장할 수 있습니다. PM2를 사용하면 앱이 충돌, 재부팅, 업데이트 상황에서도 안전하게 실행되며, 백그라운드에서 조용히 작동합니다.

추가 PM2 명령어

앞서 다룬 내용들 외에도, PM2는 애플리케이션을 관리하거나 정보를 조회할 수 있는 여러 하위 명령어를 제공합니다.

이 명령으로 애플리케이션을 중지하세요 (PM2 앱 이름 또는 id를 지정):

pm2 stop app_name_or_id

애플리케이션을 재시작하세요:

pm2 restart app_name_or_id

현재 PM2로 관리되는 애플리케이션을 나열하세요:

pm2 list

앱 이름을 사용하여 특정 애플리케이션에 대한 정보를 얻습니다:

pm2 info app_name

애플리케이션의 실시간 로그를 보려면:

pm2 logs

PM2 프로세스 모니터는 monit 하위 명령어로 불러올 수 있습니다. 이를 통해 애플리케이션 상태, CPU 및 메모리 사용량을 표시합니다:

pm2 monit

인수 없이 pm2를 실행하면 사용 예제가 포함된 도움말 페이지가 표시된다는 점에 유의하세요.

이제 Node.js 애플리케이션이 실행되고 PM2로 관리되고 있으므로, 리버스 프록시를 설정해 봅시다.

Nginx를 리버스 프록시로 설정하기

Node.js 앱이 PM2에서 실행되고 있으면, 다음 단계는 사용자가 웹을 통해 앱에 접근할 수 있도록 만드는 것입니다. 앱을 직접 3000번 포트에서 노출하는 대신, Nginx를 리버스 프록시로 사용할 것입니다. Nginx는 모든 수신 HTTP 및 HTTPS 요청을 처리하고 이를 localhost에서 실행 중인 Node.js 앱으로 전달합니다.

기본 리버스 프록시 설정

사이트의 Nginx 구성 파일을 엽니다. 사전 요구 사항 섹션의 설정을 따라 했다면, 이 파일은 /etc/nginx/sites-available/example.com에 위치해야 합니다:

sudo nano /etc/nginx/sites-available/example.com

server 블록 안에서 location / 섹션을 찾거나 새로 만들고, 해당 내용을 다음 구성으로 교체하세요. Node.js 앱이 다른 포트에서 수신 대기하는 경우, 강조 표시된 값을 그에 맞게 업데이트하세요.

server {
...
    listen 80;
    server_name example.com www.example.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_cache_bypass $http_upgrade;
    }
...
}

이것은 Nginx에 포트 80(HTTP)에서 수신하도록 지시하고, 들어오는 모든 트래픽을 포트 3000에서 실행 중인 Node.js 애플리케이션으로 전달하도록 합니다.

여러 앱을 테스트하려면 추가 location 블록을 정의할 수 있습니다. 예를 들어, 포트 3001에서 실행 중인 다른 Node.js 앱이 있다면, 동일한 서버 블록에 다음을 추가하세요:

server {
...
    location /app2 {
        proxy_pass http://localhost:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
...
}

파일을 저장한 후, 다음을 실행하여 구성에 구문 오류가 있는지 테스트하세요:

sudo nginx -t

모든 것이 확인되면 Nginx를 다시 로드하세요:

sudo systemctl reload nginx

이 시점에서 서버의 도메인(예: http://example.com)을 방문하면 Nginx 프록시를 통해 실행 중인 Node.js 앱이 로드되어야 합니다.

HTTPS(SSL) 구성 추가

앱을 보호하려면 Nginx를 구성하여 HTTPS 트래픽을 처리하십시오. 이미 Let’s Encrypt에서 발급받은 SSL 인증서가 있다면, Nginx 구성에 정의되어 있는지 확인하세요. 업데이트된 서버 블록이 어떻게 보일 수 있는지 예시는 다음과 같습니다:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    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_cache_bypass $http_upgrade;
    }
}

첫 번째 서버 블록은 모든 HTTP 요청을 자동으로 HTTPS로 리디렉션하고, 두 번째 블록은 포트 443에서의 보안 트래픽을 처리합니다.

다중 애플리케이션 라우팅 (선택 사항)

서버에서 여러 애플리케이션을 호스팅하는 경우, Nginx는 서로 다른 경로나 서브도메인을 정의하여 모두 처리할 수 있습니다. 예를 들어, 다음과 같이 구성할 수 있습니다:

  • https://example.com/은 포트 3000으로 포워딩됩니다
  • https://example.com/app2가 포트 3001로 포워딩됩니다
  • https://api.example.com/가 포트 4000으로 전달됩니다

각 앱은 자체 PM2 프로세스로 관리될 수 있으며 Nginx를 통해 제공될 수 있습니다.

모든 것이 정상적으로 작동하는 것을 확인했다면, 이제 Node.js 앱에 Nginx를 통해 안전하게 접근할 수 있습니다.

프로덕션을 위한 UFW 방화벽 구성

이제 Node.js 앱이 Nginx 뒤에서 실행되고 있으므로 마지막으로 네트워크 보안 계층을 추가해 보겠습니다. Ubuntu에는 UFW(간단한 방화벽)라는 내장 방화벽이 포함되어 있습니다. 가볍고 구성하기 쉬우며, 서버에 대한 원치 않는 접근을 차단하는 데 적합합니다.

방화벽 상태 확인

변경을 하기 전에 UFW가 이미 활성화되어 있는지 확인하세요:

sudo ufw status

만약 비활성이라고 표시되더라도 걱정하지 마세요. 곧 활성화할 예정입니다. 하지만 먼저, 어떤 유형의 트래픽을 허용할지 정의할 것입니다. 켜기 전에 규칙을 설정하는 것이 항상 가장 좋습니다.

필수 트래픽 허용

최소한으로, 서버에 계속 연결할 수 있도록 SSH를 허용하고, Nginx가 웹 트래픽을 처리할 수 있도록 HTTP/HTTPS도 허용해야 합니다. 이를 위해 다음 명령어를 실행하세요:

sudo ufw allow OpenSSH

sudo ufw allow 'Nginx Full'

이제 방화벽을 활성화할 수 있습니다:

sudo ufw enable

확인을 요청받으면 y를 입력하고 Enter를 누르세요.

규칙이 활성화되어 있는지 확인하려면 방화벽을 다시 확인하세요:

sudo ufw status

다음과 비슷한 것을 볼 수 있을 것입니다:

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)

그게 다야! 이제 SSH와 웹 트래픽만 허용되고, 나머지는 모두 차단됩니다.

운영 환경에서 방화벽이 중요한 이유

방화벽은 네트워크 트래픽이 시스템에 도달할 수 있는 범위를 엄격하게 제어하기 때문에 서버 보안에 필수적입니다. 불필요한 포트가 열려 있는 경우, 잘 구성된 애플리케이션과 웹 서버조차도 취약할 수 있습니다.

다음은 방화벽이 운영 환경에서 중요한 몇 가지 주요 이유입니다:

  • 노출 최소화: 명시적으로 허용된 포트만 접근 가능하며, 잠재적인 공격 경로의 수를 줄입니다.
  • 격리: 침해나 잘못된 구성 발생 시, 방화벽은 공격자가 시스템이나 네트워크 내에서 이동할 수 있는 범위를 제한합니다.
  • 준수 및 감사: 많은 보안 표준에서는 감사 가능한 접근 규칙이 있는 적극적인 방화벽 보호를 요구합니다.
  • 다층 방어: 방화벽은 TLS, 인증, 애플리케이션 강화와 같은 다른 보안 계층을 보완합니다.

대부분의 운영 서버에서는 필수 포트만 열어두고 방화벽을 활성화하는 것이 접근성과 보호 사이의 적절한 균형을 유지하는 방법입니다. 이는 유지보수가 적은 안전 장치로, 추가적인 제어 계층을 제공하고 무단 네트워크 접근을 방지하는 데 도움이 됩니다.

UFW가 설정되었으므로 이제 시스템이 네트워크 수준에서 더 안전해졌습니다.

Let’s Encrypt로 SSL 활성화

이제 애플리케이션이 Nginx를 통해 공개적으로 접근 가능하게 되었으므로, 안전한 통신을 위해 HTTPS를 추가할 때입니다. SSL은 서버와 사용자 간의 모든 트래픽을 암호화하여 프라이버시와 데이터 무결성을 보장합니다. 사이트에 SSL을 추가하는 가장 쉬운 방법은 무료이자 자동화된 인증서 기관인 Let’s Encrypt를 사용하는 것입니다.

Certbot과 Nginx 플러그인을 설치하세요

Let's Encrypt는 Certbot이라는 도구를 통해 작동하며, 인증서 요청, 설치, 갱신의 전체 과정을 처리합니다. Ubuntu에서는 Certbot과 Nginx 플러그인을 한 번에 설치할 수 있습니다:

sudo apt update

sudo apt install certbot python3-certbot-nginx -y

이것은 Certbot 클라이언트와 Nginx 통합 플러그인을 모두 설치하며, 이는 기존 Nginx 서버 블록에서 HTTPS를 자동으로 구성합니다.

SSL 인증서 받기

Certbot이 설치되면 이를 사용하여 도메인용 새 SSL 인증서를 요청할 수 있습니다. example.com과 www.example.com을 실제 도메인 이름으로 바꾸십시오:

sudo certbot --nginx -d example.com -d www.example.com

이 과정에서 Certbot은 다음을 수행합니다:

  • 도메인이 이 서버를 가리키는지 확인하세요 (HTTP 챌린지를 사용하여)
  • Let’s Encrypt에서 SSL 인증서를 받으세요
  • HTTPS를 활성화하도록 Nginx 구성을 자동으로 편집
  • 새 설정이 적용되도록 Nginx를 다시 로드하세요

갱신 알림에 사용될 이메일 주소를 입력하라는 메시지가 표시되며, 서비스 약관에 동의하라는 요청이 있을 것입니다. 설정이 완료되면 인증서가 성공적으로 설치되었음을 알리는 확인 메시지가 표시됩니다.

다음에서 인증서 세부 정보를 다시 확인할 수 있습니다:

sudo certbot certificates

그런 다음 도메인을 방문하여 사이트가 이제 안전하게 제공되고 있는지 확인하세요.

자동 갱신

Let’s Encrypt 인증서는 90일 동안만 유효하지만 수동 갱신에 대해 걱정할 필요는 없습니다. Certbot은 하루에 두 번 만료되는 인증서를 확인하고 자동으로 갱신하는 시스템 타이머를 설치합니다.

언제든지 다음을 사용하여 이 갱신 과정을 테스트할 수 있습니다:

sudo certbot renew --dry-run

드라이 런이 오류 없이 완료되면 인증서가 백그라운드에서 자동으로 갱신되며, Nginx가 새 인증서를 적용하기 위해 다시 로드됩니다.

HTTPS가 중요한 이유

애플리케이션을 HTTPS로 실행하는 것은 더 이상 선택 사항이 아니라, 모든 운영 시스템을 운용하는 데 필수적인 부분입니다. SSL/TLS는 서버와 클라이언트 간의 모든 통신이 암호화되고 변조 방지되도록 하여 도청과 데이터 조작으로부터 보호합니다.

보안뿐만 아니라, HTTPS는 여러 가지 추가적인 이점을 제공합니다:

  • 신뢰와 신빙성: 브라우저는 HTTPS가 없는 사이트를 '안전하지 않음'으로 표시하여 사용자 신뢰를 저해할 수 있습니다.
  • 검색 순위 향상: 주요 검색 엔진은 HTTPS를 순위 결정 요인으로 사용합니다.
  • 최신 웹 기능 접근: HTTP/2, QUIC 및 기타 성능 향상 기능은 TLS가 필요합니다.
  • 준수: 많은 개인정보 보호 규제와 산업 표준에서는 전송되는 데이터의 암호화를 요구합니다.

Let's Encrypt를 사용하면 최소한의 설정과 비용 없이 이러한 혜택을 누릴 수 있습니다. 인증서는 자동으로 갱신되어 안전한 배포를 위한 장기적인 솔루션으로 간편하게 사용할 수 있습니다.

HTTPS가 적용되었으므로 이제 서버가 암호화되고 신뢰할 수 있는 트래픽을 제공하고 있습니다.

환경 변수를 안전하게 관리하기

모든 운영 환경의 애플리케이션은 API 키, 데이터베이스 자격 증명 또는 비밀 토큰과 같은 구성 데이터를 처리할 수 있는 신뢰할 수 있는 방법이 필요합니다. 이러한 값을 소스 코드에 직접 저장하면 버전 관리, 로그, 심지어 클라이언트 측 누출을 통해 노출될 수 있습니다. 보다 안전한 방법은 민감한 정보를 코드베이스와 분리하여 보관하는 환경 변수를 사용하는 것입니다.

dotenv와 함께 .env 파일 사용하기

Node.js에서 환경 변수를 관리하는 일반적이고 간단한 방법은 .env 파일과 dotenv 패키지를 함께 사용하는 것입니다. 이 설정을 사용하면 애플리케이션이 실행 시점에 구성 값을 읽을 수 있으면서도 소스 코드에는 포함되지 않도록 할 수 있습니다.

다음 명령어를 실행하여 dotenv 패키지를 설치하세요:

npm install dotenv

다음으로, 프로젝트 디렉토리에 .env 파일을 생성하세요:

nano .env

다음과 같이 키-값 쌍으로 설정 값을 추가하세요:

PORT=3000
NODE_ENV=production
DB_HOST=localhost
DB_USER=myuser
DB_PASS=securepassword
JWT_SECRET=myjwtsecretkey

이제 메인 애플리케이션 파일(예: app.js)을 수정하여 시작 시 환경 변수를 로드하도록 하세요:

require('dotenv').config();

const express = require('express');
const app = express();

app.listen(process.env.PORT, () => {
  console.log(`Server running in ${process.env.NODE_ENV} mode on port ${process.env.PORT}`);
});

앱이 실행될 때, .env 파일에 정의된 변수를 읽고 이를 사용하여 실행 환경을 구성합니다.

PM2와 환경 변수

애플리케이션을 PM2로 실행하는 경우, PM2 명령어나 설정 파일에서 환경 변수를 직접 정의할 수 있습니다. 이는 개발, 스테이징, 프로덕션과 같은 환경 간에 구성을 일관되게 유지하는 데 도움이 됩니다.

앱을 시작할 때 변수를 인라인으로 설정할 수 있습니다:

pm2 start app.js --name "myapp" --env production

더 깔끔하고 확장 가능한 솔루션을 위해 PM2 에코시스템 구성 파일을 사용하세요. 다음을 사용하여 생성할 수 있습니다:

pm2 ecosystem

그런 다음 ecosystem.config.js 파일을 편집하여 환경 변수를 포함하세요:

module.exports = {
  apps: [
    {
      name: 'myapp',
      script: 'app.js',
      env: {
        NODE_ENV: 'development',
        PORT: 3001
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 3000,
        DB_HOST: 'localhost',
        DB_USER: 'myuser',
        DB_PASS: 'securepassword'
      }
    }
  ]
};

앱을 운영 모드에서 시작하세요:

pm2 start ecosystem.config.js --env production

이렇게 하면 앱을 다시 시작할 때마다 올바른 변수가 자동으로 로드됩니다.

비밀 관리 모범 사례

비밀과 환경 변수를 안전하게 관리하는 것은 인프라의 무결성을 유지하는 데 중요합니다. 다음의 모범 사례는 위험을 최소화하는 데 도움이 될 것입니다:

  • 비밀을 소스 관리에서 제외하세요: 절대 .env 파일을 Git 저장소에 커밋하지 마세요. 실수로 노출되지 않도록 .gitignore에 추가하세요.

  • 접근 제한: 환경 구성 파일을 읽거나 수정할 수 있는 권한은 신뢰할 수 있는 사용자와 자동화 시스템에만 부여해야 합니다.

  • 환경별 파일 사용: 개발, 테스트, 스테이징, 프로덕션에 대해 별도의 설정을 유지하세요. 예를 들어 .env.development와 .env.production가 있습니다.

  • 비밀을 정기적으로 교체하세요: 잠재적인 유출의 영향을 줄이기 위해 API 키, 토큰, 비밀번호를 일정에 따라 변경하세요.

  • 대규모 시스템에는 비밀 관리자를 사용하세요: 복잡한 애플리케이션이나 팀의 경우, 로컬 .env 파일 대신 HashiCorp Vault와 같은 서비스를 사용하는 것을 고려해 보세요.

  • 민감한 데이터 기록을 피하세요: 애플리케이션이나 배포 로그에 비밀 정보를 출력하거나 기록하지 않도록 주의하세요.

  • 시작 시 환경 변수를 검증: 애플리케이션이 실행되기 전에 모든 필수 변수가 설정되어 있는지 확인하기 위해 joi 또는 env-schema와 같은 라이브러리를 사용하세요.

  • 파일 권한 제한: chmod 명령어를 사용하여 .env 파일에 대한 접근을 제한합니다. 예를 들어:

    chmod 600 .env

이러한 관행을 따르면 민감한 정보를 보호하고 우발적인 노출을 방지하며 여러 환경에서 구성 관리를 더 쉽게 할 수 있습니다.

생산 최적화 기법

애플리케이션이 안정적인 운영 환경에서 실행되고 있다면, 성능과 효율성에 집중하는 것이 중요합니다. 최적화는 사용자 경험을 향상시킬 뿐만 아니라 서버 비용을 줄이고 원활한 확장성을 보장하는 데도 도움이 됩니다. 이 섹션에서는 속도, 신뢰성 및 자원 사용을 개선하기 위해 Node.js와 Nginx 설정을 최적화하는 여러 기술을 배우게 될 것입니다.

압축 활성화 (gzip 또는 Brotli)

압축은 성능을 향상시키는 가장 간단한 방법 중 하나입니다. 서버에서 클라이언트로 전송되는 파일의 크기를 줄여 페이지 로딩 속도를 높이고 대역폭 사용량을 줄입니다. Nginx는 두 가지 인기 있는 압축 방법인 gzip과 Brotli를 지원합니다.

gzip 사용하기

gzip 압축을 활성화하려면, Nginx 메인 설정 파일을 열어주세요:

sudo nano /etc/nginx/nginx.conf

http 블록 안에 다음 지시문을 추가하거나 주석 처리를 해제하십시오:

gzip on;
gzip_types text/plain text/css application/javascript application/json application/xml;
gzip_min_length 256;
gzip_comp_level 5;

이 설정은 일반 파일 유형에 대해 gzip을 활성화하고, 256바이트보다 큰 파일을 압축하며, CPU 부하와 성능의 균형을 맞추기 위해 중간 수준의 압축을 적용합니다.

Brotli 사용

Brotli 모듈이 설치되어 있다면, 더 나은 압축률을 위해 이를 사용할 수 있습니다:

brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css application/javascript application/json application/xml;

Brotli는 CSS와 JavaScript 같은 텍스트 기반 자산에 특히 효과적입니다. 이러한 변경을 수행한 후, Nginx를 다시 로드하여 적용하세요:

sudo systemctl reload nginx

압축을 활성화하면 클라이언트가 자산을 더 빠르게 다운로드하여 데스크톱과 모바일 장치 모두에서 로드 시간이 단축되고 성능이 향상됩니다.

Nginx에서 캐싱 구성하기

캐싱은 서버가 매번 요청마다 응답을 새로 생성하지 않고 이전에 생성된 응답을 재사용할 수 있게 해줍니다. 이는 Node.js 애플리케이션의 부하를 크게 줄이고 최종 사용자에게 응답 시간을 빠르게 합니다.

이미지, 스타일시트, 스크립트와 같은 정적 자산을 캐시하려면, 이 구성을 Nginx server 블록에 추가하세요:

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 7d;
    add_header Cache-Control "public, no-transform";
}

이 구성은 브라우저가 정적 자산을 7일 동안 캐시하도록 지시합니다. 정적 파일이 변경되는 빈도에 따라 기간을 조정할 수 있습니다.

API나 동적 콘텐츠의 경우, 캐싱을 보다 선택적으로 적용할 수 있습니다. 예를 들어, 자주 접근되는 엔드포인트의 응답은 캐싱하고, 사용자별 데이터를 반환하는 엔드포인트는 제외할 수 있습니다. Nginx는 또한 트래픽이 많은 API를 위해 마이크로 캐싱(몇 초 단위의 단기 캐싱)도 지원합니다.

로깅 모범 사례

로그는 모니터링, 문제 해결, 성능 조정을 위해 필수적입니다. PM2와 Nginx 모두 시스템 전반에서 발생하는 상황을 추적할 수 있도록 도와주는 포괄적인 로깅 도구를 제공합니다.

PM2 로그

PM2는 애플리케이션 로그를 자동으로 관리합니다. 실시간으로 보려면 다음을 사용하세요:

pm2 logs

시간이 지남에 따라 로그가 커지고 디스크 공간을 차지할 수 있습니다. 이를 방지하려면 PM2 로그 회전 모듈을 설치하세요:

pm2 install pm2-logrotate

그런 다음 회전 정책을 구성합니다:

pm2 set pm2-logrotate:max_size 10M

pm2 set pm2-logrotate:retain 7

이 설정은 각 로그 파일을 10MB 이하로 유지하고, 로그를 7일간 보관합니다. 저장 용량과 로그 필요에 따라 이 값을 조정할 수 있습니다.

Nginx 로그

Nginx는 액세스 로그(각 요청 기록)와 오류 로그(문제 및 실패에 대한 정보)를 모두 유지합니다. 이러한 로그는 일반적으로 /var/log/nginx/에 저장됩니다.

액세스 로그를 수동으로 검토하거나 goaccess와 같은 도구를 사용하여 자세한 트래픽 보고서를 생성할 수 있습니다:

sudo apt install goaccess

sudo goaccess /var/log/nginx/access.log -o /var/www/html/report.html --log-format=COMBINED

이 명령어는 방문자 위치, 트래픽 양, 응답 시간에 대한 지표가 포함된 인터랙티브 HTML 보고서를 생성합니다.

잘 관리된 로그는 사용자가 영향을 받기 전에 성능 병목 현상, 실패한 요청 또는 보안 이상을 식별하는 데 매우 중요합니다.

리소스 관리 및 Node.js 튜닝

Node.js 애플리케이션은 기본적으로 단일 스레드에서 실행됩니다. 모든 사용 가능한 CPU 코어를 활용하고 동시성을 향상시키기 위해 클러스터링 및 기타 프로세스 관리 기술을 사용할 수 있습니다.

클러스터링 사용

PM2는 멀티 코어 CPU를 활용하기 위해 여러 Node.js 프로세스를 자동으로 시작할 수 있습니다:

pm2 start app.js -i max

이 명령어는 서버의 CPU 코어 수만큼 앱 인스턴스를 시작합니다. PM2는 부하 분산을 관리하고 프로세스가 실패할 경우 재시작합니다.

메모리 및 CPU 사용량 모니터링

메모리 누수나 과도한 CPU 사용을 방지하기 위해 앱의 자원 사용을 정기적으로 모니터링하세요:

pm2 monit

이것은 각 프로세스의 실시간 메모리 및 CPU 지표를 보여주는 대화형 대시보드를 엽니다.

정적 자산을 Nginx로 오프로드

정적 파일은 Node.js를 거치지 않고 Nginx가 직접 제공하도록 하세요. 이렇게 하면 Node.js 쪽의 CPU 사용량이 크게 줄어들고 캐시된 자산을 더 빠르게 전달할 수 있습니다.

환경 설정

환경 변수를 사용하여 로그 상세 수준, API 호출 제한, 캐시 지속 시간과 같은 성능 설정을 조정하세요. 환경 변수를 통해 구성 관리를 하면 애플리케이션을 유연하게 유지하고 확장하기가 더 쉬워집니다.

모니터링 및 지표

생산 환경을 최적화한 후, 다음으로 중요한 단계는 효과적인 모니터링 및 지표 수집을 설정하는 것입니다. 모니터링은 애플리케이션 성능, 자원 사용량, 시스템 상태를 파악할 수 있게 해줍니다. 이를 통해 문제를 조기에 감지하고, 장기적인 추세를 추적하며, 데이터 기반의 개선을 수행할 수 있습니다.

내장 도구와 외부 서비스를 사용하여 Node.js 애플리케이션, Nginx 웹 서버, 시스템 리소스를 모니터링하는 방법을 알아보겠습니다.

모니터링이 중요한 이유

운영 환경에서는 매초가 중요합니다. 모니터링이 없으면 성능 문제나 서비스 중단이 사용자에게 영향을 미칠 때까지 감지되지 않을 수 있습니다. 모니터링은 다음을 도와줍니다:

  • 장애를 빠르게 감지하고 발생 시 자동으로 알림을 받으세요.
  • 다양한 트래픽 부하에서 시스템 성능을 분석하세요.
  • 느린 엔드포인트, 병목 현상 또는 메모리 누수를 식별하세요.
  • 정보에 기반한 확장 및 최적화 결정을 내리세요.
  • 배포 전반에 걸쳐 가동 시간과 신뢰성을 유지하세요.

잘 설계된 모니터링 설정에는 즉각적인 알림을 위한 실시간 모니터링과 시간에 따른 성능 분석을 위한 과거 지표가 모두 포함됩니다.

PM2로 Node.js 애플리케이션 모니터링하기

PM2는 Node.js 앱을 실행하는 데 사용되는 프로세스 관리자이며, 강력한 모니터링 기능을 포함하고 있습니다.

실시간 모니터링

실행 중인 프로세스의 개요를 보려면 다음을 사용하세요:

pm2 list

이것은 모든 활성 애플리케이션과 해당 상태, 가동 시간, CPU 및 메모리 사용량을 표시합니다. 보다 상세한 실시간 보기를 원하면 다음을 사용하세요:

pm2 monit

이것은 프로세스별 CPU 및 메모리 사용률과 같은 주요 성능 지표를 보여주는 대화형 대시보드를 엽니다. 이러한 지표를 모니터링하면 자원 고갈, 무한 실행 프로세스 또는 메모리 누수를 감지하는 데 도움이 됩니다.

로그 모니터링

PM2는 또한 중앙 집중식 로그 관리를 제공합니다. 애플리케이션 로그를 확인하려면 다음 명령을 실행하세요:

pm2 logs

특정 앱의 로그는 다음을 사용하여 확인할 수 있습니다:

pm2 logs myapp

자주 재시작되는 것이 보이면, 다음 명령어로 PM2의 프로세스 목록을 확인하세요:

pm2 status

각 프로세스가 얼마나 여러 번 다시 시작되었는지 보여주며, 이는 오류나 충돌의 지표가 될 수 있습니다.

고급 모니터링 (PM2 플러스)

대규모 배포의 경우, PM2는 모든 서버의 CPU 사용량, 메모리 사용량, 애플리케이션 가동 시간과 같은 지표를 추적하는 웹 기반 대시보드인 PM2 Plus를 제공합니다. 임계값 초과에 대한 알림을 설정하고 시간 경과에 따른 성능 추세를 확인할 수 있습니다.

PM2는 또한 통합 가시성을 위해 Datadog, New Relic, Prometheus와 같은 인기 있는 타사 모니터링 시스템과도 연동될 수 있습니다.

Nginx 모니터링

Nginx는 프로덕션 스택의 최전선에 위치하며, HTTP 요청과 SSL 종료를 관리합니다. Nginx를 모니터링하는 것은 사용자가 시스템과 어떻게 상호작용하는지, 요청이 얼마나 효율적으로 처리되고 있는지를 이해하는 데 필수적입니다.

접속 및 오류 로그

Nginx는 처리하는 모든 요청을 일반적으로 다음 위치에 있는 액세스 로그에 기록합니다:

/var/log/nginx/access.log

오류는 다음에 별도로 기록됩니다:

/var/log/nginx/error.log

이 로그를 실시간으로 확인할 수 있습니다:

sudo tail -f /var/log/nginx/access.log

액세스 로그에는 클라이언트 IP, 응답 시간, 상태 코드, 사용자 에이전트와 같은 중요한 정보가 기록됩니다. 이러한 로그는 높은 지연 시간, 잦은 404 오류, 비정상적인 요청량과 같은 패턴을 식별하는 데 유용합니다.

요약되고 시각적인 보고서를 위해 goaccess를 사용할 수 있습니다. 이 도구는 트래픽 출처, 인기 있는 엔드포인트, 응답 분포를 강조 표시하는 인터랙티브 대시보드를 생성합니다.

Nginx 상태 페이지 활성화

Nginx에는 현재 연결 수와 요청 수와 같은 실시간 서버 지표를 제공하는 가벼운 stub_status 모듈이 포함되어 있습니다. Nginx 구성에 다음 블록을 추가하세요:

location /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}

Nginx를 재시작한 후, 다음을 방문하여 지표를 확인할 수 있습니다:

http://localhost/nginx_status

이러한 지표들은 모니터링 대시보드에 통합되거나 자동 알림에 사용할 수 있습니다.

Node.js에서 애플리케이션 메트릭 수집하기

더 세부적인 애플리케이션 수준 모니터링을 위해 Node.js 앱에서 직접 맞춤형 메트릭을 수집할 수 있습니다. 이러한 메트릭은 API 응답 시간, 요청률 또는 데이터베이스 쿼리 지속 시간을 추적할 수 있습니다.

다음 명령어를 실행하여 Prometheus 클라이언트 라이브러리를 설치하세요:

npm install prom-client express

애플리케이션에 /metrics 엔드포인트를 추가하세요:

const express = require('express');
const client = require('prom-client');
const app = express();

client.collectDefaultMetrics({ timeout: 5000 });

app.get('/metrics', async (req, res) => {
  res.set('Content-Type', client.register.contentType);
  res.end(await client.register.metrics());
});

app.listen(3000, () => {
  console.log('Metrics available at /metrics');
});

이 엔드포인트를 Prometheus 또는 Grafana에 연결하여 요청 지연 시간, 메모리 사용량, 가비지 수집 성능과 같은 메트릭을 시각화할 수 있습니다.

시스템 수준 모니터링

애플리케이션 모니터링 외에도 전체 서버 성능을 주시하세요. 이를 위해 Linux는 여러 도구를 제공합니다:

  • htop 또는 top: 실시간 CPU 및 메모리 사용량을 표시합니다.
  • iostat: 디스크 입출력 작업을 모니터링합니다.
  • netstat 또는 ss: 열린 네트워크 연결을 표시합니다.
  • vmstat: 프로세스, 메모리 및 I/O 통계를 추적합니다.

시스템 모니터링은 Netdata, Glances, 또는 Grafana Agent와 같은 도구를 사용하여 자동화할 수 있으며, 이 도구들은 인프라 전반의 지표를 수집하고 대시보드에 시각화합니다.

일반적인 오류, 문제 해결 및 모범 사례

잘 구성된 프로덕션 환경이라 하더라도 가끔 문제에 직면할 수 있습니다. 이러한 문제를 식별하고 진단하며 해결하는 방법을 이해하는 것은 안정성을 유지하고 다운타임을 최소화하는 데 핵심입니다. 이 섹션에서는 일반적인 오류에 대한 상세한 문제 해결 단계와 Node.js 환경을 장기적으로 안정적이고 안전하며 유지 관리 가능하게 유지하는 데 도움이 되는 모범 사례를 안내합니다.

일반적인 오류 및 수정 방법

1. 애플리케이션이 PM2로 실행되지 않음

  • 원인: 잘못된 파일 경로, 누락된 종속 항목, 또는 잘못 구성된 환경 변수.

  • 해결 방법: 로그를 확인하려면:

    pm2 logs

    PM2 설정에서 올바른 파일 경로가 사용되었는지 확인하세요. 필요하다면 종속 항목을 다시 설치하세요:

    npm install --production

    또한 필수 환경 변수가 .env 파일이나 생태계 파일에서 제대로 로드되었는지 확인하세요.

2. 포트가 이미 사용 중인 오류

  • 원인: 이미 다른 프로세스가 동일한 포트에서 대기 중입니다.

  • 해결책: 충돌하는 프로세스를 확인하고 중지합니다:

    sudo lsof -i :3000
    
    sudo kill -9 <PID>

    또는 .env 파일이나 에코시스템 설정에서 애플리케이션 포트를 변경하세요.

3. Nginx 502 잘못된 게이트웨이

  • 원인: Nginx가 Node.js 앱에 접속할 수 없습니다. 이는 보통 앱이 다운되었거나, 중지되었거나, 다른 포트에 바인딩되어 있기 때문입니다.

  • 해결 방법: Node.js 앱을 다시 시작하고 Nginx를 다시 로드하세요:

    pm2 restart all
    
    sudo systemctl reload nginx

    Nginx의 proxy_pass 값이 Node.js 애플리케이션의 수신 주소(예: http://localhost:3000)와 일치하는지 확인하세요.

4. 권한 거부 오류

  • 원인: 애플리케이션 사용자가 특정 파일이나 디렉터리를 읽거나 쓸 수 있는 권한이 없습니다.

  • 해결 방법: 올바른 소유권과 권한 설정:

    sudo chown -R $USER:$USER /var/www/myapp
    
    sudo chmod -R 755 /var/www/myapp

    Node.js나 PM2를 루트 사용자로 실행하지 마세요.

5. Certbot 갱신 실패

  • 원인: DNS 문제, 만료된 인증서, 또는 잘못된 Nginx 설정.

  • 해결 방법: DNS 레코드가 서버를 가리키는지 확인하고 갱신을 테스트하세요:

    sudo certbot renew --dry-run

    갱신에 실패하면, 구문 오류가 있는지 Nginx 설정을 검토하세요:

    sudo nginx -t

    자세한 오류는 /var/log/letsencrypt/에서 Certbot 로그를 확인할 수도 있습니다.

6. 방화벽이 액세스를 차단함

  • 원인: UFW 규칙이 들어오는 HTTP 또는 HTTPS 연결을 차단합니다.

  • 해결 방법: Nginx 트래픽이 허용되고 방화벽이 활성화되어 있는지 확인하세요:

    sudo ufw allow 'Nginx Full'
    
    sudo ufw enable
    
    sudo ufw status

문제 해결 작업 흐름

문제가 발생하면 체계적인 절차를 따르세요:

  1. 로그 확인: 대부분의 문제가 여기에서 오류 흔적을 남기므로 먼저 PM2, Nginx, 시스템 로그를 검토하세요.

  2. 서비스 확인: Node.js, PM2, Nginx가 실행 중이며 시작 시 활성화되어 있는지 확인하세요:

    pm2 status
    
    systemctl status nginx
  3. 구성 파일 검증: 서비스를 재시작하기 전에 구문 검사를 실행하세요:

    sudo nginx -t

    PM2 생태계 파일의 경우, JSON 또는 JavaScript 형식을 확인하세요.

  4. 포트 바인딩 확인: 앱이 예상된 포트에서 수신 대기 중인지 확인하세요:

    sudo netstat -tulnp | grep node
  5. 시스템 리소스 모니터링: htop 또는 pm2 monit와 같은 도구를 사용하여 CPU, 메모리, 디스크 사용량을 확인하세요. 서버 과부하는 느린 응답이나 시스템 충돌을 유발할 수 있습니다.

  6. 연결 상태 테스트: curl을 사용하여 요청을 시뮬레이션하고 엔드포인트가 올바르게 응답하는지 확인하세요:

    curl http://localhost:3000

안정성, 보안 및 유지 관리를 위한 모범 사례

모범 사례를 따르면 애플리케이션이 성장하면서도 신뢰할 수 있고, 안전하며, 유지 관리가 용이하게 유지됩니다.

보안 모범 사례

  • 시스템을 최신 상태로 유지: Node.js, PM2, Nginx 및 Ubuntu 패키지를 다음을 사용하여 정기적으로 업데이트하세요:

    sudo apt update && sudo apt upgrade -y

    이를 통해 보안 패치와 성능 향상이 적용됩니다.

  • 모든 곳에서 HTTPS 사용: Nginx 리디렉션과 Certbot을 사용한 자동 인증서 갱신을 통해 항상 HTTPS를 강제하세요.

  • SSH 접근 제한: 키 기반 인증을 사용하고, 루트 로그인은 비활성화하며, SSH 접근을 알려진 IP로 제한하세요.

  • 루트 사용자가 아닌 계정으로 실행: 문제가 발생할 경우 권한을 제한하기 위해 전용 애플리케이션 사용자를 사용하세요.

  • 스택 추적 및 오류 세부 정보 숨기기: 사용자가 자세한 오류 메시지를 보지 않도록 하세요. Express나 다른 프레임워크를 설정하여 오류를 안전하게 처리하도록 구성하세요.

  • 정기적으로 의존성을 감사하세요: 실행:

    npm audit

    취약한 패키지를 업데이트하거나 교체하십시오.

성능 및 자원 관리

  • 클러스터링 활성화: 사용 가능한 CPU 코어를 활용하여 여러 Node.js 인스턴스를 실행하려면 PM2를 사용하세요:

    pm2 start app.js -i max
  • 캐싱을 효과적으로 사용하세요: 자주 접근하는 데이터에 대해 Redis나 메모리 캐싱과 같은 캐싱 계층을 구현하세요.

  • 정기적으로 모니터링: PM2 Plus, Grafana 또는 Prometheus로 대시보드를 설정하여 성능 추세를 추적하세요.

  • 로그 최적화: 로그 회전을 활성화하여 디스크 사용 과다를 방지하고 관련 데이터만 유지하세요.

  • 응답 압축: 더 빠른 전달을 위해 Nginx에서 gzip 또는 Brotli 압축을 활성화 상태로 유지하세요.

유지보수 및 운영

  • 백업 자동화: 앱 데이터, 구성 파일 및 SSL 인증서의 백업을 예약하세요.
  • 버전 관리 사용: 구성과 스크립트를 Git 저장소에 저장하여 시간에 따른 변경 사항을 추적하세요.
  • 환경 문서화: 설정, 포트, 환경 변수 및 배포 단계를 자세히 문서화하세요.
  • 스테이징 환경 사용: 업데이트와 구성 변경 사항을 운영 서버에 적용하기 전에 스테이징 서버에서 테스트하세요.
  • 모니터 가동 시간: UptimeRobot, Pingdom, StatusCake와 같은 외부 가동 시간 모니터링 도구를 사용하여 장애를 신속하게 감지하세요.
  • 확장 계획: 트래픽이 증가함에 따라 로드 밸런서나 Docker, Kubernetes와 같은 컨테이너 오케스트레이션 도구를 사용한 수평 확장을 고려하세요.
  • 정기 감사 계획: 주기적으로 보안 설정, 성능 지표 및 환경 간 구성 일관성을 검토하세요.

지속적인 모니터링과 엄격한 보안 원칙 준수와 결합된 사전 예방적 유지보수 전략은 Node.js 애플리케이션이 운영 환경에서도 성능, 안정성, 내구성을 유지하도록 돕습니다.

자주 묻는 질문

1. Ubuntu에서 Node.js 앱을 프로덕션에 배포하려면 어떻게 해야 하나요?

프로덕션용 애플리케이션을 배포하려면 이 표준 워크플로를 따르세요. 터미널 세션에서 직접 node index.js를 실행하는 것은 피해야 합니다. 세션이 종료되면 애플리케이션도 중단되기 때문입니다.

일반적인 작업 흐름은 다음과 같습니다:

  1. 시스템 업데이트: sudo apt update && sudo apt upgrade를 실행하세요.

  2. 루트가 아닌 사용자 만들기: 앱을 root로 실행하지 마세요. 전용 사용자를 생성하세요(예: deploy).

  3. Node.js 설치: 최신 LTS 버전에는 NodeSource 바이너리 배포판을 사용하세요 (기본 Ubuntu apt 버전은 종종 오래되어 사용하지 마세요).

    curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
    
    sudo apt-get install -y nodejs
  4. 클론 및 설치: Node.js 앱이 포함된 레포지토리를 클론하고, 디렉토리로 이동한 후, 정확한 의존성 버전을 보장하기 위해 npm install 대신 npm ci (클린 설치)를 실행하세요.

  5. 환경 변수: 비밀 정보(DB 문자열, API 키)를 위해 .env 파일을 만드세요.

  6. 프로세스 매니저로 시작: node index.js를 실행하는 대신(터미널을 닫으면 중지됨) PM2를 사용하여 앱을 데몬화하세요:

    sudo npm install -g pm2
    
    pm2 start app.js --name "my-production-app"
  7. 시작 시 지속성 확보: 서버가 재부팅될 경우 이 앱을 재실행하도록 서버에 지시하세요:

    • 시작 스크립트 생성: 먼저, PM2에 특정 운영 체제(Ubuntu는 systemd 사용)에 맞는 시작 명령을 생성하도록 요청하세요.
    pm2 startup
    • 생성된 명령 실행: PM2가 출력한 명령을 복사하여 터미널에 붙여넣으세요. 명령은 다음과 비슷하게 보일 것입니다: sudo env PATH=` PATH:/home/sammy/.nvm/versions/node/v24.11.1/bin /home/sammy/.nvm/versions/node/v24.11.1/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

    • 프로세스 목록 동결: 이제 시작 시스템이 활성화되었으므로 현재 실행 중인 프로세스를 저장하여 PM2가 재부팅 시 이를 기억하도록 합니다.

    pm2 save

2. VPS에서 Node.js 프로세스를 관리하는 가장 좋은 방법은 무엇인가요?

가상 사설 서버(VPS)에서 Node.js 프로세스를 관리하기 위한 업계 표준은 PM2입니다.

직접 systemd를 사용할 수도 있지만, PM2는 일반 서비스 관리자가 갖추지 못한 Node.js 전용 기능을 제공합니다. 예를 들어:

  • 자동 재시작: 앱이 충돌하면 자동으로 다시 실행합니다.
  • 클러스터 모드: 코드를 변경하지 않고 모든 CPU 코어를 활용합니다.
  • 모니터링: RAM/CPU 사용량을 확인하는 내장 명령어 (pm2 monit).
  • 로그 관리: 디스크 과부하를 방지하기 위해 로그를 자동으로 순환합니다.

3. 프로덕션 환경에서 Node.js를 위해 PM2를 사용해야 할까요, 아니면 systemd를 사용해야 할까요?

가장 좋은 방법은 둘 다 사용하는 것입니다.

애플리케이션 관리는 PM2를, PM2 관리는 Systemd를 사용해야 합니다. PM2만 사용하면 서버가 재부팅될 경우 앱이 사라질 수 있습니다.

그들을 결합하는 방법:

  1. PM2로 앱 시작하기: pm2 start app.js
  2. systemd 시작 스크립트를 생성합니다: pm2 startup
  3. PM2가 출력하는 명령을 실행하세요(이 명령은 Ubuntu의 systemd에게 부팅 시 PM2를 시작하도록 알려줍니다).
  4. 프로세스 목록을 저장하려면: pm2 save

4. Node.js의 리버스 프록시로 Nginx를 어떻게 사용하나요?

Node.js는 애플리케이션 로직 처리에는 뛰어나지만, SSL, 정적 파일, 로드 밸런싱 처리에서는 Nginx에 비해 부족합니다.

설정:

  1. Nginx 설치: sudo apt install nginx.
  2. Nginx를 포트 80에서 수신하도록 설정하고 트래픽을 Node 앱(일반적으로 localhost:3000에서 실행 중)으로 전달하도록 구성하세요.

예제 구성 (/etc/nginx/sites-available/default):

server {
    listen 80;
    server_name your_domain.com;

    location / {
        proxy_pass http://localhost:3000; # Forward to Node
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

저장한 후, sudo nginx -t로 설정을 테스트하고 Nginx를 재시작하세요.

5. Nginx와 함께 Node.js에서 HTTPS를 어떻게 활성화하나요?

Node.js 애플리케이션 로직 내에서 HTTPS를 처리하지 마세요. 암호화/복호화(SSL 종료)는 Nginx가 처리하도록 하세요.

가장 쉬운 방법 (Certbot):

  1. Certbot 설치: sudo apt install certbot python3-certbot-nginx

  2. 자동 생성기를 실행하세요:

    sudo certbot --nginx -d your_domain.com
  3. Certbot은 HTTPS를 활성화하고 인증서를 자동 갱신하도록 크론 작업을 설정하기 위해 Nginx 구성 파일을 자동으로 편집합니다.

6. 우분투에서 Node.js 앱을 어떻게 안전하게 보호할 수 있나요?

보안은 다층적인 접근이 필요합니다.

  • 운영 체제 수준:

    • UFW 방화벽: 방화벽을 활성화하고 필요한 포트만 허용하세요.

      sudo ufw allow ssh
      
      sudo ufw allow 'Nginx Full'
      
      sudo ufw enable
    • SSH 강화: 비밀번호 로그인을 비활성화하고 SSH 키만 사용하세요.

  • 애플리케이션 수준:

    • 비루트 사용자로 실행: Q1에서 언급한 것처럼, 공격자가 루트 프로세스를 침해하면 서버의 권한을 완전히 갖게 됩니다.
    • Helmet.js: 이 미들웨어를 설치하세요 (npm install helmet) 앱에서 자동으로 보안 HTTP 헤더를 설정합니다.
    • 포트를 노출하지 마세요: Node 앱이 localhost(127.0.0.1)에 바인딩되도록 하여 인터넷에서 직접 접근할 수 없게 하고, 트래픽이 Nginx를 통해서만 접근되도록 하세요.

요약

이제 Ubuntu에서 Node.js 애플리케이션을 위한 완전한 프로덕션 환경을 설정했습니다. Node.js가 PM2로 실행되고, Nginx가 웹 트래픽을 관리하며, Let’s Encrypt의 SSL 암호화와 UFW가 네트워크를 보호하기 때문에 서버는 안정적이고 안전하며 실제 사용에 준비되어 있습니다. 캐싱, 압축, 적절한 모니터링을 추가함으로써 성능이 우수하고 유지 관리가 쉬운 시스템을 구축했습니다. 이 설정은 서버 확장, 배포 자동화, 또는 향후 더 발전된 툴 추가 등 애플리케이션을 성장시키기 위한 견고한 기반을 제공합니다.

Node.js에 대한 더 많은 튜토리얼은 다음 글들을 확인해 보세요:

  • 우분투에 Node.js 설치하는 방법 (단계별 가이드)
  • 우분투 20.04에서 Node.js와 함께 PostgreSQL 사용 방법

Linux-Console.net 커뮤니티와 함께 학습해 주셔서 감사합니다.