Quartz v4 + Remark42 Comment System Integration
Quartz v4 블로그에 익명+OAuth 댓글 지원하는 Remark42를 OCI Free Tier에 셀프호스팅으로 연동하는 방법
🎯 개요
Quartz v4는 공식적으로 Giscus만 댓글 시스템으로 지원한다. Giscus는 GitHub Discussions 기반이라 댓글을 달려면 GitHub 계정이 필요하다. 기술 블로그 특성상 독자가 개발자라면 큰 문제는 없지만, 익명 댓글도 허용하고 싶었음.
댓글 시스템 비교
| 솔루션 | 익명 | 서버 필요 | 비고 |
|---|---|---|---|
| Giscus | ❌ | ❌ | Quartz 공식 지원 |
| Utterances | ❌ | ❌ | GitHub Issues 기반 |
| Waline | ✅ | ✅ | 중국 개발자 메인테이너 |
| Isso | ✅ | ✅ | Python/SQLite, 심플 |
| Remark42 | ✅ | ✅ | Go 기반, 기능 풍부 |
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 versionStep 3: Remark42 Docker Compose 설정
mkdir -p ~/remark42
cd ~/remark42docker-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 -yOCI 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 nginxCloudflare 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 QuartzComponentConstructorquartz/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에 추가하면 모든 페이지에 표시됨. 게시물 페이지에만 표시하려면defaultContentPageLayout의afterBody에 추가함.
빌드 및 배포:
npx quartz build --serve
npx quartz syncStep 7: Telegram 알림 설정
BotFather에서 봇 생성 후 채팅 ID 확인:
curl https://api.telegram.org/bot<토큰>/getUpdatesdocker-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 환경에서 여유롭게 운용 가능함.