인프라

하나의 도메인으로 Framer 와 Blog 연결 시키는 방법 2 (with Cloudfront)

consolelog 2025. 7. 3. 07:28

이전에 Cloudflare 를 이용해서 Framer 와 Inblog 를 연결하는 글을 적었습니다.

 

하지만, Cloudflare 로 두 서비스를 연결하기 위해서는 네임서버를 Cloudflare 로 이전해야하는 문제가 있었습니다.

 

이 문제는 이미 서비스가 활성화된 곳에서는 쉽게 결정하기 어려운 문제입니다.

 

그래서 이번엔 AWS 의 Cloudfront 를 이용해서 두 서비스를 연결하는 방법을 가져왔습니다.

 

AWS Cloudfront 가 뭐에요?

 

AWS Cloudfront 는 Amazon Web Services 에서 제공하는 콘텐츠 전송 네트워크(CDN) 서비스입니다.

 

이 서비스가 해줄 수 있는 일에 대해서 간략히 나열해보면 다음과 같습니다.

 

  • CDN 서비스: 사용자와 가까운 엣지 서버에서 콘텐츠를 전달하여 지연(latency)을 최소화합니다.
  • 정적 콘텐츠 전송: HTML, CSS, JavaScript, 이미지, 동영상 등 다양한 정적 파일을 빠르게 제공합니다.
  • 캐싱 기능: 자주 요청되는 정적 콘텐츠뿐만 아니라, 조건에 따라 API 응답도 캐싱하여 성능을 최적화합니다.
  • HTTPS 지원: AWS Certificate Manager(ACM)를 통해 SSL 인증서를 적용하여 안전한 HTTPS 연결을 제공합니다.
  • Lambda@Edge: 요청 및 응답을 엣지 서버에서 실시간으로 조작할 수 있는 서버리스 기능을 제공합니다.
  • DDoS 보호: AWS Shield와 통합되어 기본적인 DDoS 공격으로부터 서비스를 보호합니다.
  • URL 패턴 기반 오리진 라우팅: 요청 경로(/, /blog, /api 등)에 따라 각기 다른 오리진으로 트래픽을 분기시킬 수 있어, 다양한 서비스와 유연하게 연동할 수 있습니다.

CloudFront는 다양한 기능을 제공하지만, 이 중에서도 URL 패턴 기반 오리진 라우팅Lambda@Edge를 활용하여 Framer와 Inblog를 연동하는 구성을 직접 설정해보겠습니다.

 

Cloudfront  연동 구조 및 준비 단계

사전준비

 

이번 실습을 따라하기 위해서 준비할 것들은 다음과 같습니다.

  • 도메인
  • AWS 계정
  • Framer - mini 이상의 결제 계정
  • Inblog - 프리기간 or 유료 결제 계정

Cloudfront 연동 미리보기

 

1. Inblog 설정

2. Framer 설정

3. AWS 에서 인증서 발급

4. Cloudfront 설정

5. Lambda@Edge 설정

6. Route53 설정

7. Cloudfront 블로그 설정

8. Lambda@Edge 블로그 설정

 

Framer 에 관련된 이슈

 

실제 설정에 들어가기에 앞서, Framer 의 정책에 관련된 이슈를 공유하고, 이에 대해 고민한 부분을 공유하려고 합니다.

 

.framer 가 붙은 url

 

Framer 에서 .framer 가 붙어있는 도메인을 사용하는경우, 해당 도메인에 접근시 canonical tag 를 붙여서 서빙합니다.

 

canonical tag

 

Canonical 이란 "검색 엔진에게 이 페이지의 원본(대표 주소)는 이것입니다" 라고 알려주는 html 태그입니다. 예를들어, www.example.com  인 사이트가 있다고 했을때, canonical 태그의 href 가 www.playground.com  으로 적혀있다면, 검색엔진이 해당 사이트의 원 주소를 www.example.com  이 아닌 www.playground.com  란 사이트로 인식할 가능성이 있다는 말입니다.

 

그럼 우리가 하려는 작업 (Framer 와 Inblog 를 하나의 도메인으로 서비스하기) 과 무슨 상관이 있길래 듣기에도 생소한 canonical 태그에 대해서 설명냐하면, Framer 에서 우리가 하려는 작업에 대한 가이드 방식에 (Framer 공식 문서) 해당 태그를 때기 위해서는 scale plan 이상을 사용해야 된다고 설명하기 때문입니다.

 

Canonical 태그를 처리하지 않고, Cloudfront 를 이용하여 도메인을 연결하게되면, 구글 검색엔진에서 원하지 않는 도메인의 노출 또는 SEO (검색 엔진 최적화) 에 불리한 점수를 받을 가능성이 생기게 됩니다.

 

Framer 는 scale plan (월 약 25만원) 이상을 사용해야만 Canonical URL 을 수정 가능하게 바꿔주는데, Framer 를 이용하는 대부분의 고객들에게는 이는 상당한 부담으로 다가옵니다.

 

그렇다면 방법이 없을까?

 

조금 수고스러워지긴 하지만, 방법은 있습니다.

 

각 페이지 <head> 태그에 canonical 태그를 직접 적어준다.

 

방법인 즉, 각 페이지 <head> 태그의 마지막에 사용자정의의 canonical 태그를 직접 적어주는 방법입니다.

 

이렇게 하면, Framer 에서도 기존의 canonical 태그 대신 우리가 작성한 태그를 붙여서 서비스합니다.

 

단, 효과적인 SEO (검색 엔진 최적화) 를 위해선 main 페이지에는 www.example.com/main  까지 적은 href 를 적도록 합니다.

 

Cloudfront 를 이용한 도메인 연결

Inblog 설정방법

 

Inblog 설정방법은 중복이므로, 이전화 의 내용으로 대체하도록 하겠습니다.

 

Framer 설정방법

 

domain settings 페이지의 base domain

 

 

Framer 의 설정방법은 더욱 간단해졌습니다. 아무런 설정없이 domain settings 페이지에서 base domain 만 기억해줍니다.

 

AWS 에서 인증서 발급

 

AWS 의 Certificate Manager 서비스로 들어갑니다. 그리고 Cloudfront 에 붙이는 인증서는 미국 버지니아 북부(us-east-1) 에서 발급받아야되니 잊지말고 우측상단을 눌러 지역도 옮겨줍니다.

 

인증서 요청 -> 퍼블릭 인증서 요청을 차례로 누르며 넘어가 줍니다.

 

인증받을 도메인을 넣어준다

 

인증받기를 원하는 도메인을 넣어줍니다.

 

*.example.com 은 와일드카드 인증서로 xxx.example.com 에 해당하는 모든 주소를 인증받는다는 뜻 입니다.

 

인증서 발급 대기 화면

 

여기서 route53 으로 도메인 관리를 하시는 분은 Route53 에서 레코드 생성을 누르면 자동으로 인증받기 위한 값들이 생성되고, 다른 곳에서 도메인을 관리하시는 분은 CNAME 유형으로 레코드 이름, 값을 복사하셔서 생성하시면 됩니다.

 

잠시후, 상태가 발급됨으로 변한다면, 정상적으로 인증서 생성이 완료된 것입니다. (인증서 발급은 즉시 되는 작업이 아니니, 제대로 수행하셨다면, 5분 ~ 10분정도까지는 기다려보는것이 좋습니다)

 

Cloudfront 설정

 

다음은 클라우드 프론트에 대한 설정입니다.

 

클라우드 프론트 배포 탭의 배포 생성을 눌러주세요.

 

cloud front 배포 생성 탭

 

Domain 에는 우리가 사용하고 싶은 도메인을 적어줍니다. (예를들면, www.example.net)

 

cloudfront origin 설정화면

 

Origin 에는 Framer 의 base domain 을 적어줍니다.

 

보안설정은 적용하면 추가 요금이 발생하니, 이점 참고하여 활성화를 결정하고 넘어가줍니다.

 

Cloudfront 인증서 설정화면

 

좀전 단계에서 인증서가 잘 생성되었다면, 여기에서 확인 가능 할 것입니다. 혹시 인증서가 잘 생성되었는데 이곳에 뜨지 않는다면, 인증서를 us-east-1 에 생성한것이 맞는지, 인증서와는 엉뚱한 Domain 을 설정한것이 아닌지를 확인해줍니다.

 

이후 생성을 누르면 배포 생성이 완료되었을 것입니다.

 

cloudfront 배포가 생성되었다

 

하지만, 배포 도메인 이름으로 접속해보면, 내가 설정한 Framer 사이트가 뜨지는 않을것입니다. 그 이유는 Cloudfront 가 Framer 사이트에 주소를 요청할때의 host 정보가 배포 도메인 이름으로 되어있어서 그런건데, 이번엔 그 부분을 설정해 보도록 하겠습니다.

 

Lambda@Edge 설정

 

우리는 Cloudfront 가 Framer 로 보내는 요청을 중간에 가로채서 host 정보를 바꿔줄 예정입니다. 이때 사용하는게 Lambda@Edge 입니다.

 

우선 AWS Lambda 서비스로 들어갑니다. 지역은 마찬가지로 버지니아 북부(us-east-1) 입니다.

 

함수 생성을 눌러줍니다.

 

람다 함수 생성

 

함수이름만 만들어 생성을 눌러줍니다.

 

host 를 변경해주는 lambda 코드

 

export const handler = async (event) => {
  const request = event.Records[0].cf.request;

  request.headers['host'] = [
    { key: 'host', value: 'independent-xxxxxx.framer.app' }
  ];

  return request;
};

 

함수를 위와 같이 잘 작성했다면, Deploy 를 눌러줍니다. 안에 들어가는 value 는 Framer 의 base domain 값을 넣습니다.

 

25.8.19 업데이트
example.com/sitemap.xml 에 원하는 도메인을 설정하려는 경우 lambda 코드는 다음과 같습니다. 위의 설정이 포함된 내역이니 아래의 코드로 넣어주시면 됩니다.

 

import https from 'https';

function fetchSitemap(host) {
    return new Promise((resolve, reject) => {
        const options = {
            hostname: host,
            path: '/sitemap.xml',
            method: 'GET',
            timeout: 5000,
            headers: {
                'User-Agent': 'Mozilla/5.0 (compatible; CloudFront/1.0)'
            }
        };
        
        const req = https.request(options, (res) => {
            let data = '';
            
            res.on('data', (chunk) => {
                data += chunk;
            });
            
            res.on('end', () => {
                console.log('Fetched sitemap, length:', data.length);
                resolve(data);
            });
        });
        
        req.on('error', (error) => {
            console.error('Request error:', error);
            reject(error);
        });
        
        req.on('timeout', () => {
            console.error('Request timeout');
            req.destroy();
            reject(new Error('Request timeout'));
        });
        
        req.end();
    });
}

export const handler = async (event) => {
  const HOST = 'independent-words-xxxx.framer.app';
  const HOST_REGEX = /https:\/\/independent-words-xxxxx\.framer\.app/g;
  const DOMAIN = 'https://www.example.com';

  const request = event.Records[0].cf.request;

  request.headers['host'] = [
    { key: 'host', value: HOST }
  ];

  // sitemap
  if (request.uri === '/sitemap.xml') {
      try {
          const originalSitemap = await fetchSitemap(HOST);
          const updatedSitemap = originalSitemap.replace(
              HOST_REGEX,
              DOMAIN
          );
          return {
              status: '200',
              statusDescription: 'OK',
              headers: {
                  'content-type': [{
                      key: 'Content-Type',
                      value: 'application/xml; charset=utf-8'
                  }],
                  'cache-control': [{
                      key: 'Cache-Control',
                      value: 'max-age=3600'
                  }]
              },
              body: updatedSitemap
          };
      } catch (error) {
          console.error('Error:', error);
          return request;
      }
  }
  return request;
};

 

위의 코드에서 HOST, HOST_REGEX, DOMAIN 부분만을 수정해서 사용하면 됩니다.

 

Lambda@Edge 로 배포를 선택해줍니다

 

lambda@edge 배포시 에러가 난다

 

트리거 추가를 눌러 Cloudfront 를 선택, 배포를 선택해 배포를 눌러보면 위와 같은 에러가 뜹니다. 이는 우리가 만든 함수가 Lambda@Edge 에 배포할 권한이 없어서 생기는 문제인데, 이를 해결하기 위해선 AWS 의 Iam 서비스로 이동합니다.

 

우리가 만든 람다에 역할이 존재한다

 

해당 역할을 열어봅니다.

 

신뢰관계에 edgelamda 를 추가해준다

 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "edgelambda.amazonaws.com",
                    "lambda.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

 

이제 lambda 로 다시 돌아와 Cloudfront 트리거를 추가해줍니다.

 

정상적으로 트리거가 추가되었다

 

이번엔 정상적으로 트리거가 추가된것을 확인할 수 있습니다.

 

람다엣지가 Cloudfront 에 적용되기를 5분정도 기다린후, 다시 Cloudfront 의 배포 도메인 이름(예, https://xxxx.cloudfront.net) 으로 접속해보면 Framer 에서 배포한 사이트가 잘 뜨는것을 확인할 수 있습니다.

 

Route53 설정 또는 다른 호스트 설정 사이트

 

다음으로는 우리가 설정한 도메인 이름으로 Framer 사이트가 접속되도록 설정해보겠습니다.

 

route53 으로 이동해서 레코드 생성을 눌러줍니다. (route53 에선 A 레코드 별칭 연결도 가능합니다)

 

Cloudfront 배포 도메인 이름과 cname 으로 연결해준다

 

레코드 이름, 유형, 값을 잘 설정후 레코드 생성을 누르면, 우리가 설정한 주소로 Framer 에서 배포한 사이트가 잘 뜨는것을 확인할 수 있습니다.

 

Cloudfront 블로그 설정

 

이제 블로그를 연결해줄 차례입니다.

 

Cloudfront 에서 만들어 놓은 배포의 원본 탭에서 원본 생성을 눌러줍니다.

blog 연결을 위한 origin 생성

 

Origin domain 에 들어갈 값은 inblog 에서 생성한 기본 주소를 선택해줍니다. 그리고 원본 생성을 해줍니다.

 

다음은 /blog* 로 들어오는 요청에 대한 처리를 설정하기 위해 동작탭에서 동작생성을 눌러줍니다.

 

블로그 동작 설정

 

위 그림대로 경로 패턴과 원본만 설정해준 후 생성을 눌러줍니다.

 

Lambda@Edge 블로그 설정

마지막으로, 전과 같이 host 값에 대한 변환과 추가로 경로를 수정해 주는 작업을 할 것입니다.

 

lambda 의 함수로 돌아와 코드 업데이트를 눌러줍니다.

 

export const handler = async (event) => {
  const request = event.Records[0].cf.request;

  const uri = request.uri || "/";

  if (uri.startsWith("/blog")) {
    request.uri = uri.replace(/^\/blog/, "") || "/";
    
    request.headers['host'] = [
      { key: 'host', value: 'xxxxx.inblog.io' }
    ];
  } else {
    request.headers['host'] = [
      { key: 'host', value: 'independent-words-xxxxxx.framer.app' }
    ];
  }

  return request;
};

 

코드를 다음과 같이 수정해 줍니다. blog 로의 요청시에는 blog 에 해당하는 host 정보를 들고가고, inblog 내부에선 서브 디렉토리 구조 없이 서비스함으로 서브 디렉토리를 삭제한 path 로 요청을 보내게 합니다.

 

각각의 value 에는 자신에게 맞는 값으로 넣어줍니다.

 

deploy 를 눌러 업데이트가 끝났다면, 트리거 추가를 눌러서 배포를 생성해줍니다.

 

이번엔 /blog* 동작에 배포한다

 

이번엔 좀전에 만들었던 /blog* 동작에 배포해줍니다.

 

약 5분정도 배포가 되기를 기다린후, 설정해놓은 도메인의 /blog 로 접속해보면 잘 접속되는것을 확인 할 수 있습니다.

 

마치며

Cloudflare에 이어 CloudFront를 활용해 Framer와 블로그를 연결하는 방법을 정리해보았습니다.

 

앞으로도 도움이 될 만한 유익한 내용을 계속해서 공유드리겠습니다.


읽어주셔서 감사합니다!

 


 

추가 팁

 

Lambda@Edge 함수에서 로그를 찍었는데 Cloudwatch 를 통해 확인이 안되시는 분들을 위한 추가 팁

 

Lambda@Edge 는 실제로 돌아가는 위치가 우리가 생성한 버지니아 북부가 아닌 현재 유저의 위치에서 제일 가까운 서버입니다. 때문에 해당 리전의 위치의 로그를 확인해야 합니다.

 

하지만 그 이외에도, 우리가 만든 역할에는 해당 리전에 로그를 생성할 권한이 주어지지 않았습니다.

 

그러므로, 아래와 같이 해당 권한을 주도록 합니다.

 

Iam 에서 우리가 생성한 lambda@edge function 의 역할탭

 

다시 Iam 에서 우리가 생성한 Lambda@Edge 의 역할탭으로 넘어가줍니다.

 

권한의 해당 항목을 다음과 같이 편집합니다.

 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

 

이제 다시 Cloudfront 를 통해 해당 사이트에 접속하여, 로그가 찍히도록 유도해봅니다. 

 

서울 리전의 CloudWatch 에서 해당 로그그룹, 로그가 정상적으로 생성되었는지 확인해봅니다. (로그는 CloudWatch 에 1분정도 느리게 찍힐 수 있습니다)