EC2 & PM2를 활용한 배포 프로세스 살펴보기 (package.json을 톺아보며)

박상하·2025년 3월 17일

1년차

목록 보기
8/26
post-thumbnail

프론트엔드 배포 업무를 맡으면서 다양한 환경에서의 배포를 경험하고있다.
각 서비스의 특성에 따라 다르게 배포가 진행된다.

웹 서비스 (NextTS) -> ec2, nginx, docker
웹 어드민 (React) -> s3, routes53, cloudfront
웹 뷰(Next) -> ec2, pm2

내가 선택한 배포 프로세스는 아니고 기존에 정해져있는 프로세스이지만
이 코드를 읽고 배포를 경험해 보는 것만으로도 많은 도움이 된다.

배포 코드를 부분별로 정리를 해보면서 배포 프로세스를 이해하고자 이 글을 정리한다.

[NextTS로 개발됨]

package.json

어떤 서비스의 배포 프로세스를 이해하는 첫 걸음은 package.json을 살펴보는 것이다.

"AA-dev-publish": "yarn build:AA-dev && sh script/AA_deploy_dev.sh && sh script/record_deployment_info.sh",

scripts문을 보면 다양한 명령어를 볼 수 있는데 보통 서비스를 실행하거나 배포하는 것과 관련되어 있다.
우리 서비스는 dev와 prod로 나누어 관리하고
또한 하나의 소스에서 분기처리를 통해 두 개의 서비스를 운영하고 있다.
(디자인이 거의 비슷하기 때문)

배포 명령어 "AA-dev-publish"를 먼저 살펴보면 yarn build:AA-dev를 실행한다.

build:AA-dev: "env-cmd -f .env AA.development next build"

env-cmd

pacakge.json에서 파일단위로 환경변수를 주입하고
모든 파일에서 해당 환경변수에 접근할 수 있게 해주는 라이브러리

쉘 스크립트 파일(.sh)

위 쉘 스크립트 파일 (script/_deploy_dev.sh)를 살펴보자.

한 줄씩 해석을 해보면

set -f

파일명 확장 비활성화
-> bash에서는 *,?,[ 같은 특수 문자를 포함한 문자열이 자동으로 파일명으로 확장이 되는 일이 있을 수 있다. 이를 방지하기위해 위와 같은 특수문자를 문자열 그대로 사용하게끔해준다.

chmod -R 400

chmod는 권한에 대한 명령어이다. -R은 디렉터리 하위 모든 파일과 폴더에 동일한 권한을 설정
즉, my_folder안의 모든 파일과 폴더에 400권한을 적용

400의 의미는 읽기 전용으로 소유자에게만 읽기 권한을 부여한다는 것이다.

즉, ssh에 필요한 pem키에 대한 읽기 권한을 허용한 것이다.

cp -r .next/static .next/standalone/.next

cp는 복사 명령어이다. .next/static 폴더를 .next/standalone/.next로 복사한다.

next - standalone

next - standalone은 Next.js에서 제공하는 빌드 옵션이다.
알겠지만 next.js의 빌드 옵션을 정의할 수 있는 파일은 next.config.js이다.

const nextConfig = {
    output: 'standalone',
    /* config options here */

이런식으로 옵션을 추가했을 때 빌드 결과물로 /standalone 폴더를 얻을 수 있다.
최적화 된 빌드 결과물이 standalone에 담기게 된다. 이때 server.js 라는 실행 파일을 얻을 수 있는데 이를 통해 동적 배포가 가능하다.

즉, node server.js 로 실행이 가능하다는 것!

또한 최소한의 코드를 추출해 Docker Image의 사이즈를 줄일 수 있다.

공식문서

공식문서를 보면 /standalone 폴더에 /static 폴더와 /public 폴더는 존재하지 않는다고 한다.
그래서 static 폴더를 standalone 폴더로 복사를 해준 것이다.

Tree shaking - standalone 필요없는데?

내가 발견한 tree shaking point는
standalone은 결국 docker로 배포할 때 의미가 있는데 위 프로젝트는 docker를 사용하지 않기 때문에 굳이 standalone 옵션을 넣어 복잡도를 높일 필요가 없어보인다.

라고 생각을 했었는데 이번에 nginx와 동적 배포, 정적 배포에 대해 학습을 하며
선임분이 standalone 옵션을 넣은 것은 동적 배포를 위함임을 알 수 있었다.
보통 next로 개발을 한다하면 동적 배포를 하는 것이 일반적이다. 왜?
SSR을 사용했다면 또는 동적 라우팅 등 next를 적극 활용했다면..!

서버에서 동적인 작업이 필요하기 때문에 동적 배포를 선택해야한다.

// Next 정적 배포
output:export

nginx.config.js 파일에 위 옵션을 주고 빌드하면 다수의 완성된 html을 빌드과정에 생성시켜준다. 이를 정적 배포하면된다.

// Next 동적 배포
output:standalone

이렇게 옵션을 주면 server.js 라는 실행 파일이 생기고 node 엔진을 통해 읽어주면 동적 배포 완료이다.

Tree shaking 철회 ! ㅋㅋ!

tar -cf build.tar -C .next/standalone .

tar는 압축할 수 있는 명령어이다.

즉, 복사해온 next/standalone의 파일을 압축하는 것이다.
왜 압축할까? 위에서 보면 next/static 이라는 폴더를 가져와 next/standalone 이라는 폴더에 넣어주었다. 그러면 내부의 폴더 구조가 복잡해진다.

대신 tar -cf 라는 명령어를 통해 내부의 폴더를 모두 벗기고
파일만 남겨 불필요한 계층구조를 제거할 수 있다.

즉 build.tar 라는 폴더에 .next/standalone 내의 모든 파일이 수평적으로 들어가는거다.

scp -ri "PEM" build.tar AA@ec2-IP.ap-northeast-2.compute.amazonaws.com:/opt/$DIR

scp -> secure copy의 약자로 build.tar 폴더를 원격서버 ec2의 /opt/DIR로 보내겠다는 말이다.

즉, 내가 local에서 빌드한 경과물을 ec2로 보내겠다!!

ssh -i "PEM" AA@ec2-$IP.ap-northeast-2.compute.amazonaws.com << EOF

SSH로 원격 서버에 접속!

EOF는 EOF 내부의 여러 명령어를 한 번에 전달하는 역할을 한다.

nvm

-> node version manager
노드 버전 매니저로 Linux, macOs, Window 등에서 여러 버전의 node를 설치하고
관리할 수 있는 라이브러리이다.

export NVM_DIR="$HOME/.nvm"

source "$NVM_DIR/nvm.sh"

export PATH="$HOME/.npm-global/bin:$PATH"

export 를 통해 환경변수 NVM_DIR 의 위치를 저장하고
source 는 이 파일을 현재 쉘 세션에서 실행하도록 하는 명령으로 nvm.sh를 실행하도록 하여
nvm use, nvm install 등의 nvm 명령어를 사용할 수 있게 된다.

nvm use $NODE_VERSION

cd 를 통해 해당 /opt/dir로 이동 후

nvm use 명령어를 통해 node의 원하는 버전을 설치했다.

tar -xf build.tar , rm -rf build.tar

build.tar 압축을 해제하여 빌드파일을 원하는 디렉토리에서 풀어놓은 것이다.
그 후 필요없는 압축 파일인 build.tar를 제거한다.

ls -al | grep server.js

-> server.js 파일이 존재하는지 확인하고
(server.js 파일은 standalone 옵션을 주어서 생선된 파일로
그냥 next build를 하면 생성되지 않는다.)

sed -i 's/const currentPort = parseInt(process.env.PORT, 10) || .*/const currentPort = 3002;/g' server.js

sed(Stream Editor) 명령어로 server.js 내부의 process.env.PORT 값을 3002로 변경한다.

pm2 restart $DIR

pm2로 서버 재시작

오류 -> pm2 실행 중 아니면

최초 ec2에 배포를 시작할 때는 pm2가 당연 설치도 되어있지 않기 때문에 설치 후 최초 실행까지 해주어야한다.

그런데 이 부분이 빠져있어서 최초 배포시에 pm2를 실행하도록 로직을 변경해주었다.

pm2 실행 명령어는 다음과 같다.

pm2 start server.js --name app_name

pm2

pm2는 Node.js 애플리케이션을 백드라운드에서 실행해주고 관리할 수 있는 라이브러리이다.

예를 들어 pm2 start app.js를 통해 백그라운드에서(ssh 통신을 끊어도) 서버의 역할을 할 수 있게 해준다.

그리고 모니터링 기능이 있는데 pm2 list 명령어를 실행시키면

이런식의 모니터링이 가능한 기능을 제공한다.

restart 할 경우 pm2 restart app_name을 해주어야한다.

또한 로그를 생성하여 관리할 수 있는데 이 로그가 쌓이게 되면 좋지않아서

pm2-loprotate 라는 라이브러리를 사용하여 오래된 로그를 주기적으로 삭제할 수 있다.

pm2-logrotate

사실 Next는 vercel로 배포하는게 일반적이라고 생각했다. 그런데 ec2로 배포하는 방법을 알게되었다. 같은 프레임워크를 사용해도 배포의 방법은 다양하고 또 상황에 맞게 사용하는게 중요하다 느꼈다.

0개의 댓글