[Project] 포트폴리오 제작기 -5. Next.js SSG + AWS S3 403 Fallback Error

young_pallete·2022년 12월 7일
5
post-thumbnail

🚀 시작하며

슬슬 다른 프로젝트 시작하면서, 여유 시간이 나면 제작기를 조금씩 연재해보려 해요.
사실 이 문제를 해결한 건 꽤 됐는데, 거의 반나절이 걸려 해결했던 에러였어요.

이 글을 보는 분들께서 아래와 같은 상황을 겪고 계시다면, 이 글이 도움이 되었으면 좋겠어요 :)

  1. 각 페이지를 인덱스로부터 들어가는 건 정상으로 나옵니다.
  2. 인덱스에서 새로고침하는 것 역시 정상입니다.
    3. 그런데... 다른 페이지에서 새로고침을 하면 403 에러가 발생합니다. (혹은 index.html에서의 레이아웃이 렌더링됩니다.)
  3. AWS CloudFront에서 리다이렉트를 설정하지 않았는지를 살펴보았지만 이미 잘 설정했습니다. (먼저 CloudFront에서의 리다이렉트를 확인해주세요.)
  4. 404가 나와야할 곳에서는 403에러가 발생합니다.

아니! 당시에 저는 왜 계속해서 403에러가 발생하는지를 도저히 모르겠더라구요. 😖
그리고 자정부터 새벽 6시까지 밤샘 삽질 끝에 얻은 결론은, 제가 정적 배포 후 렌더링되는 원리를 제대로 이해하지 못해서 발생한 문제였어요.

이에 대해서 삽질하고 있을 누군가에게 6시간을 덜어드리기 위해, 글을 작성하게 됐어요.
그럼, 그 이유를 탐색해볼까요?

🚦 본론

문제: index.html은 찾을 수 있는데, 다른 것은 찾을 수 없다?

이게 가장 이해가 되질 않았어요.
index.html이 되는데 왜 다른 [경로 이름].html은 되지 않을까요?

심지어 CSR이 되어 페이지 이동이 가능하고 있기 때문에 저는 몇 시간 동안 이 문제 정의를 다음과 같이 내렸어요.

🔴 가설1: 아, 생성된 html 파일이 없나보다!

삐빅! 틀렸습니다 😖

그도 그럴 것이, CSR로 다른 페이지 이동이 되는데 새로고침 시 폴백이 403에러가 난다는 것은, SSG로 정적인 제너레이션이 되지 않는다는 의미에요. .html
그래서 버킷을 살펴보니... 너무나 열받게도, 버킷의 루트에 잘 있었어요.

그렇게, 30분이면 해결될 것 같던 일은... 골치 아프게도 무한으로 이어졌습니다.

🟢 가설 2: 경로가 잘못 됐나?

삐빅! 정답입니다 🎉

실제로 [URL]/[라우트]에 대한 .html은 어디서 받아올 수 있을까요?
바로 [S3 Bucket]/[Route]에서 가져올 수 있습니다.

예컨대, URL/shop이라는 경로라면, S3 버킷에는 shop/index.html이라는 경로로 존재하면 됩니다. 따라서 수동으로 AWS S3에 옮긴 결과, 동작함을 확인했습니다! 🙆🏻

🤔 그런데 왜 403 에러가 발생하는 거죠?

이는 AWS S3 객체 요청에 따른 정상적인 응답입니다. S3에서 객체를 이용하여 정적인 사이트를 생성하게 된다면, 원래라면 나왔어야 할 404에러가, 원하는 객체를 찾지 못했기 때문에 액세스 거부 오류로 403 에러를 생성하는 거죠.

이에 관련해서는 Amazon S3의 403 액세스 거부 오류 문제를 해결하려면 어떻게 해야 합니까?을 참고해주세요!

문제는 알았다. 그런데 어떻게 자동화를...?

페이지가 작으니, 그냥 cpmv 명령어를 추가해서 일일이 입력해볼까란 생각도 들었어요.
그런데 문제가 있습니다.

만약 라우트 주소가 많아지거나, 나중에 변경할 일이 생긴다면?
퍼블릭에서 뱉은 디렉토리의 이름과 중복되어 꼬이는 문제가 발생한다면?

뭔가 나중에는 정말 오류를 못찾을 수도 있을만큼 배포과정에 있어서 복잡도가 올라간다는 느낌이 들었습니다.

지금은 주기적으로 포트폴리오 프로젝트를 업데이트하지만, 나중에 3년 후 갑자기 이걸 수정할 때에도 이 주의사항들을 염두해야 한다는 것이 스트레스를 받았어요.

즉, 자동화는 할 수 있지만 안정성에 대한 문제가 발생했습니다.
따라서 이미 답은 나왔지만 시원하지 않아서... 6시간을 삽질했습니다. 😭😭

해결방법: .html을 제거하자.

결국 특이한 방법으로 문제를 해결했는데요. 약 4시간의 삽질 시도와 서칭 끝에, 저와 비슷한 고민을 하던 분의 글의 도움을 받을 수 있었어요.

이 분의 해결 방법은, .html을 제거하는 방법이었어요.
오호! 뭔가 좀 이상한 느낌은 들었지만, 납득이 갔어요.

이 리소스에 대한 타입은 결국 Content-Type으로 알려주면 되는 거 아냐?

delete .html extension

따라서 이를 위해 다음과 같이 적용했습니다.

      - name: delete all files
        run: aws s3 rm ${{ secrets.AWS_S3_BUCKET_NAME }}/ --recursive
      - run: aws s3 sync ./out/_next/ ${{ secrets.AWS_S3_BUCKET_NAME }}/_next/
      - name: remove .html files extension
        run: for file in $(find ./out -name "*.html"); do mv "$file" "${file%%.html}"; done
        

Content-Type: text/html 부여

그 다음에, 확장자가 제거된 html 파일을 주면 되겠죠?
이후에는, 저는 이 파일들만 text/html이라는 힌트를 주며 동기화를 시켜줍니다.

      - name: Set HTML Content-Type and Copy them
        run: aws s3 sync ./out ${{ secrets.AWS_S3_BUCKET_NAME }} --exclude "*.*" --content-type "text/html" --acl public-read --delete 

이해 되셨나요?
즉, 만약 extension을 위한 점 표기가 없다면 이를 동기화하라는 의미입니다.

그렇다면, 이제 나머지 퍼블릭 디렉토리에 있는 asset들을 가져와야 합니다.
다음과 같이 말이죠!

public/* asset import

      - name: Copy Other Assets
        run: aws s3 sync ./out/ ${{ secrets.AWS_S3_BUCKET_NAME }} --exclude "*" --include "*.jpg" --include "*.png" --include "*.svg" --include "*.ico" --include "*.mp4" --include "*.webp" --metadata-directive REPLACE --cache-control max-age=600

주의! AWS S3 CLI not support regex

여기서도 또 삽질이 발생했는데, 저는 --include의 옵션을 regex expression으로 지정했었는데, 이는 잘못된 지정이더라구요!

AWS S3 CLI에서는 정규표현식을 지원하지 않고, 다음 패턴 심볼만 가능합니다.

*: Matches everything
?: Matches any single character
[sequence]: Matches any character in sequence
[!sequence]: Matches any character not in sequence

이후 저는 약 10분 동안은 캐시가 유효하도록 캐시 컨트롤을 지정해주었는데, 이는 원하는대로 수정하시면 됩니다.

안타깝게도 확장자가 많아지면 일일이 작성해야 한다 단점은 분명 존재하지만, 원인을 찾기에는 더 빠를 것 같다는 확신이 들었습니다. (내가 include를 하지 않았다는 오류에 대한 인지 사고가 어렵지는 않기 때문이죠!)

그렇게 문제는 해결됐어요!

🎉 마치며

사실 이 문제를 해결할 때, SSGAWS에 배포한 건 처음인지라 더 많은 삽질이 발생했어요.
그렇지만, 결국 이를 해결하는 과정 속에서 더 많은 것을 배웠습니다. (정말 많이요! 🙆🏻)

결국, 헤매는 만큼 자기 땅인 것 같아요. 😉
이 글이 AWS S3라는 바다에서 SSG로 인해 헤매는 누군가에겐 도움이 되길 바라며. 이상! 🌈

profile
People are scared of falling to the bottom but born from there. What they've lost is nth. 😉

0개의 댓글