노션에서 작성한 모든 페이지는 공개 링크를 만들어 다른 사람에게 공유하거나 검색 엔진에 노출시킬 수 있다. 노션에 가입하지 않아도 누구나 방문할 수 있다는 말이다. 언제가 될진 모르겠지만 노션도 '커스텀 도메인' 기능을 지원할 예정이라고 했었다. 그럼 자신이 보유한 도메인을 노션 공개 페이지에 연동할 수 있기 때문에 콘텐츠 배포 공간으로 활용하기 더 좋아진다. 아쉽게도 도메인 연동 기능을 언제 지원할지는 아직 미지수다. 현재로선 모바일 앱 사용성 개선이 가장 시급해 보인다.

 

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

 

 

1. 노션 페이지 공개 설정


➊ 먼저 외부에 공유하고 싶은 노션 페이지를 공개 접근(Public Access) 상태로 만들어야 한다. 노션 페이지 우측 상단 Share 버튼 클릭 → Public Access 옵션에 체크하자. 검색엔진에도 노출하고 싶다면 Allow Search Engines에 체크하면 된다. 완료했다면 바로 아래 있는 Copy Page Link를 클릭한다.

 

➋ 그럼 아래 같은 공개 링크가 생성된다. 복사해두자.

https://www.notion.so/romantech/e6badd990a774dabac02284d704529c0

 

 

2. Cloudflare에 도메인 추가, DNS records 입력


 

 

Cloudflare 대시보드 접속 → +Add a Site 버튼을 클릭하여 자신의 도메인을 추가한다. (domain.com 형식)

 

➋ 요금제는 Free Plan을 선택한다. 

 

➌ DNS records 입력 창에선 아래처럼 추가해준다. Name은 도메인 앞에 들어가는 별칭으로 원하는 단어를 입력하면 된다. 만약 domain.com 이 본인 소유의 도메인이고, nt.domain.com 서브 도메인을 노션 공개 페이지로 연결하고 싶다면 nt 별칭을 추가하면 된다. 

  • Type : CNAME
  • Name : nt (도메인 별칭 임의 입력)
  • Target : notion.so

 

 

 

3. 네임서버 변경


➊ Cloudflare는 도메인마다 부여한 네임서버가 모두 다르므로 자신의 네임서버부터 확인해야 한다. Cloudflare 대시보드 → 등록한 도메인 클릭 → 상단 파란색 메뉴 중 DNS 클릭 → DNS 관리자 페이지 하단을 보면 Cloudflare nameservers에 나와있다. 

 

➋ 이제 자신이 구입한 도메인(호스팅) 사이트로 가서 네임서버를 변경해주자. Hosting.kr 기준 [도메인] → [정보 변경] → [네임서버 주소 변경]에서 설정할 수 있다. 

 

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

 

 

4. Web Workers 세팅


➊ 이제 CloudFlare의 Web Workers를 이용해 앞서 추가한 서브도메인의 HTTP 요청을 자동 변환하는 세팅을 해야 한다. 대시보드 상단에 있는 Worker 아이콘을 클릭한 뒤 하단 Manage Workers 버튼을 클릭한다.

 

➋ Workers 관리 사이트에서 Create a Worker 버튼을 눌러 새로운 Workers를 생성한다. 

 

➌ 좌측 상단을 클릭한 후 Workers 이름을 notion-worker로 변경한다.

 

➍  스크립트 편집창에 아래 코드를 복사하여 붙여 넣는다.

const MY_DOMAIN = "서브 도메인 주소"
const START_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
}

 

➎  붙여 넣은 스크립트의 1행, 2행을 아래처럼 수정한다. (중요!)

  • 1행(첫 번째 줄) : const MY_DOMAIN = "서브 도메인 주소" (노션 페이지로 연결시킬 별칭 도메인)
예시) const MY_DOMAIN = "nt.colorfilter.me"
  • 2행(두 번째 줄) : const START_PAGE = "노션 공개 페이지 주소" (1번에서 복사했던 노션 Public 페이지 링크)
예시) const START_PAGE = "https://www.notion.so/romantech/e6badd990a774dabac02284d704529c0"

 

➏ 스크립트 코드 추가/수정을 완료됐다면 Save and Deploy 버튼을 눌러 저장한 후 다시 Workers 페이지로 돌아간다.

 

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

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

 

 

마치며


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

 

위 : 노션 기본 링크 아래 : 도메인 주소


포스팅 업데이트 : 2020.05.14

참고 글