이제 댓글을 달아봐야겠다. utterances 댓글로 설치할 것이다. utterances 플러그인을 선택한 이유는 '왜 gatsby로 블로그를 만드려는 거야?' 포스트에 서술해두었다.

utterances페이지에 자세하게 설명되어 있지만, 개인적으로 궁금했던 점을 기록하면서 설치하려 한다.

기본적인 설치 단계

  1. 공개된 리포지토리를 (새로)만들기

    • 이 블로그에선 컨텐츠를 비공개 리포지토리로 사용하고 있어서, utterances를 설치하기 힘들 수 있다고 생각했다. 하지만, 댓글 리포지토리를 따로만들면 된다.
    • 새로 만든 리포지토리는 댓글만 따로 이슈로 관리 하는 것이다.
  2. 페이지에 들어가 깃허브 앱 설치

    • 오른쪽 상단 녹색의 설치 버튼을 누르고, Only select repositories를 선택하여 위에서 생성한 리포지토리로 설정해준다.
  3. 설정 후 스크립트를 추가한다.

    • 설치가 되고 나면 utterances페이지로 리다이렉트 될 것이다.
    • configuration 항목별로 설정을 완료해준다.
    • 사실 모두 설정할 필요는 없지만, 스크립트를 자동으로 만들어주니 편리하다.
  4. 아래에 만들어진 Enable Utterances 스크립트를 원하는 위치에 붙여넣기

    • 리액트에선 inline으로 스크립트를 꽂을 수 없어서, 따로 코드를 작성해주어야한다.
    • 하지만 스크립트 형식은 참고해야하니 잘 봐두자.

리액트에 적용하기

댓글만 보여주는 컴포넌트를 작성하여, 포스트 템플릿에 삽입할 것이다.

  1. Comment.js 형식

    • createRef로 ref 어트리뷰트를 붙일 수 있는 Ref를 하나 만든다.
    • 컴포넌트에서 useEffect로 script 내용을 미리 적용할 것이다.
    • 그렇게 만든 ref를 만들어 리턴한다.
src/components/Comments.js
import React, { createRef, useEffect } from 'react';

const Comments = () => {
  const commentRef = createRef();

  useEffect(() => {

    // utterances script!

  }, []);

  return <div className="comments" ref={commentRef}></div>;
};

export default Comments;
  1. useEffect에 넣을 utterances script 작성하기

    • stackoverflow의 Adding script tag to React 참고하였다.
    • utterances 페이지에서 만든 스크립트 내용을 작성한다.
    • document.createElement로 스크립트 element를 만든다.
    • 스크립트 element에 어트리뷰트를 setAttribute로 작성해준다.
    • 하나 하나 붙여도 되고, script에 attribute를 객체로 만들어 그 전체를 entries로 한번에 붙일 수 있다.
    • 만들어둔 script element를 ref에 appendChild로 자식노드를 붙여준다.
src/components/Comments.js
...

  useEffect(() => {
    // script element 생성
    const utterances = document.createElement('script');

    // attribute를 전체를 객체로 만들기
    const utterancesConfig = {
      src: 'https://utteranc.es/client.js',
      repo: 'user/repo',
      theme: '선택한 테마',
      'issue-term': '포스트 페이지 매핑 방법',
      async: true,
      crossorigin: 'anonymous',
    };

    // 객체 전체를 setAttribute로 붙이기
    Object.entries(utterancesConfig).forEach(([key, value]) => {
      utterances.setAttribute(key, value);
    });

    // 만든 script를 ref 항목에  appendChild로 붙이기
    commentRef.current.appendChild(utterances);
  }, []);

  return <div className="comments" ref={commentRef}></div>;
};

export default Comments;

만약 블로그 테마별로 utterances테마도 바꾸고 싶다면

  1. 테마별로 utterances 테마를 결정한다.

    • useContext로 현재 블로그 테마 읽어들인다.
    • 현재 테마에 따라 변경할 테마 설정하고, script에 적용한다.
    • 현재 테마가 바뀔때마다 effect를 발생하도록, useEffect 두번째 인자로 전달한다.
src/components/Comments.js
import React, { createRef, useEffect, useContext } from 'react';
import ThemeContext from '../store/ThemeContext';

const Comments = () => {
  // useContext로 현재 블로그 테마 가져오기
  const { state } = useContext(ThemeContext);
  const commentRef = createRef();

  // 현재 모드에 따라 설정할 테마 변수로 할당
  const currentTheme = state.isDarkMode ? 'photon-dark' : 'github-light';

  useEffect(() => {
    ...
    const utterancesConfig = {
      ...
      // theme의 value를 위에서 설정한 currentTheme로 설정
      theme: currentTheme,
      ...
    };
    ...
    // 테마가 바뀔때마다 적용되도록 두번째 인자로 전달
  }, [currentTheme]);

  return <div className="comments" ref={commentRef}></div>;
};

export default Comments;
  1. 그런데 이렇게만 하면, script가 계속 appendChild 된다.

    • 기존 테마인 댓글 아래에 바뀐 테마 댓글이 계속 생성된다.
    • 먼저 만들어진 노드를 삭제한 후 변경된 테마로 새로 appendChild 할 수 있게끔 useEffect 처음에 추가한다.
    • 노드는 하나씩만 만들어질테니, firstChild로 ref에 노드가 있는지 확인한다. (없으면 null을 반환한다.)
    • 있다면 removeChild로 노드를 지운다.
src/components/Comments.js
import React, { createRef, useEffect, useContext } from 'react';
import ThemeContext from '../store/ThemeContext';

const Comments = () => {
  const { state } = useContext(ThemeContext);
  const commentRef = createRef();
  const currentTheme = state.isDarkMode ? 'photon-dark' : 'github-light';

  useEffect(() => {
    // 노드가 있는지 확인
    const isComment = commentRef.current.firstChild;

    // 노드가 있다면 삭제
    if (isComment) {
      commentRef.current.removeChild(isComment));
    }
    ...

  }, [currentTheme]);

  return <div className="comments" ref={commentRef}></div>;
};

export default Comments;

마무리

React에 <script> 태그를 적용하는 방법을 쭉 정리할 수 있었다. 리액트에서 ref를 통해 DOM에 접근하고, 노드들을 생성했다가, 삭제하는 과정을 정리하였다. 테마가 변경될때 utterances에서 제공하는 테마를 이용하여 변경하면 새로운 테마를 설정하여 스크립트를 다시 가져오므로, css로 테마를 변경하는 것보다 좀 더 딜레이가 있는 것 같다.
블로그 포스트 원본 파일들은 비공개 리포지토리로 관리하면서, 댓글도 운영할 수 있어서 만족스럽다. 또한 깃허브를 자유롭게 사용하고 싶은데, 그런점에서 깃허브 이슈로 운영되는 형식이 무척 마음에 든다.

Reference & Learn More

utteranc.es
stackoverflow: Adding script tag to React/JSX
How to add comment support on your Gatsby blog using Github utterances
React Ref와 DOM
MDN Document.createElement()
MDN Element.setAttribute()
MDN Node.appendChild()
MDN element.firstChild
MDN Object.entries()
MDN Node.removeChild()

Comments