본문으로 건너뛰기

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

·857 단어수·5 분
Hugo 블로그 구축기 - 이 글은 시리즈의 일부입니다.
부분 : 이 글

안녕하세요! 드디어 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 계정 없어도 이메일과 닉네임만 있으면 바로 소통 가능해요!

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

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

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

Hugo 블로그 구축기 - 이 글은 시리즈의 일부입니다.
부분 : 이 글

💬 댓글