헬롤' s Blog

nextjs 정적 배포 실패기

출발점

Nextjs 개발이 거의 다 마무리가 되가는 상황에 배포를 해보기로 했다.

s3와 cloudfront를 사용해서 배포를 해보기로 했다.

하지만 잘못된 결정이었다..

정적 웹 호스팅이란?

정적 웹 호스팅은 HTML, JavaScript, 이미지, 동영상 및 기타 파일을 저장된 그대로 웹 사이트 방문자에게 단순히 제공할 뿐이며 애플리케이션 코드를 포함하지 않는다.

정적 웹 호스팅은 ssr 처럼 전체 프로세스가 각 사용자 요청에 수행되는 것이 아닌 빌드 시간에 수행된다. 따라서 속도가 더 빠르다.

또한 seo 검색엔진 최적화에 이점이 있고 cdn에서 페이지를 수신하면 되기때문에 리소스와 비용을 절감할 수 있다.

따라서 마케팅 웹사이트 , 블로그 및 문서, 포트폴리오 웹사이트 등에서 많이 사용한다.

Nextjs의 정적 배포 설정

Nextjs에서 정적 배포를 하려면 한가지 설정이 필요하다.

const nextConfig = {
  output: 'export',
};

output: 'export'옵션을 주어서 정적 빌드를 할 수 있게 만들어야한다.

Nextjs에서 정적 배포시 사용할 수 없는 기능

정적 배포를 하게 된다면 프론트 서버가 없기 때문에 Nextjs에서 서버를 사용하는 기능은 사용할 수 없다.

대표적인 것이 Cookies, Rewrites, 동적 라우팅 등 많은 기능을 사용 할 수 없게 된다.

nextjs에서 항상 배포하기 전에 나에게 맞는 배포방식이 무엇인지 확인해야한다.

나의 경우에는 동적 라우팅과 rewirtes등 몇 가지 정적 배포 시 사용 할 수없는 기능들이 있었다.

따라서 동적라우팅은 query string 형식으로 바꾸었으며 nextjs에서 할 수 있는 cors 해결 방법인 nextjs rewrite 기능도 AWS gateway의 cors를 통해서 해결했다.

따라서 배포는 에러가 나지 않고 완료 했지만..

s3 와 cloudfront

s3에서 직접 정적 웹 호스팅을 하는 것보다 cloudfront를 통해서 s3에 접근하는게 비용 측면에서 더 아낄수 있다.

1. 데이터 전송 비용 절감

CloudFront는 전 세계에 분산된 엣지 로케이션을 통해 콘텐츠를 전달한다. 사용자가 s3버켓에 직접 접근하면 모든 요청이 s3가 위치한 리전으로 전송되어야 하므로 데이터 전송 비용이 발생한다. 하지만 cloudfront를 사용하면 엣지 로케이션에서 콘텐츠를 캐시하고 사용자에게 가장 가까운 엣지 로케이션에서 콘텐츠를 제공하여 데이터 전송 비용을 아낄 수 있다.

2. 캐싱을 통한 비용 절감

cloudfront는 자주 요청되는 콘텐츠를 엣지 로케이션에 캐시한다. s3에서 직접 가져오는 것보다 빠르게 제공되고 s3의 요청 비용을 절감할 수 있다.

일단 s3를 통한 접근을 막기 위해서 퍼블릭 접근은 우선 모두 막았다.

cloudfront origin에 들어가서 사용하고 싶은 s3 버킷을 선택한다.

이런 경고 창이 하나 뜨는데 클릭하면 origin domain이 website 엔드 포인트로 바뀐다.

하지만 이렇게 사용하면 원본 엑세스 설정을 하지 못한다. 즉 s3을 통한 접근이 가능하다는 이야기이다.

따라서 경고창은 그대로 넘어간다.

cloudfront를 통한 접근만을 허용하기 위해 원본 엑세스 제어 설정을 선택한다.

그리고 OAC(Origin access control)를 만든다.

s3 와 cloudfront를 통한 배포를 하려고 많은 블로그와 자료들을 찾아다녔는데 옛날 자료들을 보면 oai를 설정하라고 나와있다.

OAI(origin access identity)OAC(Origin access control) 의 옛날 버전으로 AWS Signature Version 4(SigV4), POST method를 사용하는 HTTP/HTTPS 요청, AWS KMS를 사용한 서버 측 암호화(SSE-KMS)를 적용을 지원하지 않는 등의 제한 사항이 있다.

OAI는 기존 aws 리전들과 2022년 12월 이전에 출시한 리전에서만 지원이 된다.

OAC(Origin access control)를 만들고 정책을 복사해서 s3에 cloudfront로만 접근이 가능하도록 권한을 설정하기 위해 버킷 권한에 붙여넣는다.

설정 후

한가지 주의할 것은 혹시나 s3내의 파일이나 설정을 바꾸었을때 cloudfront 캐시 무효화를 해야한다. 하지 않으면 시간이 지나면 언젠가는 변경이 되겠지만 캐시 무효화를 통해 빠르게 적용시키자

하지만.. 왜 redirect가 안되지?

한가지 문제가 있었다. 현재 카카오 Oauth 로그인을 적용중이다.

로그인 성공 시 인가코드를 카카오에서 부터 받아오는데 302 Redirect URI로 인가 코드를 전달한다. 하지만 이 302 Redirect 가 배포시 정상적으로 동작하지 않았다.

문제는 경로에 있다.

root 경로인 요청은 자동적으로 index.html을 붙여서 요청을 하지만 하위 디렉토리에 있는 요청은 자동적으로 index.html로 요청을 하지 않는다.

예를 들어서

'/' 요청은 초반 배포할때의 설정 때문에 '/index.html'로 자동으로 요청이 가는데 '/blog' 요청은 그대로 '/blog'요청으로 가기 때문에 원하는 페이지를 볼 수없다.

해결책 후보 : cloudfront functions 사용

cloudfront functions

clundfront fuctions는 자바스크립트 런타임을 통해서 요청이 엣지 로케이션에 도착할 때 요청에 접근 할수 있다.

요청이 캐시에 도착하기 전에 실행되고 각 요청에 호출됨에 따라서 지연시간을 최소한으로 가져가야 한다.

따라서 패키기 크기는 10KB로 제한 되며 비동기 패턴, 파일 시스템 접근 등이 불가능하다.

따라서 요청 본문을 읽을 필요가 없는 짧은 작업에 이상적이다.

사용 예시

  • 간단한 조건의 Redirect

예를 들어서

function handler(event) {
    var request = event.request;
    var supportedCountries = [‘de’, ‘it’, ‘fr’];
 
    if (request.uri.substr(3, 1) !=/’) {
        var headers = request.headers;
        var nextUri;
 
        if (headers[‘cloudfront-viewer-country’]) {
            var countryCode = headers[‘cloudfront-viewer-country’].value.toLowerCase();
            if (supportedCountries.includes(countryCode)) {
                nextUri =/+ countryCode + request.uri;
            }
        }
 
        if (!nextUri) {
            nextUri =/en’ + request.uri;
        }
 
        return {
            statusCode: 302,
            statusDescription: ‘Found’,
            headers: {
                location: { value: newUri }
            }
        }
    }
 
    return request;
}

위의 함수는 특정 국가의 트래픽을 Redirect하는 코드이다. 만약 supportedContries에 없는 국가 코드라면 코드의 기본값은 en이 되는 코드이다.

  • Cloudfront 캐시 키 수정

  • CORS,CSP, X-Frame-Options 및 기타 보안 HTTP 헤더 관리

나의 문제는 redirect니 조건을 붙여 redirect를 해주면 해결 가능하다. 또한 url에 직접 입력해서 요청했을때 지금 현재 403, 404 요청이 root의 index.html로 가게 만들어 무조건 home화면이 나온다. 이. 문제도 cloudfront Function을 사용하면 해결 가능하다.

또 다른 간단한 해결책

정말 간단한 해결책이지만 카카오 redirect uri에도 /index.html을 붙이자

하지만 뭔가 마음에 안들긴한다.

또또 다른 해결책(?)

nextjs를 배포할 때는 s3, cloudfront 정적 배포를 하는 것을 많이 고민해보자

배포 방식에 따라 장단점이 있는 것이지만 nextjs의 나름 중요한 몇가지 기능이 안된다는 것과 웹 어플리케이션 서버가 없다는 것은 장점을 뛰어 넘는 큰 단점인 것 같다.

이 배포를 진행하는데 정말 많은 시간이 걸렸다. (한.. 5일 정도... 온갖 블로그나 웹 사이트는 다 찾아본것 같다.)

이제 웬만한 배포는 걱정이 없어 졌다.!

참조 : https://trackit.io/cloudfront-functions-vs-lambdaedge