πŸš€ λ™μ‹œ 접속 1만 λͺ…을 κ²¬λ””λŠ” API μ„œλ²„ 직접 λ§Œλ“€μ–΄λ΄€λ‹€

“λ™μ‹œ 접속 1만 λͺ…, 24/7 무쀑단 운영, 배포해도 λŠκΉ€ μ—†μŒ.”

이 μ„Έ κ°€μ§€ μš”κ΅¬μ‚¬ν•­μ„ μ½μ—ˆμ„ λ•Œ μ†”μ§νžˆ λ­” μ†Œλ¦¬μΈμ§€ 잘 λͺ°λžμŠ΅λ‹ˆλ‹€. κ·Έλƒ₯ μ„œλ²„ ν•˜λ‚˜ 켜면 λ˜λŠ” κ±° μ•„λ‹Œκ°€μš”? 이번 λ―Έμ…˜μ„ 톡해 κ·Έ 닡을 직접 μ†μœΌλ‘œ λ§Œλ“€μ–΄λ΄€μŠ΅λ‹ˆλ‹€. κ²°λ‘ λΆ€ν„° λ§ν•˜λ©΄, μ„œλ²„λ₯Ό μ£½μ§€ μ•Šκ²Œ λ§Œλ“œλŠ” 건 생각보닀 훨씬 μ •κ΅ν•œ μž‘μ—…μ΄μ—ˆμŠ΅λ‹ˆλ‹€.


πŸ“‹ λ―Έμ…˜ λ°°κ²½κ³Ό 핡심 과제

μ‹œλ‚˜λ¦¬μ˜€: κ°œλ°œνŒ€μž₯이 λͺ¨λ°”일 μ•± μΆœμ‹œλ₯Ό μ•žλ‘κ³  REST API μ„œλ²„ ꡬ좕을 μš”μ²­ν–ˆμŠ΅λ‹ˆλ‹€. 1만 λͺ… λ™μ‹œ 접속, 24/7 운영, 무쀑단 배포가 μš”κ΅¬μ‚¬ν•­μž…λ‹ˆλ‹€.

μ΄λ²ˆμ— μŒ“μ€ 기술 μŠ€νƒμ€ μ΄λ ‡μŠ΅λ‹ˆλ‹€.

κΈ°μˆ μ—­ν• λΉ„μœ 
Node.js + ExpressAPI μ„œλ²„ μ—”μ§„μ£Όλ°©
PM2 (Cluster Mode)ν”„λ‘œμ„ΈμŠ€ κ΄€λ¦¬μžμ£Όλ°©μž₯ β€” μš”λ¦¬μ‚¬ μ—¬λŸΏμ„ 관리
Nginx (Reverse Proxy)λ‘œλ“œλ°ΈλŸ°μ„œν™€ λ§€λ‹ˆμ € β€” μ†λ‹˜μ„ 빈 ν…Œμ΄λΈ”λ‘œ μ•ˆλ‚΄
Redis고속 λ©”λͺ¨λ¦¬ μ €μž₯μ†Œμ‘°λ¦¬λŒ€ μœ„ μž¬λ£Œν†΅ β€” λΉ λ₯Έ 곡유 데이터

Step 1: Node.js μ„€μΉ˜ πŸ“¦

μ΅œμ‹  버전(v20)을 μ„€μΉ˜ν•˜κΈ° μœ„ν•΄ NodeSource μ €μž₯μ†Œλ₯Ό λ¨Όμ € λ“±λ‘ν•©λ‹ˆλ‹€.

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install nodejs -y

npm(νŒ¨ν‚€μ§€ λ§€λ‹ˆμ €)도 ν•¨κ»˜ μ„€μΉ˜λ©λ‹ˆλ‹€.


Step 2: Express API μ„œλ²„ κ΅¬ν˜„ βš™οΈ

API μ„œλ²„ μ½”λ“œκ°€ λ“€μ–΄κ°ˆ 디렉터리λ₯Ό λ§Œλ“€κ³ , ν•„μš”ν•œ νŒ¨ν‚€μ§€λ₯Ό μ„€μΉ˜ν•©λ‹ˆλ‹€.

sudo mkdir -p /opt/techflow-api
cd /opt/techflow-api
npm init -y
npm install express cors helmet compression morgan dotenv

핡심 νŒŒμΌμ€ 두 κ°œμž…λ‹ˆλ‹€.

app.js β€” μ„œλ²„ κ·Έ μžμ²΄μž…λ‹ˆλ‹€. μš”μ²­μ„ λ°›κ³  μ²˜λ¦¬ν•˜λŠ” λͺ¨λ“  둜직이 여기에 μžˆμŠ΅λ‹ˆλ‹€.

  • /health : μ„œλ²„κ°€ μ‚΄μ•„μžˆλŠ”μ§€ 확인 (λ‘œλ“œλ°ΈλŸ°μ„œκ°€ 주기적으둜 체크)
  • /api/users : μ‚¬μš©μž λͺ©λ‘ 쑰회
  • POST /api/users : μ‹ κ·œ μ‚¬μš©μž μΆ”κ°€

.env β€” ν™˜κ²½ λ³€μˆ˜ νŒŒμΌμž…λ‹ˆλ‹€. 포트 λ²ˆν˜Έλ‚˜ DB μ£Όμ†Œμ²˜λŸΌ ν™˜κ²½λ§ˆλ‹€ λ‹¬λΌμ§€λŠ” 값을 μ½”λ“œμ™€ λΆ„λ¦¬ν•΄μ„œ κ΄€λ¦¬ν•©λ‹ˆλ‹€.

μ‚¬μš©μž λͺ©λ‘ 쑰회 및 데이터 μΆ”κ°€ ν…ŒμŠ€νŠΈ

ν…ŒμŠ€νŠΈκ°€ λλ‚˜λ©΄ λ°˜λ“œμ‹œ ν”„λ‘œμ„ΈμŠ€λ₯Ό μ’…λ£Œν•΄μ•Ό ν•©λ‹ˆλ‹€. λ‹€μŒ λ‹¨κ³„μ—μ„œ PM2κ°€ λŒ€μ‹  관리할 κ±°λΌμ„œ, μ§€κΈˆ μΌœλ‘” 것과 좩돌이 λ‚©λ‹ˆλ‹€.


Step 3: PM2 ν΄λŸ¬μŠ€ν„° λͺ¨λ“œ β€” μ½”μ–΄λ₯Ό μ „λΆ€ 써라! πŸ–₯️

Node.jsλŠ” μ‹±κΈ€ μŠ€λ ˆλ“œμž…λ‹ˆλ‹€. 아무리 쒋은 μ„œλ²„μ—¬λ„ CPU μ½”μ–΄ ν•˜λ‚˜λ§Œ μ”λ‹ˆλ‹€. 1만 λͺ…이 λ™μ‹œμ— λ“€μ–΄μ˜€λ©΄? 쀄이 μ—„μ²­ κΈΈμ–΄μ§€μ£ .

PM2 ν΄λŸ¬μŠ€ν„° λͺ¨λ“œλŠ” 이 문제λ₯Ό ν•΄κ²°ν•©λ‹ˆλ‹€. CPU μ½”μ–΄ 수만큼 Node.js ν”„λ‘œμ„ΈμŠ€λ₯Ό λ³΅μ œν•΄μ„œ λ³‘λ ¬λ‘œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

npm install -g pm2

μ„€μ • 파일 ecosystem.config.jsλ₯Ό λ§Œλ“€μ–΄ PM2μ—κ²Œ μ„œλ²„ 관리 방법을 μ•Œλ €μ€λ‹ˆλ‹€.

module.exports = {
  apps: [{
    name: 'techflow-api',
    script: 'app.js',
    instances: 'max',      // CPU μ½”μ–΄ 수만큼 μžλ™ 볡제
    exec_mode: 'cluster',  // ν΄λŸ¬μŠ€ν„° λͺ¨λ“œ ν™œμ„±ν™”
    max_memory_restart: '500M',
    env_production: {
      NODE_ENV: 'production',
      PORT: 3000
    }
  }]
};

PM2 ν΄λŸ¬μŠ€ν„° λͺ¨λ“œ μ‹€ν–‰ ν™”λ©΄

λ‘œλ“œλ°ΈλŸ°μ‹± 검증 β€” CPU별 μš”μ²­ λΆ„μ‚° 확인

PM2λ₯Ό μ‹œμŠ€ν…œ μ„œλΉ„μŠ€λ‘œ λ“±λ‘ν•˜λ©΄ μ„œλ²„κ°€ μž¬λΆ€νŒ…λ˜μ–΄λ„ μžλ™μœΌλ‘œ μ‹œμž‘λ©λ‹ˆλ‹€.

PM2 μ‹œμŠ€ν…œ μ„œλΉ„μŠ€ 등둝 ν™”λ©΄


Step 4: Nginx λ‘œλ“œλ°ΈλŸ°μ„œ β€” ꡐ톡 μ •λ¦¬μ˜ 달인 πŸ”€

PM2κ°€ μ—¬λŸ¬ ν”„λ‘œμ„ΈμŠ€λ₯Ό κ΄€λ¦¬ν•˜μ§€λ§Œ, μ™ΈλΆ€μ—μ„œλŠ” μ—¬μ „νžˆ νŠΉμ • 포트둜만 μ ‘μ†ν•©λ‹ˆλ‹€. Nginxκ°€ λ¦¬λ²„μŠ€ ν”„λ‘μ‹œ 역할을 ν•΄μ„œ λ“€μ–΄μ˜€λŠ” μš”μ²­μ„ κ°€μž₯ μ—¬μœ λ‘œμš΄ ν”„λ‘œμ„ΈμŠ€μ— λ„˜κ²¨μ€λ‹ˆλ‹€.

upstream techflow_backend {
    least_conn;               # 연결이 κ°€μž₯ 적은 μ„œλ²„ μš°μ„ 
    server 127.0.0.1:3000;
    keepalive 64;
}

SSL μΈμ¦μ„œλ„ Nginxμ—μ„œ μ²˜λ¦¬ν•˜μ—¬, μ‹€μ œ Node.js μ„œλ²„λŠ” μ•”ν˜Έν™” λΆ€λ‹΄ 없이 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—λ§Œ 집쀑할 수 μžˆμŠ΅λ‹ˆλ‹€.

Nginx μ„€μ • 파일 μž‘μ„± ν™”λ©΄

λ‘œλ“œλ°ΈλŸ°μ„œ λ™μž‘ ν…ŒμŠ€νŠΈ κ²°κ³Ό


Step 5: Redis + 무쀑단 배포 ν…ŒμŠ€νŠΈ πŸ”„

RedisλŠ” μ—¬λŸ¬ Node.js ν”„λ‘œμ„ΈμŠ€ 간에 μ„Έμ…˜μ΄λ‚˜ μΊμ‹œ 데이터λ₯Ό κ³΅μœ ν•˜κΈ° μœ„ν•΄ μ„€μΉ˜ν•©λ‹ˆλ‹€. λ©”λͺ¨λ¦¬μ—μ„œ λ™μž‘ν•΄μ„œ DB보닀 훨씬 λΉ λ¦…λ‹ˆλ‹€.

Redis μ„€μΉ˜ 및 확인, 데이터 μ €μž₯ ν…ŒμŠ€νŠΈ

이제 κ°€μž₯ μ€‘μš”ν•œ ν…ŒμŠ€νŠΈμž…λ‹ˆλ‹€. 무쀑단 배포(Zero-Downtime Deployment) β€” μ„œλΉ„μŠ€ 쀑에 μ½”λ“œλ₯Ό 바꿔도 μ‚¬μš©μžλŠ” λŠκΉ€μ„ λŠλΌμ§€ λͺ»ν•˜λŠ” κ±°μ£ .

터미널 두 개λ₯Ό μ—΄μ–΄μ„œ λ™μ‹œμ— μ§„ν–‰ν•©λ‹ˆλ‹€.

터미널 1: 0.5μ΄ˆλ§ˆλ‹€ μ„œλ²„μ— μš”μ²­μ„ 보내며 응닡을 λͺ¨λ‹ˆν„°λ§ν•©λ‹ˆλ‹€.

while true; do
  result=$(curl -sk https://api.techflow.local/health 2>/dev/null)
  echo "$(date '+%H:%M:%S') - OK - $(echo $result | grep -o '"pid":[0-9]*')"
  sleep 0.5
done

터미널 2: PM2λ₯Ό 톡해 무쀑단 μž¬μ‹œμž‘ λͺ…령을 λ‚ λ¦½λ‹ˆλ‹€.

pm2 reload techflow-api

무쀑단 배포 λͺ¨λ‹ˆν„°λ§ ν™”λ©΄

μž¬μ‹œμž‘ μ „ν›„ PID λ³€κ²½ 확인

μž¬μ‹œμž‘ μ „ 5879, 5892이던 PIDκ°€ μž¬μ‹œμž‘ ν›„ 5979, 6298둜 λ°”λ€Œμ—ˆμ§€λ§Œ, λͺ¨λ‹ˆν„°λ§ ν™”λ©΄μ—λŠ” FAIL이 ν•œ λ²ˆλ„ μ—†μ—ˆμŠ΅λ‹ˆλ‹€. μ„±κ³΅μž…λ‹ˆλ‹€!


🏁 μ΅œμ’… 점검

node -v                                          # Node.js μ„€μΉ˜ 확인
pm2 status | grep "online"                       # PM2 정상 μ‹€ν–‰
systemctl is-active nginx                        # Nginx μ‹€ν–‰ 쀑
curl -sk https://api.techflow.local/health       # HTTPS 응닡 확인
redis-cli ping                                   # PONG 응닡 확인

μ΅œμ’… 점검 μ‹€ν–‰ κ²°κ³Ό ν™”λ©΄


πŸ’‘ λ―Έμ…˜μ„ 마치며

이번 λ―Έμ…˜μ˜ 핡심 λ©”μ‹œμ§€λŠ” ν•˜λ‚˜μž…λ‹ˆλ‹€.

μ„œλ²„ ν•˜λ‚˜λ‘œλŠ” λΆ€μ‘±ν•˜λ‹€. 잘 μ„€κ³„λœ ꡬ쑰가 μžˆμ–΄μ•Ό λŒ€κ·œλͺ¨ νŠΈλž˜ν”½μ„ 버틸 수 μžˆλ‹€.

Node.js(μ£Όλ°©) + PM2(μ£Όλ°©μž₯) + Nginx(홀 λ§€λ‹ˆμ €) + Redis(곡유 μž¬λ£Œν†΅)의 쑰합은, λ‹¨μˆœν•œ μ„œλ²„ ν•˜λ‚˜λ³΄λ‹€ 훨씬 κ°•λ ₯ν•˜κ³  탄λ ₯적인 μ‹œμŠ€ν…œμ„ λ§Œλ“€μ–΄λƒ…λ‹ˆλ‹€. 특히 무쀑단 배포λ₯Ό 직접 눈으둜 ν™•μΈν–ˆμ„ λ•Œμ˜ 감동은 아직도 κΈ°μ–΅λ‚©λ‹ˆλ‹€. “μ•„, μ΄λž˜μ„œ PM2λ₯Ό μ“°λŠ”κ΅¬λ‚˜!” πŸŽ‰