nextjs 배포(aws ec2=>도메인 연결 => 자동 배포 설정)

김철준·2024년 9월 27일
2

Devops

목록 보기
8/9

신규 프로젝트 개발 환경 셋팅

신규 프로젝트를 구상중이고 개발 들어가기전에 서버 셋팅부터 하려고 한다.
프론트는 nextjs로 구성하고 백엔드는 spring boot,DB는 postgresql을 사용할 예정이다.

프론트부터 개발 환경 셋팅을 해보려한다.

이전에 aws amplify를 통해 배포를 하였지만 amplify로 배포를 하면 서비스에 이슈가 발생할 수 있다고 하여 amplify를 선택하지 않았다.

vercel로도 배포를 해보려했지만 vercel 또한 배포를 하면 서비스에 이슈가 발생할 수 있다고 하여 vercel도 선택하지 않았다.

따라서 EC2로 배포하는 방법을 선택하였다.

S3에 빌드된 정적파일을 올리면 되지 않는가?
nextjs에서 SSR을 사용할 경우에는 서버사이드에서 동적으로 렌더링 해줘야하기 때문에 react처럼 S3에 배포하지 못한다.
다만, nextjs에서 모든 페이지를 SSG(Static Site Generation)로 구성한다면 가능하다고는 한다.
하지만 내가 구상하는 서비스들의 페이지들은 거의 동적 페이지이기 때문에 서버가 필요하므로 ec2 서버를 사용하기로 결졍했다.

다음 순서대로 개발 환경 셋팅을 해보려한다.

nextjs 프로젝트를 ec2 인스턴스에 배포

ec2에 도메인 연결

자동 배포 설정

nextjs 프로젝트를 ec2 인스턴스에 배포

우선 aws 접속하여 EC2 서비스로 이동해준다.

위와 같이 인스터스 시작을 눌러준다.
시작 전에 리전이 본인이 의도한 리전으로 설정되어 있는지 확인해보자.

나의 경우에는 국내 서비스를 할 것이므로 서울로 지정하였다.

OS 선택

이름을 입력하고 os를 선택해보자.

나의 경우에는 우분투를 선택하였다

인스턴스 유형 선택

프리티어이면 t2.micro 유형으로 한달에 750시간을 무료로 이용할 수 있으므로 나는 t2.micro로 선택했다.

키 페어(pem)

키 페어란 인스턴스에 접속하기 위한 KEY이다.
해당 PEM KEY는 인스턴스 생성시 한번만 발급되고 해당 KEY로만 인스턴스에 접근할 수 있으니 잘 보관하고 있어야한다.

새 키 페어 생성을 누른 뒤, 팝업 화면에서 생성을 한다.

키 페어 생성 버튼을 누르면 컴퓨터에 자동으로 다운로드 될 것이다.

네트워크 설정

SSH를 통해 인스턴스에 접근하고 인터넷에서 접근시 http와 https로도 접근하게 할 것이므로 아래 체크 박스 3개를 모두 체크해준다.

위와 같이 설정하면 aws에서 제공하는 vpc(가상 네트워크) 내 서브넷(인스턴스가 실행될 구역)을 지정하며 자동으로 해당 인스턴스에 접근할 수 있는 ip가 부여된다.

방화벽 설정 항목은 해당 인스턴스에 접근을 제어할 수 있는 부분이라고 생각하면 된다.

스토리지 구성

다음으로 스토리지 구성을 한다.
스토리지란 운영체제 및 데이터를 저장할 수 있는 디스크 공간이라 생각하면 되겠다.

기본으로 8Gib로 설정되어있지만 프리티어는 최대 30gib까지 사용할 수 있다고 하니 30으로 설정하였다.

아래 고급 항목 설정하는 부분이 있는데 이 부분은 그냥 냅두었다.

그리고 이제 위에 설정한대로 인스턴스 시작 버튼을 눌러 만들면 된다.

인스턴스 생성 완료

조금의 시간 뒤에 인스턴스가 생성된다.

SSH로 인스턴스 접속

이제 해당 인스턴스 서버에 접속을 해야한다.

인스턴스 컴퓨터에 원격으로 접속을 하여 프로젝트를 받아와 해당 컴퓨터에서 실행해야하기 때문이다.

인스턴스에 접근하기 위해서는 SSH와 위에서 발급받은 pem key를 통해 접속하면 된다.

terminal을 키고 다음 명령어를 입력하면 된다.

여기서 주의할 것!
pem key가 있는 디렉터리로 이동하여야한다.
아까 위에서 자동 다운로드된 pem key의 경로의 디렉터리를 찾아가거나 내가 원하는 디렉터리로 pem key를 옮기고 터미널에서 해당 디렉터리로 이동을 해야한다.
나의 경우에는 pemkey라는 디렉터리를 컴퓨터에 만들어 pem key를 옮겨놨다.

ssh -i your-key.pem ubuntu@your-ec2-public-ip

위에서 your-key.pem는 받은 pem key 명을 입력하면 되고 뒤에는 ec2 public IP를 입력하면 된다.

나의 경우에는 ec2 퍼블릭 Ipv4 DNS를 입력해주었다.

만약 파일이 존재하지만 다음과 같이 접근 권한 문제가 있을 경우, 권한을 변경해주면 된다.

Warning: Permanently added 'ec2-~~.ap-northeast-2.compute.amazonaws.com' (ED25519) to the list of known hosts.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'test.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "test.pem": bad permissions
jjalseu@ec2-~~~.ap-northeast-2.compute.amazonaws.com: Permission denied (publickey).

이 에러는 .pem 파일의 권한 설정이 너무 넓어서 SSH가 보안을 이유로 파일을 무시한 상황이라고 한다. SSH는 .pem 파일이 다른 사용자들에게 읽히지 않도록 제한된 권한을 요구하는데 이를 해결하려면 .pem 파일의 권한을 수정해 주시면 된다고 한다.

chmod 400 test.pem
  • chmod: 파일의 권한(permission)을 변경하는 명령어
  • 400: 8진수로 표현된 권한 값으로, 이 값은 파일의 소유자만 읽기 권한을 가지며, 쓰기와 실행 권한은 없는 상태를 의미
  • 0644 : 공개적으로 읽어야 하는 파일에 사용

나는 아래와 같이 입력한 뒤 접속을 하려 하니 접속이 거부되었다?

이유는 OS를 우분투로 선택한 경우에는 public IP앞에 prefix로 ubuntu@를 붙여주어야한다.

아래와 같이 입력하니 잘 접속이 되는 것을 확인할 수 있다.

Node.js 및 필수 패키지 설치

이제 인스턴스에 nodejs를 설치하자.
현재 내가 글을 쓰는 시점에는 20이 LTS 버전이므로 20버전으로 설치했다.

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

apt

  • apt는 "Advanced Package Tool"의 약자로, Ubuntu 및 Debian 기반 리눅스 배포판에서 소프트웨어 패키지를 관리하는 도구
  • apt를 사용하면 소프트웨어를 설치, 업데이트, 삭제 가능

나의 경우에는 yarn을 사용할거여서 yarn을 설치했다.

sudo npm install --global yarn

프로젝트 가져온 후 빌드 및 실행

우선 프로젝트를 인스턴스에 설치하기 위해서 git으로 프로젝트를 가져오자.

git 설치 및 프로젝트 클론

git이 필요하니 git부터 설치한다.

sudo apt-get install git

프로젝트를 클론해주고 해당 프로젝트로 이동한다.

# 프로젝트 클론
git clone https://github.com/your-repo/your-nextjs-app.git
cd your-nextjs-app

프로젝트의 패키지들을 설치해준다.

yarn install

빌드 후 실행

이제 프로젝트를 build하고 실행해보자.

빌드

yarn build

실행

yarn start

이렇게 하면

잘 접속이 되는 것을 확인할 수 있다.

하지만 터미널을 종료하게 되면 해당 인스턴스도 접속이 끊기므로 위 퍼블릭 주소로 접속이 안된다.

항상 터미널을 켜둘수 없기 때문에 방법을 찾아봐야한다.

pm2 설치

PM2는 애플리케이션을 계속 실행시키고, 만약 애플리케이션이 크래시(중단)되면 자동으로 다시 시작해준다고 한다. 이로 인해 서버가 중단 없이 안정적으로 실행될 수 있다.

터미널을 우리가 종료하여도 해당 서버로 계속 접근 가능케 할 수 있는 것이다.

pm2 설치 후 실행

그렇다면 pm2를 인스턴스에 설치하자.

# PM2 설치
sudo npm install pm2 -g
# PM2로 애플리케이션 실행
pm2 start npm --name nextjs-app -- start

그리고 ec2가 재부팅될 경우,자동으로 pm2 애플리케이션이 실행될 수 있도록 하자.

# EC2 재부팅 시 자동으로 PM2 애플리케이션 시작
pm2 startup
pm2 save

위처럼 하면 이제 인스턴스를 실행하는 터미널을 종료하여도 ec2 퍼블릭 주소로 잘 접속이 되는 것을 확인할 수 있다.

하지만 뒤에 3000포트로 접속하여야한다.
3000포트없이 80포트로 접속하게 하는 방법없을까?

Nginx를 리버스 프록시로 설정

nginx는 웹 서버 소프트웨어로써 클라이언트의 요청을 받아 내부 서버로 전달하는 역할을 한다.
nginx를 사용하는 이유는 여러가지가 있다.

  • 리버스 프록시 : 사용자가 도메인(example.com)으로 접속하면 Nginx가 이 요청을 받아 실제로 애플리케이션이 실행 중인 포트(예: 3000번 포트)로 요청을 전달한다. 이렇게 하면 사용자가 직접 애플리케이션 서버의 포트로 접속할 필요 없이, 80번(HTTP) 또는 443번(HTTPS) 포트로 요청을 받을 수 있다.

  • 보안 설정 : SSL 인증서를 설치하여 HTTPS로 안전하게 트래픽을 처리

nginx를 사용하면 포트 포워딩 즉, 80포트로 진입해도 3000포트로 진입가능하도록 포워딩해주며 https로 관리하게 해줄 수 있다.

Nginx 설치 및 설정파일 편집

인스턴스에 nginx를 설치하고 vim을 통해 설정파일을 편집해준다.

# Nginx 설치
sudo apt-get install nginx
# Nginx 설정 파일 편집
sudo vi /etc/nginx/sites-available/default

위 명령어를 입력하면 여러 설정 코드들이 나올 것이다. 중간에 아래 코드를 넣으면 된다.

server {
    listen 80;
    server_name your_domain_or_ip;

    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;
    }
}

위처럼 하면 3000포트없이 80(http)로 접속하면 잘되는 것을 확인할 수 있을 것이다.

나의 경우 접속했을 때,css가 다 깨져서 화면이 나왔다.
아래와 같이 설정 코드를 입력하니 css 정상적으로 적용되었다

   location /_next/static/ {
        proxy_pass http://localhost:3000/_next/static/;
        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 systemctl restart nginx

이제 https로 설정하려면 도메인에 SSL인증서를 설정하면 된다.
그전에 도메인을 먼저 구매하고 나의 인스턴스와 도메인 먼저 연결하고자 한다.
어차피 지금 ec2 public DNS로 https 설정을 해도 도메인을 사면 다시해야하기 때문이다.

ec2 인스턴스에 도메인 연결

도메인 구매 (from aws Route53)

우선 도메인을 먼저 구매해야한다.
나는 aws route53에서 도메인도 구매할 수 있다길래 aws route53에서 도메인을 구매했다.

내가 원하는 도메인을 14달러를 주고 구매하였다.

구매하면 도메인이 바로 등록되는 것은 아니고 한 20~30분 기다리면 메일로 도메인이 등록되었다고 올 것이다.

그러면 route53으로 들어가서 이제 도메인과 나의 인스턴스를 연결해주면 된다.

호스팅 존 > 레코드 생성

route53으로 도메인을 구매하였다면 사이드바 > 호스팅 영역에 들어가면 구매한 도메인이 항목이 나타날 것이다.

해당 항목에 들어가서 레코드 생성 버튼을 누르자.

레코드란?
레코드는 도메인 이름과 해당 도메인에 대한 IP 주소 또는 기타 리소스 간의 매핑을 정의하는 항목입니다. 쉽게 말해, 도메인이 인터넷에서 어떤 서버를 가리킬지 결정하는 역할을 합니다.
A 레코드: 도메인을 IPv4 주소와 연결합니다. 예를 들어, example.com을 특정 IP 주소(예: 192.0.2.1)로 연결할 때 사용합니다.

나는 IPv4와 연결할 것이기 때문에 A 레코드를 생성할 것이다.

위에서 다른 것은 건드릴 필요없고 값에만 ec2 인스턴스 IPv4를 입력해주면 된다.

그러면 이제 나의 도메인과 인스턴스가 연결되었다.

nginx 설정 파일에서 server_name 변경

도메인이 EC2 인스턴스에 연결되면, Nginx 설정 파일에서 server_name을 도메인으로 변경해 줘야 한다.

  • Nginx는 하나의 서버에서 여러 도메인에 대한 요청을 처리할 수 있습니다. 이를 위해 Nginx는 server_name 지시문을 사용해 어떤 도메인에 대한 요청인지 구분합니다. 만약 server_name을 설정하지 않으면, Nginx는 모든 요청을 기본 서버 설정으로 처리하게 됩니다.예를 들어, server_name을 설정하지 않으면, www.thedevlounge.com과 thedevlounge.com 모두 동일한 기본 설정으로 처리될 수 있으며, 이로 인해 예상치 못한 동작이 발생할 수 있습니다. Nginx 설정에서 server_name을 설정하면 도메인에 맞는 서버 설정을 적용할 수 있습니다.
  • SSL을 적용하려면 server_name이 정확히 설정되어 있어야 합니다. Certbot 같은 도구로 SSL 인증서를 발급받을 때, Nginx의 server_name 설정에 따라 해당 도메인에 대해 SSL 인증서를 적용할 수 있습니다. 만약 server_name이 올바르게 설정되지 않았다면, SSL 인증서 발급에 문제가 발생하거나, 브라우저에서 "인증서가 올바르지 않다"는 경고 메시지가 나올 수 있습니다.

아까 위에서 설정 파일로 이동하는 명령어를 통해 설정파일을 수정하자.

# Nginx 설정 파일 편집
sudo vi /etc/nginx/sites-available/default
server {
    listen 80;
    server_name thedevlounge.com;
    
    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;
    }
}

설정한 파일을 nginx에 반영해주자.

sudo systemctl reload nginx

SSL 인증서 설정

이제 도메인도 연결했으니 SSL 인증서를 설정하여 https로 접근하게 하자.

Certbot 설치 (Let's Encrypt SSL 인증서 발급 툴)

먼저 Certbot을 인스턴스에 설치하도록 하자. Cerbot은 SSL 인증서 발급에 유용한 도구이다.

Certbot은 Let's Encrypt에서 제공하는 무료 SSL 인증서를 자동으로 발급하고 관리하는 오픈 소스 소프트웨어입니다. Let's Encrypt는 누구나 무료로 SSL 인증서를 발급받아 웹사이트에 HTTPS(보안 연결)를 적용할 수 있도록 해주는 인증 기관입니다.

  • SSL 인증서 자동 발급: Certbot을 사용하면 도메인에 대한 SSL 인증서를 무료로 발급받을 수 있습니다.
  • Nginx 및 Apache 자동 설정: Certbot은 Nginx 또는 Apache와 연동되어, SSL 인증서를 발급받고 자동으로 웹 서버 설정을 업데이트해 HTTPS를 적용할 수 있습니다.
  • SSL 인증서 갱신 자동화: Let's Encrypt에서 발급하는 SSL 인증서는 90일 동안 유효합니다. Certbot은 이를 자동으로 갱신해주는 기능을 제공하여 인증서 만료로 인한 서비스 중단을 방지합니다.
sudo apt-get install certbot python3-certbot-nginx

SSL 인증서 발급

도메인에 대해 SSL 인증서를 발급받기 위해 다음 명령어를 사용하자.

sudo certbot --nginx -d thedevlounge.com

이 명령어는 thedevlounge.com에 대한 SSL 인증서를 발급하고, Nginx 설정을 자동으로 HTTPS로 변경해준다.

SSL 인증서 갱신

Certbot은 인증서를 자동으로 갱신하게 할 수 있다.

sudo certbot renew --dry-run

자 이제 https로 까지 연결을 완료하였다.
다음으로 github에서 main branch에 올리면 자동으로 인스턴스에 배포가 되도록 설정해보자.

자동 배포 설정 with github actions

이제 github actions를 통해 main branch push 되면 자동으로 배포되게 설정해보겠다.

EC2 인스턴스에서 자동 배포를 처리하려면, 먼저 GitHub Actions가 EC2 서버에 SSH로 접속하여 코드를 업데이트할 수 있도록 설정해야한다.

pem을 활용하여 github repository secrets 설정

ec2 인스턴스를 생성할 때 발급받았던 pem키를 활용하자.

EC2 인스턴스를 생성할 때 발급받은 .pem 파일이 있는 위치로 가서, .pem 파일 내용을 확인하면 된다.

PEM 파일의 내용을 확인한다는 것은, EC2 인스턴스를 생성할 때 발급받은 .pem 파일의 내부 데이터를 확인하는 것을 의미한다.

cat enjoyjjalseu.pem

위 명령어를 입력하면

-----BEGIN RSA PRIVATE KEY-----
~~~~~~~~~~~
-----END RSA PRIVATE KEY-----

위 내가 물결표시를 한 부분에 엄청 길게 문자들이 나올 것이다.

이제 ---BEGIN 부터 ---END~~~KEY--- 까지 모두 복사하여 GitHub 리포지토리로 이동하여 Settings → Secrets and variables → Actions → New repository secret으로 들어갑니다.
새로운 Secret 이름을 EC2_PEM_KEY로 지정하고, 복사한 .pem 파일 내용을 붙여넣습니다.

주의사항
위에서 -----BEGIN RSA PRIVATE KEY-----랑 끝에 -----END RSA PRIVATE KEY-----이것도 반드시 복붙하여야한다. 필자는 이걸 생략해서 계속 고생했다.

.github/workflows/deploy.yml

이제 nextjs 프로젝트 최상단 루트에서 .github/workflows/deploy.yml 파일을 생성한다.

그리고 아래 코드를 복붙하고 pem이름과 public ip만 변경하자.

인스턴스에 yarn이 설치되어있지않다면 npm 명령어로 바꿔야한다.

name: Deploy to EC2

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Install SSH key
        run: |
          echo "${{ secrets.EC2_PEM_KEY }}" | tr -d '\r' > enjoyjjalseu.pem
          chmod 400 enjoyjjalseu.pem

      - name: Verify SSH key creation
        run: ls -l enjoyjjalseu.pem

      - name: Deploy to EC2
        run: |
          ssh -vvv -i enjoyjjalseu.pem -o StrictHostKeyChecking=no ubuntu@ec2-3-35-156-121.ap-northeast-2.compute.amazonaws.com 'cd FE_jjalseu_playground && git pull origin main && yarn install && yarn build && pm2 restart all'

위와 같이 설정하면 끝이다.
이제 main branch에 push가 되면 알아서 배포가 될 것이다.

추가

프로젝트 내에서 .env파일의 값을 사용하고 있으면 다음과 같은 작업을 해줘야합니다.

원격 서버에 .env 파일 추가

우선 원격 서버에 접속하여 나의 프로젝트 디렉터리로 이동해줍니다.

 ssh -i ~~.pem ~~
cd FE_jjalseu_playground

아래 명령어를 통해 env 파일 생성 후 사용하고자 하는 값을 입력해줍니다.

nano .env

github action > deploy.yml에 명령어 추가

다음으로 프로젝트의 deploy.yml에 아래와 같이 사용하고자하는 env 파일의 값을 설정해줍니다.

      - name: 환경 변수를 설정합니다.
        run: |
          echo "NEXT_PUBLIC_API_ENDPOINT=$NEXT_PUBLIC_API_ENDPOINT" >> .env
        env:
          NEXT_PUBLIC_API_ENDPOINT: ${{ secrets.NEXT_PUBLIC_API_ENDPOINT }}

env 섹션은 GitHub의 Secrets에 저장된 NEXT_PUBLIC_API_ENDPOINT 값을 가져와서 현재 작업 내 환경 변수로 설정합니다. secrets를 통해 중요한 정보(예: API URL, 인증 키 등)를 안전하게 관리하면서 빌드 프로세스에서 활용할 수 있게 됩니다.

run 섹션은 기본적으로 GitHub Actions 워크플로우에서 환경 변수를 .env 파일에 기록하기 위해 사용됩니다. 이 명령어를 실행하는 이유는 원격 서버에 환경 변수가 없는 경우를 대비하여, 서버로 전송할 .env 파일을 작성하기 위해서입니다.

위처럼 하면 원격서버에서 .env 파일의 값을 사용할 수 있습니다

profile
FE DEVELOPER

0개의 댓글