- [AWS] Nextjs cloudfront S3 배포 시 html 확장자 제거 (feat. lambda 함수 이용) 목차
이번 글은
Nextjs로 AWS에 베포를 진행하는 과정 중 발생하였다.
처음에는 모든 페이지들이 정상적으로 작동하는 듯 싶었으나, 역시 그럴일이 없었다.
Nextjs는 동적 라우팅 즉, Dynamic Loute를 지원한다.
위의 링크처럼 동적 라우팅이 있는 폴더 구조가 있다고 생각해보자.
결국 folder struct는 products 폴더 안에 [id].tsx와 같은 파일이 존재할것이다.
여기까지는 아주 좋다.
이제 문제는 지금부터다.
AWS의 CloudFront를 이용하여 배포를 진행하였을 때 해당 동적 페이지로 들어가면 아무 페이지가 뜨지 않는다.
해결 방법은 .html 확장자를 명시적으로 넣어주는것이다.
하지만 매 페이지 요청 시
위와 같이 매번 .html 확장자를 적어줘야한다.
아니면 없는 페이지라고 접속을 못 한다.
이유는 왜 그럴까?
React와 같은 프레임워크는 SPA이다. 즉 single page application이다.
react가 build가 되면 html 파일을 만든다.
next js 같은 경우 동적 페이지가 존재하면 해당 폴더들을 html 파일로 변환해준다.
따라서 빌드 된 파일들을 S3에 업로드 하게 되면, html 확장자를 가진 파일들이 업로드가 된다.
이렇게 되면 cloudfront url로 해당 파일의 경로를 접속할 때
ex) www.~~~.com /selfconsultation.html
위와 같은 .html 확장자를 넣은 주소로 들어가야지 정상적인 페이지가 로드된다.
그렇다면, .html을 제거하고 접속할 수 없을까?
열심히 구글링을 해봤을 때
방법은 몇 개 있었는데 그 중 제일 효과적인 방법을 찾았다
내가 적용한 건 두 가지었는데
1. 첫 번째 방법
파일명을 직접 변경한다.
S3 bucket에 들어있는 file name의 .html 확장자를 제거한다.
하지만?
이렇게 직접 바꿨을 때, 파일들이 절대 변하지 않다는 전제하에 바꿔야한다.
무슨소리냐?
다시 한 번 생각해보면 build할 때 .html 파일들을 만들어준다고 했습니다.
그렇다면 직접 파일명을 바꿔주면 덮어씌워지기는 커녕
selfconsultation.html 파일 1개와
selfconsultation 파일 1개
총 2개의 파일이 생성된다.
그렇다면, build할 때 마다 .html파일이 생성되고 기존에 직접 바꿨던 파일은 그냥 가만히 구 버전으로 남겨지게 된다.
(상당히 번거롭겠죠?)
2. 두 번째 방법
두 번째 방법은 AWS lambda function을 활용하는것이다.
이 기능을 설정할 때 상당히 삽질을 많이 했다.
AWS lambda function
- 인터셉터 같은 역할을 해주어 suffix에 나오는 .html 확장자를 자동으로 제거해주어 사이트를 안내해주는 기능을 추가해준다.
단순하게 AWS에서 제공해주는 함수라고 생각하자.
1.함수를 하나 생성하여 한다
(람다펑션 설정 시 AWS Lambda - 함수 - 구성 - 실행 역할 여기를 IAM에서 커스텀으로 만들어준 역할로 바꿔야한다.)
2.함수 코드 작성해주자 (html 확장자 제거해주는 코드)
const config = {
suffix: '.html',
appendToDirs: 'index.html',
removeTrailingSlash: false,
};
const regexSuffixless = /\/[^/.]+$/; // e.g. "/some/page" but not "/", "/some/" or "/some.jpg"
const regexTrailingSlash = /.+\/$/; // e.g. "/some/" or "/some/page/" but not root "/"
// const dynamicRouteRegex = /\/subpath\/\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/; // e.g /urs/some-uuid; // e.g. '/subpath/uuid'
const dynamicRouteRegex = /^(.+)\/\d+\/?$/; // e.g. /post/123
exports.handler = function handler(event, context, callback) {
const { request } = event.Records[0].cf;
const { uri } = request;
const { suffix, appendToDirs, removeTrailingSlash } = config;
if(uri.match(dynamicRouteRegex)) {
request.uri = uri.replace(dynamicRouteRegex, '$1/[id].html')
callback(null, request);
return;
}
// Append ".html" to origin request
if (suffix && uri.match(regexSuffixless)) {
request.uri = uri + suffix;
callback(null, request);
return;
}
// Append "index.html" to origin request
if (appendToDirs && uri.match(regexTrailingSlash)) {
request.uri = uri + appendToDirs;
callback(null, request);
return;
}
// Redirect (301) non-root requests ending in "/" to URI without trailing slash
if (removeTrailingSlash && uri.match(/.+\/$/)) {
const response = {
// body: '',
// bodyEncoding: 'text',
headers: {
'location': [{
key: 'Location',
value: uri.slice(0, -1)
}]
},
status: '301',
statusDescription: 'Moved Permanently'
};
callback(null, response);
return;
}
// If nothing matches, return request unchanged
callback(null, request);
};
3. 트리거를 추가로 만들어준다
현재 저는 로드 발란서와 routre53 통하여 domain을 연결해준다.
따라서 로드 발란서에세 트리거를 부착하여 도메인에 접속 전 html을 먼저 제거 후 안내해 주도록 하였다.
먼저, 트리거와 cloudfront(저는 로드밸런서이므로)를 연결하여 준다.
트리거 구성에서 cloudfront(저는 로드밸런서)를 추가시켜준 후
완성 예시
'AWS' 카테고리의 다른 글
[Terraform]테라폼 모듈(module) 작성 방법 (0) | 2022.03.03 |
---|---|
[Terraform]테라폼(terraform)state 관리(local,terraform cloud,s3) (0) | 2022.03.02 |
[AWS]로드밸런서(load balancer)란? (0) | 2022.03.02 |
[AWS]VPC Endpoint란?(feat.bastion(바스티온) 접속) (0) | 2022.02.28 |
[AWS]AWS 네트워크 구조(feat.VPC,Subnet,NACL,Security Group,Router) (2) | 2022.02.25 |