Skip to content

VitePress + Github Pages 블로그에 댓글 추가하기

블로그에 댓글 추가하기

utterances를 이용해서 블로그에 댓글 기능을 추가하는 방법을 소개합니다.

utterances

utterances는 댓글을 등록하고 검색하는 데 GitHub Issue를 이용합니다.

페이지에 대응하는 이슈를 utterances-bot을 통해 생성하고,

Github Rest API를 이용해 이슈의 댓글 목록을 가져와서 노출합니다.

Github App 설치

이슈를 연결할 깃허브 공개 저장소가 하나 필요합니다.

댓글 전용으로 저장소를 생성해도 좋으나, 저는 블로그 프로젝트의 저장소를 연결해 사용했습니다.

공개 저장소를 하나 정한 후, Github에 utterances app을 설치합니다.

utterances script 생성

utteranc.es의 가이드를 따르다 보면,

다음과 같은 script tag를 페이지에 삽입하도록 가이드하고 있습니다.

html
<script
  src="https://utteranc.es/client.js"
  repo="[ENTER REPO HERE]"
  issue-term="pathname"
  theme="github-dark"
  crossorigin="anonymous"
  async
></script>

하지만 Vue에는 특정 페이지/컴포넌트에 스크립트를 추가하는 구체적인 방법이 없고,

실제로 스크립트를 삽입해 보면 동작하지 않는 것을 확인할 수 있습니다.

따라서 DOM 조작을 통해 script 태그를 직접 생성해서 추가해야 합니다.

UtterancComment 컴포넌트

DOM에 script 태그를 추가하는 UtterancComment컴포넌트를 작성합니다.

vue
<script setup>
import { onMounted } from "vue";

// Methods
const appendUtteranc = () => {
  let script = document.createElement("script");

  script.src = "https://utteranc.es/client.js";
  script.async = true;
  script.crossOrigin = "anonymous";

  script.setAttribute("repo", "ENTER REPO HERE");
  script.setAttribute("issue-term", "pathname");
  script.setAttribute("label", "Comment");
  script.setAttribute("theme", "photon-dark");

  document.querySelector("#comments-container").appendChild(script);
};

// LifeCycle
onMounted(() => {
  appendUtteranc();
});
</script>

<template>
  <div id="comments-container"></div>
</template>

댓글을 노출하고 싶은 페이지에 컴포넌트를 직접 삽입할 수 있지만

Layout Slots을 이용하면 좀더 손쉽게 원하는 페이지에 댓글을 노출할 수 있을 것 같습니다.

Layout에 적용하기

BaseLayout.vue 를 작성하고, vitepress의 Layout에 적용합니다.

bash
├── .vitepress
   └── theme
        ├── BaseLayout.vue
        ├── index.js
        └── style.css
├── components
   └── UtterancComment.vue
js
// https://vitepress.dev/guide/custom-theme
import DefaultTheme from "vitepress/theme";
import BaseLayout from "./BaseLayout.vue";
import "./style.css";

/** @type {import('vitepress').Theme} */
export default {
  extends: DefaultTheme,
  Layout: BaseLayout,
  enhanceApp({ app, router, siteData }) {
    // ...
  },
};
vue
<script setup>
import { useData } from "vitepress";
import DefaultTheme from "vitepress/theme";
import UtterancComment from "@/components/UtterancComment.vue";

const { Layout } = DefaultTheme;

// Composable
const { frontmatter, page } = useData();
</script>

<template>
  <Layout>
    <template #doc-after>
      <UtterancComment
        v-if="frontmatter.comment"
        :key="page.relativePath"
      ></UtterancComment>
    </template>
  </Layout>
</template>
md
---
title: ...
titleTemplate: ...
comment: true
---

Markdown Contents

테마 변경 대응하기

블로그의 색상 테마를 변경했을 때 댓글의 테마를 변경하고 싶습니다.

우선, Vitepress 색상 테마에 따라 댓글의 테마를 설정하도록 변경합니다.

vue
<script setup>
import { computed } from "vue";
import { useData } from "vitepress";
import DefaultTheme from "vitepress/theme";
import UtterancComment from "@/components/UtterancComment.vue";

const { Layout } = DefaultTheme;

const COMMENT_THEME_DARK = "photon-dark";
const COMMENT_THEME_LIGHT = "github-light";

// Composable
const { frontmatter, page, isDark } = useData();

// Computed
const commentTheme = computed(() => {
  return isDark.value ? COMMENT_THEME_DARK : COMMENT_THEME_LIGHT;
});
</script>

<template>
  <Layout>
    <template #doc-after>
      <UtterancComment
        v-if="frontmatter.comment"
        :key="page.relativePath"
        :theme="commentTheme"
      ></UtterancComment>
    </template>
  </Layout>
</template>
vue
<script setup>
import { onMounted } from "vue";

// Props
const props = defineProps(["theme"]);

// Methods
const appendUtteranc = () => {
  let script = document.createElement("script");

  script.src = "https://utteranc.es/client.js";
  script.async = true;
  script.crossOrigin = "anonymous";

  script.setAttribute("repo", "ENTER REPO HERE");
  script.setAttribute("issue-term", "pathname");
  script.setAttribute("label", "Comment");
  script.setAttribute("theme", props.theme);

  document.querySelector("#comments-container").appendChild(script);
};

// LifeCycle
onMounted(() => {
  appendUtteranc();
});
</script>

<template>
  <div id="comments-container" class="comments-container"></div>
</template>

<style scoped>
.comments-container {
  margin-top: 40px;
}
</style>

하지만 외부 페이지인 댓글 화면은 테마 변경에 대해 반응하지 않습니다.

다음과 같이 색상 변경을 감지하여 iframe에 테마를 변경해달라는 메세지를 전달합니다.

vue
<script setup>
import { onMounted, watch } from "vue";

// ...

// Watch
watch(
  () => props.theme,
  () => {
    const iframe = document.querySelector(".utterances-frame");

    const targetOrigin = "https://utteranc.es";
    const message = {
      type: "set-theme",
      theme: props.theme,
    };

    iframe?.contentWindow?.postMessage(message, targetOrigin);
  }
);
</script>

Reference

utterances
issue search API
issue comment create API