미들웨어를 개발용에서 배포용으로 변경
1. app.js : morgan
//변경 전
app.use(morgan('dev'));
//변경 후
if (process.env.NODE_ENV === 'production') {
app.use(morgan('combined'));
} else {
app.use(morgan('dev'));
}
2. app.js : express-session
//변경 전
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
}));
//변경 후
const sessionOption = {
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
};
if (process.env.NODE_ENV === 'production') {
sessionOption.proxy = true;
// sessionOption.cookie.secure = true;
}
app.use(session(sessionOption));
데이터베이스 배포 환경으로 변경 -> 시퀄라이즈 수정 필요
1. config/config.json 삭제 -> config/config.js 생성
require('dotenv').config();
module.exports = {
development: {
username: 'root',
password: process.env.SEQUELIZE_PASSWORD, //
database: 'nodebird',
host: '127.0.0.1',
dialect: 'mysql',
},
test: {
username: "root",
password: process.env.SEQUELIZE_PASSWORD, //
database: "nodebird_test",
host: "127.0.0.1",
dialect: "mysql"
},
production: {
username: 'root',
password: process.env.SEQUELIZE_PASSWORD, //
database: 'nodebird',
host: '127.0.0.1',
dialect: 'mysql',
logging: false,
},
};
process.env(환경변수) 동적으로 변경 가능
모든 운영체제에서 동일한 방법으로 환경 변수 변경 가능
1. package.json
//변경 전
{
.....
"scripts": {
"start": "nodemon server",
"test": "jest"
},
.....
}
//변경 후
{
.....
"scripts": {
"start": "NODE_ENV=production PORT=80 node server", //동적 설정
"dev": "nodemon server", //
"test": "jest"
},
.....
}
서버실행을 위한 스크립트 두 개로 분류
리눅스나 맥에서는 위 방식으로 process.env 동적 설정 가능하지만, 윈도우의 경우 불가능!
윈도우의 경우,
npm i cross-env
//변경 후(윈도우)
{
.....
"scripts": {
"start": "cross-env NODE_ENV=production PORT=80 node server", //동적 설정(윈도우)
"dev": "nodemon server", //
"test": "jest"
},
.....
}
npm i sanitize-html
npm i csurf
1. sanitize-html
XSS
악의적인 사용자가 사이트에 스크립트를 삽입하는 공격,
악성 사용자가 게시글이나 댓글 등을 업로드할 때 자바스크립트가 포함된 태그를 올리면, 나중에 다른 사용자가 그 게시글이나 댓글을 볼 때 그 스크립트가 실행되어서 예기치 못한 동작을 하게 됨 -> 서버에서 사용자가 게시글 업로드할 때 스크립트 포함 여부를 검사하여 제거해야 함(공격성 스크립트 유형이 많으므로 라이브러리의 도움을 받는 것이 좋음)
const sanitizeHtml = require('sanitize-html');
const html = "<script>location.href = 'https://gilbut.co.kr'</script>";
console.log(sanitizeHtml(html); //''
https://www.npmjs.com/package/sanitize-html
2. csurf
CSRF
사용자가 의도치 않게 공격자가 의도한 행동을 하게 만드는 공격,
ex)특정 페이지에 방문할 때 저절로 로그아웃되거나, 게시글이 써지는 현상을 유도할 수 있음, 은행과 같은 사이트에서는 다른 사람에게 송금하는 행동을 넣는 등 상황에 따라 크게 악용될 수 있음
이에 대한 조치로 내가 한 행동이 내가 한 것이 맞다는 점을 인증해야 함 -> 이때 CSRF 토큰 사용, csurf 패키지는 이를 쉽게 발급하거나 검증할 수 있도록 도움
//공식 문서에서 발췌한 예제
const csrf = require('csurf');
const csrfProtection = csrf({cookie: true});
app.get('/form', csrfProtection, (req, res) => {
res.render('csrf', {csrfToken: req.csrfToken()});
});
app.post('/form', csrfProtection, (req, res) =>{
res.send('ok');
});
https://www.npmjs.com/package/csurf
원활한 서버 운영을 위한 패키지
"개발할 때 nodemon을 쓴다면, 배포할 때는 pm2를 쓴다"
주요기능
pm2의 멀티 프로세싱
멀티 스레딩은 아니지만 멀티 프로세싱 지원으로 노드 프로세스 개수를 한 개 이상으로 늘릴 수 있음.
pm2를 사용하여 여러개의 프로세스를 생성하면 다른 코어들도 사용 가능.
클라이언트로부터 요청이 올 때 알아서 요청을 여러 노드 프로세스에 고르게 분배하여 하나의 프로세스가 받는 부하가 적어져 서비스 더 원할하게 운영 가능.
그러나, 멀티 스레딩이 아니므로 서버의 메모리 같은 자원을 공유하지 못한다는 단점이 있음 -> 멤캐시드, 레디스 서비스 사용으로 극복
pm2는 nodemon과 같이 콘솔에 입력하는 명령어
npm i pm2
1-1. package.json :
{
.....
"scripts": {
"start": "cross-env NODE_ENV=production PORT=80 pm2 start server.js", //node server
"dev": "nodemon server",
"test": "jest"
},
.....
}
콘솔에 npm start 입력하면 바뀐 start 스크립트 명령어로 실행
pm2 실행하면 node나 nodemon 명령어와는 달리 노드 프로세스가 실행된 후에 콘솔에 다른 명령어 입력 가능,
pm2가 노드 프로세스를 백그라운드로 돌리므로 가능한 것임
백그라운드에서 돌고 있는 노드 프로세스를 확인하려면 콘솔에
npx pm2 list
pm2 관련 추가 명령어
npx pm2 logs : 프로세스 로그 확인(-err, --lines 옵션 추가 가능)
npx pm2 kill : pm2 프로세스 종료
npx pm2 reload all : 서버 재시작(다운타임이 거의 없이 서버가 재시작되어 좋음)
npx pm2 monit : 현재 프로세스 모니터링
*다운타임: 서버가 중지되어 클라이언트가 접속할 수 없는 시간
1-2. package.json : pm2의 클러스터링 모드 사용
{
.....
"scripts": {
"start": "cross-env NODE_ENV=production PORT=80 pm2 start server.js -i 0", //
"dev": "nodemon server",
"test": "jest"
},
.....
}
pm2 start server.js -i 0 / pm2 start server.js -i -1
-i 뒤에 생성하길 원하는 프로세스 개수 기입,
0은 현재 CPU코어 개수만큼 프로세스 생성,
-1은 프로세스를 CPU코어 개수보다 한 개 덜 생성 : 노드 외의 다른 작업을 할 수 있게 하기 위한 코어를 남김
실제 서버를 운영할 때 console.log와 console.error를 대체하기 위한 모듈
console.log와 console.error를 개발 중에는 편리하지만, 실제 배포 시에는 console 객체의 메서드들이 언제 호출되었는지 파악하기 힘들 뿐만 아니라 서버가 종료되는 순간 로그들도 사라진다는 점에서 좋지 않음. 이를 방지하기 위해서는 로그를 파일이나 다른 데이터베이스에 저장해야 하는데, 이때 winston 사용
1. winston 설치
npm i winston
2. logger.js 생성
const { createLogger, format, transports } = require('winston'); //winston 패키지
const logger = createLogger({ //createLogger 메서드로 logger 생성&설정
level: 'info',
format: format.json(),
transports: [
new transports.File({ filename: 'combined.log' }),
new transports.File({ filename: 'error.log', level: 'error' }),
],
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new transports.Console({ format: format.simple() }));
}
module.exports = logger;
logger 설정
위 과정으로 생성 및 설정한 logger 객체는 다른 파일에서도 사용 가능,
info, warn, error 등의 메서드를 사용하면 해당 심각도가 적용된 로그가 기록됨.
//app.js
.......
const logger = require('./logger'); //logger 객체 불러와
.......
app.use((req, res, next) => {
const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
error.status = 404;
logger.info('hello'); //심각도 info에 대한 로그 기록
logger.error(error.message); //심각도 error에 대한 로그 기록
next(error);
});
.......
winston-daily-rotate-file 패키지
로그를 날짜별로 관리할 수 있게 해주는 패키지
서버의 각종 취약점을 보완해주는 패키지,
익스프레스 미들웨어로서 사용할 수 있음,
그러나 이 패키지들을 사용한다고 해서 모든 취약점을 방어해주는 것은 아니므로 서버를 운영할 때는 주기적으로 취약점을 점검해야 함,
개발 환경에서는 사용할 필요가 없으므로 배포 환경일 때만 적용하면 됨
npm i helmet hpp
//app.js
........
const helmet = require('helmet'); //
const hpp = require('hpp'); //
........
if (process.env.NODE_ENV === 'production') {
app.use(morgan('combined'));
app.use(helmet()); //
app.use(hpp()); //
} else {
app.use(morgan('dev'));
}
........
멀티 프로세스 간 세션 공유를 위해 레디스와 익스프레스를 연결해주는 패키지
레디스 redis
기존에는 로그인할 때 express-session의 세션 아이디와 실제 사용자 정보가 메모리에 저장되어, 서버가 종료되면 메모리가 날아가면서 접속자들의 로그인이 모두 풀려버림. 이를 방지하기 위해 세션 아이디와 실제 사용자 정보를 데이터베이스에 저장하는데, 이때 사용하는 데이터베이스가 레디스. 메모리 기반의 데이터베이스로 성능 우수.
1. connect-redis와 redis 데이터베이스 설치
npm i redis connect-redis
2. redislabs: 레디스를 호스팅해주는 서비스
..........
const redis = require('redis');
const RedisStore = require('connect-redis')(session);
dotenv.config();
const redisClient = redis.createClient({
url: `redis://${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`,
password: process.env.REDIS_PASSWORD,
});
..........
app.use(cookieParser(process.env.COOKIE_SECRET));
const sessionOption = {
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
store: new RedisStore({ client: redisClient }),
};
..........
이제, 세션 정보가 메모리 대신 레디스에 저장되어 로그인 후에 서버를 껐다 켜도 로그인이 유지됨!
노드 버전을 업데이트하기 위한 패키지
윈도우에서는 nvm-installer 사용, 리눅스나 맥에서는 n패키지 사용
1. 윈도우
https://github.com/coreybutler/nvm-windows/releases
위 페이지에 접속하여 nvm-setup.zip 다운, 압축 해제한 후 실행시켜 설치
관련 명령어
nvm list : 설치된 노드 버전 확인
nvm install [버전] : 새로운 버전 설치
nvm install latest : 최신 버전으로 설치
nvm use [버전] : 설치된 버전 사용
2. 맥, 리눅스
sudo npm i -g n
관련 명령어
n : 현재 설치된 노드 버전 확인, 설치된 버전 선택 시에도 사용
n [버전] : 입력한 버전 설치
n latest : 최신 버전 설치
3. 업그레이드 후 npm 충돌 시
Error: The module '모듈명' was compiled against a different Node.js version using NODE_MODULE_VERSION 이전 버전. This version of Node.js requires NODE_MODULE_VERSION 현재버전. Please try re-compiling or re-installing the module (for instance, using npm rebuild or npm install).
깃: 분산형 버전 관리 시스템(실무에서는 협업, 코드 롤백, 배포 자동화 등 다양한 용도로 사용)
깃허브: 깃으로부터 업로드한 소스 코드를 서버에 저장할 수 있는 원격 저장소(코드 공동 관리 가능)
.gitignore 파일
node_modules
uploads
*.log
coverage
실무에서는 .env파일도 추가(보안)
mysql 설치 7.2.3절 참조
sudo apt-get update
sudo apt-get install -y mysql-server
sudo mysql_secure_intallation
mysqladmin -u root -p password 비밀번호
git clone [url] : 깃허브 소스코드 내려받기
비트나미 아파치 서버 종료
cd /opt/bitnami
sudo ./ctlscript.sh stop apache
cd ~/node-deploy : 폴더 이동
npm i : npm 패키지 설치
npx sequelize db:create --env production : 시퀄라이즈로 mysql 데이터베이스 생성
sudo npm start : 서버 실행
이제 퍼블릭IP로 접속 가능
https://console.cloud.google.com/
GCP 계정 등록 화면으로 전환 > 정보 입력 > 계정 유형과 주소 입력 > 휴대폰 인증 > 카드 등록
SSH 실행
sudo su : 루트 계정으로 변경
노드와 mysql 설치(우분투 1.4.1.3절과 7.2.3절 참조)
git clone [url] : 깃허브 소스코드 내려받기
node-deploy 폴더 생성
혹시 비밀번호를 올바르게 설정했는데도 sequelize명령어에서 ERROR: Access denied for user 'root'@'localhost' 에러가 발생한다면,
mysql 프롬프트에 다음 명령어 입력하여 비밀번호 변경
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '비밀번호';
cd node-deploy
sudo npm i
npx sequelize db:create --env production
sudo npm start
[Node.js]16장 서버리스 노드 개발 (0) | 2022.01.31 |
---|---|
[Node.js] 14장 CLI 프로그램 만들기 (0) | 2022.01.24 |
[Node.js] 13장 실시간 경매 시스템 만들기 (0) | 2022.01.17 |
[Node.js] 12장(2) 미들웨어와 소켓 연결하기 (0) | 2022.01.10 |
[Node.js] 12장 웹소켓으로 실시간 데이터 전송하기(1) (0) | 2022.01.06 |