React.js + Node.js 프로젝트 배포하기
이번 튜토리얼에서는 우리가 만든 앱을 아마존 EC2 에 배포하는 방법을 알아보겠습니다.
EC2 인스턴스 생성
우선, 아마존에서 EC2 를 생성해주겠습니다. EC2 페이지에서 [인스턴스 시작] 을 눌러주세요.

운영체제는 Ubuntu 16.04 LTS SSD Volume Type 을 선택하세요

인스턴스 유형은 t2.micro 를 선택하세요.

리액트 앱을 빌드 할 때, 램이 1G 이하라면 메모리 부족 현상이 나타날 수 있습니다. 따라서, 우리는 지금 저사양 인스턴스를 생성하기 때문에 빌드작업은 우리의 로컬 컴퓨터에서 미리 해주고 배포 할 것입니다.
그 다음엔 검토 및 시작을 눌러주세요.

그 다음에 시작을 누르게 되면, 서버에 접속 할 때 필요한 키 페어 설정을 하게 됩니다. 만약에 여러분이 기존에 사용하던 키가 있다면 기존에 사용 하던것을 계속 사용해도 되고, 그렇지 않다면 따로 키를 새로 생성하시면 됩니다.

키 페어를 새로 만드시게 되는 경우 키 페어 다운로드를 꼭 해주세요. 나중에 이 파일이 있어야 서버에 접속을 할 수 있습니다.
그 다음에 인스턴스 시작을 누르시면, 됩니다.
그리고 여러분의 EC2 목록을 확인하면 우리가 방금 만든 인스턴스가 보여질 것입니다. 처음에는 이름이 따로 정해져있지 않을텐데, 이름이 있을 자리에 마우스를 올리면 나타나는 연필 모양을 눌러서 이름을 지정해줄 수 있습니다.

우리가 만든 EC2 를 선택하고, 상단의 연결 버튼을 누르면 다음과 같이 연결을 어떻게 하는지 보여주는 안내가 나타납니다.


macOS 혹은 리눅스를 사용하고 계시다면 위에 나타난 대로, 명령어를 입력하여 서버에 접속 하시면 되고, 윈도우를 사용하는 경우 git bash 터미널에서 해당 명령어를 입력하시거나 putty 를 사용하여 접속을 하면 됩니다.
$ chmod 400 reactblog.pem
$ ssh -i "reactblog.pem" ubuntu@ec2-13-124-140-1.ap-northeast-2.compute.amazonaws.com
주의: 이전에 키 페어 파일을 다운로드 받은 경로에서 실행해주어야 합니다.

이제 서버에 연결이 되었습니다. 우리에게 필요한것을 설치해볼까요?
환경 설정
우리는 이 서버에 MongoDB 와 Node.js, 그리고 yarn 을 설치해줄 것입니다. 우리는 이번에는 공부삼아 만들어보는 것이기 때문에 이렇게 서버 한대에 모든것을 설치하지만, 여러분들이 실제로 나중에 프로젝트를 만들어서 배포 할 경우에는, 데이터베이스를 설치하는 서버와, 웹서버를 띄울 서버를 따로따로 설정하시는것을 추천합니다.
그 이유는, 나중에 만약에 여러분의 앱이 사용량이 많아져서 성능을 늘려야 해줄 경우에, 웹서버의 갯수를 늘리는 방식으로 확장을 하기도 하는데, 그렇게 하려면 데이터베이스 서버와 웹서버과 완벽히 분리되어있는 편이 확장하기도 쉽고 관리하기도 쉽기 때문입니다.
MongoDB 설치
서버에 MongoDB 를 설치해주겠습니다.
다음 명령어들을 입력하세요:
설치 준비
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5
$ echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.6 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.6.list
$ sudo apt-get update
설치
$ sudo apt-get install -y mongodb-org
시작
$ sudo service mongod start
인스턴스 재부팅시 자동 실행
$ sudo systemctl enable mongod
제대로 실행됐는지 확인
$ mongo
> version()
3.6.3

경고 부분은 무시하셔도 무방합니다.
Node.js 설치
Node.js 는 nvm 을 통하여 설치하는것이 가장 편하고 좋습니다.
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
$ source ~/.bashrc
$ nvm --version
0.33.8
설치가 잘 진행되었다면, 다음 명령어를 통하여 LTS 버전을 설치하세요.
$ nvm install --lts
그 다음엔 node 가 실행되는지 확인하세요.
$ node -v
v8.9.4
yarn 설치
노드 패키지를 설치하기 위하여 yarn 을 설치해주겠습니다.
설치 준비
$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
설치
$ sudo apt-get update && sudo apt-get install yarn
설치 확인
$ yarn --version
1.5.1
git 설정
여러분이 진행하던 프로젝트에 git 설정을 해주세요.
$ git init
.gitignore 파일 설정
프로젝트를 git 에 올릴 때에는, node_modules 디렉토리는 제외하고 올리도록 설정해주어야 합니다. 따라서, blog-backend 디렉토리에 .gitignore 라는 파일을 만들어서 node_modules 를 적고 저장하세요.
blog-backend/.gitignore
node_modules
blog-frontend 디렉토리의 경우, 우리가 create-react-app 을 통하여 프로젝트를 만들게 되면서 자동으로 생성된 파일이 있을 것입니다.
`blog-frontend/.gitignore
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
보시면, # production 부분에 /build 가 있습니다. 일반적으로는 /build 를 제외하고 올리는게 맞지만, 서버 사양이 그다지 좋지 않은 경우에는 여러분의 작업 PC 에서 미리 빌드를 하고 git 에 포함시켜주는 것이 좋습니다.
따라서, 해당 부분도 주석처리를 해주세요.
`blog-frontend/.gitignore
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
# /build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
빌드하기
프로젝트를 다시한번 빌드 해주겠습니다. blog-frontend 디렉토리에서 다음 명령어를 실행시켜주세요.
$ yarn build
$ yarn build:server
커밋하기
우리의 프로젝트 내용들을 git 에 커밋해주겠습니다. 다음 명령어를 blog-backend 와 blog-frontend 가 위치한 최상위 디렉토리에서 실행시켜주세요.
$ git add .
$ git commit -m'built for production'
리모트 저장소에 올리기
여러분의 프로젝트가 완전한 오픈소스 프로젝트라면, github 을 사용하시면 되고, 비공개 프로젝트라면 github 에서 결제를 통해 계정 업그레이드를 해서 개인 저장소를 만들거나, GitLab 혹은 BitBucket 에서 무료로 개인 저장소를 만들 수 있습니다.
저장소를 만들고, 다음과 같이 올리시면 됩니다.
$ git remote add origin git@github.com:vlpt-playground/react-blog.git
$ git push origin master
만약에 이 튜토리얼을 위하여 저장소를 따로 만드는게 번거롭다면, 이 과정을 생략하고 앞으로 제가 만든 저장소에서 프로젝트를 불러오시면 됩니다.
서버에서 프로젝트 받아오기
방금 우리가 만든 Git 저장소에 올린 프로젝트를, 서버쪽에서 클론하겠습니다.
서버 터미널에서 다음 명령어를 입력하세요
$ git clone https://github.com/vlpt-playground/react-blog.git
만약에 여러분이 git 저장소를 따로 만드셨다면 여러분의 주소를 넣어주시면 됩니다.
그 다음에는 blog-backend 디렉토리에 들어가서 yarn 명령어를 입력하여 필요한 패키지들을 설치하세요.
$ cd react-blog/blog-backend/
$ yarn
그 다음에는 yarn start 명령어를 입력해보세요.
$ yarn start
yarn run v1.5.1
$ cross-env NODE_PATH=src node src
listening to port 4000
connected to mongodb
서버가 제대로 실행이 되었나요? 에러가 없이 구동되는것을 확인했다면, Ctrl + C 를 눌러서 종료하세요.
pm2 사용하기
위와 같은 형식으로 서버를 시작한다면, unix 의 screen 기능을 사용하지 않는 이상, ssh 연결이 끊기게 되면 서버 또한 함께 종료됩니다.
프로덕션에서 노드 앱을 구동할땐, pm2 혹은 forever 라는 도구를 사용합니다. 이 도구들의 용도는, 서버가 백그라운드에서 계속 돌아갈 수 있도록 해주고, 서버에 크래쉬가 나면 자동으로 재실행 해주기도 하며, 서버 모니터링 및 로깅, 그리고 다운타임 없이 서버 재시작을 하는 기능 등을 제공합니다.
서로 비슷한 도구이지만, pm2 가 기능이 조금 더 다양하고 편리하므로, pm2 를 사용하겠습니다.
다음 명령어를 통해 pm2 를 설치하세요.
$ yarn global add pm2
$ export PATH="$PATH:$HOME/.config/yarn/global/node_modules/.bin"
$ pm2 --version
2.10.1
yarn 의 글로벌 설치 경로가 기본적으로는 PATH 에 포함되어있지 않기 때문에 추가시켜주었습니다. 이렇게 추가를 해놓고 나면, 어떤 디렉토리에서도 pm2 를 입력하여 실행 할 수 있습니다.
작업을 완료했다면 pm2 --version 명령어를 통해서 제대로 설치되었는지 확인하세요.
그 다음에는, blog-backend 디렉토리로 이동하여 다음 명령어를 실행하세요.
$ pm2 start --name server npm -- start

그러면, 이렇게 실행이 된 것을 확인 할 수 있습니다.
다음 명령어를 사용하여 서버가 제대로 가동중인지 확인해보세요:
$ pm2 log server
[TAILING] Tailing last 15 lines for [server] process (change the value with --lines option)
/home/ubuntu/.pm2/logs/server-error-0.log last 15 lines:
/home/ubuntu/.pm2/logs/server-out-0.log last 15 lines:
0|server | > backend-tutorial@1.0.0 start /home/ubuntu/react-blog/blog-backend
0|server | > cross-env NODE_PATH=src node src
0|server |
0|server | listening to port 4000
0|server | connected to mongodb
서버가 제대로 실행된것을 확인했다면, 다시 종료해주겠습니다.
# 모든 pm2 프로세스를 종료합니다
$ pm2 kill
pm2 설정파일 작성하기
pm2 를 위한 설정을 따로 파일로 저장하고 pm2 를 실행하겠습니다.
blog-backend/deploy.config.json
{
"apps": [{
"name": "server",
"script": "./src",
"env": {
"NODE_PATH": "src"
}
}]
}
package.json 도 수정하여 새 스크립트를 만들어주겠습니다.
src/blog-backend/package.json
{
"name": "backend-tutorial",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"cross-env": "^5.1.3",
"dotenv": "^4.0.0",
"joi": "^13.1.1",
"koa": "^2.3.0",
"koa-bodyparser": "^4.2.0",
"koa-router": "^7.2.1",
"koa-session": "^5.8.1",
"koa-static": "^4.0.2",
"mongoose": "^5.0.1"
},
"devDependencies": {
"eslint": "^4.8.0",
"eslint-config-airbnb-base": "^12.0.1",
"eslint-plugin-import": "^2.7.0",
"nodemon": "^1.12.1"
},
"scripts": {
"start": "cross-env NODE_PATH=src node src",
"start:dev": "cross-env NODE_PATH=src nodemon --watch src/ src/index.js",
"deploy": "pm2 start deploy.config.json"
}
}
파일을 다 저장하고, git 에 커밋 후 배포해주겠습니다.
$ git add .
$ git commit -m'created pm2 configuration'
$ git push origin master
그 다음엔 서버쪽에서 git pull 을 해주세요.
$ git pull
이제부터는, yarn deploy 명령어를 통하여 서버를 실행 할 수 있습니다.
다운타임 없이 서버 재가동하기
pm2 를 사용하면 다운타임없이 서버 재가동 하는것은 매우 간단합니다.
$ pm2 reload server
[PM2] Applying action reloadProcessId on app [server](ids: 0)
[PM2] [server](0) ✓
Nginx 설정
현재 우리의 서버는 4000 포트로 열려있습니다. 브라우저에서 주소를 입력해서 들어가러면 기본적으로 80 포트 (SSL의 경우 443)로 열어주어야 합니다.
일반적으로 생각하기에, 그러면 우리의 노드 앱이 listen 하는 포트를 그냥 80 으로 바꿔주면 되지 않나? 라고 생각 할 수 있습니다. 하지만, 이는 좋은 방법이 아닙니다. 기본적으로는, 우분투 서버에서는 80 포트로 서버를 열으려면 관리자 권한이 필요합니다.
따라서, 서버를 실행 할 때 sudo 를 입력해줘야 한다는 것인데, 이는 좋은 방법이 아닙니다. 그렇다고 시스템 설정을 변경하는 것도 좋은 방법은 아니죠.
흔히 사용하는 방법은, nginx 서버를 설정하고 reverse proxy 를 통해서, nginx 서버 접속이 들어오면 내부의 4000 포트로 돌리는 방식입니다.
그럼, nginx 를 설치해봅시다!
설치하기
$ sudo apt-get update
$ sudo apt-get install nginx
서버 시작
$ sudo service nginx restart
인스턴스 재부팅시 자동 실행
$ sudo systemctl enable nginx
80, 443 포트 열어주기
기본적으로는 해당 포트는 외부에서 아직 접속 할 수 없습니다. EC2 네트워크 보안그룹 설정에서 해당 포트를 열어주세요.

보안 설정은, EC2 목록에서 우리가 만들었던 인스턴스를 선택 한 후에, 하단의 보안그룹 쪽의 인바운드 규칙 보기 좌측에 있는 보안 그룹 이름을 눌러서 확인 할 수 있습니다.

보안 그룹 또한 이름을 정해 줄 수 있습니다.
우선, 하단의 인바운드 규칙의 편집 버튼을 누르세요.

규칙은, 위와 같이 HTTP 와 HTTPS 를 추가하고 저장해주시면 됩니다.

그 다음에 EC2 인스턴스의 주소를 브라우저에 입력하면 다음과 같은 페이지가 나타납니다.
nginx 에 reverse proxy 설정하기
nginx 로 들어오는 요청들을 우리가 만든 노드 서버로 돌려주도록 reverse proxy 를 설정해주겠습니다.
$ sudo vim /etc/nginx/sites-available/default
# vim 에 익숙하지 않다면 vim 대신 nano 를 사용하세요
$ sudo nano /etc/nginx/sites-available/default
# nano 의 경우, 수정을 하고 Ctrl + X, Y 를 하시면 저장됩니다.
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
파일을 열어보시면, location / 부분이 있을텐데요, 해당 부분을 이렇게 입력하세요:
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
# try_files $uri $uri/ =404;
proxy_pass http://localhost:4000/;
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 설정이 제대로 되어있는지 제대로 확인하기 위하여 다음 명령어를 실행하세요.
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
문제가 없다면 위와 같이 뜰 것입니다.
그 다음에는, nginx 를 재시작하세요.
$ sudo service nginx restart
이제 EC2 주소를 브라우저로 띄우면 우리가 만든 프로젝트가 나타날 것입니다. 로그인을 하고, 글도 한번 써보세요!

SSL 적용
SSL 적용도 하고 싶으시다면, https://certbot.eff.org/#ubuntuxenial-nginx 를 따라하시면 됩니다!