백엔드 제작이 완료 된 후 본격적으로 배포를 진행하게 됩니다. 배포를 할 때는 개발을 진행하는 것이 아니라 사용자가 직접적으로 서비스를 이용하는 부분이므로, 개발환경에서 배포 환경으로 전환을 해줘야 합니다. 배포판을 만들 때 필요한 순서와 요소들을 하나씩 집어봅니다.
console.log() 삭제
성능 향상
debugging 목적으로 가장 많이 사용하는 함수가 console.log() 입니다. 하지만 실제 서버를 돌릴 때 console.log()는 서버 성능 저하에 주요 요소로 작용합니다. 따라서 production 모드에서는 console.log()를 모두 삭제해줘야 합니다.
보안 이슈
서버에 남아있는 console.log()는 디버깅 목적으로 사용됩니다. 따라서 크리티컬한 정보들이 그대로 노출될 수 있기 때문에 반드시 삭제를 해줘야 합니다.
어떻게 삭제하나?
Node.js에서는 NODE_ENV 변수를 지원합니다. 기본적으로 production와 development 모드를 지원합니다.
우선 모드를 변경해줍니다. 터미널에서 아래 명령어를 입력합니다.
$ export NODE_END=production
이제 Node 애플리케이션에서 process.env.NODE_ENV로 production으로 설정된 환경변수에 접근할 수 있습니다. 예를 들어 환경변수에 따라 console.log() 출력을 제한한다고 하면 아래와 같이 코드를 작성할 수 있습니다. 이제 production에서는 console.log()가 찍히지 않습니다.
const production = process.env.NODE_ENV === 'production';
const development = process.env.NODE_ENV === 'development';
development && console.log('this is debugging');
하지만 위와 같은 방법은 임시방편일 뿐입니다. 서버 코드가 노출되기는 힘들겠지만 제일 좋은 방법은 배포판에서는 console.log()를 모두 삭제하는 방법이 가장 깔끔합니다.
gzip compression 사용
서버에서 사용자 혹은 프론트엔드로 body를 반환해주는 과정에서 상당한 오버헤드가 발생합니다. 서버 응답 속도를 개선하기 위해서는 response 데이터를 압축해서 반환하는 것입니다. Node.js에서는 압축 미들웨어로 gzip을 제공합니다.
const compression = require('compression')
const express = require('express')
const app = express()
app.use(compression())
압축 미들웨어를 Express 서버에서 바로 적용할 수도 있지만 리버스 프록시를 사용한다면 프록시 수준에서 압축 방법을 구현하는게 훨씬 효율적입니다.
예외처리 적용
이 부분은 사실 개발 환경에서 모두 잡아내야 합니다. 배포 환경에서는 깔끔하게 정상작동하는 코드만 올려야 합니다. 기본적으로 예외 처리를 하기 위해서 사용되는 방법이 try~catch 문입니다.
추가적으로 Express에서는 미들웨어 레벨에서 next() 함수를 제공하고 있습니다. 따라서 예외가 발생했을 때 핸들링을 해줄 수 있도록 next()로 전파 후 최상단 레벨에서 next()에 대한 예외 처리를 해줄 수 있는 코드를 작성해주는 것이 좋습니다.
최악의 상황은 uncaughtException 이벤트를 걸어버리는 것입니다. uncaughtException는 Node.js 환경에서 모든 에러를 잡아내는 이벤트 키워드 입니다. 이렇게 되면 에러가 발생했음에도 불구하고 Node는 계속 작동하게 되고, 올바르지 않은 로직으로 서버가 작동하게 됩니다.
NODE_ENV = production 설정
Node.js에서 NODE_ENV를 production으로 설정해주는 것은 단순히 환경변수만 변경하는 것이 아닙니다. production으로 환경변수가 바뀌면 Express에서는 다음 기능을 수행합니다.
- 캐시 View 템플릿
- CSS 캐시
- 보다 덜 자세한 에러 메세지 출력
신뢰할 수 있는 수치인지는 모르겠지만 단순히 NODE_ENV를 production으로 변경해주는 작업만 해도 성능이 3배나 개선된다고 공식문서에서는 설명하고 있습니다. 여기서 주의사항은 위의 console.log() 함수를 거를 때 처럼 process.env.NODE_ENV를 직접 조회하면 성능 저하가 발생할 수 있습니다.
PM2 (process manager) 사용
Node.js는 기본적으로 싱글 스레드로 구동됩니다. PM2를 사용하는 가장 큰 목적은 Node.js를 멀티 스레드 환경에서 작동하도록 지원하는 것입니다. 서버 CPU의 코어 수만큼 프로세스를 생성해서 서버를 돌리고, 성능을 향상시킬 수 있습니다. PM2에서 cluster 모드로 작동시키면 됩니다.
PM2 사용법은 생각보다 간단합니다. 우선 pm2를 전역으로 설치해줍니다.
$ npm install -g pm2
① ecosystem.config.js 파일을 생성합니다.
//ecosystem.config.js
module.exports = {
apps: [{
name: 'server',
script: './index.js',
instances: 0,
exec_mode: 'cluster',
wait_ready: true,
listen_timeout: 50000
}]
}
- script : 서버 index.js 파일입니다.
- instance : 0을 입력하면 서버 CPU만큼의 프로세서가 생성됩니다.
- exec_mode : cluster 모드는 멀티 스레드 환경을 지원합니다.
- wait_ready : process.send('ready') 가 들어왔을 때 PM2가 작동합니다.
- listen_timeout : 수신 시간제한을 설정합니다.
② 이제 터미널에서 PM2를 실행해주면 됩니다.
$ pm2 start ecosystem.config.js
③ 만약 서버 파일이 변경되었을 때 바로 수정사항을 적용하려면 --watch 옵션을 주면 됩니다.
$ pm2 start ecosystem.config.js --watch
④ 현재 작동중인 pm2를 모니터링 할 수 있습니다.
$ pm2 monit
⑤ 현재 실행중인 pm2 목록을 확인할 수 있습니다.
$ pm2 status
무중단 배포를 위해서 PM2의 restart 혹은 reload 명령을 사용할 수 있습니다.
- restart : 프로세스를 완전히 중단했다가 다시 시작합니다. 잠깐 이지만 배포의 공백이 발생할 수 있습니다.
- reload : 배포를 다시 하는 과정에서 다운 시간을 zero에 가깝게 유지할 수 있습니다.
ecosystem.config.js 파일에서 name 항목에 적었던 앱 이름으로 pm2를 재시작 할 수 있습니다.
$ pm2 restart server
$ pm2 reload server
기타 미들웨어 설정
여기까지 왔으면, 이제 기타 미들웨어들의 환경을 설정해줍니다.
예를 들어 morgan 미들웨어를 사용한다면 'dev' 옵션을 'combined'으로 변경한다던지, cors 미들웨어를 사용한다면 실제 배포된 AWS IP를 입력해주는 등의 미들웨어 설정을 배포 환경으로 변경해줘야 합니다.
로드 밸런서 + 리버스 프록시
서버를 클러스터링화 해서 메인 인스턴스에서 받은 부하를 다른 인스턴스들에게 분산하면서 성능을 올릴 수 있지만 결국 단일 인스턴스가 처리할 수 있는 트래픽의 양은 제한적입니다.
이 문제를 개선하기 위해서는 리버스 프록시를 사용해야 합니다.
Reverse Proxy는 사용자와 웹앱 사이에 위치하게 되며 오류 페이지, 압축, 캐싱, 파일 제공, 로드 밸런싱 등의 작업을 수행합니다. 현재 가장 부하를 적게 받고 있는 upstream에 트래픽을 분산처리해줄 수 있고, 방화벽을 둘러싸서 DB가 털리는 일을 미연에 방지할 수 있습니다.
Reverse Proxy는 Nginx에서 처리할 수 있습니다.
🚀 도움이 되셨다면 구독 + 좋아요 부탁드립니다.
👍 감사합니다.
Reference
- Express 프로덕션 배포 방법 https://bit.ly/3f7e7Uy
'Programming' 카테고리의 다른 글
React .env 사용법 (0) | 2022.09.25 |
---|---|
Error: ENOSPC: System limit for number of file watchers reached 문제 해결 (0) | 2022.09.25 |
Reverse Proxy VS Forward Proxy 차이점 (0) | 2022.09.20 |
Nginx 란? 웹 서버 개념 이해하기 (1) | 2022.09.20 |
bash: sudo: command not found 해결 (0) | 2022.09.20 |
댓글