
한달하고도 보름이 더 걸린 MERN stack 쇼핑몰 페이지를 AWS에 배포한 과정을 기록해 보려고 한다.
그 전에도 풀스택 블로그, 기업 페이지를 제작한 적은 있었지만 Firebase(PaaS)를 사용했었기에 aws로, 특히 'IaaS 서비스'로 배포해 본 경험이 없었다. 이유는 당연히 파이어베이스가 접근하기 쉬웠고, aws는 백엔드의 역할이 강해보였기 때문이다.
그러나 개발자에게 aws는 바이블같은 존재가 아니겠는가!!
aws 공부를 시작했고, 시작한 김에 자격증까지 도전했다. 물론 가장 기초적인 자격증이지만 개념 잡기에 더할나위 없었다.
자격증 시험 공부했던 시간까지 포함하면 이번 프로젝트가 3개월은 걸리지 않았나 싶다. 하하.
어쨌든간 이번에 도전한 프로젝트 MERN stack 쇼핑몰 웹페이지 AWS배포하기 !!
AWS 배포 과정을 설명하기에 앞서 MERN스택에 대해 간단히 설명하자면,
MongoDB, Express, React, NodeJS 기술을 사용하여 웹사이트를 개발하는 것을 일컫는다.
풀스택 초심자로써 개념 잡기에 좋은 스택이라고 생각된다.
우선 정리해보자하면 프론트엔드는 리액트가 사용되었고, 백엔드는 NodeJS, Express, MongoDB가 사용되었다.
프론트엔드는 고정된 HTML 파일로 정적웹이기 때문에 S3 버킷을 사용하여 배포한 뒤 CloudFront에 연결하여 https 통신을 완성해준다.
백엔드는 하나의 서버이기 때문에 EC2를 이용해 컴퓨팅을 빌린 뒤 Route53에서 호스팅 해주고, ACM에서 SSL인증서를 발급 받아 https 통신을 완성해준다.
각각 배포한 뒤 프론트엔드에서는 axios의 baseURL을 ec2로 배포한 주소를 연결해 주고, 백엔드에서는 s3로 배포한 프론트엔드 주소 를 cors 허용해 주면 된다.
aws가입 후 IAM 유저 생성까지 완료 했다면 프론트엔드를 배포할 준비가 되었다.
1) S3 버킷을 생성해준다.
2) 버킷 이름은 고유하게 원하는 이름을 짓고, 리전은 서울을 선택한다.

추후에 CloudFront 또는 Route53을 이용하여 도메인 생성시 버킷의 이름을 사용하기 때문에 신중하게 작성하는 것을 권장합니다.
3) 버킷의 퍼블릭 엑세스 차단을 해제해준다. (불특정 다수에게 허용되는 권한을 부여하기 위함)

S3는 기본적으로 파일을 저장하는 버킷이기 때문에 초기 설정은 퍼블릭 엑세스가 차단되어있다.
4) 나머지 옵션은 초기 값 그대로 설정해주고, 생성된 버킷을 클릭합니다.

5) 선택한 버킷의 속성 탭 가장 하단에 '정적 웹 사이트 호스팅' 편집을 눌러줍니다.

6) 정적 웹 사이트 호스팅을 활성화해주고, 인덱스 문서에 프로젝트의 루트가 되는 파일을 적어준 후 나머지 옵션은 기본으로 두고 저장해준다.

7) 다시 속성 탭 가장 하단에 '정적 웹 사이트 호스팅'으로 가보면 웹사이트의 엔드포인트가 생성되어있다. (설정이 완료되지 않아 아직 접속은 안됨)

8) 버킷의 권한 탭에 '버킷 정책' 편집을 눌러 준다.

9) 정책 생성기 버튼을 눌러 이와 같이 작성해 주면 된다.


10) 생성한 정책을 이렇게 붙혀넣고 저장해준다.

11) 객체 탭에서 빌드한 파일을 넣어주면 엔드포인트에 접속했을 때 사이트가 잘 나오는 것을 확인할 수 있다.

CloudFront는 콘텐츠 전송 네트워크(CDN) 서비스다. 전세계에 분산된 서버 네트워크를 통해 웹 콘텐츠를 빠르고 효율적으로 전달되며, 엣지 로케이션(edge location)으로 라우팅되어 지연 시간이 최소화되고 콘텐츠 로딩 속도가 향상된다.
또한 HTTPS 및 SSL/TLS 지원하기 때문에 S3와의 연결은 필수라고 할 수 있다.
1) 배포 생성을 눌러준다.
2) 원본 도메인으로 만들었던 S3버킷 주소를 선택해준다.

3) 뷰어 프로토콜 정책으로 Redirect HTTP to HTTPS를 설정해 준다.

4) WAF 방화벽 설정은 비활성화로 둔다.
5) 도메인을 따로 두지 않았기에 특별한 설정 사항 없이 배포 생성을 눌러준다.
별도의 도메인 이름이 없더라도 CloudFront 배포의 기본 도메인 이름을 그대로 사용하여 접근할 수 있습니다.

6) 배포 도메인 이름을 통해 https 접속이 가능해진다.

프론트엔드를 배포하였으니 백엔드 서버를 배포해보자.
1) 인스턴스 시작과 함께 인스턴스의 이름을 설정한다.
2) AMI 구성을 설정해준다. 아래 같은 경우는 Amazon Linux의 프리티어인 AMI를 선택했다.
AMI는 "Amazon Machine Image"의 약자로 인스턴스를 실행하기 위한 템플릿을 제공합니다. 운영체제(OS), 애플리케이션 서버, 애플리케이션과 같은 서버에 필요한 소프트웨어 구성을 포함하 요소들이 포함됩니다.

3) 인스턴스 유형도 기본값으로 두면 포트폴리오용으로 적합한 프리티어 유형이 선택된다.
4) 키페어 생성을 누르고 아래와 같이 설정해준다. 생성한 파일을 폴더에 잘 저장해 두었다가 나중에 OpenSSH로 접근시에 사용한다.

5) 네트워크는 아래와같이 설정해 주되 위치를 내 IP로 설정해준다.
SSH 트래픽 허용: SSH(Secure Shell)는 원격 컴퓨터에 안전하게 접속하기 위한 프로토콜입니다. 이 옵션을 활성화하면, 지정된 IP 주소 또는 IP 주소 범위에서 인스턴스의 22번 포트로 SSH 접속을 시도할 수 있습니다. "0.0.0.0/0"은 모든 IP 주소에서의 접속을 허용한다는 것을 의미합니다.
HTTPS 트래픽 허용: HTTPS는 웹 통신 프로토콜의 보안 버전입니다. 이 규칙을 사용하면, 인스턴스의 443번 포트로 들어오는 안전한 웹 트래픽(HTTPS)을 허용합니다.
HTTP 트래픽 허용: 이 규칙은 인스턴스의 80번 포트로 들어오는 일반 웹 트래픽(HTTP)을 허용합니다.

6) 스토리지는 기본 값으로 설정해준다.
7) 인스턴스 생성을 눌러주면 아래와 같이 인스턴스가 생성된다.

인스턴스를 생성하였으니, 인스턴스에 접근하여 내가 만든 서버를 올려 실행해보자.
1) 생성한 인스턴스에 들어가 연결 버튼을 눌러주면 SSH클라이언트 탭을 확인할 수 있다.

2) 터미널을 열고 아까 저장했던 키페어 "project-mernstack-keypair.pem" 위치로 이동한다. 이동한 위치에서 chmod 400 project-mernstack-keypair.pem 명령으로 키 페어의 접근 권한을 수정해준 뒤, ssh -i "project-mernstack-keypair.pem" ec2-user@ec2-3-37-88-63.ap-northeast-2.compute.amazonaws.com 인스턴스에 연결한다.
3) 아래와 같은 그림이 나오면 내가 생성한 인스턴스에 접속 성공.

4) 내가 빌린 컴퓨터(인스턴스)에 nodeJS를 설치해준다.
$ sudo yum update
# 리눅스 패키지 매니저 업데이트
$ sudo yum install curl
# 명령줄에서 URL을 통한 데이터 전송이 가능한 curl을 설치합니다.
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
# Node.js를 관리하는 도구인 NVM을 설치하는 GitHub에서 제공하는 설치 스크립트이다.
$ . ~/.nvm/nvm.sh
# 설치 후, NVM을 사용할 수 있도록 쉘을 다시 로드합니다.
$ nvm install node
# "node"는 최신 버전을 설치합니다.
$ node -v # Node.js의 버전을 확인합니다.
npm -v # NPM의 버전을 확인합니다.
5) nodeJS가 잘 깔렸다면, github에 올려놓은 프로젝트를 clone 해주면 인스턴스에 프로젝트 설치된다.
$ sudo yum install git
# git 설치
$ git clone <github url>
# 프로젝트 설치
6) node를 통해 서버를 실행해준 뒤, 인스턴스의 퍼블릭 주소를 통해 접속해준다.
프로젝트의 서버 포트 주소와 함께 입력해준다.
ex) http://ec2-3-63.ap-northeast-2.compute.amazonaws.com:3100
7) 하지만 우리는 인스턴스를 생성할 때 3100포트를 허용해 놓지 않았기에, 인바운드 규칙 편집에서 아래와 같이 규칙을 추가해준다.

서버가 잘 구동되겠지만, HTTPS 설정없이는 CORS 오류가 발생한다. 이는 브라우저가 보안상의 이유로 같은 출처(Origin)에서만 리소스를 로드하도록 제한하기 때문이다.
SSL/TLS 인증서 획득하여 로드밸런서를 통해 HTTP 요청을 HTTPS로 리디렉션해주어야 한다.
프론트엔드의 경우처럼 CloudFront를 사용하여 따로 도메인 생성없이 설정할 수 있지만, 이번엔 SSL인증서를 사용하기 때문에 도메인 소유가 필수적이다. SSL/TLS 인증서 발급 과정에서는 도메인 소유권 또는 관리권을 증명하는 절차가 필수적이기 때문이다.
(공부하는 목적이기 때문에 각각 다르게 설정해 보았다. CloudFront는 프리티어 종료 후에 비용도 상당하다고 한다.)
1) 가비아에서 사용할 도메인 주소를 구입해준다.
상업성 없어 보이는 주소를 찾으면 1년 계약에 500원 정도로도 구입이 가능하다.
구입한 도메인은 mern-stack.com 으로 가정하겠다.
2) 도메인을 소유했다면 AWS Route53으로 들어가 호스팅 영역을 생성해준다.
3) 구매한 도메인 이름을 적고, 나머지는 기본값으로 둔 뒤 호스팅 영역 생성 버튼을 눌러준다.

4) 호스팅 영역에서 확인할 수 있느 NS 레코드의 라우팅 대상 4개를 확인할 수 있다.
ex) ns-00.awsdns-09.com.
ns-000.awsdns-16.net.
ns-0000.awsdns-38.co.uk.
ns-0000.awsdns-18.org.

5) 가비아에 내가 구입한 도메인 네임서버 설정에 위 NS 레코드의 라우팅 대상 4개를 넣어 준다. (마지막에 있는 . 은 제거한다.)
소유자 인증까지 마치고 적용을 눌러주면,
AWS에서 해당 도메인이 나의 도메인이라는 것을 인식할 수 있습니다.
이제 호스팅한 도메인을 토대로 SSL인증서를 발급 받기위해 ACM(AWS Certificate Manager)을 이용해준다.
1) ACM 인증서 요청을 눌러준다.
2) 인증서 유형은 기본값인 퍼블릭 인증서 요청을 선택한 뒤 다음을 눌러준다.

3) 구매한 도메인 이름을 입력한 뒤, 나머지는 기본값으로 두고 요청을 눌러준다.

4) ACM 인증서 나열을 클릭하면 요청한 인증서를 확인할 수 있다.
DNS 검증 기준, 검증 대기중 / 아니요 / 부적격 이 나온다면 정상입니다.
5) 발급된 인증서 ID를 클릭 후, Route53에서 레코드 생성 버튼을 눌러 등록된 도메인을 체크하여 생성하면 CNAME 레코드를 생성한다.
소유하고 있는 도메인에 대한 인증을 수행하기 위해 Route53 레코드를 생성합니다.
도메인 이름 시스템(DNS) 내에서 '별칭'을 사용하기 위해 CNAME 레코드를 사용합니다.

6) 호스팅 영역을 생성해 도메인을 인증하고, ACM으로 SSL 인증서를 발급 받는 것을 완료했다.
1) EC2 -> 로드밸런서 항목에 접속하여 Application Load Balance 유형으로 생성해준다.

2) 계정 내에서 고유한 로드밸런서 이름을 적어주고 기본값으로 선택한다.

3) 매핑에서 내 인스턴스가 포함된 가용영역을 포함한 두가지 이상의 가용영역을 선택해준다.

4) 인스턴스를 만들 때 설정했던 보안그룹과 맞춰 설정해준다.
5) 대상 그룹 설정이 필요하여, 대상 그룹 생성을 눌러 인스턴스 유형으로 설정한다. 다른 설정 값은 기본으로 두고 Target Group 이름을 지정해주고 다음 단계로 넘어간다.

6) 사용 가능한 인스턴스 목록해서 해당 인스턴스를 체크한 뒤, 아래와 같이 포트번호를 80번과 443번 총 두가지를 대상에 포함시켜 생성해준다.

7) 다시 로드밸런서로 돌아와 리스너 및 라우팅 탭에서 리스너 추가를 눌러 HTTP와 HTTPS에 각각 만들었던 대상 그룹을 선택해준다.

8) 보안 리스너 설정에서 발급 받은 인증서를 선택해주면 로드밸런서 생성 완료.

9) 로드밸런서 생성이 완료되었으면 각 리스너들의 규칙을 편집해줘야 한다.

10) 먼저 HTTP의 규칙을 추가해 아래와 같이 HTTPS:443으로 리디렉션 하도록 설정해준다.

11) HTTPS의 규칙도 추가하여 라우팅 액션을 대상 그룹으로 전달하고, 대상 그룹은 아까 만든 대상 그룹을 선택해준다.

12) 마지막으로 Route53 호스팅 영역 레코드 설정에서 A 유형을 선택한 후 아래와 같이 별칭을 선택해 도메인의 로드밸런서를 설정하면 되는데 'dualstack' 이라는 키워드를 제거해 준다.
기존 설정된 레코드 A는 도메인 이름에 해당하는 ec2 인스턴스, 즉 서버의 IP를 가지고 있습니다. 서버가 실행 중인 ec2 앞에 로드밸런서가 있고 모든 요청은 이 로드 밸런서를 통해서 서버로 포워딩 되도록 할 것이므로 도메인 이름으로 접속을 하면 ec2 인스턴스가 아닌 로드 밸런서로 요청이 가도록 레코드 A를 변경하는 것입니다.

이렇게까지 설정했다면 내가 만든 웹페이지 HTTPS로 설정하기 완료!
이제 프론트엔드와 백엔드 모두 HTTPS로 배포가 완료되었으니, 코드를 수정해주어야 한다.