카테고리 없음

Firebase로 배포한 React 프로젝트에서 OG 정보 설정하기

티시즌 2024. 3. 13. 00:02

지난 글에서 DOM 조작으로 meta 태그를 변경하는 방식과 React helmet async 라이브러리를 사용하는 방법에 대해 시도해 보았다.

그러나 내 프로젝트는 Firebase로 배포한 것이기 때문에 해당 방법들은 적합하지 않았다.

문제 해결을 위해 Firebase Functions를 도입했다.

Functions는 무료 요금제인 Spark 요금제에서는 사용이 불가능하고, 
종량제 요금제인 Blaze 요금제로 변경해야 한다.

 

Firebase Functions를 이용한 create-react-app SPA 앱의 SEO 처리

create-react-app으로 만든 SPA 앱에 Firebase Hosting과 Functions를 이용하여 SEO 처리를 하는 법에 대해 설명한 글.

blog.roto.codes

 

How to Resolve Meta Data Issues in React SPA with Server-side Rendering & Firebase Cloud Functions?

Boost social network visibility & metadata accuracy in React single-page apps with server-side rendering & Firebase Cloud Functions.

ravikirandhulipala.medium.com

 

여기서 주의해야 할 점이 하나 있다.

Firebase Functions는 ECMAScript가 아니라 CommonJS를 사용한다.
그래서 import 대신에 require 문을 사용해야 한다.

블로그에서와는 달리 cp가 작동하지 않아서 직접 html을 복사해 넣어줬다.
이후 fs.readFile 메서드를 사용해 해당 html을 불러와서 교체해 주어야 하는데,
참고한 블로그에서와는 달리, &lt; 같은 이스케이프 표기법을 쓰면 안 되고 < 와 같이 직접 적어 주어야 했다.

const express = require('express');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const fs = require('fs');

admin.initializeApp();

const app = express();

app.get('/:alias', async (req, res) => {
  const { alias } = req.params;

  try {
    const snapshot = await admin.database().ref(`/pages/${alias}`).once('value');
    const dbData = snapshot.val();

    const title = dbData?.headerText;
    const description = dbData?.invitation?.join(' ');
    const imageUrl = `/images/gallery/${dbData.background}`;
    const url = `https://happy-ever-after.site/${alias}`;
    const themeColor = dbData?.color;

    fs.readFile('./index.html', 'utf8', (err, htmlString) => {
      if (err) {
        console.error('Error reading HTML file:', err);
        res.status(500).send('Internal Server Error');
        return;
      }

      const replacements = [
        { pattern: /<title>(.*?)<\/title>/, replacement: `<title>${title} | Happy Ever After</title>` },
        {
          pattern: /<meta name="description" content=['"](.*?)['"]\/>/,
          replacement: `<meta name="description" content="${description}"/>`,
        },
//중략
        {
          pattern: /<meta property="og:description" content=['"](.*?)['"]\/>/,
          replacement: `<meta property="og:description" content="${description}"/>`,
        },
      ];

      let replacedHTML = htmlString;
      replacements.forEach(({ pattern, replacement }) => {
        replacedHTML = replacedHTML.replace(pattern, replacement);
      });

      res.set('Content-Type', 'text/html');
      res.send(Buffer.from(replacedHTML));
    });
  } catch (e) {
    console.error(e);
    res.status(500).send('Internal Server Error');
  }
});

exports.dynamicMetatag = functions.https.onRequest(app);

 

그리고 build 후 index.html이 복사되었는지 꼭 확인해야 한다.
만약 복사되지 않았거나 기존 파일 상태라면 새로운 index.html을 집어넣어 주어야 한다.