React 앱의 링크를 바삭 바삭한 Twitter로 매료

트위터에 매료 란 무엇입니까?




이런 녀석! 이름은 Twitter Card.
링크가 Twitter에서 어떻게 표시되는지는 Card Validator | Twitter Developers 에서 확인할 수 있다.

이것을 React 앱 링크로 표시하고 싶습니다! 라는 이야기.
최근 처음 React를 만져 막상 Twitter Card 대응하려고 조사했을 때, SPA에 있어서의 OGP 대응의 고민을 알았습니다.

대응 방법은 여러 가지 하지만 이번에는 필자가 실제로 시도한 두 가지 방법을 간단히 쓰고 싶습니다.
실제로 채택한 "Static Site Generator 방법"과 "Cloud Functions에서 meta 태그를 바꾸는 방법"의 두 가지입니다.

방법 1: SSG



Server Side Rendering (SSR)과 비교하여 Static Site Generator (SSG)라고합니다.
사전에 JS 를 실행해 각 페이지의 HTML 를 생성해 그것을 전달하는 기법.

저자는 소규모 개인 앱에 대해이 방법으로 OGP를 지원했습니다.
구체적으로는 react-snap + react-helmet로 HTML을 생성하고 Firebase Hosting에서 전달합니다. 1

react-snap



로컬에서 크롤링하여 HTML을 생성하는 라이브러리.
(이름에 react라고 들어 있지만, 별도로 react 전용 라이브러리가 아니라 Vue에서도 사용할 수 있습니다!)


package.json 의 scripts 에 "postbuild": "react-snap" 를 더하는 것만으로 잘 해 준다!

package.json
"scripts": {
  "start": "react-scripts-ts start",
  "build": "react-scripts-ts build",
  "postbuild": "react-snap",
  "test": "react-scripts-ts test --env=jsdom",
  "eject": "react-scripts-ts eject"
}

이것을 사용해 수중에서 사전에 렌더링한 결과를 HTML 로서 가질 수가 있습니다.
다만, 이것만으로는 어느 페이지도 동일한 meta 태그 (루트의 index.html에 쓴 것)가 되어 버립니다.
따라서 react-helmet를 도입하여 페이지별로 meta 태그를 설정합니다.

react-helmet



hello.tsx
import { RouteComponentProps } from 'react-router-dom'
import { Helmet } from "react-helmet";

export const Hello: React.SFC<RouteComponentProps<{}>> = props => (
  <div> 
    <Helmet
      title={'Hello World'}
      meta={[
        { name: 'twitter:card', content: 'summary' },
        { property: 'og:image', content: 'path/to/og_image' },
        { property: 'og:title', content: 'Helloページ' },
        { property: 'og:description', 'サンプルページです' },
        { property: 'og:url', content: `hoge_domain${props.path}` }
      ]}
    />
    <div> Hello! </div>
  </div>
)

이런 식으로 Helmet 컴포넌트를 꽂아 meta 정보를 지정할 수 있다.

이 helemt 를 도입한 다음에 yarn build 그러면, 생성되는 HTML 의 meta 태그는 확실히 동적으로 생성할 수 있다.
build/컴포넌트명/index.html 를 보면 이런 느낌이 되어 기쁘다!

build/Hello/index.html
<meta content="Helloページ" data-react-helmet="true" property="og:title">

Firebase에 배포



매우 정상적인 설정으로 배포하기만 하면 됩니다.

firebase.json
{
  "hosting": {
    "public": "build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}

이런 느낌으로 바삭하게 버렸습니다!

방법 2: Cloud Functions로 meta 태그 교체



이쪽에 대해서는 많은 기사가 있으므로 간단하게.
처음에는 이 방법으로 만들었습니다만, 소규모의 개인 개발이고 심플한 일 밖에 없는 앱이었기 때문에, 방법 1이 더 적합하다고 생각 전환했습니다.

Cloud Functions를 통한 동적 콘텐츠 배포 에도 써있듯이, meta 태그를 바꿔 OGP 대응할 수 있습니다.

SEO를 향상시키는 단일 페이지 앱을 미리 렌더링합니다. 이렇게 하면 서로 다른 소셜 네트워크 간에 공유할 수 있는 동적 meta 태그를 만들 수 있습니다.

Cloud Functions에 TypeScript 사용 을 참고로 설정하고 이런 식으로 meta 태그를 바꿉니다.

functions/index.tsx
import * as functions from 'firebase-functions'
import * as fs from 'fs'

export const PreRender = functions.region('asia-northeast1').https.onRequest((req, res) => {
  res.set('Cache-Control', 'public, max-age=300, s-maxage=600')
  fs.readFile('./index.html', 'utf8', (e, html) => {
    const responseHtml = html.replace('トップページ', 'Helloページ') // meta タグの中身を置換する処理を書く
    res.status(200).send(responseHtml)
  })
})

그런 다음 Firebase Hosting 리라이트을 사용하여 요청을 Cloud Functions에 전달합니다.

firebase.json
{
  "hosting": {
    "rewrites": [
      {
        "source": "hello/*",
        "function": "PreRender"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

결론



상당히 썼습니다만, 참고가 되면 다행입니다.

참고



공식 문서


  • Optimize Tweets with Cards
  • Cloud Functions를 통한 동적 콘텐츠 배포
  • Cloud Functions 위치
  • Cloud Functions에 TypeScript 사용

  • 블로그 등



  • 【기사판】State of SEO for SPA 2018 : 매우 알기 쉬운 정리 기사! 조사하면서 다양한 고전하기 전에이 기사를 만나고 싶었습니다 ...
  • JAMstack은 자지 않는다.

  • SSR없이 React 앱을 OGP에 대응 (자력 prerendering 편) : 본 기사와 같이 로컬로 Pre Rendering 하는 기법.
  • Firebase에서 SPA 할 때 SEO / OGP 대응 이제 이것으로 좋지 않습니까? 2018 잠정 버전
  • Firebase에서 서버리스 SPA 앱을 만든 이야기②



  • React + TypeScript + Firebase Hosting 구성

    좋은 웹페이지 즐겨찾기