최근에 사이드 프로젝트를 진행하면서 싱글 페이지 어플리케이션(SPA)를 AWS를 활용해 호스팅 해 볼 기회가 생겼습니다. 회사에서는 이런 인프라 관련 설정들이 이미 세팅되어 있어서 제가 직접 설정할 일이 없었기 때문에, 처음부터 직접 세팅을 해 본 것은 처음이었습니다.
결론적으로 이야기하면 다음과 같은 과정을 거쳐야 합니다.
- S3에 빌드된 정적 파일을 올리기
- CloudFront가 S3의 경로를 가리키게 하기
- 도메인을 붙이려면 ACM 설정하기
- CloudFront에다가 Route 53을 활용해 도메인을 붙이기
그래서 오늘은 어떤 과정을 통해 AWS에 SPA 호스팅을 할 수 있는지를 정리해보려고 합니다. 이 포스트를 통해 AWS를 통해 SPA를 호스팅하는데 관심이 있으신 분들께 도움이 되기를 바랍니다.
S3
우선 빌드된 결과물을 S3에 업로드하는 것으로부터 배포가 시작됩니다. S3에 대해 모르는 분이 계실 수도 있을 것 같아서 간단하게 설명하자면, AWS에 파일을 업로드할 수 있는 서비스입니다.
별도의 프레임워크를 사용하지 않는 간단한 정적 웹사이트라면 index.html
을 비롯한 CSS, JavaScript 파일들만 준비하면 됩니다. create-react-app이나 vue-cli처럼 라이브러리 또는 프레임워크를 활용하고 계시다면 내장된 빌드 명령어로 추출된 /dist
디렉토리 내의 파일들을 업로드 하면 될 것입니다.
직접 /dist
내의 파일들을 업로드 해도 되지만, Github Actions 같은 CI/CD 워크플로우를 구성해 이 과정을 자동화 할 수도 있습니다. 해당 과정은 여기를 참고해주세요.
저는 특별한 설정 없이 Hello world
라는 내용이 적힌 index.html
하나만을 S3에 업로드해보았습니다. 이때 S3 버킷 이름은 Route 53을 이용해 적용할 도메인의 이름과 같은 것으로 해야 합니다.
우선 해당 S3 버킷의 접근 권한을 퍼블릭하게 만들어야 합니다. 버킷 설정에서 권한 탭에 들어가면 버킷 정책을 JSON 형태로 입력할 수 있는 텍스트 입력기가 나옵니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicRead", // 설명 용도이므로 생략해도 됩니다.
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::여기에 버킷 이름을 입력해주세요/*"]
}
]
}
AWS 공식 문서에 있는 익명 사용자에게 읽기 권한 부여를 복사하여 붙여넣습니다. ("s3:GetObjectVersion"
는 필요 없을 것 같아서 뺐습니다.)
S3에 index.html
파일을 업로드 한 상태입니다.
그 후 해당 S3 버킷에 빌드된 파일을 업로드합니다. 파일을 업로드 할 때에도 접근 권한을 퍼블릭하게 허용해줍니다.
폴백(fallback) 파일을 index.html
로 설정해줍니다.
만약 클라이언트 사이드에서 라우트를 사용하게 된다면, S3 버킷 설정 중 정적 웹사이트 호스팅 버튼을 클릭해 오류 문서를 index.html
로 설정해줍니다.
React나 Vue 같은 SPA 라이브러리 또는 프레임워크를 사용하게 되면 클라이언트 사이드의 라우팅을 사용하게 됩니다. 즉, 별다른 설정 없이 /posts/123
처럼 서브 디렉토리 형태의 URL로 접근하게 되면 S3 내에 있는 /post
라는 폴더 내의 123
이라는 파일을 찾게 되기 때문에, 의도한 바와 다르게 접근할 수 없는 URL이 됩니다. 따라서 모든 라우팅이 클라이언트 사이드에서 이루어질 수 있게, 오류 문서의 항목을 index.html
로 설정해줍니다.
접근 권한이 퍼블릭하게 잘 설정되어 있고, 정적 웹 사이트 호스팅을 잘 설정해주었을 경우라면 S3에 업로드 된 index.html
URL 로 접속했을 때 파일이 잘 열려야 합니다.
CloudFront
다음으로는 CloudFront 서비스에 접속합니다. CloudFront는 전세계에 내가 만든 웹 어플리케이션을 배포할 수 있는 CDN의 역할 뿐만 아니라 캐싱(Caching)을 이용한 응답 속도 향상, HTTPS 적용을 가능하게 해 주는 서비스입니다. 우선은 Route 53을 이용한 도메인을 입히지 않고, CloudFront에서 제공해주는 기본 URL을 사용해서 S3에 접근할 수 있게 하는 예시를 설명해보겠습니다. 별 다른 설정없이 사용하게 되면 임의의 *.cloudfront.net
라는 도메인이 생성됩니다.
CloudFront Distribution 대시보드에 접속한 모습입니다.
새로운 CloudFront 배포(Distribution)를 생성하기 위해 Create Distribution
버튼을 클릭합니다.
웹 사이트 배포를 위한 목적이므로 위의 Web
에서 Get Started
를 클릭합니다.
Origin Domain Name
을 S3로 설정합니다.
복잡한 세팅들이 많이 보일텐데, 이들 중 Origin Domain Name
을 위에서 설정한 S3로 설정합니다.
Viewer Protocol Policy
는 필요에 따라 설정하면 되는데, HTTP와 HTTPS를 모두 지원하려면 HTTP and HTTPS
를, HTTPS로 리다이렉션 시키려면 Redirect HTTP to HTTPS
로 설정하면 됩니다. 특히 일부 라이브러리나 프로토콜 같은 경우에는 보안 상의 이유로 HTTPS 환경에서만 동작하게 하는 경우가 있으므로, HTTP 접속을 시도하는 유저를 강제로 HTTPS로 리다이렉션 시켜야 합니다.
Default Root Object
는 index.html
을 가리키게 합니다.
S3에서 index.html
을 기본 접근 경로로 설정했던 것처럼, CloudFront에서도 동일한 설정을 해 줍니다.
배포를 하게 되면 In Progress
상태가 됩니다.
설정을 마친 후 생성 버튼을 누르게 되면 In Progress
상태가 되면서 배포가 되는데, Complete
가 되기 까지 보통 10분여 정도의 시간이 걸립니다.
배포가 되는 동안 해당 Distribution
으로 들어가서 Error Pages
탭을 클릭하고 Create Custom Error Response
를 클릭합니다.
S3에서 클라이언트 사이드의 라우트를 타기 위해 에러 페이지를 index.html
로 설정해주었던 것 기억나시나요? 마찬가지로 CloudFront를 통해서도 존재하지 않는 경로의 파일을 접속할 때 GET
요청을 보내는데, 403 Forbidden
에러가 뜨는 것을 방지하기 위해 200 OK
를 보내도록 설정합니다. 이를 설정하지 않아도 웹 어플리케이션은 동작은 하지만, 매번 라우트 변경이 일어날 때 마다 CloudFront에 해당 URL의 GET
요청을 보내게 되면서 콘솔에 오류가 찍히게 됩니다.
우선은 Alternative Domain Name
을 적용하지 않고 CloudFront에서 기본으로 제공해주는 URL이 적용된 모습입니다.
배포가 완료된 후라면 제공된 CloudFront에서 제공한 URL로 접속이 잘 되어야 하고, HTTPS 적용도 되어 있어야 합니다.
ACM
Alternative Domain Names
으로 커스텀 도메인을 설정하려면 ACM으로 인증된 커스텀 SSL 인증서가 있어야 합니다.
지금까지의 예시는 Default CloudFront Certificate
를 선택해서 CloudFront에서 제공해주는 기본 URL을 사용해 배포한 경우입니다. 이번에는 Alternative Domain Names
을 이용해 구매한 도메인을 CloudFront에 적용해보겠습니다.
우선 커스텀 도메인을 사용하려면 CloudFront Distribution 설정에서 Alternative Custom Domain
을 입력해야 합니다. 그 후 SSL Certificate 메뉴에서 Custom SSL Certicifate
를 사용해야 합니다. 사용하고자 하는 도메인에 대한 인증서가 없다면, Request or Import a Certificate with ACM
를 클릭해 새 SSL 인증서를 생성해봅니다.
들어가게 되면 도메인 이름을 입력할 수 있는 창이 나오게 됩니다. 일반적으로 루트도메인(example.com), 그리고 서브도메인(*.example.com) 형태로 입력하면 거의 대부분의 도메인 인증범위를 포함할 수 있습니다. 2단계의 검증 방법은 DNS 검증을 선택합니다. 그 외의 단계는 특별한 설정 없이 넘겨도 좋습니다.
검증 단계에서 도메인 소유주가 확인되면 Route 53에서 레코드 생성
버튼이 나옵니다.
위 사진은 도메인을 Route 53에서 구매했기 때문에 검증 단계에서 도메인 아코디언을 확장하게 되면 위의 사진처럼 Route 53에서 레코드 생성
버튼이 나옵니다.
클릭하면 임의의 문자열로 생성된 이름과 값, CNAME을 Route 53에 추가하겠다는 모달이 뜨는데, 생성
을 눌러줍니다.
생성되면 위와 같은 성공 메시지가 뜹니다.
ACM 대시보드에서 상태가 발급 완료로 되어야 합니다.
ACM 대시보드에서 방금 설정한 도메인 인증서가 발급 완료 상태가 되어야 합니다.
ACM에서 자동으로 등록된 CNAME 영역이 존재해야 합니다.
ACM 등록 과정 마지막에 Route 53에서 레코드 생성을 눌렀다면 해당 호스팅 영역에 acm-validation
이라는 이름의 CNAME이 등록되어 있어야 합니다.
외부의 도메인을 등록하는 경우이거나 이 값이 제대로 등록되지 않은 경우라면 수동으로 등록해야 합니다. 위에서 확인한 이름과 값에 해당하는 문자열을 등록해주면 됩니다.
다시 CloudFront로 돌아와보면 해당 SSL 인증서를 선택할 수 있습니다.
다시 CloudFront로 돌아와보면 방금 추가한 SSL 인증서가 목록에 나타나야 합니다. 내가 Alternative Domain Names
로 설정할 도메인을 포괄할 수 있는 SSL 인증서를 선택해줍니다. 즉 만약 dev.example.com 도메인을 설정한다면 도메인이 *.example.com 인 SSL 인증서를 선택해야겠죠.
Route 53
Route 53은 AWS에서 제공하는 도메인 이름 서비스(DNS)입니다. 본문의 에시는 AWS Route 53에서 직접 도메인을 구입한 경우이고, 외부 서비스를 이용해 도메인을 구입한 경우에는 AWS에서 사용할 수 있게 별도의 설정을 거쳐야 합니다. 해당 과정은 이 게시물에서 확인하실 수 있습니다.
우선 Route 53의 호스팅 영역 메뉴에 접속해서 호스팅 영역을 생성해줍니다. 그 후 해당 호스팅 영역을 클릭해 레코드 생성
버튼을 눌러줍니다.
트래픽 대상을 CloudFront 배포에 대한 별칭
, 레코드 유형을 A
로 설정합니다. CloudFront의 Alternative Domain Name
을 제대로 입력했다면 배포 선택 인풋에서 CloudFront Distribution을 선택할 수 있습니다. 서브 도메인을 사용하려면 레코드 이름
을 추가로 입력합니다.
위의 예시는 example.com 호스팅 영역 내에서 dev.example.com 레코드가 생성되었고 그것이 CloudFront와 연결된 모습입니다.
Route 53을 통해 CloudFront에 성공적으로 도메인이 입혀진 모습입니다.
마무리
위 과정을 통해 루트 도메인(example.com) 뿐만 아니라 서브 도메인(dev.example.com)에도 별도의 서비스를 연결시킬 수 있고, 이를 응용하면 www.example.com 같이 특정 서브 도메인이 앞에 붙은 경우에는 루트 도메인으로 리다이렉션 시키는 경우도 가능합니다.
이 과정에서 현재 로그인한 AWS의 계정이 IAM으로 생성된 계정이라면, 위에서 사용하는 모든 서비스에 대한 권한이 있는지 확인해야 합니다. 당연하게도 권한이 없다면 중간 과정에서 오류가 발생할 수 있습니다.
혹시나 내용 중 잘못된 부분이 있다면 코멘트로 남겨주시면 감사하겠습니다.