이제 댓글을 달아봐야겠다. utterances 댓글로 설치할 것이다. utterances 플러그인을 선택한 이유는 '왜 gatsby로 블로그를 만드려는 거야?' 포스트에 서술해두었다.
utterances페이지에 자세하게 설명되어 있지만, 개인적으로 궁금했던 점을 기록하면서 설치하려 한다.
기본적인 설치 단계
-
공개된 리포지토리를 (새로)만들기
- 이 블로그에선 컨텐츠를 비공개 리포지토리로 사용하고 있어서, utterances를 설치하기 힘들 수 있다고 생각했다. 하지만, 댓글 리포지토리를 따로만들면 된다.
- 새로 만든 리포지토리는 댓글만 따로 이슈로 관리 하는 것이다.
-
페이지에 들어가 깃허브 앱 설치
- 오른쪽 상단 녹색의 설치 버튼을 누르고, Only select repositories를 선택하여 위에서 생성한 리포지토리로 설정해준다.
-
설정 후 스크립트를 추가한다.
- 설치가 되고 나면 utterances페이지로 리다이렉트 될 것이다.
- configuration 항목별로 설정을 완료해준다.
- 사실 모두 설정할 필요는 없지만, 스크립트를 자동으로 만들어주니 편리하다.
-
아래에 만들어진 Enable Utterances 스크립트를 원하는 위치에 붙여넣기
- 리액트에선 inline으로 스크립트를 꽂을 수 없어서, 따로 코드를 작성해주어야한다.
- 하지만 스크립트 형식은 참고해야하니 잘 봐두자.
리액트에 적용하기
댓글만 보여주는 컴포넌트를 작성하여, 포스트 템플릿에 삽입할 것이다.
-
Comment.js 형식
- createRef로 ref 어트리뷰트를 붙일 수 있는 Ref를 하나 만든다.
- 컴포넌트에서 useEffect로 script 내용을 미리 적용할 것이다.
- 그렇게 만든 ref를 만들어 리턴한다.
import React, { createRef, useEffect } from 'react';
const Comments = () => {
const commentRef = createRef();
useEffect(() => {
// utterances script!
}, []);
return <div className="comments" ref={commentRef}></div>;
};
export default Comments;
-
useEffect에 넣을 utterances script 작성하기
- stackoverflow의 Adding script tag to React 참고하였다.
- utterances 페이지에서 만든 스크립트 내용을 작성한다.
- document.createElement로 스크립트 element를 만든다.
- 스크립트 element에 어트리뷰트를 setAttribute로 작성해준다.
- 하나 하나 붙여도 되고, script에 attribute를 객체로 만들어 그 전체를 entries로 한번에 붙일 수 있다.
- 만들어둔 script element를 ref에 appendChild로 자식노드를 붙여준다.
...
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테마도 바꾸고 싶다면
-
테마별로 utterances 테마를 결정한다.
- useContext로 현재 블로그 테마 읽어들인다.
- 현재 테마에 따라 변경할 테마 설정하고, script에 적용한다.
- 현재 테마가 바뀔때마다 effect를 발생하도록, useEffect 두번째 인자로 전달한다.
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;
-
그런데 이렇게만 하면, script가 계속 appendChild 된다.
- 기존 테마인 댓글 아래에 바뀐 테마 댓글이 계속 생성된다.
- 먼저 만들어진 노드를 삭제한 후 변경된 테마로 새로 appendChild 할 수 있게끔 useEffect 처음에 추가한다.
- 노드는 하나씩만 만들어질테니, firstChild로 ref에 노드가 있는지 확인한다. (없으면 null을 반환한다.)
- 있다면 removeChild로 노드를 지운다.
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()