[Next.js + AWS CodePipeline] 고정 Build Id에 따른 브라우저 캐싱

SMG·2021년 7월 17일
3

이전 포스트 이어서,

두번째 문제 발생

고정 빌드Id를 설정해 놓은 후 Auto scaling에 따라 트래픽 라우트가 정상적으로 작동했다.

이로부터 얼마 후, 다른 업데이트 사항이 있어서 배포를 진행했는데

UI상의 변화가 일어나지 않았다.

UI가 A -> B로 업데이트 했다고 치면,

리프레쉬시 A,

다른 페이지로 라우팅 했다가 다시 라우트 백하면 B가 나오기도 했다.

WebView앱을 개발해서 서비스중인데, 웹뷰앱 상에선 전혀 변화가 나타나지 않았다.
(몇 시간이 지나도....)

문제 점검 순서

  1. 이미 서버에 올라간 파일은 바뀌었는데 나타난다는 건 캐시문제로 짐작했고,
    추가로 캐시 삭제를 하면 배포된 리소스가 정상적으로 받아져서 확신했다.

그러던 중 Next Docs 중 Disabling Etag Generation 부분에서 캐시관련 내용을 발견했다.
(이땐 고정 빌드ID를 적용한지 좀 지나서 바로 생각이 나지 않았었다.)

Next.js will generate etags for every page by default. You may want to disable etag generation for HTML pages depending on your cache strategy.
(캐시 전략에 따라 etag를 끌수있다.)

Open next.config.js and disable the generateEtags option:

module.exports = {
  generateEtags: false,
}

출처: https://nextjs.org/docs/api-reference/next.config.js/disabling-etag-generation

Etag는 버전 리소스 식별자인데
이런식으로 Http 응답 헤더에 있는 식별자이다.

그래서 이렇게 추측했다.
" 아, 새로운 리소스 마다 캐싱되고, Etag가 변하지 않으면 캐싱된 리소스를 사용하겠구나"

그래서 아예 Etag를 생성하지 않으면 캐시와 무관하겠다고 생각했고

바로 Etag generation을 끄고 재배포를 했다.
(응답 헤더의 etag부분이 없이 나오게 됨.)

하지만 변화가 없었다. 계속 이전 배포 리소스만 나왔다.

  1. 리소스마다의 식별자가 캐싱에 영향을 준다고 생각을 했고 리서치 하던 중
    페이지 캐시와 빌드Id와의 상관관계에 대한 Next.js 블로그 글을 발견했다.

출처 : https://nextjs.org/blog/next-9-5#persistent-caching-for-page-bundles

이 글의 전반적인 내용은 캐시 방법의 변경에 대한 내용인데.

/_next/static/ovgxWYrvKyjnlM15qtz7h/pages/about.js
The path segment ovgxWYrvKyjnlM15qtz7h above is what we called the build ID. While these files were easily cacheable at the edge and on the user's machine, after re-building your app, the build ID would change and all caches would be busted.

이 중 눈에 들어온 내용은

"빌드시마다 빌드Id가 매번 바뀌고 캐시가 깨진다." 였다.

문제 결론

7월 1일 배포 -> 고정 빌드Id = hi  
유저 기기: "빌드Id가 hi이면 캐시된 소스를 사용하자."

7월 3일 배포 -> 고정 빌드Id = hi  
유저 기기: "7월 1일 배포내용을 사용 하자" -> 7월 3일 배포 내용이 캐시된 유저에겐 반영되지 않음.

해결 방안

빌드Id를 매 배포마다 다른 값이 들어가도록 하자

해결 과정

  1. AWS Code Pipeline로 CI/CD가 구성되어 있는데, 매 푸쉬마다 최종적인 커밋을 바탕으로 소스가 구성된다. 그러니 git의 HEAD commit해시를 빌드Id로 사용되게 해준다.
const nextBuildId = require('next-build-id')
//next-build-id 모듈을 사용해서 쉽게 commit 해쉬값을 불러오자.
module.exports = () => {
  return {
    generateBuildId: () => nextBuildId({ dir: __dirname }),
  ...
  }
}
  1. 하지만 pipeline build테스트 과정 도중
    .git파일을 찾지 못한다며 위의 빌드Id에 넣을 commit해시값을 넣는 부분이 실패를 한 것 같았다.


위 커맨드가 실패했고 그 이유는 .git이 없기 때문이다.

이를 해결 하기위해,
소스의 출력 아티팩트 형식을 CodePipeline기본값에서 전체 복제로 변경 시켜 주었다.

형식 설명에 나와있듯,
기본값은 github에서 Download ZIP하는것과 흡사.
전체 복제는 git clone과 흡사하다.

그렇기 때문에 전체 복제시 .git파일을 갖게되고 깃 관련 메타데이터들을 가진다.

주의!
전체 복제를 하려면,

  • Build Artifact를 사용해야한다.
  • CodeBuild에 서비스 역할 정책에 권한 설정을 해줘야 한다.
    -> UseConnection권한이 action필드에 들어가야 한다.


(i) 버튼을 누르면

ConnectionArn이 나온다 이부분을 복사해서

Pipeline에 구성된 CodeBuild에서 빌드 세부 정보에 들어가서
서비스 역할 링크로 들어가면 정책 연결을 할 수 있는데 여기서 정책을 추가하고

// aws docs sample 
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "codestar-connections:UseConnection",
            "Resource": "이곳에 복사한 arn을 넣어줍니다"
        }
    ]
}

이를 정책에 부여 해줍니다.

혹시라도 arn을 잘못적거나 이 문제가 아닐수도 있으니
처음엔 Resource에 "*"를 넣어 에러를 최대한 건너뛰어서 테스트 해보셔도 좋습니다.

여기까지 완료하면, 빌드테스트까지 문제가 없었습니다.

하지만 배포 인스턴스에서 빌드시 에러가 나서 (에러 내용 까먹음..)

buildspec.yml파일 까지 수정을 해주고 해결 되었습니다.

version: 0.2

phases:
  pre_build:
    commands:
      - something
  build:
    commands:
      - echo Build start
      - echo Building the Docker image...
  post_build:
    commands:
      - echo Build completed
artifacts:     
  files:
    - '**/*'

artifacts 이하 내용을 추가해줬습니다.

성공적으로 배포되었습니다!

사이트의 빌드Id또한 commit해시값으로 적용이 된 것을 확인할 수 있습니다.

결론

배포시 캐시 버스팅이 잘 일어나도록 해주고 잘 체크하고, 오토스케일링에 따라 늘거나 줄어드는 서버에 대해 대응이 되도록 고려해서 만들어야겠다.

삽질끝.

3개의 댓글

comment-user-thumbnail
2022년 3월 4일

저도 비슷한 문제때문에 고생했는데, 정말 감사합니다. 덕분에 살았습니다.

1개의 답글
comment-user-thumbnail
2022년 9월 30일

감사합니다. 덕분에 문제 빠르게 해결할 수 있었습니다!
저희 프로젝트는 아직 젠킨스나 깃허브 액션을 연동하지 못한 바람에 next-build-id라는 패키지 사용했네요.

답글 달기