Next.js 블로그 만들기 — giscus로 댓글 기능 추가

5 min read
giscusGitHub댓글
Next.js 블로그 만들기 — giscus로 댓글 기능 추가

댓글 기능이 필요한 이유

블로그에 댓글이 없으면 일방적인 소통이 된다. 독자의 피드백을 받을 수 있고, 잘못된 내용을 지적받을 수도 있다. 하지만 댓글 시스템을 직접 만들려면 DB, 인증, 스팸 필터링 등 생각보다 해야 할 일이 많다.

정적 블로그에서 쓸 수 있는 서드파티 댓글 시스템을 비교해봤다:

서비스기반장점단점
Disqus자체기능 풍부무겁고, 광고 있음
utterancesGitHub Issues가벼움답글, 리액션 없음
giscusGitHub Discussions답글, 리액션, 다크모드, 지연 로딩GitHub 계정 필요

개발 블로그 독자 대부분은 GitHub 계정이 있으니, giscus가 가장 적합하다고 판단했다.

giscus란?

giscus는 utterances에서 영감을 받은 오픈소스 댓글 위젯이다. GitHub Discussions를 댓글 저장소로 사용한다.

핵심 특징:

  • 무료 — 오픈소스, 서버 비용 없음
  • 답글 지원 — Issues와 달리 Discussions는 스레드 형태의 답글이 가능
  • 리액션 — 👍, ❤️ 등 이모지 리액션
  • 다크모드 — 테마 전환 API 제공
  • 지연 로딩 — 뷰포트에 들어올 때 로딩

사전 설정

1. GitHub Discussions 활성화

레포 Settings → General → Features에서 Discussions를 체크한다. CLI로도 가능하다:

gh repo edit terajh/tera-log --enable-discussions

2. giscus 앱 설치

github.com/apps/giscus에서 giscus 앱을 레포에 설치한다. 이걸 해야 giscus가 Discussions에 접근할 수 있다.

3. 레포 정보 확인

giscus 설정에 필요한 repo-idcategory-id를 가져온다:

gh api graphql -f query='{
  repository(owner: "terajh", name: "tera-log") {
    id
    discussionCategories(first: 10) {
      nodes { id name }
    }
  }
}'

응답에서 repository.id와 "General" 카테고리의 id를 사용한다.

구현

Comments 컴포넌트

giscus 공식 사이트에서 생성해주는 <script> 태그를 그대로 쓸 수도 있지만, Next.js에서는 React 컴포넌트로 래핑하는 것이 더 깔끔하다.

src/components/post/Comments.tsx
'use client'
 
import { useEffect, useRef } from 'react'
import { useTheme } from 'next-themes'
 
export default function Comments() {
  const containerRef = useRef<HTMLDivElement>(null)
  const { resolvedTheme } = useTheme()
 
  useEffect(() => {
    const container = containerRef.current
    if (!container) return
 
    // 이미 로드된 경우 — 테마만 변경
    const existingIframe = container.querySelector<HTMLIFrameElement>(
      'iframe.giscus-frame'
    )
 
    if (existingIframe) {
      existingIframe.contentWindow?.postMessage(
        {
          giscus: {
            setConfig: {
              theme: resolvedTheme === 'dark'
                ? 'dark_dimmed'
                : 'light',
            },
          },
        },
        'https://giscus.app'
      )
      return
    }
 
    // 최초 로드
    const script = document.createElement('script')
    script.src = 'https://giscus.app/client.js'
    script.setAttribute('data-repo', 'terajh/tera-log')
    script.setAttribute('data-repo-id', 'YOUR_REPO_ID')
    script.setAttribute('data-category', 'General')
    script.setAttribute('data-category-id', 'YOUR_CATEGORY_ID')
    script.setAttribute('data-mapping', 'pathname')
    script.setAttribute('data-reactions-enabled', '1')
    script.setAttribute('data-input-position', 'bottom')
    script.setAttribute(
      'data-theme',
      resolvedTheme === 'dark' ? 'dark_dimmed' : 'light'
    )
    script.setAttribute('data-lang', 'ko')
    script.crossOrigin = 'anonymous'
    script.async = true
    container.appendChild(script)
  }, [resolvedTheme])
 
  return <div ref={containerRef} className="mt-16" />
}

여기서 핵심은 다크모드 전환 처리다.

다크모드 자동 전환

useEffect의 의존성 배열에 resolvedTheme이 들어있다. 테마가 바뀔 때마다:

  1. 이미 iframe이 있으면 → postMessage로 테마만 변경
  2. 아직 없으면 → 새로 <script>를 생성

giscus는 iframe 기반이기 때문에 직접 DOM을 조작할 수 없다. 대신 postMessage API로 설정을 변경할 수 있다. 이 방식 덕분에 블로그의 테마 토글 버튼을 누르면 댓글 위젯의 테마도 즉시 바뀐다.

페이지에 적용

포스트 상세 페이지의 본문 아래에 배치한다:

src/app/posts/[slug]/page.tsx
export default async function PostPage({ params }) {
  // ... post, content 로딩
  return (
    <article>
      <header>...</header>
      <PostContent>{content}</PostContent>
      <div className="mx-auto max-w-3xl px-4">
        <Comments />
      </div>
    </article>
  )
}

max-w-3xl로 본문과 동일한 폭으로 맞춰준다.

결과

댓글 기능이 정상 동작하는지 확인할 포인트:

  • GitHub 로그인 후 댓글 작성 가능
  • 작성된 댓글이 레포의 Discussions 탭에 저장됨
  • 다크/라이트 모드 전환 시 댓글 위젯도 테마 변경
  • 리액션, 답글 기능 동작

관심 있을 만한 포스트

CI/CD의 새 언어는 자연어다 — GitHub Agentic Workflows 해부

GitHub이 마크다운 기반 AI 워크플로우를 기술 프리뷰로 공개했다. 구조, 보안 모델, 실전 시나리오를 분석한다.

GitHubAI-agent

AI 코딩의 맹점 — Artifacts 없이 에이전트는 기억을 잃는다

PRD, ADR, TDD가 AI 코딩 워크플로우에서 왜 선택이 아닌 필수인지, 실전 구조와 함께 살펴본다.

AI 코딩Artifacts

Next-Translate 3.0 — Turbopack과 App Router를 위한 i18n 재건

1년간 공백 후 돌아온 Next-Translate 3.0이 Turbopack 지원, 비동기 params, App Router 안정화를 한 번에 처리하는 방법.

Next.jsi18n

V8 WasmGC 투기적 최적화 — 가상 메서드를 인라인으로 만드는 법

V8이 WasmGC의 가상 메서드 디스패치에 투기적 인라이닝을 도입해 Dart와 Java 앱에서 최대 8% 성능을 끌어낸 방법.

V8WebAssembly

Vinext — Vite 위에서 Next.js를 1주일 만에 다시 만든 이야기

Cloudflare가 AI와 함께 단 일주일, $1,100의 API 비용으로 Next.js 호환 프레임워크를 Vite 위에 구축한 과정.

VinextNext.js

Tsonic — TypeScript를 네이티브 바이너리로 컴파일하는 실험

TypeScript → C# → NativeAOT 파이프라인으로 네이티브 실행 파일을 만드는 Tsonic. 어떻게 동작하고, 어떤 한계가 있는지 살펴봤다.

TypeScriptNativeAOT

VS Code 팀의 AI 에이전트 병렬화 — 월간 릴리스를 주간으로 만든 워크플로우

VS Code 팀이 월간 릴리스에서 주간 릴리스로 전환한 비결. 에이전트 세션 병렬화, 자동화 파이프라인, 품질 게이트 설계 전반을 공개했다.

VS CodeAI

React Compiler의 한계 — 뭘 최적화하고 뭘 못 하는가

React Compiler가 자동 메모이제이션으로 해결하는 것과 해결하지 못하는 것. 컴파일러 기반 UI 프레임워크의 능력 경계를 정리했다.

ReactReact Compiler