<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Troubleshooting on Yarang's Tech Lair</title><link>https://blog.agentthread.dev/ko/tags/troubleshooting/</link><description>Recent content in Troubleshooting on Yarang's Tech Lair</description><generator>Hugo -- gohugo.io</generator><language>ko</language><lastBuildDate>Sun, 03 May 2026 14:04:28 +0900</lastBuildDate><atom:link href="https://blog.agentthread.dev/ko/tags/troubleshooting/index.xml" rel="self" type="application/rss+xml"/><item><title>VS Code 커밋 메시지 'Co-Authored-by' 자동 삽입 끄기: GitHub Copilot 설정 정리</title><link>https://blog.agentthread.dev/ko/post/vs-code-%EC%BB%A4%EB%B0%8B-%EB%A9%94%EC%8B%9C%EC%A7%80-co-authored-by-%EC%9E%90%EB%8F%99-%EC%82%BD%EC%9E%85-%EB%81%84%EA%B8%B0-github-copilot-%EC%84%A4%EC%A0%95-%EC%A0%95%EB%A6%AC/</link><pubDate>Sun, 03 May 2026 14:04:28 +0900</pubDate><guid>https://blog.agentthread.dev/ko/post/vs-code-%EC%BB%A4%EB%B0%8B-%EB%A9%94%EC%8B%9C%EC%A7%80-co-authored-by-%EC%9E%90%EB%8F%99-%EC%82%BD%EC%9E%85-%EB%81%84%EA%B8%B0-github-copilot-%EC%84%A4%EC%A0%95-%EC%A0%95%EB%A6%AC/</guid><description>&lt;h1 id="vs-code-커밋-메시지-co-authored-by-자동-삽입-끄기"&gt;VS Code 커밋 메시지 &amp;lsquo;Co-Authored-by&amp;rsquo; 자동 삽입 끄기
&lt;/h1&gt;&lt;p&gt;최근 VS Code를 사용해 Git 커밋을 생성할 때, 의도치 않게 &lt;code&gt;Co-Authored-by: GitHub Copilot &amp;lt;copilot@github.com&amp;gt;&lt;/code&gt;와 같은 메시지가 커밋 메시지 하단에 자동으로 삽입되는 현상을 겪으셨을 것입니다. Hacker News 등에서도 &amp;ldquo;VS Code inserting &amp;lsquo;Co-Authored-by Copilot&amp;rsquo; into commits regardless of usage&amp;quot;라는 이슈로 화제가 되었습니다.&lt;/p&gt;
&lt;p&gt;AI가 코드를 작성하지 않았거나, 단순히 문법만 확인했음에도 불구하고 이 메시지가 들어가면, 커밋 히스토리가 지저분해질 수 있고 &lt;strong&gt;공로(Credit)&lt;/strong&gt; 관리가 모호해질 수 있습니다.&lt;/p&gt;
&lt;p&gt;이번 포스트에서는 이 기능의 원인과 VS Code 설정을 통해 이를 깔끔하게 해제하는 방법을 단계별로 설명합니다.&lt;/p&gt;
&lt;h2 id="현상-확인"&gt;현상 확인
&lt;/h2&gt;&lt;p&gt;VS Code의 소스 제어(Source Control) 패널에서 커밋 메시지를 입력하고 커밋을 생성하면, 실제 &lt;code&gt;.git/COMMIT_EDITMSG&lt;/code&gt;나 푸시된 히스토리에는 다음과 같은 줄이 추가되어 있습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;feat: update user authentication logic
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Co-Authored-by: GitHub Copilot &amp;lt;copilot@github.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="원인-분석"&gt;원인 분석
&lt;/h2&gt;&lt;p&gt;이 현상은 주로 &lt;strong&gt;GitHub Copilot 확장 프로그램&lt;/strong&gt;과 VS Code의 &lt;strong&gt;Smart Commit(스마트 커밋)&lt;/strong&gt; 기능이 연동되면서 발생합니다. Copilot이 IDE 내에서 활성화되어 있고, 코드 생성이나 완성을 제안한 맥락이 있으면 확장 프로그램이 자신을 공동 저자로 추가하려고 시도합니다.&lt;/p&gt;
&lt;p&gt;사용자 입장에서는 단순히 변수명 자동 완성을 받았을 뿐인데, 이 기능이 모든 커밋에 적용되는 것은 과도한 행동일 수 있습니다.&lt;/p&gt;
&lt;h2 id="해결-방법-vs-code-설정-변경"&gt;해결 방법: VS Code 설정 변경
&lt;/h2&gt;&lt;p&gt;가장 확실한 해결책은 VS Code의 사용자 설정(&lt;code&gt;settings.json&lt;/code&gt;)을 수정하여, 커밋 작성 시 Copilot이 개입하지 않도록 막는 것입니다.&lt;/p&gt;
&lt;h3 id="1-설정-열기"&gt;1. 설정 열기
&lt;/h3&gt;&lt;p&gt;VS Code에서 &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; (맥: &lt;code&gt;Cmd + Shift + P&lt;/code&gt;)를 눌러 명령 팔레트를 엽니다. **&lt;code&gt;Preferences: Open User Settings (JSON)&lt;/code&gt;**을 입력하여 설정 파일을 엽니다.&lt;/p&gt;
&lt;h3 id="2-설정-코드-추가"&gt;2. 설정 코드 추가
&lt;/h3&gt;&lt;p&gt;열린 &lt;code&gt;settings.json&lt;/code&gt; 파일의 중괄호 &lt;code&gt;{}&lt;/code&gt; 안에 아래 내용을 추가합니다. 만약 &lt;code&gt;github.copilot&lt;/code&gt; 관련 설정이 이미 있다면 병합하세요.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;github.copilot.enableInlineCompletions&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;github.copilot.advanced&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;inlineSuggest.count&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// [핵심 수정 사항] 커밋 메시지에 Co-Authored-by 자동 삽입 비활성화
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;github.copilot.inlineSuggest.enable&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 또는 Copilot 자체의 공동 저자 표시 기능 끄기 (최신 버전 지원 시)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;github.copilot.commitMessage&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;off&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;참고:&lt;/strong&gt; VS Code와 Copilot 확장 프로그램의 버전에 따라 옵션 키가 다를 수 있습니다. 가장 일반적인 방법은 커밋을 생성하는 순간 Copilot과의 통신을 최소화하는 것입니다.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="3-대체-방법-git-훅hook-사용-선택-사항"&gt;3. 대체 방법: Git 훅(Hook) 사용 (선택 사항)
&lt;/h3&gt;&lt;p&gt;만약 설정만으로 해결되지 않는다면, Git의 &lt;code&gt;prepare-commit-msg&lt;/code&gt; 훅을 사용하여 강제로 해당 줄을 제거할 수 있습니다. 프로젝트 루트의 &lt;code&gt;.git/hooks/prepare-commit-msg&lt;/code&gt; 파일(확장자 없음)을 생성하거나 수정합니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 커밋 메시지 파일 경로 가져오기&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COMMIT_MSG_FILE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 파일 내용에서 &amp;#39;Co-Authored-by: GitHub Copilot&amp;#39; 줄 제거&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# macOS/BSD sed (macOS 기본)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sed -i &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/Co-Authored-by: GitHub Copilot/d&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$COMMIT_MSG_FILE&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Linux sed (WSL, Linux 서버 등)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# sed -i &amp;#39;/Co-Authored-by: GitHub Copilot/d&amp;#39; &amp;#34;$COMMIT_MSG_FILE&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 스크립트를 저장하고 실행 권한(&lt;code&gt;chmod +x .git/hooks/prepare-commit-msg&lt;/code&gt;)을 부여하면, 앞으로 커밋을 생성할 때마다 자동으로 해당 라인이 삭제됩니다.&lt;/p&gt;
&lt;h2 id="검증"&gt;검증
&lt;/h2&gt;&lt;p&gt;이제 다시 소스 제어 패널로 돌아가서 간단한 수정(예: 주석 추가) 후 커밋을 생성해 보세요.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 커밋 로그 확인&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git log -1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;결과에 &lt;code&gt;Co-Authored-by: GitHub Copilot&lt;/code&gt; 문구가 보이지 않는다면 성공입니다.&lt;/p&gt;
&lt;h2 id="요약"&gt;요약
&lt;/h2&gt;&lt;p&gt;개발 도구가 편리함을 제공하는 것은 좋지만, 커밋 메시지는 개발자의 작업 내역을 대변하는 중요한 기록입니다. 불필요한 텍스트가 섞이는 것을 방지하기 위해 위 설정을 적용하여 더 깔끔한 Git 관리 환경을 만들어 보시기 바랍니다.&lt;/p&gt;</description></item><item><title>블로그 AI 자동 댓글 시스템 구축기 (3/3) — 배포와 트러블슈팅</title><link>https://blog.agentthread.dev/ko/post/ai-auto-comment-system-part3-deployment/</link><pubDate>Sun, 03 May 2026 01:20:00 +0900</pubDate><guid>https://blog.agentthread.dev/ko/post/ai-auto-comment-system-part3-deployment/</guid><description>&lt;h2 id="개요"&gt;개요
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://blog.agentthread.dev/ko/post/ai-auto-comment-system-part1-architecture/" &gt;1부&lt;/a&gt;에서 아키텍처와 구현을, &lt;a class="link" href="https://blog.agentthread.dev/ko/post/ai-auto-comment-system-part2-security/" &gt;2부&lt;/a&gt;에서 보안 강화를 다뤘습니다. 이번 3부에서는 실제 OCI ARM 서버에 배포하고 겪은 트러블슈팅 과정을 기록합니다.&lt;/p&gt;
&lt;p&gt;특히 &lt;strong&gt;GITHUB_TOKEN이 로드되지 않는 문제&lt;/strong&gt;를 4단계에 걸쳐 추적하고 해결한 실제 디버깅 과정을 상세히 공유합니다. &amp;ldquo;설정했는데 왜 안 되지?&amp;ldquo;라는 상황에서 어떻게 원인을 좁혀나갔는지가 이 글의 핵심입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="인프라-구성"&gt;인프라 구성
&lt;/h2&gt;&lt;h3 id="서버-구성"&gt;서버 구성
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;서버&lt;/th&gt;
					&lt;th&gt;역할&lt;/th&gt;
					&lt;th&gt;스펙&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;ec1 (x86)&lt;/td&gt;
					&lt;td&gt;웹 서버 (nginx, Hugo 블로그)&lt;/td&gt;
					&lt;td&gt;OCI&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;arm1 (ARM)&lt;/td&gt;
					&lt;td&gt;워커 서버 (Flask, Claude Code)&lt;/td&gt;
					&lt;td&gt;OCI ARM&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;블로그는 ec1에서 Hugo로 빌드·서빙하고, AI 댓글 워커는 arm1에서 실행합니다. GitHub Webhook은 arm1으로 직접 전달됩니다.&lt;/p&gt;
&lt;h3 id="워커-서버-디렉토리-구조"&gt;워커 서버 디렉토리 구조
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;www&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker&lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 애플리케이션&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;├──&lt;/span&gt; scripts&lt;span style="color:#f92672"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;│&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;└──&lt;/span&gt; auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker&lt;span style="color:#f92672"&gt;.&lt;/span&gt;py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;├──&lt;/span&gt; deploy&lt;span style="color:#f92672"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;│&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;└──&lt;/span&gt; auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker&lt;span style="color:#f92672"&gt;.&lt;/span&gt;service
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;├──&lt;/span&gt; venv&lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Python 가상환경&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;└──&lt;/span&gt; logs&lt;span style="color:#f92672"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;etc&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker&lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 인증 정보&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;├──&lt;/span&gt; github&lt;span style="color:#f92672"&gt;-&lt;/span&gt;token &lt;span style="color:#75715e"&gt;# 640, ubuntu:ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;└──&lt;/span&gt; credentials&lt;span style="color:#f92672"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;└──&lt;/span&gt; webhook&lt;span style="color:#f92672"&gt;-&lt;/span&gt;secret &lt;span style="color:#75715e"&gt;# 600, ubuntu:ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;home&lt;span style="color:#f92672"&gt;/&lt;/span&gt;ubuntu&lt;span style="color:#f92672"&gt;/.&lt;/span&gt;local&lt;span style="color:#f92672"&gt;/&lt;/span&gt;bin&lt;span style="color:#f92672"&gt;/&lt;/span&gt;claude &lt;span style="color:#75715e"&gt;# Claude Code CLI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="systemd-서비스-구성"&gt;systemd 서비스 구성
&lt;/h2&gt;&lt;h3 id="서비스-파일"&gt;서비스 파일
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Description&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;Auto Comment Worker for Blog&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;After&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;network.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;simple&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;User&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;WorkingDirectory&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/var/www/auto-comment-worker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;PORT=8081&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;CLAUDE_CODE_PATH=/home/ubuntu/.local/bin/claude&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;BLOG_OWNERS=yarang&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;GITHUB_TOKEN_FILE=/etc/auto-comment-worker/github-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;GITHUB_WEBHOOK_SECRET_FILE=/etc/auto-comment-worker/credentials/webhook-secret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ExecStart&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/var/www/auto-comment-worker/venv/bin/python /var/www/auto-comment-worker/scripts/auto-comment-worker.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Restart&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;RestartSec&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;StandardOutput&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;journal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;StandardError&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;journal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;SyslogIdentifier&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;auto-comment-worker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Security&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;NoNewPrivileges&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;PrivateTmp&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ProtectSystem&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;strict&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ProtectHome&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ReadWritePaths&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/var/www/auto-comment-worker /var/log/auto-comment-worker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ReadOnlyPaths&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 리소스 제한&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;MemoryMax&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;512M&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;CPUQuota&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;50%&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;TasksMax&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;WantedBy&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;multi-user.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="주요-설정-해설"&gt;주요 설정 해설
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Type=simple&lt;/code&gt;&lt;/strong&gt;: Flask 워커는 포그라운드에서 실행되므로 &lt;code&gt;simple&lt;/code&gt;이 적합합니다. &lt;code&gt;forking&lt;/code&gt;은 데몬화하는 프로세스에 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;User=ubuntu&lt;/code&gt;&lt;/strong&gt;: 전용 서비스 계정을 만들 수도 있지만, Claude Code CLI가 &lt;code&gt;ubuntu&lt;/code&gt; 사용자의 홈 디렉토리 설정에 의존하므로 &lt;code&gt;ubuntu&lt;/code&gt;로 실행합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ProtectHome=false&lt;/code&gt;&lt;/strong&gt;: 보통은 &lt;code&gt;true&lt;/code&gt;로 설정하지만, Claude Code가 &lt;code&gt;~/.agent_forge_for_zai.json&lt;/code&gt; 설정 파일을 필요로 하므로 홈 디렉토리 접근을 허용합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ReadOnlyPaths=&lt;/code&gt;&lt;/strong&gt; (빈 값): 초기에 &lt;code&gt;/etc/auto-comment-worker&lt;/code&gt;를 지정했지만, &lt;code&gt;ProtectSystem=strict&lt;/code&gt;와 충돌하여 비워두었습니다.&lt;/p&gt;
&lt;h3 id="서비스-관리-명령어"&gt;서비스 관리 명령어
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 서비스 파일 복사&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo cp deploy/auto-comment-worker.service /etc/systemd/system/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 서비스 등록 및 시작&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl enable auto-comment-worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl start auto-comment-worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 상태 확인&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl status auto-comment-worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 로그 확인 (실시간)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo journalctl -u auto-comment-worker -f
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 최근 로그 확인&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo journalctl -u auto-comment-worker --since &lt;span style="color:#e6db74"&gt;&amp;#34;10 minutes ago&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="nginx-리버스-프록시"&gt;nginx 리버스 프록시
&lt;/h2&gt;&lt;h3 id="webhook-엔드포인트-설정"&gt;Webhook 엔드포인트 설정
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;server&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;listen&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;443&lt;/span&gt; &lt;span style="color:#e6db74"&gt;ssl&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server_name&lt;/span&gt; &lt;span style="color:#e6db74"&gt;your-domain.com&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# SSL 설정
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ssl_certificate&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/etc/letsencrypt/live/your-domain.com/fullchain.pem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ssl_certificate_key&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/etc/letsencrypt/live/your-domain.com/privkey.pem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Webhook 프록시
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;location&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/webhook&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_pass&lt;/span&gt; &lt;span style="color:#e6db74"&gt;http://localhost:8081&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;Host&lt;/span&gt; $host;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Real-IP&lt;/span&gt; $remote_addr;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Forwarded-For&lt;/span&gt; $proxy_add_x_forwarded_for;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Forwarded-Proto&lt;/span&gt; $scheme;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# GitHub Webhook 시그니처 헤더 포워딩 (필수!)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Hub-Signature-256&lt;/span&gt; $http_x_hub_signature_256;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 타임아웃 (Claude Code 응답 대기)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_read_timeout&lt;/span&gt; &lt;span style="color:#e6db74"&gt;120s&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_connect_timeout&lt;/span&gt; &lt;span style="color:#e6db74"&gt;10s&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Health check
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;location&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/health&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_pass&lt;/span&gt; &lt;span style="color:#e6db74"&gt;http://localhost:8081&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;proxy_read_timeout 120s&lt;/code&gt;는 Claude Code CLI가 AI 응답을 생성하는 데 최대 60초가 걸릴 수 있으므로 여유 있게 설정합니다. GitHub Webhook의 기본 타임아웃은 10초이므로, 실제로는 비동기 처리를 고려할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="배포-과정"&gt;배포 과정
&lt;/h2&gt;&lt;h3 id="수동-배포-rsync-실패-후"&gt;수동 배포 (rsync 실패 후)
&lt;/h3&gt;&lt;p&gt;초기에는 rsync로 배포를 시도했지만, 서버에 대상 디렉토리가 없어 실패했습니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rsync: [Receiver] mkdir &lt;span style="color:#e6db74"&gt;&amp;#34;/var/www/auto-comment-worker/scripts&amp;#34;&lt;/span&gt; failed:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;No such file &lt;span style="color:#f92672"&gt;or&lt;/span&gt; directory
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;대안으로 scp 기반 수동 배포를 진행했습니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1. 서버에 디렉토리 생성&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo mkdir -p /var/www/auto-comment-worker/scripts&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo chown -R ubuntu:ubuntu /var/www/auto-comment-worker&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 2. 파일 전송&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scp scripts/auto-comment-worker.py ubuntu@arm1:/var/www/auto-comment-worker/scripts/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scp deploy/auto-comment-worker.service ubuntu@arm1:/tmp/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 3. 서비스 파일 설치&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo cp /tmp/auto-comment-worker.service /etc/systemd/system/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 4. Python 가상환경 설정&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;cd /var/www/auto-comment-worker &amp;amp;&amp;amp; python3 -m venv venv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;cd /var/www/auto-comment-worker &amp;amp;&amp;amp; venv/bin/pip install flask flask-limiter marshmallow requests&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 5. 인증 파일 설정&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo mkdir -p /etc/auto-comment-worker/credentials&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 토큰 파일은 서버에서 직접 생성 (scp로 전송하지 않음)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 6. 서비스 시작&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo systemctl daemon-reload &amp;amp;&amp;amp; sudo systemctl enable --now auto-comment-worker&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="heredoc-변수-확장-함정"&gt;heredoc 변수 확장 함정
&lt;/h3&gt;&lt;p&gt;설치 스크립트를 heredoc으로 작성할 때 흔한 실수:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 작은따옴표: 변수가 확장되지 않음!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;lt;&amp;lt; &amp;#39;ENDSSH&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;echo $CREDENTIALS_DIR # 빈 문자열 출력
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;ENDSSH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 큰따옴표 없음: 변수가 로컬에서 확장됨&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;lt;&amp;lt; ENDSSH
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;echo $CREDENTIALS_DIR # 로컬 변수값으로 확장
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;ENDSSH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 문제를 피하기 위해 스크립트 대신 명령어를 개별 실행하는 방식으로 전환했습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="트러블슈팅-github_token-로드-실패"&gt;트러블슈팅: GITHUB_TOKEN 로드 실패
&lt;/h2&gt;&lt;p&gt;이 시스템을 배포하면서 가장 많은 시간을 소비한 문제입니다. 댓글 Webhook이 도착하면 다음 에러가 반복되었습니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;INFO:__main__:GITHUB_TOKEN configured: False
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;INFO:__main__:GitHub API response status: 401
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ERROR:__main__:Failed to get Discussion GraphQL ID
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;원인을 추적하는 과정을 단계별로 기록합니다.&lt;/p&gt;
&lt;h3 id="1단계-loadcredential-경로-문제"&gt;1단계: LoadCredential 경로 문제
&lt;/h3&gt;&lt;p&gt;처음에는 systemd의 &lt;code&gt;LoadCredential&lt;/code&gt; 디렉티브를 사용했습니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;LoadCredential&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;github-token:/etc/auto-comment-worker/github-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;GITHUB_TOKEN_FILE=%d/github-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;%d&lt;/code&gt;는 credentials 디렉토리 경로로 대체되는 systemd 특수 변수입니다. 하지만 이 변수가 의도대로 해석되지 않아 토큰 파일 경로가 잘못 설정되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;해결&lt;/strong&gt;: &lt;code&gt;LoadCredential&lt;/code&gt; 대신 절대 경로를 직접 지정했습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;GITHUB_TOKEN_FILE=/etc/auto-comment-worker/github-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2단계-파일-소유권-문제"&gt;2단계: 파일 소유권 문제
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;GITHUB_TOKEN configured: False&lt;/code&gt;가 여전히 나타났습니다. 파일을 확인해보니:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ls -la /etc/auto-comment-worker/github-token
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw------- &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; root root &lt;span style="color:#ae81ff"&gt;93&lt;/span&gt; May &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; 01:10 github-token
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;파일 소유자가 &lt;code&gt;root&lt;/code&gt;이고 권한이 &lt;code&gt;600&lt;/code&gt;이므로, &lt;code&gt;ubuntu&lt;/code&gt; 사용자로 실행되는 서비스는 이 파일을 읽을 수 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;해결&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo chown ubuntu:ubuntu /etc/auto-comment-worker/github-token
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo chmod &lt;span style="color:#ae81ff"&gt;640&lt;/span&gt; /etc/auto-comment-worker/github-token
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3단계-readonlypaths-충돌"&gt;3단계: ReadOnlyPaths 충돌
&lt;/h3&gt;&lt;p&gt;소유권을 변경한 후에도 &lt;code&gt;GITHUB_TOKEN configured: False&lt;/code&gt;가 계속되었습니다. systemd 서비스 파일에 있던 &lt;code&gt;ReadOnlyPaths&lt;/code&gt; 설정이 원인이었습니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 이 설정이 파일 읽기를 차단함&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ReadOnlyPaths&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/etc/auto-comment-worker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;ProtectSystem=strict&lt;/code&gt;가 이미 전체 파일시스템을 읽기 전용으로 마운트합니다. 여기에 &lt;code&gt;ReadOnlyPaths&lt;/code&gt;를 추가하면 일부 환경에서 마운트 네임스페이스 충돌이 발생할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;해결&lt;/strong&gt;: &lt;code&gt;ReadOnlyPaths&lt;/code&gt;를 빈 값으로 변경했습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ReadOnlyPaths&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="4단계-python-파일-권한-검증-코드-근본-원인"&gt;4단계: Python 파일 권한 검증 코드 (근본 원인)
&lt;/h3&gt;&lt;p&gt;이전 3단계를 모두 해결한 후에도 토큰이 로드되지 않았습니다. 마지막 원인은 Python 코드의 지나치게 엄격한 파일 권한 검증이었습니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 파일 권한 640 → 그룹 읽기 비트(0o040)가 설정됨 → 거부!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; st&lt;span style="color:#f92672"&gt;.&lt;/span&gt;st_mode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (stat&lt;span style="color:#f92672"&gt;.&lt;/span&gt;S_IRWXO &lt;span style="color:#f92672"&gt;|&lt;/span&gt; stat&lt;span style="color:#f92672"&gt;.&lt;/span&gt;S_IRWXG):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;PermissionError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Token file must be 600 or 400&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;2단계에서 &lt;code&gt;chmod 640&lt;/code&gt;으로 변경했기 때문에, 그룹 읽기 비트가 설정되어 이 검증에 걸렸습니다. 하지만 에러 메시지가 로그에 나타나지 않아 발견이 늦었습니다 — &lt;code&gt;PermissionError&lt;/code&gt;가 모듈 임포트 시점에 발생하여 서비스 시작 자체를 방해했기 때문입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;해결&lt;/strong&gt;: 2부에서 설명한 것처럼 &lt;code&gt;stat.S_IWOTH&lt;/code&gt;만 검사하도록 수정했습니다.&lt;/p&gt;
&lt;h3 id="디버깅-로그의-중요성"&gt;디버깅 로그의 중요성
&lt;/h3&gt;&lt;p&gt;이 문제를 추적하기 위해 추가한 디버깅 로그:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;GITHUB_TOKEN configured: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;bool(GITHUB_TOKEN)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;GitHub API response status: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;GitHub API response body: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;text[:&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이 로그들이 없었다면 원인을 파악하는 데 훨씬 더 오래 걸렸을 것입니다. 인증 관련 코드에는 항상 토큰 로드 성공/실패 여부와 API 응답 상태를 로깅해야 합니다.&lt;/p&gt;
&lt;h3 id="디버깅-흐름-요약"&gt;디버깅 흐름 요약
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[1] LoadCredential %d 미해석 → 절대 경로로 변경
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓ (여전히 실패)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[2] 파일 소유자 root:root → ubuntu:ubuntu로 변경
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓ (여전히 실패)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[3] ReadOnlyPaths 충돌 → 제거
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓ (여전히 실패)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[4] Python 권한 검증 S_IRWXG → S_IWOTH로 완화
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [해결!]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;4단계에 걸친 이 디버깅에서 얻은 교훈:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;한 번에 하나씩 변경하고 확인&lt;/strong&gt;: 여러 설정을 동시에 바꾸면 어떤 것이 원인인지 알 수 없음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;로그를 믿되, 로그가 없는 곳을 의심&lt;/strong&gt;: 모듈 로드 시점의 예외는 일반 로그에 안 나타날 수 있음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;보안 검증 코드도 버그의 원인&lt;/strong&gt;: 보안 코드가 정상 동작을 차단하는 경우 — 보안과 운영의 균형&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="헬스-체크"&gt;헬스 체크
&lt;/h2&gt;&lt;p&gt;서비스가 정상 동작하는지 확인하는 헬스 체크 엔드포인트:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/health&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;health&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;헬스 체크&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; jsonify({&lt;span style="color:#e6db74"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;healthy&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;모니터링 시스템에서 주기적으로 &lt;code&gt;/health&lt;/code&gt;를 호출하여 서비스 상태를 확인합니다:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s http://localhost:8081/health
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# {&amp;#34;status&amp;#34;: &amp;#34;healthy&amp;#34;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="향후-개선-사항"&gt;향후 개선 사항
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;비동기 처리&lt;/strong&gt;: GitHub Webhook 타임아웃(10초) 내에 응답하기 위해 Celery나 Redis Queue로 AI 응답 생성을 비동기화&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;재시도 로직&lt;/strong&gt;: GitHub API 호출 실패 시 지수 백오프 재시도&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;모니터링 대시보드&lt;/strong&gt;: Prometheus + Grafana로 응답 시간, 성공률, 에러율 모니터링&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자동 배포&lt;/strong&gt;: GitHub Actions로 코드 변경 시 자동 배포 파이프라인 구축&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;테스트&lt;/strong&gt;: Webhook 페이로드 모킹으로 통합 테스트 작성&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="마무리"&gt;마무리
&lt;/h2&gt;&lt;p&gt;3부에 걸쳐 블로그 AI 자동 댓글 시스템의 전체 구축 과정을 기록했습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1부&lt;/strong&gt;: giscus → GitHub Webhook → Flask → Claude Code → GraphQL의 전체 아키텍처&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2부&lt;/strong&gt;: 파일 기반 인증, HMAC 검증, 입력 sanitization, systemd 보안&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3부&lt;/strong&gt;: 실제 배포, nginx 프록시, 4단계 디버깅 과정&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 시스템의 가장 큰 가치는 &lt;strong&gt;블로그 독자와의 소통을 자동화&lt;/strong&gt;한다는 점입니다. 블로그 운영자가 모든 댓글에 즉시 응답하기 어렵지만, AI 어시스턴트가 1차 응답을 제공하여 독자 경험을 개선할 수 있습니다.&lt;/p&gt;
&lt;p&gt;코드 전체는 &lt;a class="link" href="https://github.com/yarang/blogs" target="_blank" rel="noopener"
 &gt;GitHub 리포지토리&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;이 글은 AgentForge 블로그 자동 댓글 시스템 시리즈의 3부(마지막)입니다.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Claude Code Discord MCP Plugin - asdf bun 경로 문제 해결</title><link>https://blog.agentthread.dev/ko/post/claude-code-discord-mcp-plugin-asdf-bun-%EA%B2%BD%EB%A1%9C-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0/</link><pubDate>Thu, 26 Mar 2026 00:00:00 +0900</pubDate><guid>https://blog.agentthread.dev/ko/post/claude-code-discord-mcp-plugin-asdf-bun-%EA%B2%BD%EB%A1%9C-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0/</guid><description>&lt;h2 id="문제-상황"&gt;문제 상황
&lt;/h2&gt;&lt;p&gt;Claude Code CLI에서 Discord 채널 연결을 위해 &lt;code&gt;discord&lt;/code&gt; 플러그인을 설치했는데 다음과 같은 에러가 발생했습니다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; discord Plugin · claude-plugins-official · √ enabled
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; └ discord MCP · × failed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;플러그인은 활성화되었지만, MCP 서버는 실패 상태였습니다.&lt;/p&gt;
&lt;h2 id="원인"&gt;원인
&lt;/h2&gt;&lt;p&gt;이 문제는 &lt;strong&gt;asdf로 설치한 bun&lt;/strong&gt;을 사용할 때 발생합니다. Claude Code CLI는 MCP 서버 실행을 위해 &lt;code&gt;bun&lt;/code&gt; 명령어를 찾지만, asdf는 셸 래퍼를 사용하므로 시스템 전역 경로에서 실제 바이너리를 찾지 못합니다.&lt;/p&gt;
&lt;h2 id="해결-방법"&gt;해결 방법
&lt;/h2&gt;&lt;p&gt;asdf로 설치한 bun의 &lt;strong&gt;절대 경로&lt;/strong&gt;를 직접 입력하여 해결했습니다.&lt;/p&gt;
&lt;h3 id="asdf로-설치한-bun-절대-경로-찾기"&gt;asdf로 설치한 bun 절대 경로 찾기
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;which bun
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;또는:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;asdf where bun
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;출력 예시:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/yarang/.asdf/installs/bun/1.x.x/bin/bun
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="설정-적용"&gt;설정 적용
&lt;/h3&gt;&lt;p&gt;이 절대 경로를 Discord MCP 설정의 실행 경로로 지정하면 문제가 해결됩니다.&lt;/p&gt;
&lt;h2 id="핵심-정리"&gt;핵심 정리
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;상황&lt;/th&gt;
					&lt;th&gt;해결 방법&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;시스템 전역 설치된 bun&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;bun&lt;/code&gt; 명령어 사용&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;asdf로 설치한 bun&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;절대 경로&lt;/strong&gt; 직접 입력&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;asdf를 사용하는 경우, 셸 래퍼가 아닌 실제 바이너리 경로를 지정해야 MCP 서버가 정상적으로 실행됩니다.&lt;/p&gt;
&lt;h2 id="참고-자료"&gt;참고 자료
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/anthropics/discord-plugin" target="_blank" rel="noopener"
 &gt;Claude Code Discord Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://asdf-vm.com/" target="_blank" rel="noopener"
 &gt;asdf 버전 관리자&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>