Amplify에서 S3 CloudFront로 전환하기

Amplify의 불편한 점

Amplify는 웹 서비스를 개발하고 배포하기 위해 필요한 AWS의 기능들을 짜깁기한 서비스다. github을 연동하고 설정만 조금 만지면 웹 앱 하나가 뚝딱 만들어진다. S3, CloudFront를 설정하고 연동해야 하는 부담이 없어 사용했다. 하지만 기능이 간소화 된 만큼 디테일한 부분을 다루는 게 불가능했다. 나의 경우 배포 후 캐시를 삭제할 수 없어서 기다리면 되겠거니 했는데 거의 하루 반나절 동안 페이지를 볼 수 없었다.

리다이렉트 규칙을 세세히 적용할 수 없었다. gatsby의 정적 리소스들을 서빙하는 데 문제가 발생했다. 예를 들어 *.js, *.css로 끝나는 요청의 경우 해당 파일을 서빙해야 하고. 그 외에는 index.html을 서빙해야 하는데. 해당 기능을 설정하면 이게 기능이 각 서버에 배포 중인지 아닌지에 대한 정보도 표시되지 않고. 스크립트 오류는 계속 발생했다.

Amplify Redirect 설정
Amplify Redirect 설정

위와 같이 정규식으로 Redirect를 설정하는데 배포 상태를 알 수 없으니 식을 맞게 작성한것인지 파악하기도 어려웠다. *.js 요청에 html본문이 응답되는 현상이 해결되지 않아서 Amplify를 쓰지 않기로 결정했다.

S3, CloudFront로 Gatsby 서빙하기

그리하여 Gatsby를 사용하기 위한 AWS의 서비스들을 직접 설정하고 연동했다. 번거롭지만 확실하게 해당 문제들을 해결할 수 있게 되어 만족스러웠다. 아래부터는 mnkim.com을 S3, CloudFront에 설정했던 내용에 대해 자세히 다룬다.

S3 버킷 설정하기

S3는 AWS서비스 중 스토리지 관리와 모니터링을 제공하는 기능이다. 사실 나도 무슨 말인지 모르겠다. 다만 우리는 gatsby build 명령을 통해 만들어진 public/* 파일들을 업로드하고 각 파일에 서비스 별 접근 권한을 설정하는 기능을 사용하게 될 것이다.

먼저 버킷을 만든다. 버킷의 이름은 나중에 다른사람들이 블로그에 방문할 때 쓰는 주소로 한다. 예를 들어 www를 붙이지 않은 mnkim.com을 원하는 경우 이름은 mnkim.com이 되어야 한다. 반대의 경우 www.mnkim.com로 이름을 짓는다.

s3 1

생성할 때는 위 화면처럼 퍼블릭 엑세스를 가능하게끔만 설정하면 된다. 생성이 완료되었다면 개요 탭에서 gatsby build를 통해 생성된 public/*의 하위 파일들을 모두 선택해 업로드한다. 다음 속성 탭에서 정적 웹 사이트 호스팅을 선택하고 아래와 같이 설정한다.

perm

다음으로는 권한 탭의 버킷 정책에서 '정책 편집기' 타이틀 옆 ARN값을 복사해 두고 에디터 아래 정책 생성기를 클릭힌다. 그럼 다음곽 같은 화면이 나오는데 위의 스샷처럼 입력한다. Actions는 GetObject를 선택하면 된다.

이어서 Add Statement를 클릭하면 아래 정책목록이 추가되고. Generate Policy를 클릭하면 팝업에 json값이 출력되는데 이것이 S3 버킷에 대한 권한 설정 내용이다. 복사하여 부모 창의 편집기에 넣고 저장을 누른다. 이 때 반드시 Resource뒤에 '/*'를 붙여 버킷 내 파일 모두에 접근할 수 있도록 해 주어야 한다.

{
  "Version": "2012-10-17",
  "Id": "Policy1239293829383",
  "Statement": [
    {
      "Sid": "Stmt15910279239283",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::<내 도메인>/*"
    }
  ]
}

s3 2

이 버킷을 사용하여 웹 사이트를 호스팅합니다 체크, 인덱스 문서는 index.html, 오류 문서는 404.html로 적고 저장을 누른다. 그럼 이제부터 상단에 있는 엔드포인트 주소로 사이트를 서빙할 수 있게 된다. 다만 이 주소는 기억하기 어려운 형태이고 http이기도 해서 이후 과정에서 별도의 도메인을 연결한다.

Certificate Manager로 인증서 만들기

먼저 https로 서빙하기 위한 인증서를 만들어야 한다. Certificate Manager에서 인증서 요청 을 누르고 이어 공인 인증서 요청 선택. 도메인 이름에 johnny.com. 아래 다른 이름 추가 를 누르고 *.johnny.com (소유한 도메인으로 입력한다)

cm 1

이후 과정에서 해당 도메인의 진짜 소유자인지를 검사하는 과정을 거친다. DNS인증을 선택하고 나오는 각 도메인 별 이름과 값을 도메인을 구입한 사이트에서 제공하는 DNS구성 기능으로 CNAME으로 등록해야 한다. 드롭다운으로 A인지 CNAME인지 TXT인지 선택하고 이름과 값을 입력하는 UI구성으로 아마 조금 살펴보면 알 수 있을 것이다.

도메인을 AWS Route 53에서 구입했거나. 구입처가 다르지만 Route 53에서 관리하도록 설정한 경우에는 아래 버튼을 누르면 자동으로 그 값들을 등록해준다.

cm 2

CloudFront 설정하기

CloudFront는 S3버킷에 담긴 파일을 전세계 서버에 캐싱해서 빠르게 서빙할 수 있도록 해 주는거 같다. 안해도 상관은 없는데 하면 사이트 성능이 크게 개선되므로 해보는것도 나쁘지 않다.

Create Distribution클릭. Web 섹션의 Get Started클릭. 아래 필드에서 따로 언급하지 않은 것들은 기본값으로 내버려 둔다.

cf 1

아래 Distribution Settings에서 Alternative Domain Names에 사용할 도메인을 입력하고. 아래 Custom SSL Certificate 를 선택한다. 그 후 아래 텍스트박스에 포커스하면 만들어둔 인증서가 보일텐데 그것을 선택한다.

cf 2

마지막으로 Default Root Object를 반드시 index.html로 입력한 후 다음으로 넘어간다. 이렇게 되면 세팅이 끝나고 전세계의 모든 서버에 해당 설정이 반영되는 시간 후 Distribution StatusDeployed로 바뀌며 화면의 Domain Name 으로 블로그에 접속할 수 있게 된다. 하지만 우리는 이 주소가 아닌 내 소유의 도메인 johnny.com을 연결해야 한다 이후에서 진행한다.

cf 3

Route 53으로 커스텀 도메인 연결하기

일단 나처럼 Godaddy의 도메인을 Route 53에서 관리하도록 설정하든, Route 53에서 직접 구입하든 도메인이 하나 있어야 한다. (위에서 인증서를 커스텀으로 설정했다면 있을 것이다) 호스팅 영역을 생성하고 레코드 세트를 하나 생성한다. 유형A. 은 방금 설정했던 CloudFront의 Domain Name을 입력하면 되는데.

직접 입력해도 되지만 별칭로 선택하고 텍스트박스에 포커스 하면 목록이 출력되는데. 그 중 CloudFront배포 에 이전에 설정한 값이 있으므로 선택해서 채워도 된다. Route 53 미사용자는 앞서 도메인을 인증할 때 썼던 페이지에서 주소를 직접 입력하면 된다.

r 1

저장 후 10분 정도 기다리면 해당 도메인으로 블로그에 접속할 수 있다!

www에서 non-www로 보내기 (선택)

안해도 되는데 나처럼 non-www로만 서빙을 원하는 경우 설정하면 된다. 아까 johnny.com으로 S3버킷을 만들었던 것 처럼 똑같이 www.johnny.com이름으로 버킷을 만든다. 이 때 퍼블릭 엑세스는 풀고. 파일은 업로드하지 않아도 된다.

속성에서 정적 웹사이트 호스팅을 체크하고 이번엔 요청 리디렉션을 선택한다. 대상 버킷 또는 도메인에 johnny.com을 입력하고 프로토콜은 https를 선택한다. 반대의 경우는 반대로 입력하면 된다. 물론 위의 세팅들이 모두 반대 (www)로 설정되었어야 한다.

non www

그 다음 Route 53에서 같은 호스팅 영역에 새 레코드 세트를 만든다. 이름은 www.johnny.com. 유형은 A. 값은 별칭으로 동일하게 선택하되 www.johnny.com버킷을 선택한다.

배포 설정

이제 맨 처음 S3에 빌드 결과물을 직접 업로드 했던 것을 명령으로 할 수 있게 할 차례다. 그런데 별도의 인증 없이 내 버킷에 업로드가 가능하다면 다른사람도 업로드할 수 있다는 말이 되니까 인증을 해야 한다. 먼저 AWS서비스 중 IAM에서 인증 토큰을 발급한다.

대시보드에서 사용자를 누르고 상단 사용자 추가를 누른다. 사용자 이름을 입력하고 AWS액세스 유형은 프로그래밍 방식 액세스. 그룹 생성을 눌러 그룹 이름은 원하는 대로 짓고 AdministratorAccess 권한을 부여한다.

모든 과정을 완료하면 마지막에 액세스 키 ID와 시크릿 액세스 키가 보이고 따로 인증 csv를 다운로드 받을 수 있는 화면이 보인다. 이 페이지를 벗어나면 더 이상 키를 확인할 수 없으므로 csv파일은 잘 보관해둔다.

먼저 프로젝트에 dotenv패키지를 설치한다.

npm install --save-dev dotenv
yarn add -D dotenv

다음 .env파일을 생성하고 다음 내용을 입력한다. 해당 파일은 git에 커밋되지 않도록 해야 한다. 만약 커밋을 한다면 다른사람들이 이 내용을 볼 수 없도록 repository자체를 private으로 만들어야 한다.

AWS_ACCESS_KEY_ID=xxxx
AWS_SECRET_ACCESS_KEY=xxxx

이어서 gatsby-plugin-s3패키지를 설치하고 gatsby-config.js의 플러그인 항목에 아래 내용을 추가한다.

require('dotenv').config() // .env 파일 읽어서 환경변수에 추가한다

module.exports = {
  plugins: [
    {
      resolve: 'gatsby-plugin-s3',
      options: {
        bucketName: 'johnny.com', // 업로드 대상 S3 버킷 이름
        protocol: 'https', // 프로토콜
        hostname: 'johnny.com', // 호스트명
      },
    },
  ],
}

그 후 gatsby build && npx -n "-r dotenv/config" gatsby-plugin-s3 deploy 명령을 실행하면 빌드 후 내용이 S3 버킷에 업로드 된다. 아래처럼 package.json에 설정해두고 쓰면 편하다.

"scripts": {
    "deploy": "npx -n \"-r dotenv/config\" gatsby-plugin-s3 deploy"
}

작업 후기

  • johnny.com으로는 접속이 잘 되는데 johnny.com/blog와 같이 서브도메인에서 새로고침했을 때 AccessDenied에러가 나는 경우는 S3버킷 정책이 올바르게 설정되지 않았기 때문일 수 있다. 최종적으로 설정했을 때 정책이 바뀌어 있다면 맨 위 S3버킷 설정과 동일하게 다시 설정한다.
  • 이제 캐시를 직접 제거할 수 있다. CloudFront에서 해당 Distribution을 선택하고 아래 처럼 Invalidation을 만들어 주면 된다.

Cache Invalidation 설정
Cache Invalidation 설정

  • CloudFront의 내용 수정이 모든 Edge에 반영되기까지 시간이 꽤 걸린다. 그리고 삭제를 원하는 경우 Distribution이 비활성화가 되어야 한다.