요즘 SEO 에 관해 신경을 안쓸래야 안쓸수가 없다.
SEO 란 Search Engine Optiomization 의 약자로 검색엔진 최적화란 뜻이다.
즉 SEO 가 잘 처리되어야 작성한 내글이 구글, 네이버 등의 검색엔진 플렛폼에 노출이 된다는 거다.
하지만 이런 SEO 에 잘 맞지 않는게 있으니 그건 바로 요즘 프론트로 많이 사용되는 SPA 로 만들어진 웹사이트 들이다.
SPA 는 Single Page Application 의 약자로 하나의 메인페이지에서 동적으로 데이터를 불러오는 웹사이트를 말한다.
위에서 말한것처럼 하나의 페이지에서 데이터를 동적으로 불러오기 떄문에 검색봇이 해당 사이트를 조회했을때 필요한 정보가 로딩되어있지않아 검색엔진에 정보가 노출되지않게 된다. 봇이 해당 페이지를 조회할때는 우리가 보여주고 싶어하는, 스크립트에 의해 호출된 데이터가 조회되는게 아니라 초기 메인 페이지의 빈 껍데기 HTML 만 조회가 되기 떄문이다.
( php 같은 MPA 사이트는 페이지 로딩시 이미 데이터를 가지고 있기 때문에 문제가 없지만 angular, react 등의 spa 사이트들이 문제가 된다. )
이 부분을 해결하기 위해 Next.js 등의 프레임워크가 나왔지만, 이미 angular 이나 react 로 된 웹사이트를 Next.js 로 변경하는것도 상당한 공수가 들기 때문에 문제가 된다.
이에 프론트엔트 코드를 수정하지 않고 AWS 의 리소스를 통해 해당 문제를 해결할수 있는 방법을 기술한다.
선제 조건은 해당 웹사이트가 CloudFront 를 통해 배포되어 운영되고 있어야 한다는 점이다.
사용되는 AWS 리소스는 다음과 같다.
CloudFront
, Lambda@Edge
이미 셋팅된 CloudFront 웹사이트가 있다는 가정하에 진행한다.
셋팅된 서버가 없다면 아래의 페이지 참고..
S3 + Cloudfront 를 사용한 프론트 서버 띄우기
Lambda@Edge 는 CloudFront 를 통해 전달되는 콘텐츠를 사용자 지정 함수를 실행할 수 있게 해주는 AWS Lambda 가 확장된 컴퓨팅 서비스다.
즉 Lambda@Edge 를 CloudFront 에 연결하게 되면 사용자의 요청이 발생되었을때 사용자와 CloudFront, CloudFront 서버 사이에서 데이터를 가로채어 정보를 가공할수 있다.
여기서 하려는 것은 바로 해당 기능을 통해 봇을 통한 요청이 왔을때 원하는 OG TAG 응답을 주는것이라 할수 있겠다.
( OG TAG 란 URL 만 입력했는데 해당 사이트의 내용 및 이미지가 메신져에 노출되기 위한 기능이다. )
일반적인 람다함수는 리전별로 설정이 가능하나 Lambda@Edge 함수는 버지니아 리전에서만 설정이 가능하니 참고하길 바란다.
AWS 문서를 보면 Lambda@Edge 와 CloudFront 의 연결에 대해 다음과 같이 설명하고 있으니 참고하면 될것같다.
https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/lambda-edge.html
우리가 설정할 내용은 아래와 같다.
해당 작업은 위의 이미지의 Viewer Request 와 Origin Response 에 해당된다.
두개의 작업을 해야 하므로 2개의 Lambda@Edge 함수를 생성해줘야 한다.
먼저 Viewer Request 함수를 생성하도록 한다.
버지니아 북부(us-east-1) 리전에서 람다 함수를 생성
함수명은 임의로 지어도 상관없으나 여기서는 직관적으로 viewer-request-func 로 셋팅했다.
중요한것은 해당 함수는 Lambda@Edge 역할을 가지고 있어야 하기 때문에
권한 > 기본 실행 역할 변경 > AWS 정책 템플릿에서 새 역할 생성
선택 후 정책 템플릿에서 기본 Lambda@Edge 권한 (cloudFront 트리거 용)
을 선택해서 생성해주어야 한다.
(이미 해당 권한을 가진 역할이 있다면 기존 역할을 선택해서 사용하면 된다 )
우측 하단의 함수 생성
버튼을 클릭하여 람다 함수 생성.
생성된 함수의 내용에는 봇으로 접근하는경우 header 에 is-bot
라는 값을 넣어주는 코드를 입력해주면 된다.
const bot = /googlebot|bingbot|yandex|baiduspider|twitterbot|facebookexternalhit|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator|kakaotalk-scrap|yeti|naverbot|kakaostory-og-reader|daum|postman/g;
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const user_agent = request.headers['user-agent'][0]['value'].toLowerCase();
if (user_agent) {
const found = user_agent.match(bot);
request.headers['is-bot'] = [
{
key: 'is-bot',
value: `${!!found}`,
},
];
}
callback(null, request);
};
위의 코드 입력 + 저장 + Deploy 후 트리거 추가를 통해 Lambda@Edge 설정을 해준다.
( 위의 설정을 하기전까지는 그냥 람다 함수 일 뿐... )
트리거 추가 버튼 클릭.
트리거구성에서 CloudFront 를 선택 후 Lambda@Edge로 배포
버튼을 클릭해 준다.
열리는 화면에서 다음을 선택 후 우측 하단의 배포
버튼 클릭
새로운 cloudFront 트리거 구성
cloudFront이벤트 > 뷰어 요청
Lambda@Edge로 배포확인 체크박스 체크
배포버튼을 누르면 버전이 설정되고 배포가 진행된다.
람다엣지는 특정 리전의 서비스가 아닌 글로벌 서비스이기 때문에 배포에 몇분의 소요기간이 걸리는 점을 기억하도록 하자.
( cloudFront 의 마지막수정 부분이 "배포" 로 되어있으면 배포가 아직 진행중인 상태이고 배포가 완료되면 완료된 날짜가 출력된다 )
배포가 완료되고 대상 cloudFront 에서 확인을 해보면 뷰어요청에 방금연결한 람다엣지 함수가 셋팅되어있는것을 확인할 수 있다.
cloudFront > 동작 탭 선택 > 편집 > 하단의 함수연결
호출 설정을 해주었으니 이번에는 응답 설정을 해준다.
viewer request 를 생성해 줄때와 똑같이 버지니아 리전에서 람다 함수를 생성해준다.
차이점이라면 viewer 함수 성생서 람다엣지 권한을 가진 역할을 만들었기 때문에 역할 설정에서 기존역할을 선택하고 앞서 생성했던 역할을 선택해 주면 된다.
함수를 생성했다면 아래의 코드를 참고하여 코드를 입력한다.
'use strict';
exports.handler = async (event, context, callback) => {
const { request, response } = event.Records[0].cf;
const { headers, uri } = request || {};
let is_bot = undefined;
if ('is-bot' in headers) {
is_bot = headers['is-bot'][0].value.toLowerCase();
}
if (is_bot === 'true') {
const title = '테스트 입니다.';
const intro = '두두둥....';
const thumb_nail_url = '';
response.status = 200;
response.headers['content-type'] = [
{
key: 'Content-Type',
value: 'text/html',
},
];
// 꼭 캐시를 사용하지 않도록 설정해야 합니다.
response.headers['cache-control'] = [
{
key: 'cache-control',
value: 'no-cache, no-store, must-revalidate',
},
];
response.body = `<html><head>
<meta property="og:url" content="http:test.io" />
<meta property="og:type" content="website" />
<meta property="og:locale" content="en_US" />
<meta property="og:title" content="${title}" />
<meta property="og:description" content="${intro}"/>
<meta property="og:image" content="${thumb_nail_url}" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="600" />
<meta property="og:image:height" content="600" />
<meta name="twitter:card" content="summary">
<title>${title}</title>
</head></html>`;
}
callback(null, response);
};
위의 코드는 테스트용으로 간단하게 OG Tag 를 리턴하고 있지만 주소별로 다른 값을 리턴애햐 하는경우 axios 모듈을 추가해서 호출주소에 따라 백엔드에서 OG Tag 를 조회해서 셋팅하도록 처리하면 된다.
이전과 동일하게 트리거 추가
> 트리거 구성에서 CloudFront
선택 > Lamgda@Edge로 배포 버튼
클릭 > 다음의 정보 설정
Lambda@Edge로 배포확인
체크배포
버튼 클릭cloudFront
> 동작
에서 함수연결
부분을 확인하면 원본응답 부분까지 연결이 되어있는것이 확인된다. 함수의 수정이 있는경우 수정 저장 > 배포 후 똑같이 트리거 추가 버튼을 클릭 > cloudFront 선택 > Lamgda@Edge로 배포
버튼을 클릭해 준다.
그 후 열리는 모달에서 새로운 cloudFront 트리거 구성
이 아닌 이 함수에 기존 cloudFront 트리거 사용
을 선택 후 값을 선택 해주면 버전이 하나 올라가면서 람다엣지함수가 배포가 진행되니 참고.
이전 배포 내역을 확인하려면 람다 함수의 버전탭을 선택하면 지금까지 배포된 버전들이 나온다.
각 버전 클릭시 해당 버전의 코드를 확인할 수 있고 특정 버전으로 돌리고 싶다면 해당 버전 클릭 후 트리거 추가 버튼을 통해 해당 버전의 코드를 배포 할 수 있다.
테스트는 postman 을 통하여 cloudFront 웹서버를 호출했을때 origin response 에 입력했던 OG Tag 가 출력되면 된다.
그런데 아마 테스트시에도 정상적으로 OG Tag 가 출력되지 않을것이다.
그 이유는 viewer 에서 넘긴 is-bot 헤더값이 origin 에 출력되지않아 bot 인식이 되지않기 때문이다.
이를 해결하기 위해서는 is-bot 이라는 헤더값을 cloudFront 가 origin reponse 에 넘겨줄 수 있도록 사용자 정의 헤더 설정을 해주어야 한다.
cloudFront
동작
탭 선택 > 편집
> 캐시 키 및 원본 요청
> Lagacy cache settings
선택 > 헤더추가
영역에서 사용자 정의 추가
버튼 클릭 > 열리는 모달에서 is-bot
입력 후 추가버튼 클릭 > 우측 하단의 변경 사항 저장
버튼 클릭
이제 postman 을 통한 호출시 다음과 같이 OG Tag 가 출력되면 정상적으로 모든게 적용된 것이다.
적용된 내용의 디버깅을 하려면 cloudWatch 에서 람다에서 출력한 값을 확인해 봐야 한다.
람다는 버지니아 리전에 설정되었으나 람다엣지는 호출 사용자의 가장 가까운 리전에서 호출되기 때문에 cloudWatch 의 로그 확인시 호출한 지역의 가장 가까운 리전을 확인해보면 된다.
아래의 이미지처럼 해당 리전의 로그그룹을 확인하여 호출함수의 내역을 체크해보면 된다.
설정한대로 잘 되지 않는경우 람다엣지 함수에서 console.log 를 찍어보고 배포한 후 호출한 다음 cloudFront 로그를 확인해보면 되겠다.
끗..