Quartz v4 + Remark42 Comment System Integration

Quartz v4 블로그에 익명+OAuth 댓글 지원하는 Remark42를 OCI Free Tier에 셀프호스팅으로 연동하는 방법


🎯 개요

Quartz v4는 공식적으로 Giscus만 댓글 시스템으로 지원한다. Giscus는 GitHub Discussions 기반이라 댓글을 달려면 GitHub 계정이 필요하다. 기술 블로그 특성상 독자가 개발자라면 큰 문제는 없지만, 익명 댓글도 허용하고 싶었음.

댓글 시스템 비교

솔루션익명서버 필요비고
GiscusQuartz 공식 지원
UtterancesGitHub Issues 기반
Waline중국 개발자 메인테이너
IssoPython/SQLite, 심플
Remark42Go 기반, 기능 풍부

Remark42를 선택한 이유

  • 익명 댓글 + GitHub OAuth 동시 지원
  • Go 바이너리라 메모리 사용량이 적음 (~50-80MB)
  • 대댓글, 투표, 마크다운, 관리자 페이지 지원
  • OCI Free Tier (1vCPU/1GB RAM) 에서 충분히 운용 가능

환경

  • 서버: Oracle Cloud Free Tier (1 vCPU / 1GB RAM / Ubuntu 24.04)
  • 도메인: Cloudflare DNS 관리
  • 블로그: Quartz v4 + GitHub Pages

🔧 구성 절차

Step 1: GitHub OAuth 앱 등록

GitHub → Settings → Developer settings → OAuth Apps → New OAuth App

Application name: jmlee.net comments
Homepage URL: https://jmlee.net
Authorization callback URL: https://comments.jmlee.net/auth/github/callback

Client ID와 Client Secret을 발급받아 저장해둠.

GitHub 유저 ID 확인 (관리자 설정에 필요):

curl -s https://api.github.com/users/<GitHub유저> | grep '"id"'

Step 2: Docker 설치

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# 재로그인 후 확인
docker --version
docker compose version

Step 3: Remark42 Docker Compose 설정

mkdir -p ~/remark42
cd ~/remark42

docker-compose.yml 작성:

services:
  remark42:
    image: umputun/remark42:latest
    container_name: remark42
    restart: unless-stopped
    environment:
      - REMARK_URL=https://comments.jmlee.net
      - SITE=jmlee.net
      - SECRET=<openssl rand -hex 32 으로 생성>
      - AUTH_GITHUB_CID=<GitHub Client ID>
      - AUTH_GITHUB_CSEC=<GitHub Client Secret>
      - AUTH_ANON=true
      - ADMIN_SHARED_ID=github_<GitHub 유저 ID>
      - NOTIFY_TYPE=telegram
      - NOTIFY_TELEGRAM_TOKEN=<Telegram 봇 토큰>
      - NOTIFY_TELEGRAM_CHAN=<Telegram 채팅 ID>
    volumes:
      - ./data:/srv/var
    ports:
      - "127.0.0.1:8080:8080"

⚠️ TELEGRAM_CHANNEL이 아니라 NOTIFY_TELEGRAM_CHAN이다. 공식 문서와 실제 환경변수명이 다른 경우가 있으니 확인 필요.

SECRET 생성:

openssl rand -hex 32

컨테이너 실행:

docker compose up -d
docker compose logs -f

정상 로그 확인:

remark42  | 2026/02/20 00:00:00 [INFO]  start server on port :8080
remark42  | 2026/02/20 00:00:00 [INFO]  anonymous access enabled
remark42  | 2026/02/20 00:00:00 [INFO]  init oauth2 service github

Step 4: Nginx + SSL 설정

Nginx 및 Certbot 설치:

sudo apt install nginx certbot python3-certbot-nginx -y

OCI iptables 포트 개방:

sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -I INPUT -p tcp --dport 443 -j ACCEPT
sudo netfilter-persistent save

⚠️ OCI는 콘솔의 Security List와 서버의 iptables 두 곳 모두 포트를 열어야 함.

Nginx 설정 파일 생성:

# /etc/nginx/sites-available/comments.jmlee.net
 
server {
    listen 80;
    server_name comments.jmlee.net;
 
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo ln -s /etc/nginx/sites-available/comments.jmlee.net /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Cloudflare DNS 설정:

Type: A
Name: comments
IPv4: <OCI 공인 IP>
Proxy status: DNS only (회색 구름)

⚠️ Proxied(주황 구름) 상태에서는 Cloudflare IP가 노출되어 Certbot이 도메인 소유권 확인을 못 한다. SSL 발급 전에는 반드시 DNS only로 설정.

SSL 발급:

sudo certbot --nginx -d comments.jmlee.net

발급 완료 후 Cloudflare DNS를 Proxied(주황 구름)로 변경해도 됨.


Step 5: Remark42 정상 동작 확인

루트 URL은 404를 반환하는 게 정상이다. 아래로 확인:

curl -I http://127.0.0.1:8080
# App-Name: remark42 헤더 확인

또는 브라우저에서:

https://comments.jmlee.net/web/embed.js

JS 파일이 로드되면 정상임.


Step 6: Quartz 커스텀 컴포넌트 작성

quartz/components/Remark42.tsx 생성:

import { QuartzComponent, QuartzComponentConstructor } from "./types"
 
const Remark42: QuartzComponent = () => {
  return (
    <>
      <div id="remark42"></div>
      <script
        dangerouslySetInnerHTML={{
          __html: `
            var remark_config = {
              host: "https://comments.jmlee.net",
              site_id: "jmlee.net",
              components: ["embed"],
              theme: "light",
              locale: "ko",
            };
            (function(c){
              for(var i=0;i<c.length;i++){
                var d=document,s=d.createElement('script');
                s.src=remark_config.host+'/web/'+c[i]+'.js';
                s.defer=true;
                (d.head||d.body).appendChild(s);
              }
            })(remark_config.components||['embed']);
          `,
        }}
      />
    </>
  )
}
 
export default (() => Remark42) satisfies QuartzComponentConstructor

quartz/components/index.ts에 추가:

import Remark42 from "./Remark42"
 
export {
  // ... 기존 exports
  Remark42,
}

quartz.layout.ts에서 afterBody에 추가:

export const sharedPageComponents: SharedLayout = {
  head: Component.Head(),
  header: [],
  afterBody: [Component.Remark42()],  // 추가
  footer: Component.Footer({ ... }),
}

sharedPageComponents에 추가하면 모든 페이지에 표시됨. 게시물 페이지에만 표시하려면 defaultContentPageLayoutafterBody에 추가함.

빌드 및 배포:

npx quartz build --serve
npx quartz sync

Step 7: Telegram 알림 설정

BotFather에서 봇 생성 후 채팅 ID 확인:

curl https://api.telegram.org/bot<>/getUpdates

docker-compose.yml에 추가:

- NOTIFY_TYPE=telegram
- NOTIFY_TELEGRAM_TOKEN=<봇 토큰>
- NOTIFY_TELEGRAM_CHAN=<채팅 ID>

⚠️ TELEGRAM_CHANNEL이나 TELEGRAM_CHAN이 아니라 반드시 NOTIFY_TELEGRAM_CHAN이어야 함.


🐛 트러블슈팅

Cloudflare 521 에러

Cloudflare Proxy가 Proxied 상태일 때 발생. SSL 발급 전에는 반드시 DNS only로 설정함.

Certbot 발급 실패

Cloudflare DNS가 Proxied 상태면 발급 불가. DNS only로 변경 후 재시도함.

Telegram 알림 미수신

  • 환경변수명 확인: NOTIFY_TELEGRAM_CHAN (CHANNEL 아님)
  • 봇에게 /start 메시지를 먼저 보내야 함
  • 직접 테스트:
curl "https://api.telegram.org/bot<토큰>/sendMessage?chat_id=<ID>&text=test"

Remark42 루트 404

정상 동작임. /web/embed.js로 확인함.


📋 요약

전체 구성도

flowchart LR
    User["사용자"]
    Blog["jmlee.net (GitHub Pages)"]
    CF["Cloudflare DNS"]
    OCI["OCI Free Tier"]
    Nginx["Nginx (Reverse Proxy + SSL)"]
    R42["Remark42 (Docker)"]
    TG["Telegram 알림"]

    User --> Blog
    Blog -->|"댓글 로드"| CF
    CF --> OCI
    OCI --> Nginx --> R42
    R42 -->|"새 댓글 알림"| TG

리소스 사용량 (OCI Free Tier 기준)

항목사용량
Remark42 컨테이너~50-80MB RAM
Nginx~10MB RAM
총 메모리~100MB / 1GB
디스크최소

OCI Free Tier 1GB RAM 환경에서 여유롭게 운용 가능함.


🔗 참고 자료


🔗 관련 문서