로맨테크

노션에서 작성한 모든 페이지는 공개 링크를 만들어 다른 사람에게 공유할 수 있다. 링크를 공유받은 사용자는 노션에 가입하지 않아도 공개 설정한 페이지에 방문할 수 있다. 언제가 될진 모르겠지만 추후 "커스텀 도메인" 기능도 지원한다고 했다. 이 기능을 지원하면 자신이 보유한 도메인을 노션 공개 페이지에 연동하여 사용할 수 있다. 그럼 노션을 블로그 같은 콘텐츠 배포 공간으로 활용하기 더 좋아진다. 하지만 아쉽게도 도메인 연동 기능을 언제 지원할지는 아직 미지수다. 

 

그래서 오늘은 자신이 보유하고 있는 도메인을 노션 페이지에 연동하는 방법을 소개하고자 한다. Cloudflare의 Web Worker를 활용하여 HTTP 요청을 수동으로 변경해주면 자신의 도메인으로도 Notion의 공개 페이지에 접속할 수 있다. 복잡해 보이지만 차근차근 따라 해 보면 정말 간단하다. 

 

 

1. 노션 페이지 공개 설정


Notion 페이지 공개 설정 화면

▲ 먼저 공유하고 싶은 노션 페이지를 공개(Public) 상태로 만들어야 한다. 원하는 페이지에서 우측 상단 Share 버튼을 눌러 Public Access 옵션을 체크한다. 검색엔진에도 노출하고 싶다면 Allow Search Engines에 체크하면 된다. 공개 설정했다면 바로 아래에 있는 Copy Page Link를 클릭해 공개 페이지 링크를 복사해둔다. 그럼 아래 같은 공개 링크가 생성된다. 

https://www.notion.so/colorfilter/Romantech-for-Notion-e6badd990a774dabac02284d704529c0

 

 

2. 네임서버 변경


호스팅케이알 네임서버 변경 화면

▲ 본인이 사용하고 있는 도메인을 CloudFlare 네임서버로 변경해야 한다. Hosting.kr 기준 "도메인" ➝ "정보 변경" ➝ "네임서버 주소 변경"에서 변경할 수 있다. 완료했다면 다시 Cloudflare 사이트 로그인 후 "+ Add Site" 버튼을 눌러 자신의 도메인을 추가하자.

 

네임서버를 변경하면 기존 등록했던 네임서버 설정이 모두 지워지므로 Cloudflare에서 다시 등록해야 한다. 더 자세한 네임서버 변경 내용은 링크를 참조하면 된다. 

 

 

 

3. 도메인 별칭(CNAME) 설정


네임서버 변경을 완료했다면 CloudFlare 대시보드에서 도메인 CNAME을 설정해야 한다. 만약 DOMAIN.COM 이 본인 소유의 도메인 주소이고, NT.DOMAIN.COM 이런 식의 별칭 도메인을 Notion 공개 페이지로 연결하고 싶다면 NT 별칭 레코드를(CNAME) 추가하면 된다. 

 

CloudFlare의 CNAME 설정 화면

▲ CloudFlare 대시보드에서 "DNS" 아이콘을 눌러 DNS 관리 페이지로 들어간 뒤 위 이미지처럼 추가해준다. Name 부분은 도메인 앞에 들어가는 별칭으로(nt.domain.com) 원하는 단어를 입력해주면 된다. 

  • Type : CNAME 선택
  • Name : nt (임의 입력)
  • Target : notion.so

 

 

4. Web Workers 세팅


CloudFlare의 Workers 페이지 화면

▲ 다음 할 일은 CloudFlare의 Web Workers를 이용해 앞서 추가한 서브도메인에 대한 HTTP 요청을 자동으로 바꿔주는 세팅을 해야 한다. 대시보드 상단에 있는 "Worker" 아이콘을 클릭한 뒤 하단 "Launch Editor" 버튼을 클릭한다.

 

▲ Launch Editor 페이지 좌측에 있는 "Add Script" 버튼을 클릭하여 Script Name을 notion-worker 로 입력한다. 스크립트 편집창엔 아래 내용을 추가해준다.

 

const MY_DOMAIN = "example.com"
const START_PAGE = "https://www.notion.so/link/to/your/public/page"
​
addEventListener('fetch', event => {
  event.respondWith(fetchAndApply(event.request))
})
​
const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET, HEAD, POST,PUT, OPTIONS",
  "Access-Control-Allow-Headers": "Content-Type",
}
​
function handleOptions(request) {
  if (request.headers.get("Origin") !== null &&
    request.headers.get("Access-Control-Request-Method") !== null &&
    request.headers.get("Access-Control-Request-Headers") !== null) {
    // Handle CORS pre-flight request.
    return new Response(null, {
      headers: corsHeaders
    })
  } else {
    // Handle standard OPTIONS request.
    return new Response(null, {
      headers: {
        "Allow": "GET, HEAD, POST, PUT, OPTIONS",
      }
    })
  }
}
​
async function fetchAndApply(request) {
  if (request.method === "OPTIONS") {
    return handleOptions(request)
  }
  let url = new URL(request.url)
  let response
  if (url.pathname.startsWith("/app") && url.pathname.endsWith("js")) {
    response = await fetch(`https://www.notion.so${url.pathname}`)
    let body = await response.text()
    try {
      response = new Response(body.replace(/www.notion.so/g, MY_DOMAIN).replace(/notion.so/g, MY_DOMAIN), response)
      // response = new Response(response.body, response)
      response.headers.set('Content-Type', "application/x-javascript")
      console.log("get rewrite app.js")
    } catch (err) {
      console.log(err)
    }
​
  } else if ((url.pathname.startsWith("/api"))) {
    response = await fetch(`https://www.notion.so${url.pathname}`, {
      body: request.body, // must match 'Content-Type' header
      headers: {
        'content-type': 'application/json;charset=UTF-8',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'
      },
      method: "POST", // *GET, POST, PUT, DELETE, etc.
    })
    response = new Response(response.body, response)
    response.headers.set('Access-Control-Allow-Origin', "*")
  } else if (url.pathname === `/`) {
        let pageUrlList = START_PAGE.split("/")
    let redrictUrl = `https://${MY_DOMAIN}/${pageUrlList[pageUrlList.length-1]}`
    return Response.redirect(redrictUrl, 301)
  } else {
    response = await fetch(`https://www.notion.so${url.pathname}`, {
      body: request.body, // must match 'Content-Type' header
      headers: request.headers,
      method: request.method, // *GET, POST, PUT, DELETE, etc.
    })
  }
​
  return response
}
  • 위 스크립트 내용 중 모든 MY_DOMAIN 부분을 서브 도메인 주소로 대치해야 한다(노션 페이지로 연결시킬 별칭 도메인).
    예시) "NT.DOMAIN.COM"
  • START_PAGE 는 노션 공개 페이지의 URL로 대치해준다(1번에서 복사해뒀던 노션 Public Page Link).

 

▲ 스크립트 내용 추가/수정이 완료됐다면 Deploy(Save) 버튼을 눌러 저장한 후 다시 Worker 페이지로 돌아간다. 

 

Route 편집 화면

▲ Worker 페이지에서 Add Route 버튼을 눌러 Route와 Worker 항목을 각각 아래처럼 입력한다.

  • Route : 사용자 지정 도메인 뒤에 /* 와일드카드 문자 추가(해당 도메인의 모든 하위 콘텐츠는 Web Worker로 액세스).
    예시) NT.DOMAIN.COM/*
  • Worker : Launch Editor에서 세팅한 notion-worker 선택

 

위: 도메인 접속 주소, 아래: 노션 공개 링크 주소

여기까지 기본적인 세팅을 완료했다. 이제 앞서 설정한 서브도메인 주소를 입력하여(예를 들면 본인의 https://colorfilter.me) 외부에 공개한 Notion 페이지로 접속할 수 있다. 이 방식은 CloudFlare의 Edge Computing 기능을 활용, Notion 공개 페이지로 액세스 하는 HTTP 요청 패킷의 Host를 수정하는 방법으로 구현한 것이다. CloudFlare의 우수한 CDN 성능 때문인지 도메인으로 접속한 노션 페이지 속도가 더 빠른 느낌이다.

 

참고 글 : Use a custom domain for a public Notion page