본문 바로가기

BackEnd/Node

[노드교과서] 섹션 14. 15장 AWS에 배포해보기

15.1 서비스 운영을 위한 패키지

 1. 실 서비스 배포 준비하기

  ⇒ 서비스 개발 후 외부환경인 AWS에 배포준비 과정을 정리

   - 서버 실행 관리, 에러 내역관리, 보안 위협 대처

 2. morgan

  ⇒ 개발용으로 설정된 익스프레스 미들웨어를 배포용으로 전환한다.

   - process.env.NODE_ENV를 통해 배포환경인지 배포환경인지를 판단

   - 배포 환경일 때는 combined를 사용한다(dev보다 더 많은 사용자 정보를 남김)

if (process.env.NODE_ENV === 'production') {
	app.use(morgan('combined'));
} else {
	app.use(morgan('dev'));
}

 3. express-session

  ⇒ 기존 설정들을 배포용과 개발용으로 분기 처리

   - session 옵션 중 proxy속성은 배포용일때는 true로 설정한다. (기본값 : false)

   - proxy 속성여부 : 보안쿠키를 설정 할 때 역방향 프록시를 신뢰한다.( "X-Forwarded-Proto" 헤더를 통해)

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;
    app.enable('trust proxy');   // proxy서버를 사용하면 넣어두면 좋다
}
app.use(session(sessionOption));

 4. sequelize config

  ⇒ 시퀄라이즈 설정도 json이 아닌 js로 변경 후 process.env.NODE_ENV를 활용한다.

// 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"
    },
    production: {
        username: "root",
        password: process.env.SEQUELIZE_PASSWORD,
        database: "nodebird",
        host: "127.0.0.1",
        dialect: "mysql",
        logging : false,
    }
}

 

// .env
COOKIE_SECRET=nodebirdsecret
SEQUELIZE_PASSWORD=DB비밀번호

 5. cross-env를 package.json에 파라미터 설정

  ⇒ 동적으로 process.env 변경 가능

   - 운영체제 상관없이 일괄 적용을 위한 cross-env를 설치

   - cross-env를 설치하지 않으면 맥/리눅스에서만 정상적으로 설정된다. (윈도우X)

$ npm i cross-env

 

// package.json
{
  "name": "nodebird",
  "version": "0.0.1",
  "description": "익스프레스로 만드는 SNS 서비스",
  "main": "server.js",
  "scripts": {
    "start": "cross-env NODE_ENV=production PORT=80 pm2 start server.js -i 0",
    "dev": "nodemon server",
    "coverage": "jest --coverage"
  },
  ....
}

 7. sanitize-html

  ⇒ XSS(Cross Site Scripting) 공격을 방어한다.

   - 허용하지 않은 html 입력을 막아준다.

 

$ npm i sanitize-html

  

const sanitizeHtml = require('sanitize-html');
const html = "<script>localhost.href = 'https://gilbut.co.kr'</script>"
console.log(sanitizeHtml(html)); // '' (빈 문자열로 치환처리)

 8. scurf

  ⇒ CSRF(Cross Site Request Forgery) 공격방어

   - csrfToken을 생성해 프론트로 보내주고(쿠키로) Form 등록 시 csrfToken을 같이 받아 일치하는지 비교한다.

$ npm i csurf

 

// routes/index.js

const csrf = require('csurf');
const csrfProtection = csrf({ cookie : true });

app.get('/form', csrProtoction, (req, res) => {
	res.render('csrf', { csrfToken : rq.csrfToken()});
});
app.post('/form', csrfProtoction, (req, res) => {
	res.send('ok');
});

 

view/csrf.html

...
          <form id="twit-form" action="/post" method="post" enctype="multipart/form-data">
            <input type="hidden" name="_csrf" value="{{csrfToken}}" />
            ...
          </form> 
...

 9. pm2 소개

  ⇒ node.js를 통해 만든 프로그램을 관리해주는 프로그램이며 원활한 서버 운영에 도움을 준다.

   - 서버가 에러가 발생해 꺼졌을 때 다시 서버를 실행한다. (무중단 서비스)

   - 멀티 프로세싱 지원 (노드 프로세스 수를 1개 이상으로 늘린다)

   - 요청을 프로세스들에게 고르게 분배해 준다.

   - 단점 : 프로세스 간 서버의 메모리 같은 자원은 공유할 수 없다. (memcached나 redis같은 메모리 DB를 사용해서 해소)

 

 10. pm2 사용하기

  ⇒ pm2 전역 설치 후, 명렁어 사용

$ npm i pm2 -g

 

// package.json
  "scripts": {
    "start": "cross-env NODE_ENV=production PORT=80 pm2 start server.js"

 11. 프로세스 목록 확인하기

  ⇒ pm2 list로 현재 실행시킨 프로세스 목록을 확인할 수 있다.

$ npx pm2 list

 

 12. pm2로 멀티 프로세싱하기

  ⇒ pm2 start [파일명] -i [프로세스 수] 명령어로 멀티 프로세싱이 가능하다.

   - 프로세스 수를 0으로 입력 시 CPU 코어 개수만큼 생성, -1이면 CPU 코어 개수보다 1개 적게 생성한다.

   - -1은 하나의 프로세스를 노트 외에 작업 수행을 위해 풀어주는 편이다.

// package.json
  "scripts": {
    "start": "cross-env NODE_ENV=production PORT=80 pm2 start server.js -i 0"

 13. 서버 종료 후 멀티 프로세싱 하기

  ⇒ npx pm2 kill명령어를 통해 pm2로 실행시킨 전체 프로세스 종료처리

 14. 프로세스 모니터링 확인

  ⇒ pm2 monit으로 프로세스를 모니터링 할 수 있다.

$ npx pm2 monit

 15. winston

  ⇒ console.log와 console.error를 대체하기 위한 모듈

   - 위 두메서드는 휘발성이므로 로그를 기록할 수 있는 윈스턴을 설치해 관리한다.

$ npm i winston

 

 16. winston 메서드

  ⇒ createLogger로 로거 인스턴스를 생성 후 필요속성들을 설정한다.

   - level은 로그의 심각도(error, warn, verbose, debug, silly 순, 중요도 순)

   - info를 고른 경우 info보다 심각한 단계 로그도 같이 기록한다.

   - format은 로그의 형식(json, label, timestamp, printf, combine, simple 등 지원)

   - 기본적으로 json으로 기록하지만 로그 시간을 표시하려면 timestamp를 쓰는 게 좋다.

   - transports는 로그 저장 방식

   - new transports.File은 파일로 저장한다는 뜻, new transports.Console.은 콘솔에 출력한다는 뜻.

   - 인자로 finename(파일명), level(심각도) 제공

// logger.js

const { createLogger, format, transports } = require('winston');

const logger = createLogger({
    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;

 

// app.js

...
const logger = require('./logger');
...
app.use((req, res, next) => {
   const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
   error.status = 404;
   logger.info('hello');
   logger.error(error.message);
   next(error);
});
...

 19. helmet, hpp로 보안 관리하기

  ⇒ 모든 취약점을 방어해주진 않지만 실무에서 필수인 패키지

   - 배포 환경일 때만 사용하면 되며 필요없는 옵션들은 해제해서 반영한다.

$ npm i helmet hpp

 

// app.js

...
const helmet = require('helmet');
const hpp = require('hpp');
...
if (process.env.NODE_ENV === 'production') {
    ...
    app.use(helmet({
        contentSecurityPolicy : false,
        crossOriginEmbedderPolicy : false,
        crossOriginResourcePolicy : false,
    }));
    app.use(hpp());
    ...
} else {
    ... 
}

 20. connect-redis

  ⇒ 멀티 프로세스 간 메모리 공유를 위해 redis를 사용한다.

   - connect-redis가 익스프레스와 레디스를 연결해준다.

$ npx i redis connect-redis

 

  ⇒ 레디스 호스팅 생성 완료 후 Endpoint와 Redis password를 복사해 .env에 값 삽입

// .env
...
REDIS_PASSWORD=x5qAUOzdJ1mEoC7lGmts7dh1xvO1VcWS
REDIS_HOST=redis-11793.c323.us-east-1-2.ec2.cloud.redislabs.com
REDIS_PORT=11793

 

 28. connect-redis 연결하기

  ⇒ app.js express.session 미들웨어 부분에 store 속성을 추가한다.

   - RedisStore 생성자의 인스턴스를  store 속성에 등록한다.

...
const redis = require('redis');
const RedisStore = require('connect-redis').default;
...
const redisClient = redis.createClient({
    url : `redis://${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`,
    password : process.env.REDIS_PASSWORD,
    legacyMode : false, (버전에 따라 상태값이 다를 수 있으므로 유의)

});
redisClient.connect().catch(console.error);
...
const sessionOption = {
    resave : false,
    saveUninitialized : false,
    secret : process.env.COOKIE_SECRET,
    cookie: {
        httpOnly : true,
        secure : false,  // https여부
    },
    // store의 기본값은 메모리이므로 레디스에 저장하는 것으로 변경 처리
    store : new RedisStore({ client : redisClient}),
};

 29. nvm, n

  ⇒ 노드 버전을 업데이트하기 위한 패키지이다.

   - 윈도우에서는 nvm-installer, 리눅스/맥에서는 n 패키지

 30. nvm-installer

  ⇒ https://github.com/coreybutler/nvm-windows/releases

   - nvm-setup.zip을 내려받아 압축 해재 후 실행한다.

   - nvm list로 노드 버전을 확인, nvm instal로 버전을 설치한다(nvm install lastest로 최신버전을 설치한다)

$ nvm list
  * 21.6.0 (Currently using 64-bit executable)
$ nvm install 21.6.0
Version 21.6.0 is already installed.  // 현재 최신버전이므로 기존버전 유지

 31. nvm-installer로 노드 버전 바뀍

  ⇒ nvm use [버전명] 명령어로 버전 변경 가능

$ nvm use 18.7.0
Now using node v18.7.0 (64-bit)
$ node -v
v18.7.0

 32. 맥, 리눅스에서 n으로 노드 업데이트

  ⇒ n을 전역설치

   - n 명령어로 현재 노드 버전을 확인 및 n 버전으로 새 버전 설치

$ sudo npm i -g n

$ n 18.7.0
installed v18.7.0
$ node-v
v18.7.0

15.2 Git과 GitHub 사용하기

 1. Git으로 소스코드 관리하기 

  ⇒ 배포 후 소스 코드가 수정될때마다 직접 업로드하는 것이 불편하므로 소스의 형상관리가 필요하다.

   - 협업 시에도 서로 소스가 다를 경우 소스 충돌이 발생한다.

   - Git이라는 분산형 버전 관리 시스템을 사용하여 위 사항들을 해소하여 여러 사람의 코드를 공동관리할 수 있다.

   - GitHub는 GIt으로부터 업로드한 소스코드를 서버에 저장할 수 있는 원격 저장소이다.

   - https://git-scm.com/downloads : git 설치경로

 4. git 명령어 사용하기

  ⇒ 콘솔에서 git --version 입력하여 버전을 확인할 수 있다.

$ git --version
git version 2.43.0.windows.1

 

  ⇒ .gitgnore에 git으로 소스 관리하지 않을 파일과 디렉터리를 등록한다.

// .gitignore

node_modules
uploads
*.log
coverage
.env

 5. GibHub 사용하기

  ⇒ github.com 에 접속하여 회원가입, 플랜은 무료 Plan 선택(Choose Free)

  ⇒ GitHub 리포지터리를 생성한다.

   - 좌측 Create repository 버튼을 클릭

   - 리포지터리 이름은 node-deploy로 하고 Create repository 버튼을 클릭한다.

노드교과서

  ⇒ 생성 완료 화면 확인 후 콘솔로 이동

노드교과서

 10. 프로젝트에 git 설정하기

  ⇒ nodebird 프로젝트로 이동 후 git init 명령어 입력

$ git init
Initialized empty Git repository in C:/nodejs/nodebird/.git/

 11. 모든 파일을 git에 추가하기

$ git add .
warning : LF will be replaced by CRLF in models/index.js.
The file will have its original line endings in your working directory.
...

 12. 소스 코드 커밋하기

  ⇒ 사용자 정보를 설정한 후 소스 코드 커밋(상태 저장)

$ git config --global user.email "ykji1003@hotmail.co.kr"
$ git config --global user.name "jaeikKim"
$ git commit -m "Initial commit"
[master (root-commit) cf7ad51] Initial commit
24 files changed, 5322 insertions(+)
create mode 100644 .env
create mode 100644 .gitignore
create mode 100644 app.js
...

 13. 개인 접근 토큰 발급하기

  ⇒ https://github.com/settings/tokens 에서 Generate new token 클릭

노드교과

 14. GitHub 주소 등록하기

  ⇒ 아이디와 비밀번호 부분을 자신의 토큰으로 교체한다.

   - remote할 때 별명, 주소를 잘못 입력했을 경우 : git remote rm [별명] 명령어를 사용 해 지운 후 다시 git remote add한다.

$ git remote add origin https://아이디:토큰@github.com/아이디/node-deploy

 15. gibHub에 코드 올리기

  ⇒ git push origin master 입력해 소스 코드가 깃허브에 올라간 것을 확인할 수 있다.

$ git push origin master

15.3 AWS 시작하기

 1. AWS 이용하기

  ⇒ aws.amazon.com/ko 에 접속해서 회원가입 수행

  ⇒ 간단하게 노드 서비스를 배포할 수 있는 Lightsail을 선택한다.

노드교과서

 

⇒ 인스턴스 생성

노드교과서

 

  ⇒ 인스턴스의 시작지점은 한국과 가까울수록 이점이 많다. Linux와 Node.js를 선택한다.

 

  ⇒ 인스턴스 계획을 선택( 첫 세달은 무료)

노드교과서

 

  ⇒ 생성된 인스턴스를 확인한다.

노드교과서

 

  ⇒ 인스턴스 삭제 방법( 첫 세달만 무료이므로 반드시 삭제필요)

노드교과서

15.4 AWS에 배포하기

 1. SSH 연결하기

  ⇒ SSH를 사용하여 연결 버튼을 클릭한다.

노드교과서

 2. LightSail 콘솔을 열어 Mysql을 설치한다.

$ sudo apt-get update
$ sudo apt-get install -y gnupg
$ sudo wget https://dev.mysql.com/get/mysql-apt-config_0.8.23-1_all.deb
$ sudo dpkg -i mysql-apt-config_0.8.23-1_all.deb

Ok에 넣고 엔터처리

 

  ⇒ sudo apt update 오류 참고링크 : https://weich.tistory.com/28

$ sudo apt update (여기서 오류발생함)
$ sudo apt-get install -y mysql-server

 

  ⇒ 비밀번호 설정 후 Use Lengacy Authentication Method 선택

노드교과서

 

  ⇒ Mysql 프롬프트 접근 후 비밀번호 수정 명령어를 입력한다.

bitnami@ip-172-26-1-117:~$ sudo mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 95
Server version: 8.0.36 MySQL Community Server - GPL

Copyright (c) 2000, 2024, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '0000';
Query OK, 0 rows affected (0.08 sec)

mysql> exit;

 

 3. Github에서 소스 내려받기

  ⇒ git clone 명령어로 소스 내려받기

   - github에서 공개 리포지터리는 비밀번호가 필요하지 않다.
   - 비밀번호가 필요한 경우는 깃헙 비밀번호를 발급받는 게 아니라 pat를 발급받아야 한다

$ git clone https://github.com/아이디/node-deploy

 4. 퍼블릭 IP 확인 후 브라우저에 입력해 접속

노드교과서

 6. 추가로 알아둘 점

  ⇒ Https를 사용하고자 하면 도메인을 발급 받아야 한다. ( 도메인 구입 후 AWS의 Route 53번 서비스에서 연결)

  ⇒ 수정된 소스코드 반영하기

   - 추후 소스 코드를 수정해서 업데이트 된 내용으로 배포하고 싶을 때는 git clone이 아니라 git pull 명령어 사용

$ git pull
$ sudo pm2 reload all

// 서버가 재시작되면서 변경 내용들이 반영된다.