본문으로 건너뛰기
CoderRed Blog

Article

Hugo 블로그 구축기 5편 - 댓글 시스템 구축 대작전

안녕하세요! 드디어 Hugo 블로그 구축기 시리즈 5편이 나왔네요 🎉

지금까지 블로그를 만들고, 삽질하고, 자동배포하고, 애널리틱스까지 연동했는데… 뭔가 허전했어요. 바로 댓글 기능이 없었거든요!

특히 제가 주식 투자 관련 글도 쓸 예정인데, 주식하는 분들이 GitHub 계정이 있을 리가 없잖아요? 😅 그래서 이번엔 누구나 쉽게 댓글을 달 수 있는 시스템을 만들어보기로 했습니다.

결과부터 말씀드리면… 4시간 동안 5가지 문제와 씨름했고, 특히 CORS 에러는 정말 지옥이었어요. 하지만 결국 해냈습니다! 🔥

🤔 댓글 시스템 선택의 고민

처음엔 당연히 GitHub 기반인 Giscus를 쓸 생각이었어요. 개발자들 사이에서 유명하고, 무료에 광고도 없고 좋잖아요?

하지만 곰곰 생각해보니… 🤔

  • 내 블로그 독자층: 개발자 + 주식 투자자
  • GitHub 계정 보유율: 개발자 90% vs 투자자 5%
  • 결론: GitHub 계정 필수인 시스템은 절반의 독자를 포기하는 것

그래서 다른 대안들을 찾아봤어요:

시스템장점단점결정
GiscusGitHub 기반, 무료, 광고 없음GitHub 계정 필요❌
UtterancesGitHub Issues, 간단 설치GitHub 계정 필요❌
Disqus누구나 사용 가능광고 있음❌
Cusdis이메일만으로 댓글 가능, 셀프 호스팅설정 복잡✅

Cusdis를 선택한 이유는 단순했어요. 이메일과 닉네임만 있으면 댓글을 달 수 있거든요! 게다가 셀프 호스팅이라 완전한 데이터 소유권도 가질 수 있고요.

🛠️ 구축 과정 - 생각보다 험난했던 여정

1단계: DNS 설정 (무난함)

Cloudflare에서 서브도메인 추가
Type: A
Name: comments
Content: 서버IP주소
Proxy: 🟠 Proxied (활성화)

이 부분은 금방 끝났어요. 이제 comments.coderred.com으로 접근할 수 있게 됐네요!

2단계: Cusdis Docker 컨테이너 실행

docker run -d \
  --name cusdis \
  --restart unless-stopped \
  --network npm_default \
  -p 3000:3000 \
  -e USERNAME="admin" \
  -e PASSWORD="강력한비밀번호" \
  -e JWT_SECRET="ofcourseistillloveyou" \
  -e DB_URL="file:/data/db.sqlite" \
  -e NEXTAUTH_URL="https://comments.coderred.com" \
  -v ~/cusdis-data:/data \
  djyde/cusdis:latest

여기서부터 삽질이 시작됐어요… 😅

😭 삽질의 연속 - 5가지 문제 대방출

문제 1: 포트 충돌 - “아, 그래 3000번 쓰고 있었지…”

컨테이너가 시작이 안 되더라고요. 알고 보니 Grafana가 3000번 포트를 이미 사용 중이었어요.

# 확인해보니...
sudo netstat -tlnp | grep :3000
# Grafana가 떡하니 자리잡고 있음 ㅠㅠ

해결: Grafana를 잠시 꺼두고 Cusdis부터 설치했어요.

문제 2: Docker 네트워크 통신 실패 - 502 Bad Gateway

Nginx Proxy Manager(NPM)에서 localhost:3000으로 설정했는데 502 에러가 나더라고요.

원인: NPM은 별도 컨테이너라서 localhost로 다른 컨테이너에 접근할 수 없었어요.

해결:

  1. Cusdis를 NPM과 같은 네트워크에 연결 (--network npm_default)
  2. NPM Forward 설정을 cusdis:3000으로 변경

이제 통신이 되기 시작했어요! 🎉

문제 3: Hugo 빌드 에러 - “내 프로젝트에 왜 SQLite가?”

갑자기 Hugo 빌드할 때 permission denied on db.sqlite-journal 에러가 나더라고요.

원인: Docker 볼륨 마운트를 ~/cusdis-data 대신 Hugo 프로젝트 폴더에 했더니 SQLite 파일이 프로젝트에 생성됨.

해결:

rm -rf ~/coderblog/data/  # 잘못 생성된 폴더 삭제

간단했지만 당황스러웠어요 😅

문제 4: CORS 에러 1차 - “헤더가 허용되지 않습니다”

이제 진짜 지옥의 시작이었어요…

Access to fetch at 'https://comments.coderred.com/api/open/comments' 
from origin 'https://coderred.com' has been blocked by CORS policy: 
Request header field x-timezone-offset is not allowed by Access-Control-Allow-Headers

해결: NPM Advanced 설정에 커스텀 헤더 추가

add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,x-timezone-offset' always;

하나 해결! 그런데…

문제 5: CORS 에러 2차 - “헤더 값이 여러 개야?”

The 'Access-Control-Allow-Origin' header contains multiple values 
'https://coderred.com, https://coderred.com', but only one is allowed.

이건 정말 골치 아팠어요. Cusdis와 NPM에서 중복으로 CORS 헤더를 보내는 문제였거든요.

GPT한테 물어보고, 제미니한테 물어보고… 결국 제미니가 완벽한 해결책을 제시해줬어요! 🙏

최종 해결책 (NPM Advanced 설정):

location / {
    # 백엔드가 보낸 중복 헤더를 먼저 숨김
    proxy_hide_header 'Access-Control-Allow-Origin';
    
    set $allowed_origin "";
    if ($http_origin ~* "https://(www\.)?coderred\.com") {
      set $allowed_origin $http_origin;
    }
    
    # 모든 CORS 헤더를 한번에 정의
    add_header 'Access-Control-Allow-Origin' $allowed_origin always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,x-timezone-offset' always;
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
    add_header 'Access-Control-Max-Age' 1728000 always;
    
    # Preflight OPTIONS 요청 처리
    if ($request_method = 'OPTIONS') {
        return 204;
    }
    
    # NPM의 기본 프록시 설정 사용
    include conf.d/include/proxy.conf;
}

이 설정을 적용하고 나서야 드디어… 댓글 시스템이 작동했어요! 🎉🎉🎉

🎯 Hugo 블로그와 연결

layouts/partials/comments.html 파일을 만들었어요:

<div class="article-comments">
  <h3>💬 댓글</h3>
  <div id="cusdis_thread"
    data-host="https://comments.coderred.com"
    data-app-id="APP_ID"
    data-page-id="{{ .File.ContentBaseName }}"
    data-page-url="{{ .Permalink }}"
    data-page-title="{{ .Title }}">
  </div>
  <script async defer src="https://comments.coderred.com/js/cusdis.es.js"></script>
</div>

그리고 포스트 Front Matter에 showComments: true만 추가하면 끝!

🎉 최종 결과 - 드디어 완성!

4시간의 삽질 끝에 이런 멋진 기능들을 갖게 됐어요:

  • ✅ GitHub 계정 없이 댓글 작성 가능 (이메일 + 닉네임만)
  • ✅ 광고 없는 깨끗한 인터페이스
  • ✅ 완전한 데이터 소유권 (셀프 호스팅)
  • ✅ 다크/라이트 테마 자동 대응
  • ✅ 모바일 반응형 지원

시스템 구조는 이렇게 됐어요:

블로그 사용자 → Cloudflare CDN → Nginx Proxy Manager → Cusdis Container
https://coderred.com → https://comments.coderred.com → localhost:3000

📚 이번에 배운 것들

Docker 네트워킹의 중요성

컨테이너끼리 통신하려면 같은 네트워크에 있어야 하고, localhost 대신 컨테이너 이름으로 접근해야 한다는 걸 확실히 알게됐어요.

CORS는 정말 복잡하다

특히 서브도메인 간 리소스 공유 시 발생하는 CORS 정책과 헤더 중복 문제… 정말 골치 아팠지만 덕분에 웹 보안에 대해 많이 배웠어요.

끈기의 중요성

복잡한 CORS 문제도 단계별로 접근하면 결국 해결할 수 있더라고요. 포기하지 않는 게 중요해요!

🚀 다음 계획

이제 기본적인 블로그 시스템은 완성됐어요! 다음에는 이런 것들을 해보려고 합니다:

  • 댓글 시스템 스타일링 개선 (더 자연스럽게)
  • 검색 기능 추가
  • SEO 최적화 심화
  • 광고 시스템 연동 (수익화!)

💬 마무리

정말 험난한 4시간이었지만, 결국 해냈어요! 🎉

이제 개발자 분들은 물론이고 주식 투자하시는 분들도 쉽게 댓글을 달 수 있게 됐네요. GitHub 계정 없어도 이메일과 닉네임만 있으면 바로 소통 가능해요!

혹시 비슷한 작업을 하시다가 막히시면 댓글로 알려주세요. 함께 해결해봐요! 😊

그리고 이 글이 도움이 되셨다면 댓글로 한 말씀 남겨주시면 정말 힘이 될 것 같아요. 아직 새로 만든 댓글 시스템이라 첫 댓글이 누가 될지 궁금하네요! 🤔

다음 편도 기대해 주세요! 🚀

댓글