Infra

[AWS EC2] Next.js 프로젝트 배포하기 (ubuntu + nginx + pm2)

unhandled 2025. 4. 22. 23:44

 

들어가기 전에

 

다음 과정은 매우 간단한 Next.js App Router 프로젝트를 배포하는 과정입니다. AWS 가입 과정, 탄력적 IP연결, HTTPS설정, 도메인 등록, Docker 사용, 환경 변수 관리 등의 내용은 이 글에 포함되지 않았습니다.

 

 

이번 배포를 시도하는 이유

 

간단한 프론트엔드 프로젝트를 굳이 EC2로 배포할 필요가 있는가? 라는 의문이 들 수밖에 없다고 생각합니다. 이번 EC2 배포의 배경에는 3가지 이유가 있습니다.

 

첫 번째 이유

원래 배포를 Vercel로 했었습니다. 배포과정이 쉽고 무료였기에 편했죠. 하지만 배포나 인프라 관련 학습을 할 수 있는 부분이 없다고 느껴졌습니다. 그래서 AWS라는 또 다른 배포 플랫폼에 관심을 가지게 되었습니다. 이전 팀 프로젝트에서 인프라 담당 팀원들은 항상 AWS(EC2)를 통해 배포를 하더군요. 저는 인프라나 클라우드 관련 지식도 경험도 거의 전무한 만큼 AWS EC2나 ECS 같은 학습곡선이 높다고들 하는 방식은 당장은 해보기 어려울 것 같았습니다. 그보다는 좀 더 학습곡선이 낮고 Next.js의 SSR라는 특성에도 적합하다는 AWS Amplify로 배포를 해보았는데 Vercel과 별 차이가 없었습니다. 전체적인 과정은 거의 동일하다시피 했고(다만 Amplify가 커스텀에 있어서는 조금 더 자유로운 것 같은 느낌은 받았습니다.) 인프라 관련 학습을 하려면 더 어려운 선택을 해야 한다는 판단하에 시도한 것입니다.

 

두 번째 이유

첫 팀 프로젝트에서 프론트엔드 프로젝트 배포를 시도해 본 적이 있습니다. 그때 팀원들이 mobaxterm라는 프로그램도 깔아주시고 그랬었는데, 결국 성공하지 못한 경험이 있었습니다. 그래서 그랬던 기억을 이번에 제 스스로 만회해보고 싶다는 생각이 있었습니다.

 

세 번째 이유 (사실 이게 제일 큼)

이전 프로젝트에서 배포 관련해서 문제가 생겼었는데 프론트 코드의 문제인지 백엔드 코드의 문제인지 인프라 설정의 문제인지 구별할 수 없었습니다. (문제라는 표현이 책임소재를 따지는 것 같아서 좀 그렇다면, 어떤 영역의 코드를 수정해야 디버깅이 가능한가를 기준이라고 보면 될 것 같습니다.) 이러한 문제는 보통 특정 환경에서는 멀쩡한데 또 다른 환경에서는 에러가 발생하는 경우들이었습니다. 예를 들면 로컬 개발 환경에서는 멀쩡한 페이지가 배포된 도메인에서는 에러가 발생하여 접근하지 못하는 경우, 포스트맨에서는 정상 작동하는데 로컬 개발 환경에서는 동작하지 않는 경우 등이 있겠습니다. 이전에 겪었던 이러한 문제 중에 당장 떠오르는 경우만 3가지입니다.(다이나믹 라우팅을 서버 컴포넌트에서 사용할 경우 프로덕션 환경에서만 그 페이지에서 발생하는 에러, 이미지 업로드 시 blob 관련 이슈, 서버 컴포넌트에서의 인증 쿠키 이슈) 그 당시 제가 봤을 때 프론트 코드에서의 확실한 문제는 찾을 수 없었습니다. 하지만 저는 백엔드나 인프라 관련 지식이 없어서 다른 영역의 코드까지 보고 판단할 수 없었습니다. 이에 더해 프로젝트에서의 각 영역의 개발은 다음과 같은 특징이 있습니다.

 

  1. 인프라-백엔드-프론트엔드는 상호 관련성이 있으며 상호 의존적일 수 있습니다.(다른 영역까지 "고려"한 코드를 적어야 합니다. 예를 들어 프론트엔드에서 순수 React가 아니라 Next.js를 채택했기 때문에 백엔드 코드와 인프라 환경설정이 변경(React로 프론트엔드를 구축했을 때와는 다르게)되어야 할 수도 있습니다.)
  2. 프론트엔드 개발은 백엔드 개발과 그 진행 상황에 있어서 어느 정도 종속적입니다.

 

그래서 저는 그걸 확인하고 싶었습니다. 그러기 위해서는 프론트엔드 영역을 넘어가는 인프라 내지는 백엔드 지식도 필요하다고 생각했습니다. 이 시도는 그를 위한 시작점입니다.

 

 

"이 프로젝트"를 EC2를 통해 배포함으로 얻을 수 있는 이점

 

기능이나 성능상의 이점은 당장은 없습니다. Vercel이라는 Next.js 프로젝트를 배포하기에 최적화된 플랫폼이 있죠. 게다가 Vercel에서의 기본적인 배포는 무료로도 가능합니다. EC2는 개발자가 배포 관련하여 많은 부분을 커스마이징하거나 최적화할 수 있지만 일단 저는 그럴 수 있는 경험과 실력을 갖추지 못했습니다. 그럼 왜 EC2를 선택했냐고요? 이번 시도의 의의는 "경험"입니다. 이전 단락인 "이번 배포를 시도하는 이유"를 다시 봐주세요. 

 

전체적인 EC2 배포 과정을 가볍게 경험해보고 싶었습니다. 그러기 위해서는 문제 발생의 여지가 없는 가벼운 프로젝트가 제격일 것이라고 생각했습니다.간단한 프론트엔드를 EC2로 배포한다는 것은 많은 시행착오를 겪지 않을 것이라는 걸 의미합니다. 바로 이것 때문에 실제 배포에 있어서 이점이 없는 가벼운 프론트엔드 프로젝트를 EC2로 배포하기로 결정한 것입니다. 물론 복잡한 로직의 백엔드나 큰 규모의 프로젝트를 EC2로 배포하는 것이 더 의미 있고 더 많은 것을 배우게 되겠지만 모든 것의 시작은 "찍먹"이 아닐까요? "찍먹"을 해봐야 제가 이것을 앞으로 계속 먹을 것인지, 얼마나 먹을 것인지 가늠할 수 있다고 생각합니다. 혹시 또 모르지 않겠습니까? 예상보다 훨씬 더 제 취향의 맛일수도.. 

 

 

배포 과정

 

가장 먼저 해야 될 일은 EC2 인스턴스를 생성하는 것입니다. AWS 로그인 후 EC2 메인 페이지로 들어가 주세요.

 

 

상단 내비게이션 바의 오른쪽에서 리전(region)“서울”로 선택해 주세요.

 

 

인스턴스 시작 버튼을 눌러주세요.

 

 

1. 인스턴스 이름

인스턴스 이름을 지정해 주세요.

 

2. AMI(Amazon Machine Image) 선택

저는 가장 대중적인 Ubuntu로 선택하겠습니다. 

 

 

3. 인스턴스 유형

프리티어 사용 가능 인스턴스 유형이 정해져 있을 것입니다. 그것을 선택하시면 됩니다. 

 

 

저는 t2.micro로 선택하겠습니다.

 

4. 키 페어 생성하기

키 페어 생성 버튼을 눌러주세요.

 

 

그 다음 키 페어 이름을 설정해 주세요. 이것이 다운받을 .pem 확장자 파일의 이름이 됩니다. 이 .pem 파일은 AWS에서 대여한 가상서버에 입장할 수 있는 일종의 열쇠와 같은 역할을 한다고 생각하시면 됩니다. 

 

 

입력한 키 페어 이름의 pem 파일이 다운로드됩니다. (저 같은 경우에는 portfolio_key.pem 파일이겠죠.) 이 파일이 없으면 해당 인스턴스 서버에 접속할 수 없으니 절대 분실하거나 삭제하시면 안 됩니다!

 

 

6. 네트워크 설정

 

SSH 트래픽 허용은 이미 체크되어 있을 겁니다. 오른쪽 부분을 클릭해 기본값인 “위치무관”에서 “내 IP”로 변경해 주세요.(보안을 위해 이렇게 했지만 IP주소가 변경될 경우(카페 등 외부 와이파이를 사용하는 경우) AWS에 직접 접속해서 보안그룹을 수정해주어야 합니다.)

 

그리고 HTTPS, HTTP 트래픽 허용을 체크해 주세요.

 

 

별다른 세부 설정 없이 체크박스에 체크만 한다면 이는 HTTP/HTTPS 트래픽으로 들어오는 모든 IP(0.0.0.0/0)의 접근을 허용한다는 의미입니다. 저는 임시 배포 테스트를 위해 이렇게 했지만 실제 프로덕션 환경에서는 반드시 특정 IP 주소의 접근만 허용하도록 보안 그룹을 따로 설정해주어야 합니다.

 

스토리지 구성은 8 GiB 그대로 하겠습니다. (프리티어는 30기가까지 된다는데 전 기간이 지나서..) 그리고 이제 우측 하단의 인스턴스 시작 버튼을 눌러주세요.

 

 

인스턴스 시작 중 화면이 나옵니다.

 

 

인스턴스 생성이 완료되면 인스턴스에 연결을 눌러주세요.

 

아래의 저 명령어(ssh -i 로 시작하는)를 복사해 주세요.

그리고 저 퍼블릭 DNS 주소(ec2로 시작하는)는 배포된 웹사이트에 접근할 수 있는 임시 주소가 될 것이니 기억해 두세요.

 

 

(저는 블로그 포스팅을 위해 저 인스턴스의 실제 퍼블릭 DNS 주소를 가리고 ubuntu@ec2-12-345-678-90.ap-northeast-2.compute.amazon.aws.com이라는 실제로는 존재하지 않는 퍼블릭 DNS 주소를 이미지에 넣었습니다. 여러분들은 본인의 AWS 인스턴스에서 생성된 퍼블릭 DNS 주소를 넣으셔야 합니다.)

 

자 이제 폴더를 하나 편한 위치에 생성하고 다운로드한 pem파일을 옮겨주세요.

 

 

그리고 이 폴더에서 터미널을 실행합니다. (전 powershell로 진행했습니다.)

 

(잠깐!) 만약 지금 사용하고 있는 운영체제가 맥, 리눅스라면 터미널에서 아래 명령어를 통해 키 페어 파일의 권한을 풀어주셔야 합니다.

chmod 400 portfolio_key.pem

 

윈도우 환경에서도 git bash를 터미널로 사용한다면 위 명령어(chmod ~)를 실행시킬 수 있습니다.(powershell이나 cmd에서는 다음의 chmod 명령어를 실행해도 동작하지 않을 것입니다. 다만 윈도우에서는 다음 명령어를 실행하지 않아도 다음 단계로의 진행이 가능합니다.)

 

그리고 아까 복사했던 명령어를 입력합니다.

ssh -i "portfolio_key.pem" ubuntu@ec2-12-345-678-90.ap-northeast-2.compute.amazonaws.com

 

저 “portfolio_key.pem”은 제가 지정한 이름이니, 여러분은 저 구역에 아까 직접 지정한 키 페어 파일명을 적으셔야 합니다. 뒤에 퍼블릭 DNS 주소도 마찬가지입니다.

ssh -i "키페어파일명" 여러분의_퍼블릭_DNS_주소

 

 

만일 port 22: Connection refused가 출력된다면 다시 한번 입력하세요. (보통 인스턴스를 새로 만들고 곧바로 시작하는 경우에 발생합니다.) 계속하겠냐는 질문이 나오면 yes를 입력해 줍니다.

yes

 

 

( “~ can’t be establisted.” 이 내용은 이전에 저 퍼블릭 DNS 주소가 이 PC에서 확립된 적이 없다는 뜻입니다. 원래 나오는 것이니까 놀라지 마세요. 제가 가린 부분은 AWS 관련 정보입니다. 노출된다고 큰 보안상의 위협은 없겠지만 그래도 가렸습니다.)

 

완료되었습니다. 상단의 “Windows PowerShell” 부분이 “ubuntu@ip 주소”로 변경됐습니다. (ubuntu@ip-333-33-33-33 ← 이 주소는 실제 존재할 수 없는 가짜 퍼블릭 ip 주소입니다. 앞으로 터미널 캡처 이미지에서는 실제 주소가 아닌 저 주소로 대체하겠습니다. 원래 저 위치에는 여러분의 퍼블릭 DNS 주소가 표시됩니다. 저는 블로그 포스팅을 위해 이런 형식으로 가린 것입니다.) 아까 생성한 EC2 인스턴스는 AWS로부터 가상 서버를 “대여”한 것이고, 이제 이 터미널로 접속한 것은 그 “대여”한 가상 서버 환경에 접속한 것이라 생각하시면 됩니다. 이제부터 진행하는 과정들(nvm, nginx 설치 등)은 그 가상 서버에 프로젝트 배포를 위한 기본적인 환경설정을 하는 것입니다.

 

 

전 powershell로 접속했지만 ssh -i 이후의 모든 명령어들은 가상 ubuntu 서버의 shell에서 실행됩니다. (이 shell은 bash 문법을 기본으로 사용하기에 이제부터 나오는 코드블록은 형식에 bash를 지정하겠습니다.)

 

다음 명령어로 update를 해주세요. (우분투 시스템의 업그레이드할 “패키지 목록”을 업데이트하겠다는 의미입니다. )

sudo apt-get update

 

 

다음 명령어로 업그레이드를 해주세요. (방금 업데이트 한 “패키지 목록”을 바탕으로 우분투 시스템의 업그레이드를 진행한다는 의미입니다. )

sudo apt-get upgrade

 

“이 작업은 NNNkb의 디스크 용량을 필요로 하는데 그래도 계속 진행하겠습니까? [예/아니오] (After this operation, NNN kB of additional disk space will be used. Do you want to continue? [Y/n])”는 질문이 나오면 Y를 입력해 주세요.

Y

 

 

이제 nginx를 설치해 줍니다.

sudo apt-get install nginx

 

이번에도 디스크 용량을 얼마나 차지하는데 그래도 진행할 것인지를 물어보는 질문이 나올 것입니다. 이때도 Y를 입력해 주시면 됩니다.

Y

 

 

이제 nvm(노드 버전 매니저)도 설치해 줍니다. 저는 nvm 최신 버전이 0.40.2이라서 그걸로 설치했습니다. (그냥 node 최신 lts 버전을 설치해 주셔도 됩니다.)

하단 nvm 깃허브 레포지토리에서 최신 버전을 확인해 주세요. (Installing and Updating 항목을 보시면 바로 아래에 있습니다.)

 

https://github.com/nvm-sh/nvm

 

GitHub - nvm-sh/nvm: Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions

Node Version Manager - POSIX-compliant bash script to manage multiple active node.js versions - nvm-sh/nvm

github.com

 

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash

 

 

nvm을 설치하면 원래 터미널을 재시작해줘야 하는데, 그 과정을 생략하기 위해 다음과 같은 명령어를 쳐줍니다.

source ~/.bashrc

 

다음엔 설치된 nvm도 확인해 주세요.

nvm

 

 

다음 명령어로 nvm이 가장 안정화된 최신 버전(lts 버전)의 npm을 설치할 것입니다.

nvm install --lts

 

 

이제 다음 명령어들로 node와 npm가 ubuntu 가상 서버에 잘 설치되었는지 확인해 주세요.

 

node -v
npm -v

 

 

 

이제 배포할 프로젝트를 클론 해줍니다.

git clone 클론할_프로젝트_깃_레포지토리_https_url

 

 

그리고 프로젝트 루트 폴더 안으로 들어가 주세요.

cd 프로젝트_폴더

 

 

이제 npm install로 의존성 설치를 해주세요.

npm install

 

 

완료되면 프로젝트를 빌드해 주세요.

npm run build

 

 

이렇게 빌드 과정과 결과가 터미널 콘솔에 출력됩니다. vscode에서 로컬로 빌드할 때 콘솔에 출력되는 것과 똑같이 나오네요.

 

이제 리버스 프록시를 설정해 주겠습니다. 리버스 프록시는 클라이언트의 요청을 받아 그 요청을 웹 서버에 전달하고 그에 대한 응답을 다시 클라이언트에게 전달하는 역할을 합니다. 주로 성능이나 보안상의 이점을 위해 활용합니다.

 

앞으로의 과정은 이 프로젝트를 위한 nginx 설정 파일을 생성하는 것이고, 리버스 프록시는 그 설정된 내용 중 하나라고 생각하시면 됩니다. 

 

리버스 프록시를 설정하기 위한 파일을 생성해야 하기에 다음과 같은 명령어로 나노 편집기를 실행해 주세요. 파일명은 프로젝트 명이나 도메인 명 등 구분할 수 있는 이름으로 지정해 주시면 좋습니다. “/etc/nginx/sites-available/kea-portfolio” 이런식으로요. 그럼 이 파일은 이 프로젝트(kea-portfolio)를 위한 설정파일이라는 것을 명시적으로 알 수 있습니다.

sudo nano /etc/nginx/sites-available/생성할 파일명

 

그리고 나노 편집기에 다음과 같은 내용을 복사 붙여 넣기 해주세요. (저 퍼블릭 IPv4 주소는 임시입니다. 고정된 주소가 아니기 때문에 인스턴스를 중단 후 재실행하거나하면 계속 변경됩니다. 즉, 그럴 때마다 매번 이 파일을 열어서 수정해주어야 합니다. 결국 server_name에는 탄력적 IP를 할당받아서 작성해야 합니다.)

server {
    listen 80;
    server_name 퍼블릭 IPv4 주소;

    location / {
        proxy_pass http://localhost:3000;
        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;
    }
}

 

 

(탄력적 IP를 사용하지 않는다면) site-available의 이 파일을 매번 다시 열어서 수정하는 건 매우 번거롭겠죠. 이때 server_name에 _; 를 지정해 주면 IPv4 주소가 변경되어도 괜찮습니다. (그러나 이는 임시방편일 뿐이고, 결국 정석적인 방법은 탄력적 IP(고정 IP)를 발급받아 sever_name을 탄력적 IP주소로 변경해 주는 것입니다. )

server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://localhost:3000;
        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;
    }
}

 

 

Ctrl + O 를 누르시면 편집기 하단에 작성할 파일명과 경로가 맞는지 확인할 수 있도록 나옵니다. 

 

 

이 상태에서 엔터키를 누르시면

 

하단에 [ Wrote 13 lines ] 라는 메시지가 나옵니다. 이제 Ctrl + X 를 눌러서 나노 편집기에서 나오세요.

 

그리고 다음 명령어를 쳐주세요. 방금 설정 파일을 심볼릭 링크에 등록하겠다는 의미입니다.

sudo ln -s /etc/nginx/sites-available/생성한파일명 /etc/nginx/sites-enabled/

 

그리고 방금 새로운 설정파일을 만들었으니 다음 명령어로 기본 설정 파일(같은 경로의 파일명 default)는 삭제해야 합니다.

아래의 명령어로 현재 /etc/nginx/sites-enabled 경로에 어떤 파일이 있는지 확인할 수 있습니다.

ls /etc/nginx/sites-enabled

 

기본 설정 파일(default)과 방금 생성한 설정 파일(test-portfolio)이 있군요.

 

이 중 default 파일을 다음 명령어를 통해 삭제합니다.

sudo rm /etc/nginx/sites-enabled/default

 

default 파일을 삭제하지 않으면 이 기본 설정이 방금 우리가 지정한 커스텀 설정을 가로채버릴 수 있습니다. 이렇게 되면 마지막에 퍼블릭 DNS 주소로 접속했을 때 우리가 원하는 프로젝트 화면이 아니라 다음과 같은 “Welcome to nginx!” 화면만 떠있을 확률이 높습니다. (경험자)

 

 

이제 다음 명령어로 nginx 설정이 잘 되었나 확인해 주세요.

sudo nginx -t

 

아래처럼 syntax is ok, successfult 이란 글자가 보이면 성공입니다.

 

콘솔에 위 내용이 보이면 다음 명령어로 nginx를 재시작해주시면 됩니다. 

sudo systemctl restart nginx

 

그런데 저는 콘솔에서 syntax is ok가 아닌 에러를 마주했습니다.  

 

1번째 트러블 슈팅 과정(server_names_hash_bucket_size 에러)은 접은 글을 펼쳐서 확인해 주세요. 

(총 2번의 트러블 슈팅 과정이 있는데 배포 과정의 흐름을 끊을 수 있어서 접은 글로 분리했습니다. 문제가 없으시다면 패스하시고 다음 단계로 넘어가셔도 됩니다.)

더보기

에러 내용을 확인해 보겠습니다.

 

찾아보니까 server_names_hash 길이가 너무 길어서 생긴 에러라고 합니다. nginx.conf 파일에서 해쉬 버켓의 최대 사이즈를 수정해 줍니다. (잠깐! nginx.conf는 nginx 전역에 적용되는 설정이고, 아까 site-available폴더에 만든 설정 파일은 해당 프로젝트 전용 설정이라고 생각하시면 됩니다. 프론트엔드의 CSS 개념에 비유하자면 global CSS와 CSS Modules의 관계와 유사합니다.)

sudo nano /etc/nginx/nginx.conf

 

하단의 nginx.conf 파일을 나노 편집기로 연 이미지를 확인해 주세요.

 

저 맨 앞의 # 표시는 나노 편집기에서 주석처리를 의미합니다. 64로 설정되어 있지만 주석처리가 되어있죠. 그럼에도 디폴트 값은 64이기에 에러가 발생한 것입니다.

 

주석을 풀고 128로 바꿔줍니다.

 

Ctrl + O + Enter로 저장하고 Ctrl + X로 나노 편집기에서 나옵니다.

그리고 다시 nginx 설정이 잘 되었나 확인해 보겠습니다.

sudo nginx -t

 

 

syntax is ok, successful 이란 단어가 보이네요. 이제 nginx 설정 자체에는 문제가 없습니다. 

nginx 설정이 바뀌었으니 nginx를 한번 재시작해야 합니다. 

sudo systemctl restart nginx

 

 

이제 pm2를 설치하겠습니다. pm2는 ssh 접속이 끊겨도 배포된 프로젝트 프로세스를 계속 유지시켜 주는 프로그램입니다. 터미널을 종료한다고 배포된 웹사이트가 안 나오면 매우 곤란할 테니까요.

 

다음 명령어로 pm2를 설치해 주세요.

npm install pm2 -g

 

그리고 아래 명령어로 아까 프로젝트를 pm2로 시작해 줍니다. 저 porfolio-app 부분은 pm2에서 해당 프로세스 이름을 뭐라 붙일지입니다. 편하게 식별가능한 것으로 하시면 됩니다.

 

pm2 start npm --name portfolio-app -- start

 

앗.. 근데 아래 캡처 이미지를 보시면 아시겠지만 제가 마지막에 오타를 냈습니다. --와 start 사이에 공백이 하나 있어야 하는데 붙여 써버렸습니다. 그럼에도 저 PM2 아스키코드와 다음 내용들은 콘솔에 출력되는군요.

 

 

다시 제대로 된 명령어를 입력해 주었습니다.

 

 

이제 처음 퍼블릭 DNS 주소로 들어가 보세요.

 

아래와 같이 웹사이트가 잘 나온다면 EC2를 활용한 배포에 성공한 것입니다. 축하드립니다!

 

 

..저는 바로 이 화면을 보진 못했습니다. 퍼블릭 DNS 주소에 들어가니 502 bad gateway 에러가 떠있었거든요. 전 2번째 트러블 슈팅 과정으로 넘어가겠습니다. 

 

2번째 트러블 슈팅 과정(프로젝트 폴더 밖에서 pm2를 시작해서 발생한 에러)은 접은 글을 펼쳐서 확인해 주세요. 

더보기

pm2의 에러로그를 조회해 보겠습니다.

pm2 logs portfolio-app

 

 

package.json을 찾을 수 없다고 합니다. 알고 보니 이는 제가 중간에 실수로 터미널을 껐다가 켜서 다시 가상 서버에 연결한 적이 있었는데, 깜빡하고 cd 프로젝트명을 하지 않았습니다. 결국 클론한 프로젝트 밖에서 pm2를 시작하려고 했기 때문에 생긴 에러였습니다. (지난번 배포 때는 이 에러가 발생하지 않았던 이유는 그때는 중간에 터미널을 종료한 적이 없어서 이런 상황이 발생할 여지가 없었기 때문입니다.)

 

이번엔 제대로 프로젝트 폴더 내로 들어와 아까의 명령어를 쳐주었습니다.

 

pm2 start npm --name portfolio-app -- start

 

 

퍼블릭 DNS 주소에 들어가 보니 이제 배포된 프로젝트 화면이 보입니다. 배포에 성공했습니다 짝짝!

 

 

그런데 위의 pm2 프로세스 내역에서 현재 status가 online인 정상작동하는 프로세스 이외에 status가 error인 프로세스가 하나 있었죠. 이전에 프로젝트 폴더 밖에서 실행했던 pm2가 아직 종료되지 않았기 때문입니다. 다음과 같이 정리해 주었습니다.

 

다음 명령어는 pm2의 프로세스 id 0번을 중단합니다.

pm2 stop 0

 

그다음 명령어는 pm2의 프로세스 id 0번을 삭제합니다.

pm2 delete 0

 

프로세스를 정리한 결과는 다음과 같습니다.

 

 

구글 확장 앱인 Wappalyzer를 통해 이 웹사이트는 ubuntu와 nginx를 사용하여 배포되었다는 것을 확인할 수 있습니다.

 

 

 

AWS EC2 요금

 

AWS EC2는 유료 서비스입니다. 물론 AWS를 가입한 지 12개월이 지나지 않았고 배포하려는 프로젝트가 가볍다면 프리티어를 통해 거의 무료로 이용할 수도 있을 것입니다. 저는 AWS 가입 자체는 시간이 좀 지나서 그 혜택으로 받을 수 있는 프리티어는 이미 끝났습니다. (하지만 그 당시 해킹으로 인한 AWS 요금 폭탄이 두려워 MFA를 설정했었는데 그건 지금까지 너무 잘 사용해 왔습니다.)

 

지금 나오는 요금 관련 내용은 위 과정이 아닌 일주일 전쯤 제가 처음으로 행한 EC2 배포에 대한 청구서입니다. 이전의 첫 번째 EC2 배포에서는 저 내용에 더해 탄력적 IP도 등록하고 약 5시간 30분 정도 인스턴스를 활성화했다가 중단했습니다. 그리고 그 후로 몇 시간이 지나고 인스턴스를 삭제할 때 등록한 탄력적 IP도 같이 삭제했습니다. (인스턴스가 사용 중이라면 탄력적 IP는 무료입니다. 그런데 탄력적 IP가 등록된 상태에서 인스턴스를 중단한다면 오히려 비용이 책정됩니다. 이는 타인이 사용할 기회가 있는 탄력적 IP을 활용하지 않고 점유만 하고 있는 상태라고 판단하기 때문에 비용을 부과하는 것입니다. 저는 인스턴스 중단부터 해당 인스턴스가 삭제되기 전까지 할당된 탄력적 IP에 대한 요금은 계속 부과되고 있었을 것입니다.)

 

 

(오늘 배포를 제외한) 지난번 첫 번째 배포 기준에서의 요금입니다. 이때 Amplify는 0.07 달러가 나왔습니다. (Amplify는 반나절을 유지했지만 EC2는 5시간 30분 정도 사용하고 인스턴스를 중단했습니다. 그리고 Amplify 배포를 삭제하는 시간대에 EC2 인스턴스를 삭제했습니다.) EC2 요금은 인스턴스 사용료, VPC, 기타 요금까지 합치면 0.17달러쯤 되는 것 같습니다.

 

 

약 242원의 요금이 책정되었습니다. 이는 아주 간단한 프론트엔드 포트폴리오 기준입니다. 이 요금은 프로젝트 규모, 인스턴스 특성 등 수많은 변수에 의해 가격이 달라지니 본인의 프로젝트 또한 이러한 금액이 나올 거라 예상하시면 안 됩니다. 앞서 말한 변수에 따라 천차만별일 수 있습니다. 

 

 

앞으로 시도해 볼 인프라 관련 기능들

 

오늘 진행한 EC2 배포는 사실 프로덕션 환경에서의 기본적이고 필수적인 과정을 모두 포함했다고 말하기는 어렵습니다. 원래는 탄력적 IP 할당, HTTPS설정과 도메인 등록, 보안 그룹 설정, 도커 컨테이너화 등의 과정들도 들어가야 합니다. 그리고 오늘 한 방식은 수동 배포이기에 오늘 과정의 많은 부분을 “자동화”할 수 있는 방법들이 있을 것입니다.

 

탄력적 IP를 할당해 HTTPS 설정

→ 도메인 연결 및 HTTPS 설정

 

배포의 용이성을 위해 프로젝트 파일을 컨테이너화 해서 관리

→ 도커 사용(컨테이너화)

 

EC2는 Vercel이나 Amplify와 같이 main 브랜치 업데이트 시 자동으로 배포가 진행되는 기능은 기본적으로 탑재하고 있지 않습니다. 아마 git pull로 원격 레포지토리의 업데이트 상황을 가져온 후에 npm install로 추가된 의존성을 설치하는 과정을 매번 거쳐야 할 것입니다.

→ CI/CD (Github Actions, Jenkins 등을 활용한 자동 배포)

 

지금은 모든 포트를 받아들이지만 일부 포트만 받아들이게

→ 보안 그룹 설정

 

API 엔드포인트나 Redirect URI 등을 사용하는 경우

→ 환경 변수 관리

 

배포 시에도 서비스가 중단되지 않게

→ 무중단 배포 (Zero Downtime Deployment)

(무중단 배포는 배포 중에도 서비스를 이용할 수 있도록 하는 기능입니다. 방금 설정한 pm2도 일종의 서비스를 중단하지 않고 유지해 주는 기능을 하지만(ssh 접속을 끊어도 웹사이트에 접근이 가능하게 해 주니까요) 통상적으로 쓰이는 “무중단 배포”는 배포가 진행되는 순간에도 유저들이 서비스를 이용할 수 있도록 한다는 의미로 사용됩니다. 이 개념을 혼동하고 있는 경우가 적지 않아 보였습니다.)

 

다른 유형 내지는 다른 규모의 프로젝트 배포 시도

→  더 규모가 큰 프로젝트, 다른 언어나 다른 프레임워크 기반의 프로젝트, 백엔드 프로젝트 배포해 보기

 

 

마무리

 

첫 번째 EC2 배포 때는 막힘없이 원활하게 진행되었었는데 이번에는 오히려 2번의 트러블 슈팅을 경험하게 되었습니다. 탄력적 IP 할당, Https설정과 도메인 등록, 도커 등의 사용은 언젠가 다루어보겠습니다. 이번 EC2를 통한 포트폴리오 프로젝트 배포는 배포상의 실질적 이익이 아닌 AWS 인프라 학습을 위함이었기 때문에 제 포트폴리오는 Vercel에서의 배포를 유지할 생각입니다. 이전부터 EC2를 꼭 경험해보고 싶었는데 이제야 그걸 이뤘네요!