[SEO실강] 2강

youngseo·2022년 7월 12일
0

FireBase

목록 보기
7/9
post-thumbnail

학습목표

  • netlify와 vercel을 이용한 배포

1. 이슈

  const id = event.path.split('/').filter(p => p).reverse()[0]

이슈 해결 1

  • 지난 코드의 경우 render.ts라는 파일에서 workspace.ts로 내부 요청을 보내고 있었습니다. 넷니파이 function에서 다른 function을 호출이 안된다고 합니다.
  • 이슈를 해결하기 위해 굳이 내부함수로 요청하지 않고 axios호출되는 부분을
  • workspace.ts에 만들어진 axios부분을 복사해 붙이도록 하겠습니다.
    • method는 GET으로 바꾸어주고 data는 지워줍니다.
  • 환경변수를 가져옵니다.

변경 전 render.ts

  const { data } = await axios({  //2.
    url: '/.netlify/functions/workspace',
    method: 'POST',
    data: {
      id,
      method: 'GET'
    }
  })
    const { title, content, poster } = data //3.
}

변경 후 render.ts

const { APIKEY, USERNAME } = process.env

const handler: Handler = async event => {
  const id = event.path.split('/').filter(p => p).reverse()[0]

  const { data } = await axios({
    url: `https://asia-northeast3-heropy-api.cloudfunctions.net/api/notion/workspaces/${id}`,
    method: 'GET',
    headers: {
      'content-type': 'application/json',
      'apikey': APIKEY as string,
      'username': USERNAME as string
    }
  })
  const { title, content, poster } = data

이슈2

  • npm run build를 통해서 dist폴더에 만들어진 코드를 옮겨왔었습니다. 빌드를 할 때 보면 해시가 붙어있는 것을 확인할 수 있습니다.
  • 해시의 경우 읽을 떄는 고유하게 읽을 수 있지만 계속해서 변하는 값입니다. 그렇기에 서버에 올려서 사용할 때는 문제가 발생하는 것입니다.
  • 따라서 해당하는 부분이 빌드 시 붙지 않도록 작업할 것입니다.

**vite.config.ts

  server: {
    port: 2999
  },
  build: { ✅
    rollupOptions: {
      output: {
        entryFileNames: 'assets/[name].js',
        chunkFileNames: 'assets/[name].js',
        assetFileNames: 'assets/[name].[ext]' //정적파일
      }
    }
  }
})
  • vite.config.ts의 내부적으로 웹팩대신 롤업을 사용하게 됩니다.
  • 이번에 설정한 부분은 롤업에서 해쉬태그가 붙지 않도록 하는 옵션입니다. 빌드를 해보면 index.html에서 해시가 사라진 것을 확인할 수 있습니다.

이슈3

  • 루트 경로의 index.html의 <script defer type="module" src="/src/main.ts"></script> 부분도 가져가야합니다.
  • 그런데 <script defer type="module" src="/src/main.ts"></script> 부분은 <div id="app"></div>다음에 작성이 되어 있습니다.
  • 이를 렌더로 옮기되, defer을 붙여 head부분에 작성을 해줍니다.
<script type="module" src="/src/main.ts"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script defer type="module" src="/src/main.ts"></script>
  • 그런데 이부분은 개발할 때만 필요한 코드입니다. 따라서 분기 처리를 해주도록 하겠습니다.

2. 분기처리

env

APIKEY=FckdtJs202204
USERNAME=ParkYoungWoong
MODE=development ✅
# PUBLIC_URL=https://enchanting-bublanina-4dd5c6.netlify.app
PUBLIC_URL=http://localhost:3000
  • .env파일에 MODE를 추가해줍니다.
  • .env파일에 MODE가 있는 경우 <script defer type="module" src="/src/main.ts"></script>가 돌아갈 수 있도록 만들 것입니다.

➕PUBLIC_URL

  • 기본 도메인 주소의 경우에도 바뀔 수가 있기 때문에 환경변수로 설정해도 좋습니다.)
  • 이렇게 추가한 PUBLIC_URL을 netlify의 환경변수에도 추가를 해줍니다.

render.ts

const { APIKEY , USERNAME, MODE} = process.env //✅

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
${
MODE === 'development' 
? ' <script defer type="module" src="/src/main.ts"></script>' //개발모드
: ' <script type="module" crossorigin src="/assets/index.js"></script><link rel="stylesheet" href="/assets/index.css">'
}//배포모드, 한줄이 아니면 에러가 나니 한줄로 작성해줍니다.

netlify.toml

배포를 할 때 id가 제대로 들어가지 않는 경우 닫히게 됩니다. 실제로 render함수의 경우 넷니파이에서는 그렇게 필요하지 않습니다.
따라서 workspaces안의 주소부분에서만 렌더함수를 사용할 수 있도록 redirects를 하나 더 만들어 분기시켜줍니다.

  • '/workspaces/*'의 *이 아이디값이기 때문에 충분히 아이디를 추출할 수 있습니다.
//netlify.toml
[dev]
  targetPort = 2999 # 연결할 프로젝트 개발 서버의 포트를 지정합니다.
  port = 3000 # 출력할 Netlify 서버의 포트를 지정합니다.

[[redirects]]
  from = '/workspaces/*'
  to = '/.netlify/functions/render'
  status = 200
  
[[redirects]]
  from = '/*'
  to = '/index.html'
  status = 200
$ npm run netlify

$ git push origin main

배포를 해보면 정상적으로 잘 배포가 되는 것을 확인할 수 있습니다.

3. 트위터카드

카카오톡은 트위터카드에 조금 더 최적화되어 있습니다. 트위터카드도 설정해보도록 하겠습니다.

<meta property="twitter:card" content="summary" />
<meta property="twitter:site" content="Notion Clone!" />
<meta property="twitter:title" content="${title}" />
<meta property="twitter:description" content="${content}" />
<meta property="twitter:image" content="${poster}" />
<meta property="twitter:url" content="https://enchanting-bublanina-4dd5c6.netlify.app/workspaces/${id}" />

4. 엣지함수 도입

  • functions 내부에 edge-functions라는 폴더를 하나 만들고 내부에 workspaces.ts를 만들어줍니다.
  • edge-functions의 경우 node.js가 아닌 디노에서 돌아가게 됩니다.
  • 엣지 함수가 도입되면 더이상 render.ts는 필요하지 않습니다.
  • 서버함수가 실행 약 30초정도 걸린다면 엣지함수는 약 1~2초로 매우 빠릅니다.
    • 엣지함수는 사용자에 맞게 리전을 선택합니다
    • 처리를 해야하는 무거운 함수는 엣지함수에 넣을 수가 없습니다. 따라서 분기처리를 해줘야합니다.(봇인지 사용자인지 구분)

4-1 디노

디노란?

디노 자바스크립트와 타입스크립트로 동작하는 노드제이에서 환경중 하나입니다. node.js를 만든 사람이 노드제이에스의 문제점을 보완해 만든 것이 바로 디노입니다.
(환경 자체가 노드제이에스가 아닙니다)

노드제이에스의 문제점은?

  • node-modules
    • 실제로 프로젝트를 구성해서 작업을 하다보면 npm i로 패키지를 설치하게 됩니다. 또한 그 패키지가 동작하기 위해 내부적으로 패키지를 설치하게 됩니다. 이패키지를 작성한 코드에 맞게 빌드해서 그 빌드된 결과를 가지고 브라우저에 출력을 하게 됩니다.
    • 그런데 코드 한글자만 바꿨음에도 그바뀐 글자를 위해 또 다시 빌드를 해야하는 것입니다.
    • 이경우 프로젝트 규모가 크면 클수록 한글자 바꾸고 화면에서 확인하는데 1분~4분~10분까지도 걸리게 됩니다.
    • 그래서 나온게 스노우팩, 더확정된 것이 vite.js입니다.(빌드과정을 최소화)
    • 빌드과정은 완벽히 똑같지만 개발할 때는 다 모듈화시켜서 직접 브라우저에서 돌립니다.
    • 결국 디노는 노드모듈수가 너무 무거워서 나오게 된것입니다.
    • 디노는 모두 CDN방식을 사용합니다.
    • 즉, 디노는 인터넷이 안되면 개발할 수 없습니다.

4-2 workspaces.ts

import { Context } from 'netlify:edge' //2


export default async (request: Request, context: Context) => {//1
  console.log('user-agent ::', request.headers.get('user-agent') //3
  console.log('request.url', request.url)           
              
  return await context.next()    //4. 다음으로 넘겨줍니다.        
} 
  1. 넷니파이에서 제공하는 엣지함수의 경우 매개변수로 request와 context가 필요합니다.
  2. request와 context의 타입을 지정해줘야하는데는 context의 타입은 import { Context } from 'netlify:edge'해와야합니다.(request는 전역으로 가지고 있는 Request를 사용하면 됩니다.)
  • 디노는 설치해서 쓰는 개념이 아니기에 노드모듈스에서 가져올 수가 없습니다.
  • 넷니파이 엣지의 경우 실제 서버상에서 선언이 된 것입니다
  • 이 문제는 디노라는 확장프로그램을 설치하면 인식을 해서 해결이 됩니다.
  • 여전히 에러가 나게 되는데 이부분은 npm run netlify해서 돌리면 해결이 됩니다.
  1. 어떻게 생겼는지 보기 위해 request.headers에 있는 user-agent를 가져와서 콘솔에 찍어봅니다.
  2. npm run netlify를 실행해봅니다.

4-3 netlify.toml

  • 엣지함수가 도입되면 더이상 functions부분의 render함수는 필요가 없습니다. 주석처리를 해줍니다.
[dev]
  targetPort = 2999 # 연결할 프로젝트 개발 서버의 포트를 지정합니다.
  port = 3000 # 출력할 Netlify 서버의 포트를 지정합니다.

# [[redirects]]  //✅
#   from = '/workspaces/*'
#   to = '/.netlify/functions/render'
#   status = 200

[[edge_functions]]  //✅
  path = "/workspaces/*"
  function = "workspaces"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200  
  • http://localhost:3000/.netlify/edge/sorkspaces로 접근 해 특정 페이지에 접근 한 후 새로고침을 한 후 콘솔을 확인해봅니다.

  • user-agent의 경우 맥과 윈도우가 다르게 출력될 것입니다.

    • user-agent를 통해 일반사용자인지 봇인지를 1차적으로 구분합니다.
  • reqeust_url은 사용자가 접속한 그 해당하는 주소입니다.

    • 봇인 경우 접속한 페이지에 맞게 나온 아이디에 맞게 해당하는 메타정보를 제공해줍니다.

    4-4 ISbot

    접근하는 유저 에이전트가 봇인지, 일반사용자인지 구분을 하는 용도로 사용되는 패키지 입니다.
    skypack으로 접근을 해 코드를 가져옵니다.

import isbot from 'https://cdn.skypack.dev/isbot'//1

const APIKEY = Deno.env.get('APIKEY') as string
const USERNAME = Deno.env.get('USERNAME') as string

export default async (request: Request, context: Context) => {
  const userAgent = request.headers.get('user-agent')//1
  const id = request.url.split('/').filter(p => p).reverse()[0]
  
    if (isbot(userAgent) && id) {//1
    const res = await fetch(`https://asia-northeast3-heropy-api.cloudfunctions.net/api/notion/workspaces/${id}`, {
      method: 'GET',
      headers: {
        'content-type': 'application/json',
        'apikey': APIKEY,
        'username': USERNAME
      }
    })
    const { title, content, poster } = await res.json()
    
   return new Response(
      /* html */ `
        <!DOCTYPE html>
        <html lang="ko">
        <head>
          <meta charset="UTF-8">
          <title>Heropy's Notion</title>
          
          <meta property="og:type" content="website" />
          <meta property="og:site_name" content="Notion Clone!" />
          <meta property="og:title" content="${title}" />
          <meta property="og:description" content="${content}" />
          <meta property="og:image" content="${poster}" />
          <meta property="og:url" content="https://charming-moonbeam-67283c.netlify.app/workspaces/${id}" />
          
          <meta property="twitter:card" content="summary" />
          <meta property="twitter:site" content="Notion Clone!" />
          <meta property="twitter:title" content="${title}" />
          <meta property="twitter:description" content="${content}" />
          <meta property="twitter:image" content="${poster}" />
          <meta property="twitter:url" content="https://charming-moonbeam-67283c.netlify.app/workspaces/${id}" />
        </head>
        <body></body>
        </html>
      `, 
      {
        headers: { 'content-type': 'text/html' }
      }
    )
  }   
  return await context.next()  
} 
  • 봇인 경우 메타정보를 주어야합니다. 그런데 그 메타정보는 백엔드 서버 주소에 위치하고 있습니다.https://asia-northeast3-heropy-api.cloudfunctions.net/api/notion/workspaces/${id}
  • 디노에서는 fetch함수가 내장되어 있습니다.
    • 최대한 빠르게 돌려야함으로 굳이 axios를 가져와서 사용하기 보다 기본 내장된 fetch함수를 사용합니다.
  • ⭐node.js의 경우 환경변수를 가져올 떄 process.env를 쓰지만 디노는 다릅니다.
    -Deno.env.get('APIKEY')
  • new Response의 첫번째 인자로 render.ts에 사용했던 html을 붙여넣어줍니다.
    • 엣지함수의 경우 return await context.next()를 통해 일반사용자인경우 그냥 지나가게 합니다.
    • 따라서 html부분은 오직 봇에게만 제공이 되기에 meta정보를 제외한 link는 모두 삭제해도 괜찮습니다.
  • id의 경우 주소에서 추출해옵니다. const id = request.url.split('/').filter(p => p).reverse()[0]
  • new Response의 두번째 인자를 추가해줄 수 있습니다.
    • {headers: { 'content-type': 'text/html' }}

4-5 확인

$ npm run netlify

4-6 배포

  • 깃허브에 올리고 나면 배포가 됩니다.
  • 넷니파이의 edge-functions를 확인해보면 아래와 같이 뜨는 것을 확인할 수 있습니다.

0개의 댓글