최근 드라마앤컴퍼니에서는 리멤버 커리어라는 구직자와 리쿠르터를 연결해주는 서비스를 출시하였습니다 🙂
저는 리멤버 커리어 프로젝트를 진행하면서 S3, CloudFront, Route53, CodePipeline, CodeBuild, Lambda@Edge를 사용하여 웹 서비스 배포, 배포 자동화 구축, Server Side Rendering 없이 SEO하기 등 다양한 작업을 진행하였는데요, 리멤버 커리어를 만들면서 경험한 것들을 글로 옮겨 서버 없이 웹을 배포하려고 하는 사람들에게 도움이 되고자 글을 작성하게 되었습니다.
이번 글에서는 서버리스 아키텍쳐의 필요성에 대해 간략히 공유하고, S3와 CloudFront, Route53을 이용해 서버 없이 React Project를 배포하는 방법에 대해 얘기해보도록 하겠습니다.
서버리스 아키텍쳐의 필요성
웹 서버가 존재한다는 것은 서버 관리 비용 또한 존재한다는 것을 의미합니다. 작게는 OS 보안 업데이트에 따른 새로운 AMI 생성부터 크게는 EC2 인스턴스 장애 해결까지 다양한 관리 비용 생깁니다. 드라마앤컴퍼니에서는 DevOps 엔지니어가 따로 존재하지 않고 Server/Web 팀에서 자신이 개발하는 프로젝트의 코드뿐만 아니라 인프라도 함께 유지보수 및 관리를 하고 있기 때문에 서버 관리 비용이 늘어날수록 서비스 개발 속도에 제동이 걸리는 문제가 있습니다.
그렇기 때문에 더욱 서버리스 아키텍쳐를 선택할 수 밖에 없었는데, 서버리스 아키텍쳐로 서비스를 운영한다는 것은 서버 관리에 대한 대부분을 Cloud Service 사업자, 즉 AWS가 많은 부분을 관리해 준다는 것이고, 이러한 장점은 개발자가 코드를 작성하는 것에 더 많은 시간을 투자 할 수 있게 도와준다는 것을 의미합니다.
또한, 관리 비용 측면 이외에도 가격이 저렴하다는 장점도 있는데요, AWS에서 제공하는 비용 계산기로 아주 간단하게 기존 리멤버에서 사용하던 Instance 사양과 가격 그리고 리멤버 커리어의 비용을 비교해 보았습니다.
리멤버에서 실제로 사용하고 있는 t2.medium 인스턴스 2대의 한 달 가격은 매달 89달러의 비용이 들지만 서버리스 아키텍쳐로 구성한 리멤버 커리어의 가격을 간단히 계산해보면 약 1/200의 비용으로 한 달을 운영 할 수 있다는 것을 알 수 있습니다.
서버리스 아키텍쳐로 웹 배포하기
앞에서도 얘기했듯이 리멤버 커리어는 S3와 CloudFront 그리고 Route53을 기본으로 사용하여 서비스하고 있는데요, 리멤버 커리어 서비스를 만든 방법 그대로 S3 Bucket 생성부터 CloudFront Distribution 생성, Route53 연동까지 스크린샷과 설명을 통해 하나씩 살펴보도록 하겠습니다 🙂
- 이 예제에서는 CRA(create-react-app)로 만든 기본 App을 이용합니다.
- CRA로 만든 초기 코드에 react-router-dom을 이용하여 /test route를 추가하였습니다.
1. S3 Bucket만들기
AWS S3에 들어가 버킷 생성을 누른 후 버킷의 이름을 설정합니다. 버킷의 이름은 원하는 이름으로 생성하면 되지만 추후 사용할 Domain을 그대로 사용하는 것이 관리에 용이합니다. 버킷의 옵션들은 따로 설정하지 않고 진행하겠습니다.
2. S3 Bucket에 React Build File Upload 하기
버킷을 생성하셨다면 로컬에서 Build 한 React app을 업로드 해주어야 합니다.
업로드가 완료되었다면 CloudFront Distribution을 생성해봅시다.
3. CloudFront Distribution 생성하기
Amazon CloudFront는 .html, .css, .js 및 이미지 파일과 같은 정적 및 동적 웹 콘텐츠를 사용자에게 더 빨리 배포하도록 지원하는 CDN 서비스이며 CloudFront는 엣지 로케이션이라고 하는 데이터 센터를 통해 사용자에게 빠르게 콘텐츠를 전달해 줄 수 있습니다. 또한 CloudFront를 사용해야만 SSL 인증서를 사용할 수 있습니다. 그러면 바로 생성을 해보도록 하겠습니다. CloudFront Dashboard에 들어가서 Create Distribution 버튼을 눌러 Distribution을 만들 준비를 합니다.
저희는 Web을 배포할 것이기 때문에 Web의 Get Started를 눌러 다음 단계로 넘어가 주세요.
Origin Settings
가장 먼저 보이는 Origin Setting에서는 Origin Server에 대한 설정을 해줄 수 있습니다.
- Origin Domain Name을 세팅해줍니다.
Origin Domain은 CloudFront가 web content를 받아 올 서버의 Domain을 뜻하며 우리가 만들었던 S3의 버킷에서 content들을 받아와야 하므로 아까 만들어둔 S3 Bucket의 Domain을 찾아 설정해줍니다. 자동완성이 되기 때문에 쉽게 찾을 수 있습니다.
다음은 보안 설정을 해주어야 합니다. 해당 설정을 통해 원치 않는 경로로 Bucket Object에 접근하는 요청을 막을 수 있습니다. (ex. S3 Domain을 알아내서 특정 파일을 탈취)
- Restrict Bucket Access를 yes로 만들어 줍니다. 이 설정을 키게 되면 S3 Domain으로 직접 접근하는 요청을 막아주고 Cloudfront에서 정해진 규칙에 맞는 요청만 Bucket의 Object에 접근 할 수 있도록 해줍니다. 이 정해진 규칙에는 signed cookies나 signed URL 그리고 OAI(Origin Access Identity)가 있고 이 중에서 저희는 OAI를 이용하여 설정할 예정입니다.
- Origin Access Identity 설정에서 Create a New Identity를 선택합니다
- Grant Read Permissions on Bucket 설정에서 Yes, Update Bucket Policy를 선택합니다. 이렇게 되면 OAI가 자동으로 생성되며 S3의 Buckey Policy에 해당 OAI를 가진 User만 (이 경우엔 Cloudfront) Bucket의 Object에 접근하도록 자동으로 설정을 해줍니다.
Default Cache Behavior Settings
Default Cache Behavior Settings에서는 Path Pattern을 눈여겨봐야 합니다. Default로 설정이 되어있고 이는 바꿀 수 없는데요(Distribution 생성 후 Behavior 추가 가능), 모든 path의 요청에 대해서 Cloudfront가 Request에 대해 어떠한 처리를 할 것인지 설정할 수 있습니다.
- Viewer Protocol Policy를 Redirect HTTP to HTTPS로 선택해줍니다.
Distribution Setting
Distribution Setting에서는 CloudFront 자체에 대해서 설정을 해줄 수 있습니다. Domain이나 SSL 등이 이에 해당합니다.
- Alternate Domain Names: Route53에 등록할 Domain을 적는다.
(단, Custom Domain을 사용하지 않고 *.cloudfront.net 도메인을 쓰려면 공란으로 놔둠)
(예제일 경우 sample.dramancompany.com) - SSL Certificate: Custom SSL Certificate
(도메인에 맞는 인증서를 선택, 예제일 경우 *.dramancompany.com.)
(단, Custom Domain을 사용하지 않고 *.cloudfront.net 도메인을 쓰려면 Default 선택)
(SSL 인증서는 us-east-1 region에 존재해야 사용할 수 있다) - Default Root Object: index.html
(sample.dramncompany.com/ 으로 request가 들어오면 sample.dramncompany.com/index.html로 request를 바꾸어줌)
Distribution 생성 마무리
모든 설정을 완료했다면 마지막으로 생성 버튼을 눌러 Distribution을 생성해야 합니다. 생성하고 나면 InProgress 상태의 Distribution을 확인 할 수 있는데 모든 작업이 완료되고 Deployed 상태가 되는 데까지 약 20분의 시간이 걸립니다.
Deployed 상태가 되었다면 *.cloudfront.net과 같은 pattern의 Domain이 주어지는데 이를 통해 웹에 접근 할 수 있습니다.
지금처럼 각자 S3에 업로드 해놓은 page가 뜬다면 성공입니다.
4. 생성된 Distribution에 Error pages 설정 추가하기
지금까지 S3 Bucket을 생성하고 Cloudfront에 S3를 연결하여 웹페이지를 배포하는 것까지 진행하였습니다.
하지만 아직 끝난 게 아닙니다. 우리가 개발하고 있는 Web App들에는 Route마다 보여야 하는 페이지가 다를 수 있습니다. 만약 예를 들어 /test라는 route가 존재한다고 하면 현재 상태에서 해당 route로 접근 시 에러가 발생하게 됩니다. 내가 react-router-dom 등을 이용하여 앱 내부적으로 Route를 나누었어도 실제론 제대로 동작하지 않습니다.
그 이유는 바로 Cloudfront가 test라는 File을 찾아 유저에게 전달하려고 하기 때문입니다. cloudfront는 단순히 S3의 Object들을 유저에게 가장 가까운 Edge에 Caching 해두어 요청이 왔을 때 보다 빠르게 Object를 전달해주기 위해 만들어진 서비스이기 때문이죠.
즉 우리는 에러가 났을 때도 index.html로 접근 할 수 있도록 설정해 주어야 합니다.
이러한 설정은 어떻게 할 수 있을까요? 바로 Error pages 설정을 가지고 해결 할 수 있습니다.
서버에 정해진 File이 존재하지 않을 때 발생하는 Error인 403 Forbidden의 response로 index.html을 대신 전달하게 하면 문제를 해결 할 수 있습니다.
Error Pages
우리가 만들어놓은 Distribution의 Dash Board에 진입하면 상단에 Error Pages라는 Tab을 볼 수 있습니다.
Create Custom Error Response를 클릭하여
다음과 같이 설정 후에 Create를 눌러줍니다. 반영되는 시간이 조금 걸리니 일정 시간이 흐른 후 /test에 다시 접근해 보도록 합시다.
설정 후 Error가 뜨지 않고 제대로 Rendering 되는 모습을 볼 수 있습니다.
5. Route53에 CloudFront Domain 설정하기
이제 거의 막바지 작업입니다. 우리는 CloudFront Default Domain으로 서비스를 할 것이 아니기 때문에 Route53에서 우리가 만든 Domain Record와 CloudFront의 Default Domain을 연결해줘야 합니다.
일단 AWS의 Route53 Dashboard에 접근하여 위에 CloudFront 생성 시 등록했던 Domain의 호스팅 영역을 생성 혹은 Dashboard에 진입합니다. (예제일 경우 dramancompany.com)
진입 후에는 레코드 세트 생성 버튼을 클릭하여 다음과 같이 설정합니다.
유형은 A – IPv4 주소, 별칭은 예를 선택하며 별칭 대상으로는 CloudFront의 Default domain으로 검색하면 CloudFront 배포 항목으로 자동 완성되며 선택 후 레코드 생성을 완료하면 됩니다.
Route53 설정까지 완료됐다면 해당 Domain으로 접근했을 때 성공적으로 Web이 뜨고 자물쇠 모양도 잘 보인다면 성공입니다!
6. 간단한 배포 스크립트 만들기
지금까지 S3와 CloudFront 그리고 Route53을 통해서 웹을 배포해보았습니다. 하지만 배포는 한번 하고 끝나는 것이 아닌 개발이 완료될 때마다 해야 하는 반복 작업이기 때문에 배포를 쉽게 해주는 작업이 매우 중요합니다. 따라서 배포 스크립트를 작성해보도록 하겠습니다.
- 기본적으로 아래의 script를 사용하려면 local에 aws cli 설정이 되어있어야 합니다.
build
"build": "react-scripts build"
- create-react-app으로 project를 만들었다면 처음부터 만들어져 있는 Build script입니다.
- 해당 스크립트를 실행하고 나면 Project의 Root에 build라는 폴더가 만들어집니다.
sync
"sync": "aws s3 sync ./build s3://S3-Bucket-Name --delete"
- S3 bucket에 local에 있는 build 폴더를 sync 합니다.
- 스크립트에서 S3-Bucket-Name에는 각자의 Bucket Name을 적습니다 (예: s3://sample.dramancompany.com)
- delete option을 주어 이전 버전의 File들을 자동으로 삭제하도록 하여 불필요하게 S3 bucket의 용량이 늘어나는 것을 방지합니다.
invalidations
"invalidations": "aws cloudfront create-invalidation --distribution-id Distribution-ID --paths '/*'"
- CloudFront는 유저의 요청을 기본 24시간 동안 Caching 해두기 때문에 캐시 무효화 작업을 해주지 않으면 유저는 이전 버전의 파일들을 CloudFront에 요청하게 됩니다.
- 캐시 무효화 작업을 해주는 것을 invalidations라고 하며 스크립트에서 Distribution-ID에는 각자 생성한 Distribution의 ID 값을 넣어 사용합니다.
deploy
"deploy": "react-scripts build && aws s3 sync ./build s3://S3-Bucket-Name --delete && aws cloudfront create-invalidation --distribution-id Distribution-ID --paths '/*'",
- build → sync → invalidations 작업을 순차적으로 진행합니다.
맺으며
이렇게 React를 S3와 CloudFront 그리고 Route53을 이용해 서버 없이 배포하는 법에 대해 살펴보았는데요, 초기 서버 세팅 시간도 많이 들지 않고 간단하게 서비스를 배포할 수 있으면서, 비용적인 측면에서도 많은 장점이 있는 운영 방식인 것 같습니다.
다음 포스트에서는 Github, CodePipeline, CodeBuild를 이용하여 자동 배포 시스템을 구축하고 Lambda@Edge를 사용하여 Server Side Rendering을 하지 않고도 특정 page의 meta data를 다르게 해주는 SEO 방법에 대해 알아보도록 하겠습니다.
그리고 글이 도움이 되셨다면 리멤버 커리어를 한번 써보시는 것은 어떨까요? 나의 가치를 알아주는 회사가 더 높은 연봉과 함께 찾아올지도 모릅니다! 🙂
드라마앤컴퍼니 또한 언제나 문이 열려있으니 관심 있으시면 지원해보시는 것도 좋을 것 같습니다. 감사합니다.
안녕하세요. 포스팅 잘봤습니다.
s3를 통해 클라이언트를 배포하면 실제 운영중의 서버와의 통신은 도메인이 달라서 cors가 발생할텐데,
해당 부분은 어떻게 처리 하셨나요?
cors를 서버단에서 허용해도 클라이언트의 다른 요청 메소드(put/delete)를 사용 못하는데 어떻게 처리 하셨는지 궁금합니다.
안녕하세요 hare 님, 읽어주셔서 감사합니다 🙂
두 가지 질문을 주셨는데요,
1. s3를 통해 클라이언트를 배포하면 실제 운영 중의 서버와의 통신은 도메인이 달라서 cors가 발생할 텐데,
해당 부분은 어떻게 처리하셨나요?
-> 리멤버에서는 cors 요청이 필요할 경우 서버 단에서 cors 요청을 허용 해주고 있습니다. 또한, cross-origin HTTP 요청을 제한하는 것은 보안상의 이유로 브라우저 단에서 제한하는 것이기 때문에 S3로 배포한 것과는 상관이 없습니다.
2. cors를 서버단에서 허용해도 클라이언트의 다른 요청 메소드(put/delete)를 사용 못 하는데 어떻게 처리하셨는지 궁금합니다.
-> 서버단에서 cors 허용 메소드 또한 설정 할 수 있습니다. put과 delete에 대해서도 추가로 허용해주시면 될 것 같습니다.
원하시는 답변이 되었는지 모르겠습니다 🙂 궁금한 점이 있다면 댓글로 남겨주세요.
제가 공부했던 링크를 추가로 전달 드립니다. 감사합니다.
https://developer.mozilla.org/ko/docs/Web/HTTP/Access_control_CORS
https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
답변 정말 감사드립니다!!
서버단에서 다른 메소드도 허용할 수 있었군요!!
정보 정말 감사드립니다!!
좋은 글 정말 감사합니다!!
저도 기존사이트 aws로 웹호스팅 하려고 하는데
seo 부분이 잘 안되더라고요.
“Server Side Rendering 없이 SEO하기” 글이 기대됩니다 ㅎㅎㅎ
도움이 많이 됐습니다!
다음 편도 기대되네요 🙂
다음편 고대하고 있습니다!
안녕하세요 내용 잘 봤습니다 그럼 web server 단을 완전히 S3로 대체 할 수 있다는 말씀이신지 궁금합니다