- Published on
블로그 커스텀 도전! - 댓글 추가하기 (with. giscus, next13, vercel, tailwind)
정적 블로그에서의 댓글
문득 정적 블로그에서는 댓글 기능을 어떻게 만들지 궁금증이 생겼다. 보통 댓글의 경우 내가 작성하는 것이 아닌 다른 사람이 작성하는 경우가 많고 이 데이터를 저장할 방법과 공간이 필요하다. 찾아보니 giscus 시스템으로 서버 없이도 댓글 기능을 사용할 수 있었다! (+ 무료 👍)
블로그에 적용하기
현재 내 블로그는 Tailwind Nextjs Starter Blog Starter 템플릿으로 만들어진 상태다.
위 가이드라인에 제공하는 조건으로 세팅하도록 하자 (giscus 접속)
- 저장소를 public으로 유지할 것.
- giscus 설치
- 블로그 리포지토리 -> Settings -> General(Settings 클릭 시 나오는 화면) 중간에 위치한 Features에서 Discussions 체크
- 바로 아래 저장소에서 myusername/myrepo 입력하기
- 나머지는 특별하게 체크할 부분은 없다. Discussion 카테고리, 테마, 기능의 경우 본인 취향에 맞게 선택하면 된다.
(필자는 카테고리: General, 테마: Github Light, 기능: 메인 포스트 반응 남기기만 선택했다.) - 그러면 하단에 script를 자동으로 생성 해준다.
이제 사이트에서 해야 될 설정은 끝이고 코드로 작성하면 된다. 어제 새벽에 여기서 문제를 많이 겪었었는데, 다행히도 자고 일어나서 해결할 수 있었다. 새벽에 겪었던 내용을 트러블 슈팅해보자!
트러블 슈팅 🚀
문제상황 1 - 생성된 script를 어떻게 적용하는가
id 같은 은밀한 정보는 .env 파일을 생성해서 환경 변수로 만들어 주었고 검색했던 아티클을 참고해서 siteMetadata.js 파일에 comments 부분에 script 내용을 입력했으나 아무 일도 일어나지 않았다. (실패..❌)
또 다른 아티클을 참고해 이번에는 Giscus라는 컴포넌트를 하나 만들고 script 태그를 만들어 내용을 추가시켰다.
'use client'
import { useTheme } from 'next-themes'
import { useEffect, useRef } from 'react'
import 'css/prism.css'
export default function Giscus() {
const ref = useRef<HTMLDivElement>(null)
const { resolvedTheme } = useTheme()
const theme = resolvedTheme === 'dark' ? 'dark' : 'light'
useEffect(() => {
if (!ref.current || ref.current.hasChildNodes()) return
const scriptElem = document.createElement('script')
scriptElem.src = 'https://giscus.app/client.js'
scriptElem.async = true
scriptElem.crossOrigin = 'anonymous'
scriptElem.setAttribute('data-repo', process.env.NEXT_PUBLIC_GISCUS_REPO)
scriptElem.setAttribute('data-repo-id', process.env.NEXT_PUBLIC_REPO_KEY)
scriptElem.setAttribute('data-category', 'General')
scriptElem.setAttribute('data-category-id', process.env.NEXT_PUBLIC_CATE_KEY)
scriptElem.setAttribute('data-mapping', 'pathname')
scriptElem.setAttribute('data-strict', '0')
scriptElem.setAttribute('data-reactions-enabled', '1')
scriptElem.setAttribute('data-emit-metadata', '0')
scriptElem.setAttribute('data-input-position', 'bottom')
scriptElem.setAttribute('data-theme', theme)
scriptElem.setAttribute('data-lang', 'ko')
ref.current.appendChild(scriptElem)
}, [])
useEffect(() => {
const iframe = document.querySelector<HTMLIFrameElement>('iframe.giscus-frame')
iframe?.contentWindow?.postMessage({ giscus: { setConfig: { theme } } }, 'https://giscus.app')
}, [theme])
return <section ref={ref} />
}
그런데 여기서 또 문제가 발생했다.
'string | undefined' 형식의 인수는 'string' 형식의 매개 변수에 할당될 수 없습니다. 'undefined' 형식은 'string' 형식에 할당할 수 없습니다.ts
TS에서 발생하는 형식 안정성 관련 에러로 환경 변수(process.env
)를 사용하여 설정값을 가져오는 부분에서 환경 변수의 값이 항상 존재한다는 것을 보장할 수 없기 때문에 에러가 발생하고 있었다.
(process.env에서 가져온 값은 string | undefined 형식이기 때문)
환경 변수가 undefined일 때 발생할 수 있는 문제를 사전에 방지하도록 해야 한다..
const repo = process.env.NEXT_PUBLIC_GISCUS_REPO
const repoId = process.env.NEXT_PUBLIC_GISCUS_REPOSITORY_ID
const categoryId = process.env.NEXT_PUBLIC_GISCUS_CATEGORY_ID
// 환경 변수 값이 없으면 사전에 return
if (!repo || !repoId || !categoryId) {
return
}
전체 코드
'use client'
import { useTheme } from 'next-themes'
import { useEffect, useRef } from 'react'
import 'css/prism.css'
export default function Giscus() {
const ref = useRef<HTMLDivElement>(null)
const { resolvedTheme } = useTheme()
const theme = resolvedTheme === 'dark' ? 'dark' : 'light'
useEffect(() => {
if (!ref.current || ref.current.hasChildNodes()) return
const repo = process.env.NEXT_PUBLIC_GISCUS_REPO
const repoId = process.env.NEXT_PUBLIC_GISCUS_REPOSITORY_ID
const categoryId = process.env.NEXT_PUBLIC_GISCUS_CATEGORY_ID
// 환경 변수 값이 없으면 return
if (!repo || !repoId || !categoryId) {
return
}
const scriptElem = document.createElement('script')
scriptElem.src = 'https://giscus.app/client.js'
scriptElem.async = true
scriptElem.crossOrigin = 'anonymous'
scriptElem.setAttribute('data-repo', repo)
scriptElem.setAttribute('data-repo-id', repoId)
scriptElem.setAttribute('data-category', 'General')
scriptElem.setAttribute('data-category-id', categoryId)
scriptElem.setAttribute('data-mapping', 'pathname')
scriptElem.setAttribute('data-strict', '0')
scriptElem.setAttribute('data-reactions-enabled', '1')
scriptElem.setAttribute('data-emit-metadata', '0')
scriptElem.setAttribute('data-input-position', 'bottom')
scriptElem.setAttribute('data-theme', theme)
scriptElem.setAttribute('data-lang', 'ko')
ref.current.appendChild(scriptElem)
}, [])
useEffect(() => {
const iframe = document.querySelector<HTMLIFrameElement>('iframe.giscus-frame')
iframe?.contentWindow?.postMessage({ giscus: { setConfig: { theme } } }, 'https://giscus.app')
}, [theme])
return <section ref={ref} />
}
만들어진 Giscus 컴포넌트는 기본 템플릿으로 생성된 템플릿에서 생성된 components/Comments.tsx 에 기존 내용을 지우고 배치했다.
📌 만약 필자와 같이 기존에 생성된 템플릿 내용을 제거할 경우 아래 3개의 컴포넌트에서 Comments에 argument인 slug를 제거해주면 된다.
- layouts/PostBanner.tsx
- layouts/PostLayout.tsx
- layouts/PostSimple.tsx
이렇게 성공적으로 끝내는가 싶었는데 문제가 또 발생했다. 댓글창이 보이긴 하는데 크기가 작다..??
문제상황 2 - 댓글창(iframe) 크기가 이상하다??
개발자 도구에서 요소를 확인해 보니 iframe 크기에 문제가 있었다. section 태그에 인라인 스타일도 지정해 보고 했으나 iframe의 크기를 바꾸지 않는 한 크기의 변화는 없었다. 새벽에 시간이 너무 늦은 관계로 침대에 누워 마지막으로 구글에 검색해 보던 중 giscus issue에 나와 같은 고민을 하는 외국인 개발자분의 질문이 있었다.
생각보다 간단한 해결방법이 존재했다.
.giscus, .giscus-frame {
width: 100%;
}
iframe의 className인 .giscus-frame에 스타일링을 하면 된다. 하지만 직접적으로 스타일링 하기 위해서는 전역 스타일로 처리가 필요했다. 전역 스타일로 지정하기 위해 _app.tsx를 찾아봤으나 존재하지 않았다. Next.js 13부터는 app 디렉토리로 변경되었기 때문에 전역으로 스타일을 어떻게 적용해야 할지 고민이 많았다. 내가 내린 결론은 기존에 있는 css 파일이 전역으로 관리되고 있을 것 같다는 생각을 했고 css/prism.css 파일에 코드를 추가하고 Giscus 컴포넌트에 import 한 결과 댓글의 크기가 정상적으로 생성되었다.
후기
오늘 새벽에 갑자기 생각나서 만들어보게 되었는데, 막힐 때마다 네이버 벨로그가 너무 그리웠다.. 하지만 이번 기회에 next.js를 공부한다는 느낌으로 블로그를 조금씩 커스텀 해 나아갈 예정이다. 다음에는 탭에 표시되는 아이콘과 openGraph 설정을 건드려볼 생각이다. 🧐