배포 과정 전체 단계
1. IAM 계정 생성 (보안을 위해 Root 계정 대신 사용)
2. EC2 인스턴스 설정
3. 필수 소프트웨어 설치 및 프로젝트 설정
4. GitHub Actions 설정
5. Nginx 설정
6. 도메인 설정
7. HTTPS(SSL) 설정
이전 S3에 클라이언트 코드를 배포할 때는, Root계정으로 진행했었지만, 보안상 좋지않습니다.
이번에 진행했던 프로젝트에는 openAI의 API키를 사용하기 때문에 더욱 보안에 신경써야 하므로 IAM계정을 사용합니다.
Ubuntu Server + 프리티어(무료)버전의 tc.micro 인스턴스로 선택해줍니다.
RSA유형, .pem형식으로 발급받고 저장해줍니다.
보안그룹 설정으로 다음 포트들을 열어 줍니다.
22 - SSH (기존 설정되어 있던 것)
80 - HTTP (Nginx) 👈🏻 추가!
443 - HTTPS (SSL) 👈🏻 추가!
3000 - Next.js 👈🏻 추가!

30GB까지 무료이므로 30GB로 설정해줍니다.
인스턴스 시작 버튼을 눌러 생성해줍니다.
인스턴스 연결 > SSH 클라이언트탭의 명령어로 인스턴스에 접속해줘야합니다.
아까 저장했던.pem 파일이 위치한 경로에서 터미널을 열어줍니다.
SSH는 보안상의 이유로 프라이빗 키 파일의 권한이 너무 개방적인 경우 접속을 거부합니다.
그렇기 때문에 키 파일의 권한이 나에게만 읽기 권한이 있도록 권한을 축소시킵니다.
(chmod 400의 의미는 4/0/0 이므로 )
그 다음 인스턴스에 연결합니다.
❯ chmod 400 ~~~.pem
❯ ssh -i "job-cracker-key.pem" ubuntu@ ~~~amazonaws.com
# Node.js 최신 버전 설치 (또는 프로젝트에 맞는 버전)
❯ curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
❯ sudo apt-get install -y nodejs
# npm 설치
❯ sudo npm install -g npm@latest
# pnpm 설치 (프로젝트 버전)
❯ sudo npm install -g pnpm@9.12.3
기본적인 무중단 배포 흐름
: 사용자 요청 -> Nginx -> PM2로 관리되는 Node.js 앱
# PM2 전역 설치
❯ sudo npm install -g pm2
# Nginx 설치
❯ sudo apt-get install nginx
# Nginx 설정 파일 열기
❯ sudo nano /etc/nginx/sites-available/프로젝트이름
server {
listen 80;
server_name ; #일단비워둠(나중에 도메인 생기면 변경)
location / {
proxy_pass http://localhost:3000; # Next.js 앱 포트
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를 눌러 저장
Enter를 눌러 확인
Ctrl + X를 눌러 나가기
# 심볼릭 링크 생성해서 설정 활성화
❯ sudo ln -s /etc/nginx/sites-available/프로젝트이름 /etc/nginx/sites-enabled/
# 기본 설정 제거
❯ sudo rm /etc/nginx/sites-enabled/프로젝트이름
# Nginx 설정 테스트
❯ sudo nginx -t
# Nginx 재시작
❯ sudo systemctl restart nginx
이제 EC2의 IP주소로 직접 접속하면 (포트 3000 없이) Next.js 앱에 접근할 수 있습니다.
# 홈 디렉토리로 이동
❯ cd ~
❯ git clone ~
.env에 사용하는 값들과, SSH_KEY, EC2 IP주소를 등록해줍니다.
EC2_HOST: 퍼블릭 IPv4 주소
예: 54.163.47.244 형태의 IP 주소
EC2_SSH_KEY: 인스턴스 연결할 때 사용했던 .pem 파일의 내용
로컬 컴퓨터에서 .pem 파일을 텍스트 에디터로 열어서
-----BEGIN RSA PRIVATE KEY-----부터 -----END RSA PRIVATE KEY-----까지 모든 내용을 복사하여 넣어줍니다.
# EC2 접속 후 각각 위치를 확인
❯ which pnpm # pnpm 위치 확인
❯ which pm2 # pm2 위치 확인
name: Deploy to EC2
on:
push:
branches: ['main']
# 동시 실행 방지 추가
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '20'
- name: Install pnpm
run: npm install -g pnpm@9.12.3
- name: Deploy to EC2
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd ~/Job-Cracker
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" > .env
git pull
/usr/bin/pnpm install
/usr/bin/pnpm build
/usr/bin/pm2 delete all || true
/usr/bin/pm2 start ecosystem.config.cjs
로컬의 프로젝트 루트에 ecosystem.config.cjs 파일을 생성하고, git에도 올려줍니다.
module.exports = {
apps: [{
name: '프로젝트 이름',
cwd: './',
script: 'node_modules/next/dist/bin/next',
args: 'start -p 3000', // 3000 포트 설정
exec_mode: 'cluster',
instances: 0,
autorestart: true,
listen_timeout: 50000,
kill_timeout: 5000
}]
}
next.config.ts 파일말고 해당 파일에서 server설정을 해줍니다.
저는 가비아에서 저렴한 도메인을 구매했습니다.
Route53 에서 호스팅 영역 생성
도메인 이름에 "구매한 도메인" 입력
생성 완료 후 NS(네임서버) 레코드 4개 복사
www 서브도메인을 설정하기위해 Route53에서 A 레코드를 하나 더 추가합니다.
가비아에서 구매했언 도메인을 선택하고 네임서버 설정
Route53에서 제공한 네임서버 주소 4개를 차례대로 입력합니다. 뒤에 '.'은 빼주세요
(예: ns-1234.awsdns-12.org 형식)
DNS 수정 > 레코드 관리까지 수정해줍니다.
호스팅 영역에서 "레코드 생성" 클릭
레코드 유형: A
값: EC2의 퍼블릭 IP 주소 입력
nano 에디터에서 저장하고 나가는 방법
저장: Ctrl + O 를 누른 후 Enter
종료: Ctrl + X
❯ sudo nano /etc/nginx/sites-available/프로젝트이름
구매한 도메인으로 변경합니다.
server {
listen 80;
server_name 도메인주소 www.도메인주소; #아까 비워둔 곳을 구매한 도메인으로 변경
location / {
proxy_pass http://localhost:3000; # Next.js 앱 포트
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;
}
}
설정을 변경한 후에는 반드시!
❯ sudo nginx -t # 설정이 올바른지 테스트
❯ sudo systemctl reload nginx # 새로운 설정 적용
Let's Encrypt라는 무료 SSL 인증서를 사용하여 설정해보겠습니다.
왜 HTTPS를 써야할까?
HTTPS는 웹사이트의 보안을 위해서 매우 중요합니다.
1. 데이터 보안
- 사용자와 서버 간의 모든 통신을 암호화
- 개인정보, 비밀번호 등이 노출되는 것을 방지
_특히 제가 배포하는 사이트는 OpenAI API 키를 사용하므로 보안이 중요!_
2. 브라우저 경고 방지
- 최신 브라우저들은 HTTP 사이트를 "안전하지 않음"으로 표시
- 사용자들에게 경고 메시지를 보여줄 수 있음
3. 신뢰성
- 브라우저의 자물쇠 아이콘이 사이트의 신뢰성을 보여줌
- 사용자들이 안심하고 사이트 이용 가능
4. SEO 이점
- Google은 HTTPS 사이트를 검색 순위에서 우대
- 검색 엔진 최적화에 도움
Let's Encrypt 인증서는 90일마다 갱신이 필요한데, certbot이 자동으로 갱신해줍니다.
snap으로 설치해보겠습니다. 자동으로 갱신 크론잡이 설정됩니다.
# 1. snap으로 certbot 설치
❯ sudo snap install core
❯ sudo snap refresh core
❯ sudo snap install --classic certbot
❯ sudo ln -s /snap/bin/certbot /usr/bin/certbot
# 2. 인증서 발급
❯ sudo certbot --nginx -d 도메인 -d www.도메인