0. Next.js로 개발한 웹을 aws ec2 로 배포하게 된 계기
Next.js는 Vercel에서 만들어서 보통 Vercel 로 빠르고 편하게 빌드/배포할 수 있습니다.
하지만 제가 개발한 'Stellar-Link' 는 실시간 채팅 웹앱으로 Socket.io 를 사용하여 개발하였습니다.
0-1. 문제점
Vercel은 서버리스(Serverless) 아키텍처를 기반으로 동작하기 때문에, 지속적인 WebSocket 연결이 필요한 Socket.io와 같은 기술을 활용하는 데 제약이 있습니다. 주요 문제점을 정리하면 다음과 같습니다.
1. WebSocket 연결 유지 불가
Vercel의 서버리스 환경은 요청이 들어올 때마다 새로운 인스턴스를 실행하는 방식으로 동작합니다. 즉, 지속적인 연결을 유지해야 하는 WebSocket의 특성과 맞지 않습니다.
- WebSocket은 지속적인 연결을 유지해야 하지만, Vercel에서는 요청마다 새로운 함수 실행 환경이 생성되므로, 연결이 끊어지는 문제가 발생합니다.
- 따라서 Socket.io를 사용한 실시간 기능이 정상적으로 작동하지 않습니다.
2. Lambda 기반의 제한
Vercel은 내부적으로 AWS Lambda를 사용하여 서버리스 기능을 제공합니다. Lambda의 특성상 무상태(stateless) 환경이며, 연결 유지보다는 개별 요청을 처리하는 데 최적화되어 있습니다.
- Lambda 환경에서는 실행 시간이 제한되어 있어, 장시간 유지되는 WebSocket 연결을 유지할 수 없습니다.
- 서버리스 함수가 일정 시간이 지나면 종료되므로, Socket.io 연결이 계속 끊기는 문제가 발생합니다.
3. Sticky Session 미지원
Socket.io는 Sticky Session(고객의 요청이 항상 동일한 서버로 라우팅되는 방식)을 필요로 하지만, Vercel에서는 이를 지원하지 않습니다.
- 서버리스 환경에서는 요청이 여러 서버 인스턴스에 분산되기 때문에 같은 클라이언트의 요청이 매번 다른 인스턴스로 전달될 수 있습니다.
- 이는 **세션 기반의 실시간 통신(예: 채팅, 알림 시스템)**에 적합하지 않습니다.
0-2. AWS EC2를 선택한 이유
위와 같은 문제로 인해 Vercel에서 Socket.io 기반의 실시간 기능을 원활하게 운영하기 어려웠습니다. 이에 따라 AWS EC2를 선택하여 배포하게 되었습니다. EC2에서는 다음과 같은 장점이 있습니다.
- 항상 실행되는 서버 → 서버리스와 달리 연결이 유지되는 환경을 제공하므로, WebSocket과 Socket.io를 안정적으로 실행할 수 있습니다.
- 고정 IP 제공 → 특정 도메인과 연결하여 안정적으로 서비스를 제공할 수 있습니다.
- 완전한 서버 제어권 → Nginx, PM2, Docker 등 다양한 배포 및 운영 환경을 직접 설정할 수 있습니다.
- Sticky Session 설정 가능 → 클라이언트 요청이 항상 동일한 서버 인스턴스로 전달될 수 있도록 설정할 수 있습니다.
결론적으로, 실시간 채팅 및 WebSocket 기반 기능을 안정적으로 운영하기 위해 AWS EC2를 선택하게 되었습니다. 현재 EC2에서 PM2를 사용하여 Next.js 애플리케이션을 실행하고 있으며, WebSocket을 통해 실시간 기능을 원활하게 제공할 수 있는 환경을 구축했습니다.
1. AWS EC2 인스턴스 설정
- EC2 대시보드로 이동합니다.
- 인스턴스 시작을 클릭합니다.
- Amazon Machine Image (AMI) 선택 : 일반적으로 우분투를 선택합니다. 프리티어 제공해주는 것으로 선택합니다.
- 인스턴스 유형 선택:
- 개발 및 소규모 애플리케이션의 경우 t2.micro (프리 티어)도 충분할 수 있습니다.
- 키 페어 생성:
- SSH를 통해 인스턴스에 접속하기 위해 키 페어를 생성합니다.
- 기존 키 페어가 없다면, 새로 생성하고 .pem 파일을 안전한 곳에 저장합니다
- 보안 그룹 설정:
- SSH (포트 22): 자신의 IP로 제한.
- HTTP (포트 80): 모두에게 허용.
- HTTPS (포트 443): 모두에게 허용.
- 애플리케이션에서 사용하는 추가 포트 (예: Socket.IO용 포트)도 설정합니다.
- 인스턴스 시작을 클릭하여 인스턴스를 시작합니다.
2. 필수 소프트웨어 설치
- .pem key를 저장한 폴더에서 우클릭을 통해 터미널을 열어 줍니다.
2-1. 파일의 권한 변경하기
- 파일의 권한을 변경해줍니다.
chmod 400 '자신의 pem 키 파일'
2-2. SSH를 통한 접속
ssh -i 'Stellar_Link_key.pem' ubuntu@your-ec2-public-dns
여기서 your-ec2-public-dns 에는 다음과 같은 값을 입력합니다:
- 우선, 인스턴스 목록창으로 들어갑니다.
- 해당 인스턴스를 체크하고 작업에서 세부정보보기를 클릭합니다.
- 빨간색 사각형 표시한 부분의 값을 복사합니다. (앞의 ec2- 도 당연히 포함해서 복사)
- 해당 값을 your-ec2-public-dns 에 입력하여 명령어를 완성합니다.
잘 된다면,
다음과 같이 인스턴스에 접속 성공합니다.
2-3. 서버 업데이트
sudo apt update && sudo apt upgrade -y
위와 같은 명령어를 입력하여 서버를 업데이트합니다.
2-4. Node.js 및 npm 설치
최신 LTS 버전을 설치하는 것이 좋습니다.
# NodeSource PPA 추가
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
# Node.js 설치
sudo apt install -y nodejs
node -v
npm -v
설치가 잘 되었는지 확인해줍니다.
2-5. Git 설치
sudo apt install -y git
3. 프로젝트 클론 및 빌드
3-1. 프로젝트 클론
# 프로젝트 클론
git clone https://github.com/your-repo/your-nextjs-app.git
cd your-nextjs-app
3-2. .env 파일 생성
nano .env
- 파일을 생성하는 명령어 입니다.
CLIENT_URL=https://your-client-domain.com
DATABASE_KEY=mongodb+srv://<username>:<password>@cluster0.mongodb.net/StellarLink?retryWrites=true&w=majority
PORT=3001
JWT_SECRET=your_jwt_secret
# REDIS_URL=redis://<your-redis-url> # Redis를 사용하지 않으므로 주석 처리
- 해당 파일에 위와 같은 내용들을 적습니다. (단순 예시로, 프로젝트에 필요한 환경 변수들을 적어줍니다.)
- 작성 완료 후, Ctrl + x 를 누르면 변경사항을 저장할 것인지 묻는 문구가 도출됩니다.
- yes 를 적으면 파일명을 적는 칸이 도출됩니다.
- 파일명을 적은 후 enter(엔터)를 하면 저장됩니다.
cat .env
- cat 명령어를 통해 파일의 내용을 확인해봅니다.
3-3. 프로젝트 종속성 설치
npm install
3-4. 프로젝트 빌드
npm run build
- 오류가 발생했습니다. (오류가 발생하지 않은 분들은 건너뛰어도 됩니다.)
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
<원인>
Next.js 빌드 도중 “JavaScript heap out of memory” 오류가 발생했습니다.
이는 메모리가 부족하여 V8 엔진이 크래시한 경우입니다.
<해결 방법>
- 1. 인스턴스 목록에서 해당하는 인스턴스를 체크 표시합니다.
- 2. 스토리지를 클릭합니다.
- 3. 볼륨 ID를 클릭합니다.
- 해당하는 볼륨을 볼륨 목록에서 체크 표시합니다.
- 볼륨 수정을 클릭합니다.
- 8GiB -> 20GiB 로 바꿔줍니다
- df -h 명령어를 입력해보면, 20GiB인 것을 확인할 수 있습니다.
- swap 메모리를 설정해줍니다.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
- 다시 빌드를 해주는데 이때, Linting과 Type Checking 을 건너뛰고, 8기가로 늘려서 빌드해줍니다.
NEXT_DISABLE_ESLINT=1 NEXT_DISABLE_TSC_CHECK=1 NODE_OPTIONS=--max-old-space-size=8192 npm run build
4. 프로세스 매니저 설정 (PM2 사용)
4-1. pm2 사용이유?
1. 프로그램이 꺼지면 자동으로 다시 켜줄 수 있습니다.
2. 코드가 바뀌었을 때 , 자동으로 프로세스를 컸다 켜줍니다. (즉, 리소스 수정시 자동 반영)
3. 로그를 한번에 볼수있는 화면 지원합니다.
4-2. PM2 설치
sudo npm install -g pm2
4-3. PM2로 애플리케이션 실행
pm2 start npm --name Stellar-Link -- start
4-4. EC2 재부팅 시 자동으로 PM2 애플리케이션 시작
pm2 startup systemd
출력된 명령어를 복사하여 실행합니다. 예시:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
4-5. 현재 PM2 프로세스 저장
pm2 save
5. 실제로 웹사이트에 접속하기
SSH를 통한 접속에 사용한 본인의 퍼블릭 IPv4 DNS:3000 하면 접속이 되어야 합니다.
(접속이 잘 되는 분들은 건너뛰어도 됩니다.)
- 보안그룹규칙(인바운드 규칙)을 보니, 포트에 3000번이 포함되어 있지 않습니다.
- 3번을 클릭하여줍니다.
-인바운드 규칙 편집을 클릭합니다.
- 규칙 추가를 클릭합니다.
- 위와 같은 설정값으로 규칙을 추가해주고 저장합니다.
- 접속이 제대로 되는 것을 확인할 수 있습니다.
그렇다면 , 꼭 3000번 포트가 아니라 80포트로 접속하게 하는 방법은 없을까요?
바로, Nginx를 통해 해결할 수 있습니다.
6. Nginx를 통한 리버스 프록시 설정
Nginx를 사용하여 HTTP/HTTPS 요청을 Node.js 애플리케이션으로 프록시합니다.
6-1. Nginx 사용 이유
- 리버스 프록시 : 사용자가 도메인(example.com)으로 접속하면 Nginx가 이 요청을 받아 실제로 애플리케이션이 실행 중인 포트(예: 3000번 포트)로 요청을 전달합니다.이렇게 하면 사용자가 직접 애플리케이션 서버의 포트로 접속할 필요 없이, 80번(HTTP) 또는 443번(HTTPS) 포트로 요청을 받을 수 있습니다.
- 보안 설정 : SSL 인증서를 설치하여 HTTPS로 안전하게 트래픽을 처리합니다.
nginx를 사용하면 포트 포워딩 즉, 80포트로 진입해도 3000포트로 진입가능하도록 포워딩해주며 https로 관리하게 해줄 수 있습니다.
6-2. Nginx 설치
sudo apt install nginx -y
6-3. Nginx 설정 파일 생성
sudo vi /etc/nginx/sites-available/Stellar-Link
- Stellar-Link 부분에 자신의 앱이름을 작성합니다.
server {
listen 80;
server_name ec2-3-39-191-148.ap-northeast-2.compute.amazonaws.com;
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;
}
}
- server_name 옆 부분의 자신의 웹 도메인 주소를 적어줍니다.
- esc 키를 누르고 :wq! 를 작성하고 엔터를 하여 파일을 저장하고 나가줍니다.
-주의!!! : 도메인 앞에 http:// 무조건 제거 후 작성합니다.
6-5. Nginx 설정 테스트 및 재시작
sudo nginx -t
- 테스트 해줍니다.
sudo systemctl restart nginx
- 재시작 해줍니다.
- 도메인 뒤에 :3000을 붙히지 않아도 제대로 접속이 됩니다.
이후, ec2 인스턴스에 도메인 연결 및 SSL 설정 포스팅은 다음에 작성하겠습니다~~!
다음 글 )
https://choi-hee-yeon.tistory.com/243
https://muna76.tistory.com/259
'웹개발' 카테고리의 다른 글
EC2에 배포한 웹 코드 수정 & VSCode에서 SSH로 접근 (0) | 2025.01.30 |
---|---|
[Next.js] aws ec2 배포하기(2) ( feat. 도메인 연결, SSL ) (0) | 2025.01.30 |
[Next.js] 눈물의 vercel 배포 ( ReferenceError: document is not defined) feat. lottie 애니메이션 (0) | 2025.01.21 |
[Next.js] router.push 한 후, 이동한 페이지의 useEffect가 실행되지 않는 이유 (0) | 2025.01.12 |
[ Intl.DateTimeFormat ] 날짜와 시간을 로케일(지역) 및 특정 형식에 따라 포맷하기 (0) | 2025.01.07 |