<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>개발 일지 on Simon의 AI 시대에 살아남는 법</title><link>https://mhub.uk/categories/%EA%B0%9C%EB%B0%9C-%EC%9D%BC%EC%A7%80/</link><description>Recent content in 개발 일지 on Simon의 AI 시대에 살아남는 법</description><generator>Hugo</generator><language>ko-KR</language><copyright>2026 mhub. 이 블로그의 모든 글·그림과 전자책(그림으로 배우는 시리즈)의 저작권은 mhub에 있으며, 저작권자의 허락 없는 무단 복제·배포·전재를 금지합니다. 이용 허락이나 오류 제보는 문의하기로 보내 주세요.</copyright><lastBuildDate>Wed, 04 Nov 2026 06:30:00 +0900</lastBuildDate><atom:link href="https://mhub.uk/categories/%EA%B0%9C%EB%B0%9C-%EC%9D%BC%EC%A7%80/index.xml" rel="self" type="application/rss+xml"/><item><title>전부 통과시키는 Worker에서 시작하기 — 패스스루로 무중단 전환, www 이동, 404, 폰 폴백</title><link>https://mhub.uk/posts/cf-03-passthrough-worker-first/</link><pubDate>Wed, 04 Nov 2026 06:30:00 +0900</pubDate><guid>https://mhub.uk/posts/cf-03-passthrough-worker-first/</guid><description>&lt;p&gt;도메인 앞에 Worker를 세우는 순간 모든 방문자가 그 코드를 지나간다. 그래서 첫 배포는 아무것도 하지 않는 Worker였다. 들어온 요청을 그대로 폰으로 넘기기만 하는 코드다. 이 글은 그 &amp;ldquo;통과만 하는 Worker&amp;quot;에서 시작해 하나씩 역할을 빼앗아 온 과정이다.&lt;/p&gt;
&lt;h2 id="1단계-fetchrequest-한-줄"&gt;1단계: fetch(request) 한 줄&lt;/h2&gt;
&lt;p&gt;DNS는 그대로 폰의 터널을 가리키고 있었다. 이 상태에서 라우트 &lt;code&gt;mhub.uk/*&lt;/code&gt;를 Worker에 붙이면, Worker 안에서 &lt;code&gt;fetch(request)&lt;/code&gt;를 호출했을 때 Cloudflare가 원래 목적지(폰)로 요청을 보낸다. 즉 Worker는 투명한 중계기가 된다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 일단 전부 폰으로
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;이걸 먼저 배포한 이유는 &amp;ldquo;라우트를 붙였을 때 뭔가 깨지는가&amp;quot;를 서비스 중단 없이 확인하기 위해서였다. 깨지면 라우트만 떼면 그만이다. 아무 문제 없이 돌아가는 것을 보고 다음으로 갔다.&lt;/p&gt;</description></item><item><title>부팅 스크립트 한 줄이 서비스 전체를 막다 — 첫 워치독의 오탐까지</title><link>https://mhub.uk/posts/ops-01-boot-script-one-line-and-first-watchdog/</link><pubDate>Tue, 03 Nov 2026 10:05:00 +0900</pubDate><guid>https://mhub.uk/posts/ops-01-boot-script-one-line-and-first-watchdog/</guid><description>&lt;p&gt;이 글은 &amp;lsquo;폰 서버 안정화 일지&amp;rsquo; 시리즈의 첫 번째다. 재부팅만 하면 절반쯤의 서비스가 안 올라오던 시절, 원인이 부팅 스크립트의 한 줄이었다는 이야기와, 그걸 고치려고 만든 첫 워치독이 거꾸로 건강한 봇을 매번 죽이던 이야기다.&lt;/p&gt;
&lt;h2 id="증상-재부팅-뒤-claude-세션이-안-뜬다"&gt;증상: 재부팅 뒤 Claude 세션이 안 뜬다&lt;/h2&gt;
&lt;p&gt;폰을 재부팅하면 카카오톡 봇은 살아나는데 Claude Code 세션과 터널 몇 개가 안 올라오는 날이 반복됐다. 재부팅 직후 tmux 세션 목록을 보면 &lt;code&gt;iris&lt;/code&gt;만 있고 &lt;code&gt;claude&lt;/code&gt;, &lt;code&gt;cloudflare&lt;/code&gt;, &lt;code&gt;blogadmin&lt;/code&gt; 같은 세션이 없었다. 하루는 수동으로 띄우고 넘어갔지만, 같은 일이 세 번째 반복되자 부팅 스크립트를 처음부터 끝까지 읽었다.&lt;/p&gt;</description></item><item><title>Workers Builds로 Hugo 빌드하기 — 버전 고정, 시간대, previews 블록, 그리고 workers.dev가 꺼지는 함정</title><link>https://mhub.uk/posts/cf-02-workers-builds-hugo/</link><pubDate>Tue, 03 Nov 2026 00:10:00 +0900</pubDate><guid>https://mhub.uk/posts/cf-02-workers-builds-hugo/</guid><description>&lt;p&gt;Cloudflare 이전의 첫 단계는 &amp;ldquo;GitHub에 커밋하면 Cloudflare가 알아서 Hugo를 돌려 배포한다&amp;quot;를 만드는 것이었다. Workers Builds는 저장소를 연결하면 빌드 명령과 배포 명령을 실행해 준다. 간단해 보였지만, 폰에서 만들던 결과물과 똑같이 나오게 하려면 네 가지를 못 박아야 했다.&lt;/p&gt;
&lt;h2 id="빌드-명령은-스크립트-하나로"&gt;빌드 명령은 스크립트 하나로&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;wrangler.jsonc&lt;/code&gt;의 &lt;code&gt;build.command&lt;/code&gt;에 &lt;code&gt;bash scripts/cf-build.sh&lt;/code&gt;를 넣었다. Cloudflare 빌드 환경에는 Hugo가 없으므로 스크립트가 직접 내려받는다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MHUB_HUGO_VERSION&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.154.5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL -o hugo.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;https://github.com/gohugoio/hugo/releases/download/v&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PIN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/hugo_extended_&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PIN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_linux-amd64.tar.gz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tar -xz -f hugo.tar.gz hugo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;TZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Asia/Seoul
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo --minify
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;버전 변수 이름을 &lt;code&gt;HUGO_VERSION&lt;/code&gt;이 아니라 &lt;code&gt;MHUB_HUGO_VERSION&lt;/code&gt;으로 한 데는 이유가 있다. Cloudflare 대시보드의 빌드 변수에 &lt;code&gt;HUGO_VERSION&lt;/code&gt;이 있으면 플랫폼이 그 값을 다른 의미로 읽을 수 있어서, 내 스크립트만 읽는 전용 이름을 쓰는 편이 안전했다.&lt;/p&gt;</description></item><item><title>폰 호스팅을 끝내기로 한 날 — "폰 없이도 블로그 전부"라는 원칙과 4단계 계획</title><link>https://mhub.uk/posts/cf-01-why-leave-the-phone/</link><pubDate>Mon, 02 Nov 2026 20:20:00 +0900</pubDate><guid>https://mhub.uk/posts/cf-01-why-leave-the-phone/</guid><description>&lt;p&gt;이 블로그는 한동안 루팅한 갤럭시 S9 한 대가 전부 떠받치고 있었다. Hugo로 만든 정적 파일을 폰 안의 Caddy가 내보내고, 글쓰기 화면과 조회수·댓글 API는 폰 안의 Node 서버가 처리하고, 바깥과는 Cloudflare Tunnel로 이어져 있었다. 잘 돌았지만 &amp;ldquo;폰이 꺼지면 블로그도 꺼진다&amp;quot;는 사실이 늘 마음에 걸렸다. 2026년 10월 10일, 이 구조를 끝내기로 했다. 이 글은 그 결정의 이유와 계획이고, 이어지는 시리즈는 실제로 옮기면서 겪은 일들이다.&lt;/p&gt;
&lt;h2 id="왜-지금이었나"&gt;왜 지금이었나&lt;/h2&gt;
&lt;p&gt;계기는 두 가지였다. 하나는 폰 쪽 감시 스크립트가 블로그 터널을 닷새 동안 17번이나 재시작한 기록을 본 것이다. 서비스는 대체로 살아 있었지만, 사람이 없을 때 폰 혼자 버티는 구조는 언제든 조용히 멈출 수 있었다. 다른 하나는 내가 AI 코딩 세션을 길게 쓰는 기간이 끝나간다는 점이었다. 그 전에 블로그를 &amp;ldquo;혼자서도 운영할 수 있는 모양&amp;quot;으로 만들어 두고 싶었다. 폰 안의 tmux 세션과 크론과 셸 스크립트를 유지보수하는 것보다, 관리 화면에서 글을 쓰고 GitHub에 저장되고 알아서 배포되는 쪽이 훨씬 단순하다.&lt;/p&gt;</description></item><item><title>2026년 10월 기준, 내 폰 서버 전체 지도 — 무엇이 어디서 돌아가는가</title><link>https://mhub.uk/posts/it-17-phone-server-full-map-2026-10/</link><pubDate>Mon, 02 Nov 2026 07:30:00 +0900</pubDate><guid>https://mhub.uk/posts/it-17-phone-server-full-map-2026-10/</guid><description>&lt;p&gt;루팅한 갤럭시 S9 한 대가 내 홈 서버다. 처음엔 카카오톡 봇 하나 돌리려고 시작했는데, 어느새 블로그와 자동화 루틴, 데이터 수집기까지 전부 이 폰 위에 올라가 있다. 지난 한 달 동안 이 폰에서 겪은 장애와 수정 기록을 시리즈로 남기기 전에, 먼저 &amp;ldquo;지금 이 폰에서 무엇이 어디서 돌아가는가&amp;quot;를 한 장의 지도로 정리해 둔다. 이 글은 이어질 시리즈들의 서문이자 색인이다.&lt;/p&gt;
&lt;h2 id="두-겹의-리눅스"&gt;두 겹의 리눅스&lt;/h2&gt;
&lt;p&gt;폰 안에는 리눅스가 두 겹으로 있다. 바깥은 Termux다. 안드로이드 위에 리눅스 유저랜드를 올려주는 앱이고, tmux 서버와 cloudflared 터널, 그리고 카카오톡 봇처럼 안드로이드와 직접 닿아야 하는 것들이 여기서 돈다. 안쪽은 proot-distro로 올린 Ubuntu 컨테이너다. 블로그 관리 서버, 데이터 수집기, 자동화 루틴, 그리고 이 모든 것을 관리하는 Claude Code 원격 세션이 여기서 돈다.&lt;/p&gt;</description></item><item><title>유투브 영상을 스크립트로 — 개인용 자동화 파이프라인 설계기</title><link>https://mhub.uk/posts/yt-01-youtube-script-pipeline-plan/</link><pubDate>Sun, 01 Nov 2026 21:35:00 +0900</pubDate><guid>https://mhub.uk/posts/yt-01-youtube-script-pipeline-plan/</guid><description>&lt;p&gt;매일 아침 유투브로 전일 미국 시장 정보를 습득하는 편인데 영상을 처음부터 보려면 너무 많은 시간이 소비된다. 그리고 이러한 영상들은 대부분 스크립트를 제공하지 않는다. 그래서 &lt;strong&gt;스크립트를 제공하지 않는 영상의 스크립트를 추출해서 AI를 활용해 요약본을 만들 수 있게&lt;/strong&gt; 만드는 개인용 파이프라인을 설계했다. 아직 구상 단계지만, 각 단계를 어떤 도구로 어떻게 연결할지는 구체적으로 정리해뒀다.&lt;/p&gt;
&lt;h2 id="1단계-오디오-추출--ai-없이-로컬에서-가능"&gt;1단계: 오디오 추출 — AI 없이 로컬에서 가능&lt;/h2&gt;
&lt;p&gt;가장 먼저 필요한 건 영상에서 오디오만 뽑아내는 작업이다. 이건 AI가 필요 없는 단순 작업이라 로컬에서 처리한다.&lt;/p&gt;</description></item><item><title>렌터카 미납 통행료, 납부자 변경 기한 이전에 변경 못 하면 받기 힘들다 — B2B SaaS 아이디어 실현 가능성 검토기</title><link>https://mhub.uk/posts/biz-01-rental-car-toll-saas-feasibility/</link><pubDate>Sat, 31 Oct 2026 12:00:00 +0900</pubDate><guid>https://mhub.uk/posts/biz-01-rental-car-toll-saas-feasibility/</guid><description>&lt;p&gt;렌터카 업계 지인에게 우연히 들은 이야기 하나가 아이디어의 출발점이 됐다. 개인들의 경우 미납된 고속도로 통행료나 과태료가 있을 경우 개인 차량에 이 비용이 귀속되니 문제가 없지만 렌터카 회사는 이야기가 다르다. 미납 통행료나 과태료 발생 사실을 일찍 확인한다면 각 통행료 징수 주체와 과태료 부과 관공서에 실제 차량을 빌려간 손님으로 납부 대상자 변경을 하면 된다. 문제는 이 변경 신청 기간이 짧다는 것이다. 통상 고지서는 미납 통행료, 과태료가 발생한 시점보다 2~3개월 뒤 통지된다. 당시 차량을 렌트했던 고객이 연락이 잘되고 통행료/과태료 부과 사실을 수긍하여 정상적으로 납부를 해준다면 아무런 문제가 없겠으나, 고객 입장에서도 몇달 전 벌어진 기억도 안나는 통지서를 쉽게 수긍하기는 어렵다. 때문에 렌터카 회사에서 이 비용을 추후에 받아내는 것은 여간 성가신 일이 아니다. 전국 50개가 넘는 유료도로 운영사에 흩어져 있는 미납 내역과 미납 과태료를 납부자 변경 신청 마감 전에 일일이 확인하는 건 사람이 손으로 하기엔 너무 번거로운 일이다.&lt;/p&gt;</description></item><item><title>AI 모델을 한 번만 테스트하면 안 되는 이유 — 벤치마크 재현성 검증기</title><link>https://mhub.uk/posts/it-16-llm-benchmark-reproducibility-lessons/</link><pubDate>Fri, 30 Oct 2026 10:25:00 +0900</pubDate><guid>https://mhub.uk/posts/it-16-llm-benchmark-reproducibility-lessons/</guid><description>&lt;p&gt;로컬 코딩 LLM 파이프라인을 검증하면서 처음엔 단순하게 접근했다. 모델별로 딱 한 번씩 돌려보고, 가장 빠르고 결과물이 깔끔한 모델을 고르면 된다고 생각했다. 그런데 최종 결정을 내리기 직전에 &amp;ldquo;정말 이 결과가 우연이 아닐까?&amp;ldquo;라는 의문이 들어서, 가장 유력했던 모델(DeepSeek-Coder-V2-16B)만 완전히 동일한 프롬프트로 한 번 더 돌려봤다.&lt;/p&gt;
&lt;h2 id="두-번째-실행에서-벌어진-일"&gt;두 번째 실행에서 벌어진 일&lt;/h2&gt;
&lt;p&gt;1차 실행에서는 9분 30초 만에 요청한 4개 함수(add, subtract, multiply, divide)만 정확히 작성하고 끝났다. 2차 실행은 22분 55초가 걸렸고, &lt;strong&gt;요청하지 않은 함수 4개(거듭제곱, 나머지, 제곱근, 팩토리얼)를 임의로 추가&lt;/strong&gt;했으며, 코드 스타일 오류(flake8)도 훨씬 많이 남겼다.&lt;/p&gt;</description></item><item><title>왜 Claude Pro에서 Max로 갈아탔나 — 요금제 업그레이드 결정과 그 여파</title><link>https://mhub.uk/posts/it-15-claude-pro-to-max-upgrade-decision/</link><pubDate>Thu, 29 Oct 2026 20:15:00 +0900</pubDate><guid>https://mhub.uk/posts/it-15-claude-pro-to-max-upgrade-decision/</guid><description>&lt;p&gt;한동안 월 20달러 Claude Pro 플랜을 쓰다가, 110달러짜리 Max 플랜으로 갈아탔다. 단순 변심이 아니라 명확한 이유가 있었다.&lt;/p&gt;
&lt;h2 id="pro에서-걸린-벽"&gt;Pro에서 걸린 벽&lt;/h2&gt;
&lt;p&gt;Pro 플랜은 사용량 한도(5시간 단위 세션 한도, 주간 한도)가 있는데, 여러 프로젝트를 동시에 굴리다 보니 &lt;strong&gt;이 한도에 자주 걸렸다.&lt;/strong&gt; 특히 코딩 작업처럼 컨텍스트를 많이 쓰는 작업을 하는 날에는 하루에도 몇 번씩 한도 초과 메시지를 만났다. 작업 흐름이 끊기는 게 누적되니, 요금 차이(월 90달러)보다 그로 인한 비효율이 더 크다고 판단했다.&lt;/p&gt;
&lt;h2 id="max로-올린-뒤"&gt;Max로 올린 뒤&lt;/h2&gt;
&lt;p&gt;Max 플랜으로 올린 뒤로는 지금까지 단 한 번도 한도에 걸린 적이 없다. 여러 기기(아이맥, 루팅폰 2대)에서 동시에 원격 세션을 돌리고, 코딩 작업과 문서 작업을 병행해도 여유가 있다.&lt;/p&gt;</description></item><item><title>OpenAI vs Gemini vs Claude — 2026년 기준 LLM API 샘플링 파라미터 비교</title><link>https://mhub.uk/posts/it-14-llm-api-sampling-parameters-comparison-2026/</link><pubDate>Wed, 28 Oct 2026 08:30:00 +0900</pubDate><guid>https://mhub.uk/posts/it-14-llm-api-sampling-parameters-comparison-2026/</guid><description>&lt;p&gt;여러 LLM API를 오가며 자동화 파이프라인을 만들다 보면, 같은 이름의 파라미터라도 플랫폼마다 지원 범위와 동작이 미묘하게 다르다는 걸 자주 느낀다. 헷갈릴 때마다 매번 문서를 다시 뒤지는 대신, 한 번 정리해뒀다.&lt;/p&gt;
&lt;h2 id="기본-파라미터-정의"&gt;기본 파라미터 정의&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;temperature&lt;/strong&gt;: 확률 분포를 얼마나 평평하게(무작위성을 높게) 만들지 조절. 값이 낮을수록 결정적이고 반복 가능한 출력, 높을수록 다양하고 창의적인 출력.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;top_p (nucleus sampling)&lt;/strong&gt;: 누적 확률이 p에 도달할 때까지의 후보 토큰만 남기고 샘플링.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;top_k&lt;/strong&gt;: 확률 상위 k개 토큰만 후보로 남기고 샘플링.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;frequency_penalty / presence_penalty&lt;/strong&gt;: 이미 등장한 토큰의 재등장 확률을 낮춰 반복을 억제.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stop_sequence&lt;/strong&gt;: 특정 문자열이 등장하면 생성을 중단.&lt;/li&gt;
&lt;/ul&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;OpenAI&lt;/th&gt;
&lt;th&gt;Claude&lt;/th&gt;
&lt;th&gt;Gemini(2026 최신)&lt;/th&gt;
&lt;th&gt;xAI Grok&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;temperature&lt;/td&gt;
&lt;td&gt;0~2&lt;/td&gt;
&lt;td&gt;0~1&lt;/td&gt;
&lt;td&gt;지원(단, 최신 플래시 모델은 비권장)&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;top_p&lt;/td&gt;
&lt;td&gt;0~1&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;td&gt;지원(최신 모델은 비권장)&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;top_k&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;td&gt;지원(최신 모델은 비권장)&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frequency_penalty&lt;/td&gt;
&lt;td&gt;-2~2&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;presence_penalty&lt;/td&gt;
&lt;td&gt;-2~2&lt;/td&gt;
&lt;td&gt;미지원&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="눈에-띄는-트렌드-두-가지"&gt;눈에 띄는 트렌드 두 가지&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. Claude는 애초에 파라미터 폭이 좁다.&lt;/strong&gt; temperature도 0~1 범위로 제한되어 있고, frequency/presence penalty 자체가 없다. 게다가 최근 Opus급 최상위 모델 중 일부는 기본값 이외의 샘플링 오버라이드 자체를 거부하는 경우도 있었다. 세밀한 샘플링 튜닝보다는, 프롬프트 자체의 품질로 원하는 출력을 유도하는 방향에 무게를 두는 설계 철학으로 보인다.&lt;/p&gt;</description></item><item><title>AI와 나눈 긴 대화를 통째로 날리지 않는 법 — Claude Docs로 세션 백업하기</title><link>https://mhub.uk/posts/it-13-backing-up-long-ai-sessions-with-docs/</link><pubDate>Tue, 27 Oct 2026 10:20:00 +0900</pubDate><guid>https://mhub.uk/posts/it-13-backing-up-long-ai-sessions-with-docs/</guid><description>&lt;p&gt;Claude Code로 작업하다 보면 세션 하나가 몇 주씩 이어지는 경우가 생긴다. 아이맥 로컬 LLM 실험, 루팅폰 서버 관리, 사이드 프로젝트 기획까지 — 한 대화창 안에 여러 프로젝트가 뒤섞여 쌓이다 보니, 어느 순간 &amp;ldquo;이 대화가 너무 무거워졌다&amp;quot;는 느낌이 들었다. 특히 이 세션은 특정 컴퓨터(아이맥)에 대한 파일 접근 권한까지 물고 있어서, 더더욱 새 세션으로 넘어가고 싶어졌다.&lt;/p&gt;
&lt;p&gt;문제는 그동안 나눈 대화에 흩어져 있는 결정사항, 삽질 기록, 설정값들을 그냥 버리기는 아깝다는 점이었다.&lt;/p&gt;
&lt;h2 id="세션을-닫기-전에-정리부터"&gt;세션을 닫기 전에 정리부터&lt;/h2&gt;
&lt;p&gt;그래서 새 세션으로 넘어가기 전에, &lt;strong&gt;지금까지의 대화 내용을 주제별로 정리한 문서를 먼저 만들어두는 방법&lt;/strong&gt;을 썼다. Claude Docs(Claude가 만드는 살아있는 문서) 기능을 이용해서, 세션 안에서 다룬 프로젝트 하나당 탭 하나씩으로 나눠 정리했다.&lt;/p&gt;</description></item><item><title>IRIS.apk가 설치되지 않을 때 — 미서명 APK 재서명과 수동 기동 매뉴얼</title><link>https://mhub.uk/posts/s9-15-iris-apk-signing-and-runbook/</link><pubDate>Mon, 26 Oct 2026 18:05:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-15-iris-apk-signing-and-runbook/</guid><description>&lt;p&gt;루팅과 Termux 환경까지 다 갖춰놓고도, 정작 카카오톡 봇의 핵심인 IRIS 서버 APK 하나가 설치되지 않아 발목을 잡힌 적이 있다. 원인과 해결법, 그리고 이후 장애 복구 때마다 참고하는 수동 기동 절차를 정리해둔다.&lt;/p&gt;
&lt;h2 id="termux-사이드로딩부터-막혔다"&gt;Termux 사이드로딩부터 막혔다&lt;/h2&gt;
&lt;p&gt;Termux 자체를 F-Droid나 GitHub에서 받은 arm64-v8a apk로 설치하려는 시도부터 전부 &amp;ldquo;패키지를 파싱하는 중 문제가 발생했습니다&amp;quot;로 실패했다. &lt;code&gt;adb install&lt;/code&gt;로 우회해봐도 이번엔 &lt;code&gt;INSTALL_FAILED_VERIFICATION_FAILURE&lt;/code&gt;로 막혔다.&lt;/p&gt;
&lt;p&gt;원인은 Play Protect였다. 설정 → Play 스토어 → Play Protect → &lt;strong&gt;&amp;ldquo;기기에서 앱 검사&amp;rdquo;&lt;/strong&gt; 토글이 켜져 있으면, Play 스토어 밖에서 받은 apk의 설치 자체를 사전 검증 단계에서 막아버린다. 이 폰은 개인용이 아니라 전용 서버로만 쓸 것이므로 이 토글을 영구적으로 꺼두기로 했다. 이후 Termux(v0.118.3, GitHub release)는 문제없이 설치됐다.&lt;/p&gt;</description></item><item><title>폰 두 대의 서버를 하나로 — Claude Code 세션끼리 통신시켜 기능 이전하기</title><link>https://mhub.uk/posts/s9-14-quantum2-to-s9-migration/</link><pubDate>Sun, 25 Oct 2026 08:55:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-14-quantum2-to-s9-migration/</guid><description>&lt;p&gt;지금까지 나는 유휴 휴대폰 두 대를 각각 다른 용도의 서버로 굴리고 있었다. 갤럭시 퀀텀2는 Cloudflare Tunnel로 노출된 FastAPI 기반 업비트 시세 서버, 갤럭시 S9는 루팅해서 만든 IRIS 카카오톡 봇 서버. 이번에 이 두 서버의 기능을 &lt;strong&gt;S9 하나로 합쳤다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="왜-합쳤나"&gt;왜 합쳤나&lt;/h2&gt;
&lt;p&gt;두 폰을 따로 유지보수하는 비용이 생각보다 컸다. 각 폰마다 Termux 환경, proot-Ubuntu, Claude Code 원격 세션을 따로 관리해야 했고, 장애가 나면 어느 폰의 어느 서비스인지부터 확인해야 했다. 서버 자체의 부하는 두 서비스를 합쳐도 충분히 감당할 수준이라 판단해서, 한 대로 통합하는 쪽을 택했다.&lt;/p&gt;</description></item><item><title>루팅 폰에 SD카드 확장하기 — exFAT vs zram vs 실행권한, 256GB를 고른 이유</title><link>https://mhub.uk/posts/s9-13-sd-card-storage-zram-decision/</link><pubDate>Sat, 24 Oct 2026 05:50:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-13-sd-card-storage-zram-decision/</guid><description>&lt;p&gt;상시 구동 서버로 쓰고 있는 루팅 갤럭시 S9(SM-G960N)의 저장 공간이 빠듯해졌다. RAM도 4GB에 실제 스왑은 2GB뿐이라, &amp;ldquo;SD카드를 추가로 사서 스왑으로 쓰면 메모리 부족 문제까지 같이 해결되지 않을까&amp;rdquo; 하는 생각이 들었다. 결론부터 말하면 &lt;strong&gt;그렇게 하지 않기로 했다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="안드로이드는-이미-zram을-쓰고-있다"&gt;안드로이드는 이미 zram을 쓰고 있다&lt;/h2&gt;
&lt;p&gt;먼저 확인해야 했던 건 이 폰이 이미 스왑 비슷한 걸 쓰고 있는지였다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat /proc/swaps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;free -h
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;/dev/block/zram0&lt;/code&gt; 항목이 보이면, 이미 &lt;strong&gt;zram&lt;/strong&gt;(RAM 일부를 압축해서 스왑처럼 쓰는 방식)이 동작 중이라는 뜻이다. 디스크 기반 스왑이 아니라 RAM 안에서 압축·해제하는 방식이라, SD카드 스왑을 추가로 얹는 것과는 성격이 다르다.&lt;/p&gt;</description></item><item><title>로컬 LLM 코딩 파이프라인, 17편을 돌아보며</title><link>https://mhub.uk/posts/mac-18-local-llm-pipeline-retrospective/</link><pubDate>Fri, 23 Oct 2026 19:00:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-18-local-llm-pipeline-retrospective/</guid><description>&lt;p&gt;놀고 있던 인텔 아이맥 하나로 시작한 실험이 어느새 17편짜리 시리즈가 됐다. 하드웨어 한계를 확인하는 글로 시작해서, Homebrew 설치 차단, MCP 바이너리 부재, 네 개 모델 실측 비교, 그리고 결국 프로젝트를 접기로 한 결정까지 — 매 단계에서 부딪힌 벽과 우회로를 그때그때 기록해뒀다.&lt;/p&gt;
&lt;h2 id="돌아보니-남는-것"&gt;돌아보니 남는 것&lt;/h2&gt;
&lt;p&gt;시리즈를 마치고 다시 훑어보면, 이 프로젝트에서 진짜 값진 건 &amp;ldquo;로컬 LLM 파이프라인을 완성했다&amp;quot;는 결과가 아니라 &lt;strong&gt;왜 안 되는지를 실측으로 확인한 과정&lt;/strong&gt; 쪽이었다. GPU 가속이 안 된다는 걸 처음부터 알고도 CPU로 밀어붙였고, 그 판단이 얼마나 순진했는지는 재현성 테스트(같은 모델, 같은 프롬프트인데 소요 시간이 2.4배 벌어지고 요청하지 않은 함수까지 추가된 사례)에서 명확하게 드러났다. 결론을 성급하게 내리지 않고 두 번째 실행까지 검증해본 덕분에, &amp;ldquo;빠르고 안정적인 모델&amp;quot;이라는 잘못된 결론으로 파이프라인을 그대로 밀어붙이는 일은 막을 수 있었다.&lt;/p&gt;</description></item><item><title>npm install -g에서 EACCES가 뜨는 진짜 이유와 sudo 없이 고치는 법</title><link>https://mhub.uk/posts/mac-17-npm-eacces-root-cause-fix/</link><pubDate>Thu, 22 Oct 2026 10:55:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-17-npm-eacces-root-cause-fix/</guid><description>&lt;p&gt;이번에 인텔 아이맥에 Claude Code CLI를 새로 설치하면서 겪은 &lt;code&gt;EACCES&lt;/code&gt; 에러를, 다른 프로젝트에서도 반복해서 마주쳤던 참에 한 번 근본 원인부터 정리해두기로 했다.&lt;/p&gt;
&lt;h2 id="에러가-나는-이유"&gt;에러가 나는 이유&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ npm install -g @anthropic-ai/claude-code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm error code EACCES
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm error syscall access
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm error path /usr/local/lib/node_modules
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;npm의 전역 설치 기본 경로(&lt;code&gt;/usr/local/lib/node_modules&lt;/code&gt; 또는 Homebrew/Node 버전에 따라 다른 시스템 경로)는 macOS·리눅스 배포판 대부분에서 &lt;strong&gt;일반 사용자 계정에 쓰기 권한이 없는 시스템 디렉터리&lt;/strong&gt;다. &lt;code&gt;npm install -g&lt;/code&gt;는 이 경로에 패키지를 풀어야 하는데, 권한이 없으니 거부당하는 것이다.&lt;/p&gt;</description></item><item><title>Aider도 병목이 될 수 있을까 — repo-map과 컨텍스트 크기가 속도에 미치는 영향</title><link>https://mhub.uk/posts/mac-16-aider-repo-map-bottleneck/</link><pubDate>Wed, 21 Oct 2026 11:10:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-16-aider-repo-map-bottleneck/</guid><description>&lt;p&gt;지금까지의 벤치마크에서 속도 문제는 대부분 모델 추론(CPU 바운드 decode) 탓으로 돌렸다. 그런데 문득 이런 의문이 들었다. &amp;ldquo;Aider 자체는 병목이 아닐까? 모델한테 뭘 보내는지, 그 준비 과정 자체가 느린 건 아닐까?&amp;rdquo;&lt;/p&gt;
&lt;h2 id="aider가-매-턴마다-하는-추가-작업"&gt;Aider가 매 턴마다 하는 추가 작업&lt;/h2&gt;
&lt;p&gt;Aider는 단순히 사용자 프롬프트만 모델에 던지지 않는다. 매 요청마다 다음과 같은 부가 작업이 들어간다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. repo-map 생성&lt;/strong&gt;
프로젝트 전체 구조를 파악해서, 지금 대화와 관련 있을 법한 파일·함수 목록을 요약한 &amp;ldquo;지도&amp;quot;를 만든다. 이 작업은 tree-sitter라는 파서를 이용해 코드를 구문 분석하는 방식으로 이뤄진다. 프로젝트가 작을 땐(이번 테스트의 계산기 모듈처럼) 무시할 만한 수준이지만, 파일 수가 많은 대형 저장소에서는 이 repo-map 생성 자체에 시간이 걸릴 수 있다.&lt;/p&gt;</description></item><item><title>Aider가 스스로 업그레이드했다는데 버전은 그대로 — 사소하지만 신경 쓰인 불일치</title><link>https://mhub.uk/posts/mac-15-aider-self-upgrade-version-mismatch/</link><pubDate>Tue, 20 Oct 2026 20:10:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-15-aider-self-upgrade-version-mismatch/</guid><description>&lt;p&gt;Aider를 실행할 때마다 새 버전이 있다는 안내가 떴다. 안내를 따라 자체 업그레이드를 진행했는데, 완료 메시지에는 분명 &lt;code&gt;0.86.2&lt;/code&gt;로 올라갔다고 나왔다. 그런데 이후 &lt;code&gt;aider --version&lt;/code&gt;을 찍어보면 여전히 이렇게 나왔다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ aider --version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aider 0.82.3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;큰 문제로 이어지진 않았지만, 왜 이런 불일치가 생겼는지는 짚고 넘어갈 만하다.&lt;/p&gt;
&lt;h2 id="의심되는-원인들"&gt;의심되는 원인들&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. 여러 개의 pip/python 경로 공존&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip install --user aider-chat&lt;/code&gt;으로 설치했는데, 시스템에 Python 경로가 여러 개 있는 환경(macOS는 특히 Xcode Command Line Tools의 Python, Homebrew Python, &lt;code&gt;python3.9&lt;/code&gt; 등이 뒤섞이기 쉽다)이라면, 업그레이드 명령이 실행되는 pip과 실제로 PATH에서 먼저 잡히는 &lt;code&gt;aider&lt;/code&gt; 실행파일이 서로 다른 설치본을 가리킬 가능성이 있다.&lt;/p&gt;</description></item><item><title>미니PC로 로컬 LLM 서버를 살까 고민한 기록 — Ryzen 9 6900HX vs Ryzen 7 5825U</title><link>https://mhub.uk/posts/mac-14-minipc-vs-imac-local-llm-hardware-comparison/</link><pubDate>Mon, 19 Oct 2026 07:10:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-14-minipc-vs-imac-local-llm-hardware-comparison/</guid><description>&lt;p&gt;로컬 코딩 LLM 파이프라인을 인텔 아이맥으로 실험하기 전, 사실 &amp;ldquo;그냥 이 목적으로 미니PC를 새로 하나 사는 게 낫지 않을까&amp;quot;도 진지하게 고민했다. 결국 사지 않기로 했지만, 그 과정에서 비교했던 내용을 기록으로 남긴다.&lt;/p&gt;
&lt;h2 id="왜-미니pc를-고려했나"&gt;왜 미니PC를 고려했나&lt;/h2&gt;
&lt;p&gt;가장 먼저 배제한 건 유휴 휴대폰(갤럭시 퀀텀2)이었다. 로컬 LLM을 돌리기엔 RAM이 4-6GB로 너무 작고, 발열·배터리 제약도 있어 초당 1~3토큰 수준의 속도밖에 안 나올 것으로 판단했다. 그렇다면 남는 선택지는 기존 인텔 아이맥을 쓰거나, 이 용도로 새 기기를 사는 것 두 가지였다.&lt;/p&gt;</description></item><item><title>코딩 LLM 추천이 죄다 중국산인 이유 — 오픈소스 코딩 모델 국가별 지형도</title><link>https://mhub.uk/posts/mac-13-why-open-source-coding-llms-are-mostly-chinese/</link><pubDate>Sun, 18 Oct 2026 11:00:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-13-why-open-source-coding-llms-are-mostly-chinese/</guid><description>&lt;p&gt;모델 후보를 고르는 과정을 돌아보다가 문득 이상한 점을 느꼈다. 검토했던 후보 — Qwen3-Coder, Qwen2.5-Coder, DeepSeek-Coder-V2 — 가 &lt;strong&gt;전부 중국 회사(알리바바, 딥시크)의 모델&lt;/strong&gt;이었다. 의도한 건 아니었는데, 결과적으로 다양성이 전혀 없었던 셈이다.&lt;/p&gt;
&lt;h2 id="왜-이렇게-됐나"&gt;왜 이렇게 됐나&lt;/h2&gt;
&lt;p&gt;솔직히 말하면 의도적인 선택이 아니라 &lt;strong&gt;벤치마크 점수 기준으로 좁히다 보니 자연스럽게 그렇게 됐다.&lt;/strong&gt; HumanEval, MBPP 같은 코딩 벤치마크에서 로컬(7B~32B급) 규모의 오픈소스 모델 중 상위권을 차지하는 게 최근 Qwen·DeepSeek 계열에 쏠려 있는 게 현실이다.&lt;/p&gt;
&lt;h2 id="그럼-다른-나라-모델은-없나"&gt;그럼 다른 나라 모델은 없나&lt;/h2&gt;
&lt;p&gt;찾아보면 분명히 있다. 정리하면 이렇다.&lt;/p&gt;</description></item><item><title>여러 로컬 LLM 모델을 번갈아 테스트할 때 알아야 할 것 — ollama ps와 keep_alive</title><link>https://mhub.uk/posts/mac-12-ollama-ps-keep-alive-model-switching/</link><pubDate>Sat, 17 Oct 2026 18:25:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-12-ollama-ps-keep-alive-model-switching/</guid><description>&lt;p&gt;4개 모델을 순서대로 벤치마크하던 중, 이상하게 다음 모델의 로딩이 평소보다 느리게 느껴지는 순간이 있었다. &lt;code&gt;ps aux&lt;/code&gt;로 확인해보니 원인이 바로 보였다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ ps aux &lt;span class="p"&gt;|&lt;/span&gt; grep llama-server
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;simon &lt;span class="m"&gt;41203&lt;/span&gt; 539.6 ... llama-server --model .../qwen2.5-coder-14b ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;simon &lt;span class="m"&gt;40891&lt;/span&gt; 0.0 ... llama-server --model .../deepseek-coder-v2-16b ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;새로 시작한 14B 테스트는 CPU 539.6%로 활발히 계산 중인데, &lt;strong&gt;바로 아래에 방금 전에 테스트했던 DeepSeek 16B 프로세스가 CPU 0%로 여전히 살아있었다.&lt;/strong&gt; 이미 다른 터미널에서 Aider 세션을 종료했는데도 말이다.&lt;/p&gt;
&lt;h2 id="keep_alive-알아서-안-꺼주는-5분"&gt;keep_alive, 알아서 안 꺼주는 5분&lt;/h2&gt;
&lt;p&gt;원인은 Ollama의 &lt;code&gt;keep_alive&lt;/code&gt; 메커니즘이었다. Ollama는 한 번 로드한 모델을 요청이 끝난 뒤에도 &lt;strong&gt;기본 5분간 메모리에 유지&lt;/strong&gt;한다. 같은 모델로 연속 요청이 들어올 걸 대비한 최적화인데, 이게 여러 모델을 짧은 간격으로 번갈아 테스트하는 상황에서는 오히려 방해가 된다 — 이전 모델이 여전히 RAM을 점유하고 있어서, 새 모델이 로드될 때 쓸 수 있는 메모리 대역폭이 줄어들기 때문이다.&lt;/p&gt;</description></item><item><title>MoE 모델인데 왜 다 무겁다 — Ollama가 모델 전체를 메모리에 올리는 이유</title><link>https://mhub.uk/posts/mac-11-why-moe-models-still-eat-all-your-ram/</link><pubDate>Fri, 16 Oct 2026 13:35:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-11-why-moe-models-still-eat-all-your-ram/</guid><description>&lt;p&gt;이번 로컬 LLM 프로젝트를 시작할 때 세웠던 가설 하나가 있다. &amp;ldquo;MoE(Mixture of Experts) 구조 모델은 토큰마다 일부 전문가(expert) 파라미터만 활성화하니까, 활성 파라미터 수가 작을수록 CPU에서도 더 빠르고 가벼울 것이다.&amp;rdquo; DeepSeek-Coder-V2(활성 2.4B)를 Qwen3-Coder-30B(활성 3.3B)보다 더 유리하게 볼 근거로 삼았던 가설이다.&lt;/p&gt;
&lt;p&gt;실측해보니 속도 면에서는 어느 정도 맞아떨어졌지만(DeepSeek이 가장 빨랐다), &lt;strong&gt;메모리 사용량 관점에서는 이 가설이 틀렸다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="계산에-쓰는-파라미터--메모리에-올려야-하는-파라미터"&gt;계산에 쓰는 파라미터 ≠ 메모리에 올려야 하는 파라미터&lt;/h2&gt;
&lt;p&gt;MoE 모델이 &amp;ldquo;일부 파라미터만 쓴다&amp;quot;는 건 &lt;strong&gt;매 토큰의 계산량&lt;/strong&gt;에 대한 이야기다. 그런데 어떤 토큰에서 어떤 expert가 선택될지는 그 순간의 입력에 따라 동적으로 정해진다. 즉 이번 토큰에서 안 쓰인 expert가 다음 토큰에서는 쓰일 수 있다 — &lt;strong&gt;결국 모든 expert가 언제든 호출될 수 있으므로, 전체 파라미터가 메모리에 상주하고 있어야 한다.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>로컬 LLM 활용 두 가지 방식 — Aider 자율 코딩 vs MCP 위임, 왜 둘 다 써도 되는가</title><link>https://mhub.uk/posts/mac-10-aider-vs-mcp-delegation-approaches/</link><pubDate>Thu, 15 Oct 2026 07:55:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-10-aider-vs-mcp-delegation-approaches/</guid><description>&lt;p&gt;로컬 LLM으로 코딩을 시켜보겠다고 마음먹었을 때, 실은 두 가지 서로 다른 활용 방식을 놓고 고민했다.&lt;/p&gt;
&lt;h2 id="방식-a-aider-자율-코딩"&gt;방식 A: Aider 자율 코딩&lt;/h2&gt;
&lt;p&gt;터미널에서 Aider를 실행하고, 프롬프트 하나로 파일 생성·수정·테스트·커밋까지 로컬 모델이 알아서 처리하게 하는 방식이다. &lt;code&gt;yes-always&lt;/code&gt; 옵션으로 확인 프롬프트까지 다 꺼두면, 사람은 결과만 나중에 확인하면 된다. 지금까지 이 블로그 시리즈에서 다룬 벤치마크 테스트가 전부 이 방식이었다.&lt;/p&gt;
&lt;h2 id="방식-b-mcp를-통한-위임"&gt;방식 B: MCP를 통한 위임&lt;/h2&gt;
&lt;p&gt;Claude Code 세션 안에서 작업을 하다가, 특정 하위 작업만 로컬 모델에 &amp;ldquo;던지고&amp;rdquo; 결과를 받아오는 방식이다. Claude Code가 메인 오케스트레이터 역할을 하고, 로컬 LLM은 도구(tool) 중 하나로 취급된다. 이걸 위해 &lt;code&gt;mcp-local-llm&lt;/code&gt; MCP 서버를 소스에서 직접 빌드해 붙였다(이 과정은 앞선 글에서 다뤘다).&lt;/p&gt;</description></item><item><title>로컬 LLM을 외부에 노출해도 될까 — 전용 서브도메인 구상과 포기 이유</title><link>https://mhub.uk/posts/mac-09-exposing-local-llm-subdomain-decision/</link><pubDate>Wed, 14 Oct 2026 10:05:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-09-exposing-local-llm-subdomain-decision/</guid><description>&lt;p&gt;이미 휴대폰 API 주소를 Cloudflare Tunnel 서브도메인으로 열어 서버를 하나 운영하고 있다 보니, 자연스럽게 이런 생각이 들었다. &amp;ldquo;아이맥의 로컬 LLM도 전용 서브도메인으로 열어두면, 어디서든 접근할 수 있어서 더 편하지 않을까?&amp;rdquo;&lt;/p&gt;
&lt;h2 id="같은-기기-안에서는-이득이-없다"&gt;같은 기기 안에서는 이득이 없다&lt;/h2&gt;
&lt;p&gt;먼저 따져야 했던 건 &amp;ldquo;누가, 어디서 이 로컬 LLM에 접근하는가&amp;quot;였다. 지금 계획대로라면 Aider든 MCP 서버든, &lt;strong&gt;호출하는 쪽도 결국 같은 아이맥 안&lt;/strong&gt;이다. &lt;code&gt;localhost:11434&lt;/code&gt;로 바로 붙으면 되는 걸 굳이 인터넷을 한 바퀴 돌아서(Cloudflare Tunnel → DNS → 다시 내 아이맥) 접근할 이유가 없다. 오히려 왕복 지연만 늘어난다.&lt;/p&gt;</description></item><item><title>Aider로 한국어 페어프로그래밍 하기 — .aider.conf.yml 설정 가이드</title><link>https://mhub.uk/posts/mac-08-aider-conf-yaml-korean-setup/</link><pubDate>Tue, 13 Oct 2026 08:00:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-08-aider-conf-yaml-korean-setup/</guid><description>&lt;p&gt;Aider를 매번 긴 커맨드라인 옵션과 함께 실행하는 건 비효율적이다. 프로젝트 루트에 &lt;code&gt;.aider.conf.yml&lt;/code&gt; 파일 하나만 만들어두면, &lt;code&gt;aider&lt;/code&gt;라고만 쳐도 원하는 설정이 자동 적용된다.&lt;/p&gt;
&lt;h2 id="기본-골격"&gt;기본 골격&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ollama_chat/qwen3-coder:30b&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;chat-language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;korean&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;auto-test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;test-cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;python3 -m pytest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;auto-lint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;lint-cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;python3 -m flake8&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;yes-always&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&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;ul&gt;
&lt;li&gt;&lt;code&gt;model&lt;/code&gt;: 사용할 모델. &lt;code&gt;ollama_chat/&lt;/code&gt; 접두어를 붙여야 Ollama의 채팅 API 경로로 붙는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chat-language: korean&lt;/code&gt;: Aider가 채팅 응답을 한국어로 하도록 지시한다. 실제로 Qwen 계열은 다국어 모델(Qwen3 기준 119개 언어)이라 한국어 응답 자체는 자연스럽게 나온다. 다만 코드 안의 변수명·함수명은 한국어로 지시해도 관례상 영어로 짓는 경향이 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto-test&lt;/code&gt; / &lt;code&gt;test-cmd&lt;/code&gt;: 코드 수정 후 자동으로 테스트를 돌린다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto-lint&lt;/code&gt; / &lt;code&gt;lint-cmd&lt;/code&gt;: 코드 수정 후 자동으로 린트 검사·수정을 시도한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;yes-always&lt;/code&gt;: 무인 실행의 핵심 옵션이다. 이게 없으면 gitignore 추가, 버전 업그레이드 안내, 린트 수정 확인 같은 모든 프롬프트에서 매번 사람이 y/n을 눌러줘야 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;analytics: false&lt;/code&gt;: Aider의 사용량 분석 전송을 끈다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="존재하지-않는-옵션을-썼다가-걸린-실수"&gt;존재하지 않는 옵션을 썼다가 걸린 실수&lt;/h2&gt;
&lt;p&gt;처음에는 &lt;code&gt;commit-language: korean&lt;/code&gt;도 같이 넣었다가 이런 에러를 만났다.&lt;/p&gt;</description></item><item><title>로컬 LLM을 터미널 밖에서 쓰기 — VS Code 연동 옵션 비교 (Cline vs Continue)</title><link>https://mhub.uk/posts/mac-07-vscode-local-llm-integration-options/</link><pubDate>Mon, 12 Oct 2026 22:00:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-07-vscode-local-llm-integration-options/</guid><description>&lt;p&gt;로컬 LLM 파이프라인을 Aider 중심으로 터미널에서 굴리다 보니, 당연한 질문이 떠올랐다. &amp;ldquo;이거 VS Code 안에서는 못 쓰나?&amp;rdquo; 매번 터미널과 에디터를 오가는 것도 번거롭고, 코드를 눈으로 보면서 바로 지시하고 싶은 순간이 많았기 때문이다.&lt;/p&gt;
&lt;h2 id="로컬-모델은-특정-도구-전용이-아니다"&gt;로컬 모델은 특정 도구 전용이 아니다&lt;/h2&gt;
&lt;p&gt;먼저 짚고 넘어가야 할 게 있다. Ollama가 &lt;code&gt;localhost:11434&lt;/code&gt;에 띄우는 API는 OpenAI 호환/Ollama 자체 API를 따르는 &lt;strong&gt;범용 엔드포인트&lt;/strong&gt;다. 즉 Aider 전용이 아니라, 이 API를 호출할 수 있는 도구라면 뭐든 붙을 수 있다는 뜻이다. 그래서 VS Code 확장 중에서도 로컬 Ollama를 지원하는 것들을 찾아보면 된다.&lt;/p&gt;</description></item><item><title>대화창 하나에만 뜨는 '이 컴퓨터 연결됨' — Claude 데스크탑 앱 기기 연결 구조 파헤치기</title><link>https://mhub.uk/posts/mac-06-claude-desktop-device-bridge/</link><pubDate>Sun, 11 Oct 2026 21:55:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-06-claude-desktop-device-bridge/</guid><description>&lt;p&gt;로컬 LLM 파이프라인 작업을 마무리하면서 뜻밖의 부산물을 하나 발견했다. 작업 중이던 대화창 상단에 계속 &amp;ldquo;Simon&amp;rsquo;s iMac · 연결됨&amp;quot;이라는 표시가 떠 있었던 것이다. 처음엔 대수롭지 않게 넘겼는데, 자세히 보니 &lt;strong&gt;이 대화에만&lt;/strong&gt; 뜨고 다른 대화에는 안 뜨는 걸 알아챘다.&lt;/p&gt;
&lt;h2 id="왜-이-대화에만-떴을까"&gt;왜 이 대화에만 떴을까&lt;/h2&gt;
&lt;p&gt;원인은 명확했다. 이 세션 초반, 아이맥에 Ollama/Aider를 설치하려고 &lt;code&gt;~/Projects&lt;/code&gt; 폴더에 접근 권한을 요청했고, 그 요청을 승인했던 것이다. 그 승인 자체가 &lt;strong&gt;&amp;ldquo;이 대화(작업)와 이 컴퓨터를 연결&amp;rdquo;&lt;/strong&gt; 시키는 액션이었다.&lt;/p&gt;
&lt;p&gt;즉 이건 계정 전체에 걸리는 설정이 아니라, &lt;strong&gt;대화(세션) 단위로 부여되는 권한&lt;/strong&gt;이다. 다른 대화에서는 이런 폴더 접근 요청을 한 적이 없으니 연결 표시도 뜨지 않는 것이다.&lt;/p&gt;</description></item><item><title>왜 CPU 로컬 LLM 코딩 파이프라인을 포기했는가 — 속도, 일관성, 그리고 이미 있던 대안</title><link>https://mhub.uk/posts/mac-05-why-local-llm-pipeline-failed/</link><pubDate>Sat, 10 Oct 2026 21:50:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-05-why-local-llm-pipeline-failed/</guid><description>&lt;p&gt;앞선 글들에서 인텔 아이맥에 로컬 코딩 LLM 파이프라인을 설치하고, 4개 모델을 실측 비교했다. 결론부터 말하면 &lt;strong&gt;이 프로젝트는 여기서 접기로 했다.&lt;/strong&gt; 그 판단에 이르기까지의 근거를 정리한다.&lt;/p&gt;
&lt;h2 id="결정타는-재현성-부족이었다"&gt;결정타는 &amp;ldquo;재현성 부족&amp;quot;이었다&lt;/h2&gt;
&lt;p&gt;가장 빠르고 안정적이었던 DeepSeek-Coder-V2-16B를, 완전히 동일한 프롬프트로 한 번 더 돌려봤다. 결과는 이랬다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;1차&lt;/th&gt;
&lt;th&gt;2차&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;소요 시간&lt;/td&gt;
&lt;td&gt;9분 30초&lt;/td&gt;
&lt;td&gt;22분 55초 (2.4배)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;커밋&lt;/td&gt;
&lt;td&gt;feat + lint fix 시도&lt;/td&gt;
&lt;td&gt;feat 1개뿐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;함수 개수&lt;/td&gt;
&lt;td&gt;요청한 4개&lt;/td&gt;
&lt;td&gt;요청에 없던 4개(거듭제곱·나머지·제곱근·팩토리얼) 임의 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;flake8 잔여 오류&lt;/td&gt;
&lt;td&gt;일부&lt;/td&gt;
&lt;td&gt;16건&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;같은 입력, 같은 모델인데 소요 시간이 2.4배 차이 나고, 심지어 &lt;strong&gt;요청하지 않은 기능을 마음대로 추가&lt;/strong&gt;했다. 이건 &amp;ldquo;느리다&amp;quot;가 아니라 &amp;ldquo;결과물을 매번 처음부터 검토해야 한다&amp;quot;는 뜻이다. 무인 파이프라인의 핵심 전제 — 사람이 개입하지 않아도 일정 수준의 결과가 보장된다는 것 — 이 자체가 깨진 셈이다.&lt;/p&gt;</description></item><item><title>같은 프롬프트, 네 가지 로컬 코딩 모델 — 계산기 테스트로 본 실제 속도와 신뢰성</title><link>https://mhub.uk/posts/mac-04-model-benchmark-calculator-test/</link><pubDate>Fri, 09 Oct 2026 22:15:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-04-model-benchmark-calculator-test/</guid><description>&lt;p&gt;설치를 마쳤으니 이제 실측이다. Aider가 설치된 &lt;code&gt;~/Projects/pipeline-test&lt;/code&gt; 저장소에서, 모델별로 git 브랜치를 나눠 완전히 동일한 프롬프트를 던졌다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;간단한 계산기 모듈을 만들어줘. add, subtract, multiply, divide 함수와
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;각각의 pytest 테스트 코드도 함께 작성해줘.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;의도적으로 아주 쉬운 작업을 골랐다. 이 정도도 제대로 못 하면 더 복잡한 작업은 아예 시도할 이유가 없기 때문이다.&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;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;Qwen3-Coder-30B&lt;/td&gt;
&lt;td&gt;~30분&lt;/td&gt;
&lt;td&gt;✅ 성공&lt;/td&gt;
&lt;td&gt;flake8 자동 수정까지 완료. 요청하지 않은 거듭제곱·나머지·제곱근·팩토리얼 함수를 임의로 추가함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5-Coder-7B&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;❌ 실패&lt;/td&gt;
&lt;td&gt;&lt;code&gt;whole&lt;/code&gt;·&lt;code&gt;diff&lt;/code&gt; 포맷 둘 다 신뢰 불가 (아래 상세)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-Coder-V2-16B&lt;/td&gt;
&lt;td&gt;9분 30초&lt;/td&gt;
&lt;td&gt;✅ 성공&lt;/td&gt;
&lt;td&gt;가장 빠름. flake8 일부 미해결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5-Coder-14B&lt;/td&gt;
&lt;td&gt;27분 43초&lt;/td&gt;
&lt;td&gt;✅ 성공&lt;/td&gt;
&lt;td&gt;가장 느림. flake8은 완전히 깨끗하게 해결&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="7b가-두-번-다-무너진-이유"&gt;7B가 두 번 다 무너진 이유&lt;/h2&gt;
&lt;p&gt;가장 작은 모델인 Qwen2.5-Coder-7B는 흥미로운 실패 방식을 두 개나 보여줬다.&lt;/p&gt;</description></item><item><title>npm 패키지에 내 아키텍처용 바이너리가 없을 때 — MCP 로컬 LLM 서버를 소스로 빌드하기</title><link>https://mhub.uk/posts/mac-03-mcp-local-llm-binary-missing/</link><pubDate>Thu, 08 Oct 2026 21:15:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-03-mcp-local-llm-binary-missing/</guid><description>&lt;p&gt;로컬 LLM 파이프라인에는 두 가지 활용 방식이 있다고 생각했다. 하나는 Aider가 터미널에서 로컬 모델로 자율 작업을 하는 방식, 다른 하나는 &lt;strong&gt;Claude Code 세션 안에서 필요할 때만 로컬 모델에 작업을 위임&lt;/strong&gt;하는 MCP(Model Context Protocol) 서버 방식이다. 이 둘은 서로 배타적이지 않고 병행할 수 있어서, 일단 MCP 쪽도 붙여보기로 했다.&lt;/p&gt;
&lt;h2 id="connection_closed"&gt;CONNECTION_CLOSED&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;claude mcp add local-llm -- npx -y mcp-local-llm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;등록은 됐는데, &lt;code&gt;claude mcp list&lt;/code&gt;로 확인하면 상태가 이렇다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;local-llm: npx -y mcp-local-llm - ✘ Failed to connect — CONNECTION_CLOSED
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;에러 메시지만으로는 원인을 알 수 없었다. &lt;code&gt;npx&lt;/code&gt;가 내부적으로 어떤 명령을 실행하는지 직접 눈으로 확인해야 했다.&lt;/p&gt;</description></item><item><title>2026년 인텔 맥에서 Homebrew가 막혔다 — Ollama와 Aider를 우회 설치한 기록</title><link>https://mhub.uk/posts/mac-02-homebrew-intel-mac-blocked/</link><pubDate>Wed, 07 Oct 2026 14:20:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-02-homebrew-intel-mac-blocked/</guid><description>&lt;p&gt;로컬 LLM 파이프라인을 만들려고 첫 삽을 뜨자마자 예상 못 한 벽에 부딪혔다. &lt;strong&gt;Homebrew가 Intel Mac 설치 자체를 거부&lt;/strong&gt;한다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Homebrew on macOS is only supported on Apple Silicon processors!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Homebrew 공식 문서에는 Intel Mac이 여전히 &amp;ldquo;Tier 3&amp;rdquo; 지원 대상으로 적혀 있는데, 실제 설치 스크립트는 이미 하드 블록을 걸어놨다. GitHub Discussion #7088을 뒤져보니, 2026년 9월 17일자로 메인테이너가 직접 남긴 댓글에서 이게 버그가 아니라 &lt;strong&gt;의도된 정책&lt;/strong&gt;이라고 확인해준다. 문서와 실제 동작이 따로 노는 셈인데, 이미 결정된 정책이라니 우회할 수밖에 없었다.&lt;/p&gt;</description></item><item><title>인텔 아이맥으로 로컬 코딩 LLM 파이프라인을 계획하다 — 하드웨어 한계와 모델 후보 선정</title><link>https://mhub.uk/posts/mac-01-imac-local-llm-hardware-selection/</link><pubDate>Tue, 06 Oct 2026 20:05:00 +0900</pubDate><guid>https://mhub.uk/posts/mac-01-imac-local-llm-hardware-selection/</guid><description>&lt;p&gt;집에 있는 구형 인텔 아이맥이 눈에 밟혔다. 3GHz 6코어 i5, Radeon Pro 570X 4GB, 32GB DDR4 — 일반 업무용으로는 여전히 충분한 사양인데, 놀고 있는 게 아깝다는 생각이 들었다. 마침 로컬 오픈소스 LLM으로 코딩 보조 파이프라인을 만들어보고 싶었던 참이라, 이 아이맥을 실험대로 삼기로 했다.&lt;/p&gt;
&lt;p&gt;목표는 단순했다. &lt;strong&gt;로컬 LLM이 코드 초안을 자동으로 작성하고, Claude Code가 그 결과를 검증·수정·커밋하는 2단계 파이프라인&lt;/strong&gt;을 만드는 것. &amp;ldquo;결과만 정확하면 시간이 오래 걸려도 괜찮다&amp;quot;는 전제를 깔고 시작했다.&lt;/p&gt;
&lt;h2 id="gpu-가속-기대는-접어야-했다"&gt;GPU 가속, 기대는 접어야 했다&lt;/h2&gt;
&lt;p&gt;가장 먼저 확인한 건 Radeon Pro 570X로 추론을 가속할 수 있는지였다. 결론부터 말하면 &lt;strong&gt;불가능&lt;/strong&gt;이다.&lt;/p&gt;</description></item><item><title>경마 게임과 '마주' 시스템 설계기</title><link>https://mhub.uk/posts/s9-06-horse-racing-ownership/</link><pubDate>Mon, 05 Oct 2026 07:45:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-06-horse-racing-ownership/</guid><description>&lt;p&gt;Iris 봇에 포팅한 기능 중 가장 설계가 복잡했던 건 경마 게임이었습니다. 특히 &amp;ldquo;마주&amp;rdquo;(말 소유자) 시스템은 단순한 베팅 게임을 넘어서는 작은 경제 시스템에 가까웠습니다.&lt;/p&gt;
&lt;h2 id="기본-구조-경마-게임"&gt;기본 구조: 경마 게임&lt;/h2&gt;
&lt;p&gt;채팅방 유저들이 정해진 시간에 경주에 참여하고, 결과에 따라 포인트를 얻거나 잃는 방식의 미니게임입니다. 여기까지는 흔한 구조입니다. 이번 포팅에서 신경 쓴 부분은 그 위에 얹힌 &amp;ldquo;마주&amp;rdquo; 시스템이었습니다.&lt;/p&gt;
&lt;h2 id="마주-시스템-말을-소유한다는-것"&gt;마주 시스템: 말을 소유한다는 것&lt;/h2&gt;
&lt;p&gt;마주 시스템은 유저가 포인트를 내고 특정 말의 소유권을 살 수 있게 하는 기능입니다. 소유한 말이 경주에서 우승하면, 소유자는 그 경주의 상금과는 별개로 일정 비율의 수수료를 받습니다. 즉 직접 경주에 참여하지 않아도, 좋은 말을 소유하고 있는 것만으로 꾸준한 수익을 기대할 수 있는 구조입니다.&lt;/p&gt;</description></item><item><title>AI가 라이브 봇 코드를 직접 고치는 구조의 안전장치 설계</title><link>https://mhub.uk/posts/s9-12-ai-safe-editing-pipeline/</link><pubDate>Sun, 04 Oct 2026 11:52:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-12-ai-safe-editing-pipeline/</guid><description>&lt;p&gt;이 시리즈의 마지막 글입니다. Gemini 원격 코딩 어시스턴트는 &amp;ldquo;채팅으로 요청하면 관리자 검토 없이 바로 라이브 봇 코드가 고쳐진다&amp;quot;는 구조입니다. 편리하지만 동시에 가장 위험할 수 있는 구조이기도 합니다. 이번 글은 그 위험을 어떻게 관리했는지에 대한 기록입니다.&lt;/p&gt;
&lt;h2 id="왜-안전장치가-특히-중요했나"&gt;왜 안전장치가 특히 중요했나&lt;/h2&gt;
&lt;p&gt;관리자가 매번 코드를 리뷰하고 승인하는 구조였다면 위험 부담이 훨씬 적었을 겁니다. 하지만 채팅으로 요청하면 곧바로 적용되는 구조를 원했기 때문에, &amp;ldquo;AI가 실수로 코드를 망가뜨리면 어떻게 할 것인가&amp;quot;라는 질문에 관리자 개입 없이도 답할 수 있는 장치가 필요했습니다.&lt;/p&gt;</description></item><item><title>Python 3.14 환경에서 막힌 numpy·aider 설치기</title><link>https://mhub.uk/posts/s9-11-python314-numpy-aider/</link><pubDate>Sat, 03 Oct 2026 04:02:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-11-python314-numpy-aider/</guid><description>&lt;p&gt;지난 글에서 잠깐 언급했던, Gemini 원격 코딩 어시스턴트를 만들면서 겪은 설치 문제를 이번 글에서 자세히 다룹니다. 최신 환경이 오히려 발목을 잡는, 은근히 자주 마주치는 종류의 문제였습니다.&lt;/p&gt;
&lt;h2 id="증상-기본적인-패키지-설치가-안-된다"&gt;증상: 기본적인 패키지 설치가 안 된다&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;aider&lt;/code&gt;를 설치하려고 하니 의존 패키지 중 하나가 설치 단계에서 실패했습니다. 에러 메시지를 따라가 보니 &lt;code&gt;distutils.msvccompiler&lt;/code&gt;를 찾을 수 없다는 식의, 파이썬 빌드 도구 체인과 관련된 에러였습니다. &lt;code&gt;numpy&lt;/code&gt;처럼 오래되고 널리 쓰이는 과학계산 라이브러리조차 순순히 설치되지 않았습니다.&lt;/p&gt;
&lt;h2 id="원인-너무-최신-파이썬"&gt;원인: 너무 최신 파이썬&lt;/h2&gt;
&lt;p&gt;이 환경의 기본 파이썬 버전은 3.14였습니다. 최신 버전을 쓰는 것 자체는 보통 좋은 일이지만, 이번엔 오히려 문제가 됐습니다. &lt;code&gt;distutils&lt;/code&gt;는 오래전부터 파이썬 표준 라이브러리에서 단계적으로 제거되어 온 모듈이고, 여러 과학계산·빌드 관련 패키지들이 이 모듈에 의존한 채로 아직 최신 파이썬 버전에 맞춰 업데이트되지 않은 상태였습니다. 결과적으로 &amp;ldquo;너무 최신이라서&amp;rdquo; 오래된 패키지들과 호환이 깨지는, 흔치 않지만 실제로 마주치면 당황스러운 상황이었습니다.&lt;/p&gt;</description></item><item><title>Gemini 기반 원격 코딩 어시스턴트 구축기</title><link>https://mhub.uk/posts/s9-10-gemini-remote-coding-assistant/</link><pubDate>Fri, 02 Oct 2026 10:02:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-10-gemini-remote-coding-assistant/</guid><description>&lt;p&gt;봇 코드를 고치고 싶을 때마다 매번 이 S9에 직접 붙어서 작업하기는 번거로웠습니다. 그래서 브라우저로 접속해서 자연어로 요청하면 실제로 코드를 고쳐주는 원격 코딩 어시스턴트를 만들게 됐습니다. 이번 글은 그 구축 과정입니다.&lt;/p&gt;
&lt;h2 id="1차-버전-웹-터미널"&gt;1차 버전: 웹 터미널&lt;/h2&gt;
&lt;p&gt;처음 만든 버전은 단순했습니다. &lt;code&gt;ttyd&lt;/code&gt;로 웹 터미널을 띄워서, 브라우저에서 이 폰의 셸에 직접 접속할 수 있게 했습니다. 터미널에서 평소처럼 AI 코딩 도구를 실행하면 되는 구조였습니다. 동작은 했지만, 실제로 써보니 터미널 UI 자체가 진입장벽이었습니다 — 매번 정확한 명령어를 기억해서 입력해야 했고, 모바일에서 터미널을 조작하는 것도 불편했습니다.&lt;/p&gt;</description></item><item><title>서버 은퇴 선언 — quantum2가 기록자로 남는 이유</title><link>https://mhub.uk/posts/q2-10-quantum2-retires-writes/</link><pubDate>Thu, 01 Oct 2026 08:54:27 +0900</pubDate><guid>https://mhub.uk/posts/q2-10-quantum2-retires-writes/</guid><description>&lt;p&gt;블로그도, API 서버도, SSH 접속도, 정기 자동 발행 작업도 전부 다른 기기로 옮겼다. quantum2에는 이제 상시로 떠 있는 서버 프로세스가 하나도 남지 않았다. 이건 실패의 기록이 아니라, 계획된 은퇴다.&lt;/p&gt;
&lt;p&gt;정리하면서 예상 못 한 것도 하나 나왔다. 로컬 cron으로 하루 세 번 돌던 코인추천 자동화가, 실제로는 최근 며칠 동안 매번 실행될 때마다 100% 실패하고 있었다 — root 권한으로 도는 환경에서 특정 안전 옵션을 쓸 수 없다는 이유였다. 다행히 같은 작업을 처리하는 클라우드 쪽 정기 작업이 따로 살아있어서 실제 서비스에는 영향이 없었지만, 조용히 죽어 있던 자동화를 발견한 채로 그냥 넘어가지 않고 같이 정리했다. 서버를 내려놓는 작업이 아니었다면 한동안 더 몰랐을 문제다.&lt;/p&gt;</description></item><item><title>카톡 명령어로 여는 임시 설정 웹페이지와 Cloudflare 터널 전환기</title><link>https://mhub.uk/posts/s9-09-temp-settings-page-tunnel/</link><pubDate>Thu, 01 Oct 2026 06:05:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-09-temp-settings-page-tunnel/</guid><description>&lt;p&gt;방마다 다양한 설정(등급 개수, 자동발송 on/off, 랜덤박스 확률 등)이 늘어나면서, 이걸 전부 카카오톡 명령어로만 조작하기엔 한계가 있었습니다. 그래서 브라우저에서 켜고 끌 수 있는 설정 페이지를 만들게 됐고, 그 과정에서 Cloudflare Tunnel 사용 방식도 함께 바뀌었습니다.&lt;/p&gt;
&lt;h2 id="카톡-명령어로-여는-임시-링크"&gt;카톡 명령어로 여는 임시 링크&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;!설정&lt;/code&gt;, &lt;code&gt;!랜덤박스설정&lt;/code&gt; 같은 명령어를 채팅방에 입력하면, 봇이 그 방 전용의 임시 설정 페이지 링크를 발급해줍니다. 이 링크는 자체 토큰이 붙어 있고 10분 동안만 유효합니다. 링크를 열면 브라우저에서 그 방의 설정을 켜고 끌 수 있는 UI가 뜹니다. 명령어를 실행한 사람만 접근 가능하도록 토큰 기반으로 제한하고, 유효시간을 짧게 둬서 링크가 유출되더라도 위험을 최소화하는 구조입니다.&lt;/p&gt;</description></item><item><title>정기 자동발송 기능이 재시작마다 꺼지던 문제</title><link>https://mhub.uk/posts/s9-08-recurring-message-persistence/</link><pubDate>Wed, 30 Sep 2026 08:21:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-08-recurring-message-persistence/</guid><description>&lt;p&gt;Iris 봇에는 방 관리자가 명령어로 켜고 끌 수 있는 정기 자동발송 기능이 있습니다. 일정 간격으로 짧은 이모티콘 메시지를 자동으로 전송하는 단순한 기능인데, 실제 운영 환경에서는 생각보다 까다로운 문제가 있었습니다.&lt;/p&gt;
&lt;h2 id="증상-설정해도-며칠-지나면-꺼져-있다"&gt;증상: 설정해도 며칠 지나면 꺼져 있다&lt;/h2&gt;
&lt;p&gt;관리자가 명령어로 자동발송을 켜두면 잘 동작합니다. 문제는 시간이 좀 지나고 나서 확인하면 어느새 다시 꺼져 있는 경우가 반복된다는 점이었습니다. 관리자가 실수로 끈 것도 아니고, 딱히 에러 로그가 남는 것도 아니어서 처음엔 원인을 특정하기 어려웠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="문제 해결 흐름" loading="lazy" src="https://mhub.uk/images/s9-series/s9-08-recurring-message-persistence-flow.png"&gt;&lt;/p&gt;</description></item><item><title>원인불명으로 막힌 터널, 새 도메인으로 우회하기</title><link>https://mhub.uk/posts/q2-09-fapi-ssh2-tunnel-workaround/</link><pubDate>Wed, 30 Sep 2026 08:01:12 +0900</pubDate><guid>https://mhub.uk/posts/q2-09-fapi-ssh2-tunnel-workaround/</guid><description>&lt;p&gt;양쪽 서버 다 정상, DNS도 정상, 그런데 도메인 하나만 계속 안 됐다. 원인을 끝까지 좁혀갔지만 결국 답을 못 찾은 채로 우회를 택한, 조금 다른 종류의 마무리 이야기다.&lt;/p&gt;
&lt;p&gt;블로그를 옮길 때 썼던 것과 완전히 같은 구조 — 같은 토큰으로 두 기기에 커넥터를 하나씩 띄우는 방식 — 을 API용 도메인과 SSH용 도메인에도 그대로 적용했다. 블로그 때는 문제없이 됐던 방식인데, 이번엔 그 도메인만 응답이 아예 오지 않았다.&lt;/p&gt;
&lt;p&gt;원인을 하나씩 지워나갔다. 새 기기의 origin(uvicorn 8000번 포트)은 로컬에서 직접 curl로 확인하니 즉시 정상 응답했다. 기존 기기(quantum2)의 origin도 마찬가지로 멀쩡했다. DNS 레코드도 API로 직접 조회해서 정확한 CNAME이 해당 터널을 가리키고 있는 걸 확인했다. Cloudflare Access 같은 접근 정책이 이 도메인에 걸려 있는지도 확인했지만 없었다. 심지어 같은 계정, 같은 방식으로 구성된 blog.mhub.uk는 그 사이에도 계속 200을 잘 돌려주고 있었다 — 즉 이 패턴 자체가 근본적으로 안 되는 게 아니라, 이 특정 터널 하나만 문제였다.&lt;/p&gt;</description></item><item><title>Global API Key까지 요청받았던 미스디렉션 — 진짜 원인은 안 뜬 서버였다</title><link>https://mhub.uk/posts/q2-08-persist-session-misdirection/</link><pubDate>Tue, 29 Sep 2026 08:01:16 +0900</pubDate><guid>https://mhub.uk/posts/q2-08-persist-session-misdirection/</guid><description>&lt;p&gt;블로그 이전이 순조롭게 끝난 뒤, 같은 방식이면 나머지도 쉬울 줄 알았다. 정기 자동 실행 작업(routine)을 새 기기에 묶는 작업에서 예상 밖의 막힘이 나왔다.&lt;/p&gt;
&lt;p&gt;Anthropic의 정기 실행 시스템은 특정 세션에 &amp;ldquo;영구 바인딩&amp;quot;할 수 있는 옵션(&lt;code&gt;persist_session&lt;/code&gt;)이 있다. 이게 켜져 있으면, 정해진 시각에 클라우드의 새 임시 샌드박스가 아니라 실제로 그 기기에서 계속 열려 있는 세션이 깨어나서 작업을 수행한다 — 로컬 파일이나 로컬 네트워크 접근이 필요한 작업에는 이게 필수다. 문제는 API로 새 정기 작업을 만들 때, 이 옵션이 어떤 조건에서 자동으로 켜지는지가 명확한 문서로 나와 있지 않았다는 것이다. 예전 사례를 근거로 &amp;ldquo;그 세션 안에서 직접 생성하면 자동으로 바인딩된다&amp;quot;고 안내했는데, 실제로 새 기기에서 똑같이 시도하니 &lt;code&gt;persist_session: false&lt;/code&gt;로 나왔다.&lt;/p&gt;</description></item><item><title>3단계 관리자 권한 구조와 계층 검사에서 빠뜨린 버그</title><link>https://mhub.uk/posts/s9-07-admin-permission-tiers/</link><pubDate>Tue, 29 Sep 2026 07:31:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-07-admin-permission-tiers/</guid><description>&lt;p&gt;여러 오픈채팅방을 동시에 운영하는 봇이라면, 관리자 권한을 한 단계로 두기 어렵습니다. 이번 글은 Iris 봇에 3단계 권한 구조를 만들면서 겪은, 계층 검사 로직에서 흔히 빠지기 쉬운 함정 하나를 기록합니다.&lt;/p&gt;
&lt;h2 id="왜-3단계가-필요했나"&gt;왜 3단계가 필요했나&lt;/h2&gt;
&lt;p&gt;봇을 여러 방에서 동시에 운영하다 보니 권한을 단순히 &amp;ldquo;관리자냐 아니냐&amp;quot;로만 나눌 수 없었습니다. 그래서 세 단계로 나눴습니다.&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;상위 등급은 하위 등급의 권한을 당연히 포함해야 합니다. 최상위 관리자는 메인관리자가 할 수 있는 일도, 일반관리자가 할 수 있는 일도 모두 할 수 있어야 합니다.&lt;/p&gt;</description></item><item><title>터널 토큰 하나로 블로그를 무중단 이전한 방법</title><link>https://mhub.uk/posts/q2-07-blog-migration-zero-downtime/</link><pubDate>Mon, 28 Sep 2026 08:05:22 +0900</pubDate><guid>https://mhub.uk/posts/q2-07-blog-migration-zero-downtime/</guid><description>&lt;p&gt;호스팅 기기를 완전히 바꾸는 작업, 그것도 도메인이 걸린 실제 운영 중인 블로그를 옮기는 작업은 보통 다운타임을 전제로 한다. 이번엔 그걸 피할 수 있는 구조가 이미 손에 있었다.&lt;/p&gt;
&lt;p&gt;blog.mhub.uk는 Cloudflare Tunnel로 외부에 노출되어 있었다. 터널이라는 게 결국 &amp;ldquo;이 도메인으로 오는 요청을, 이 토큰을 가진 어떤 커넥터든 받아서 로컬 포트로 넘겨준다&amp;quot;는 구조라는 걸 다시 떠올렸다 — 여기서 핵심은 &amp;ldquo;어떤 커넥터든&amp;quot;이라는 부분이다. 하나의 터널에 같은 토큰으로 여러 개의 &lt;code&gt;cloudflared&lt;/code&gt; 프로세스를 동시에 띄우면, Cloudflare가 이걸 자동으로 고가용성(HA) 구성으로 인식해서 트래픽을 분산하거나 장애 시 다른 쪽으로 넘긴다.&lt;/p&gt;</description></item><item><title>Blog logo made by ChatGPT</title><link>https://mhub.uk/posts/20260927-164152/</link><pubDate>Sun, 27 Sep 2026 16:41:52 +0900</pubDate><guid>https://mhub.uk/posts/20260927-164152/</guid><description>&lt;p&gt;&lt;img loading="lazy" src="https://mhub.uk/uploads/editor/1790494996622-40707ac0.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://mhub.uk/uploads/editor/1790560908189-0e8cbbfa.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://mhub.uk/uploads/editor/1790549338316-65e1c8a7.png"&gt;&lt;/p&gt;</description></item><item><title>Cloudflare Access가 조용히 막고 있던 자동 포스팅 파이프라인</title><link>https://mhub.uk/posts/q2-06-cloudflare-access-blocked-automation/</link><pubDate>Sun, 27 Sep 2026 08:02:32 +0900</pubDate><guid>https://mhub.uk/posts/q2-06-cloudflare-access-blocked-automation/</guid><description>&lt;p&gt;블로그 관리자 페이지를 만들면서 로그인 기능을 붙여야했다. 처음에는 구글 OTP 로그인을 붙이려고 했으나 블로그 도메인이 Cloudflare 기반으로 동작하는 점에 착안하여 Cloudflare Access로 해결하기로 했다. Cloudflare Access를 걸어서 관리자가 브라우저로 직접 접속할 때만 로그인 화면을 거치게 만드는 방식으로 구축했는데, 이 설정 하나가 전혀 예상 못 한 곳에서 자동화 파이프라인 전체를 조용히 멈춰 세웠다.&lt;/p&gt;
&lt;p&gt;Cloudflare Access는 경로 단위로 보호를 건다. 관리자 페이지 전체를 Cloudflare Access 로그인이 필요하도록 설정하면, 그 경로로 들어오는 모든 요청이 예외 없이 이 검사를 통과해야 한다 — 브라우저든, curl이든, 서버 간 자동화 스크립트든 구분하지 않는다. 문제는 코인 분석과 미국 증시 마감 시황 브리핑, 이 두 자동화 파이프라인이 새 글을 올릴 때 정확히 이 관리자 경로로 POST 요청을 보내고 있었다는 것이다.&lt;/p&gt;</description></item><item><title>채팅 랭킹 시스템과 모바일 브라우저 onchange 버그</title><link>https://mhub.uk/posts/s9-05-ranking-onchange-bug/</link><pubDate>Sun, 27 Sep 2026 07:00:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-05-ranking-onchange-bug/</guid><description>&lt;p&gt;이번 글은 겉보기엔 서버 쪽 로직이 아니라 아주 평범한 웹 프론트엔드 버그처럼 보였지만, 실제로는 모바일 브라우저의 특성을 몰라서 한참 헤맸던 사례입니다.&lt;/p&gt;
&lt;h2 id="채팅-랭킹과-등급-시스템"&gt;채팅 랭킹과 등급 시스템&lt;/h2&gt;
&lt;p&gt;Iris 봇에는 누적 채팅 수에 따라 자동으로 등급이 올라가는 채팅 랭킹 시스템이 있습니다. 이번에 포팅하면서 기존 방식(누적 채팅수 기반 자동 승급)에 더해, 포인트를 소모해서 등급을 직접 &amp;ldquo;구매&amp;quot;하는 방식도 선택적으로 켤 수 있도록 기능을 추가했습니다. 등급 자체의 개수(최대 6개)도 방마다 다르게 설정할 수 있게 만들었습니다.&lt;/p&gt;
&lt;h2 id="증상-줄이기는-되는데-늘리기가-안-된다"&gt;증상: 줄이기는 되는데 늘리기가 안 된다&lt;/h2&gt;
&lt;p&gt;이 등급 개수를 설정하는 UI는 숫자 입력창(스피너)이었습니다. 테스트해보니 이상한 패턴이 나타났습니다 — 값을 줄이는 건 잘 되는데, 늘리는 조작만 화면에 반영되지 않는 것처럼 보였습니다. 처음엔 서버 쪽 유효성 검사 로직이 최댓값을 잘못 제한하고 있는 줄 알고 그쪽을 한참 들여다봤지만 문제가 없었습니다.&lt;/p&gt;</description></item><item><title>애드센스 인증 실패, 범인은 Bot Fight Mode가 아니라 빠진 DNS였다</title><link>https://mhub.uk/posts/q2-05-adsense-bot-fight-mode-red-herring/</link><pubDate>Sat, 26 Sep 2026 08:02:00 +0900</pubDate><guid>https://mhub.uk/posts/q2-05-adsense-bot-fight-mode-red-herring/</guid><description>&lt;p&gt;구글 애드센스 사이트 소유권 인증이 계속 실패했다. &amp;ldquo;사이트를 확인할 수 없습니다&amp;quot;라는 메시지만 반복해서 뜨고, 메타태그도 붙였고 스니펫도 넣었는데 변화가 없었다.&lt;/p&gt;
&lt;p&gt;가장 먼저 의심한 건 Cloudflare의 Bot Fight Mode였다. 자동화된 크롤러를 막는 기능이니, 구글 애드센스의 검증 크롤러도 같이 막고 있을 가능성이 실무에서 꽤 흔한 원인이기 때문이다. 그런데 이 설정을 대시보드에서 찾는 것부터 난관이었다 — 클라우드플레어 UI가 한글로 번역되어 있다 보니 메뉴 이름과 실제 기능이 매끄럽게 매칭되지 않았고, 예전에 봤던 것 같은 비슷한 화면은 정작 모드를 바꿀 수 있게 되어 있지도 않았다.&lt;/p&gt;</description></item><item><title>문서화 안 된 카카오톡 프로토콜 리버스엔지니어링</title><link>https://mhub.uk/posts/s9-04-kakao-protocol-reverse-engineering/</link><pubDate>Sat, 26 Sep 2026 07:00:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-04-kakao-protocol-reverse-engineering/</guid><description>&lt;p&gt;Iris로 카카오톡 봇을 포팅하면서 가장 흥미로웠던 부분은, 공식 문서가 없는 프로토콜을 상대로 직접 원인을 추적해야 하는 순간들이었습니다. 이번 글은 그중에서도 가장 인상 깊었던 사례 하나를 기록합니다.&lt;/p&gt;
&lt;h2 id="만들고-싶었던-기능-메시지-삭제-감지"&gt;만들고 싶었던 기능: 메시지 삭제 감지&lt;/h2&gt;
&lt;p&gt;오픈채팅방을 운영하다 보면 메시지가 사라지는 경우가 두 가지 있습니다. 하나는 운영진이 특정 메시지를 &amp;ldquo;가리기&amp;rdquo;(숨김) 처리하는 경우, 다른 하나는 작성자 본인이 자기 메시지를 &amp;ldquo;스스로 삭제&amp;quot;하는 경우입니다. 이 둘을 구분해서 감지하는 기능을 만들고 싶었습니다 — 운영 로그를 남기거나, 특정 조건에서 자동 대응하기 위해서였습니다.&lt;/p&gt;</description></item><item><title>에디터가 세 번 망가진 이유 — 템플릿 리터럴 이스케이프 이중 해석 버그</title><link>https://mhub.uk/posts/q2-04-editor-template-literal-bug/</link><pubDate>Fri, 25 Sep 2026 08:02:07 +0900</pubDate><guid>https://mhub.uk/posts/q2-04-editor-template-literal-bug/</guid><description>&lt;p&gt;블로그 관리자 페이지의 글쓰기 에디터를 세 번 연속으로 다시 만들었다. 세 번 다 &amp;ldquo;고쳤다&amp;quot;고 생각했는데, 실제로 브라우저를 열면 카테고리 목록도 안 뜨고 툴바 버튼도 하나도 반응하지 않았다. 원인은 라이브러리가 아니라, 훨씬 더 근본적인 곳에 있었다.&lt;/p&gt;
&lt;p&gt;처음 시도는 Tiptap이었다. esm.sh CDN에서 &lt;code&gt;@tiptap/core&lt;/code&gt;, &lt;code&gt;@tiptap/starter-kit&lt;/code&gt;, &lt;code&gt;@tiptap/extension-placeholder&lt;/code&gt;를 각각 따로 불러왔는데, 패키지마다 별도로 번들링된 esm.sh 특성상 같은 클래스가 서로 다른 인스턴스로 중복 로드될 위험이 있는 조합이었다. 전혀 동작하지 않아서 Quill.js로 바꿨다 — 단일 UMD 스크립트라 이 위험 자체가 없는 선택이었다. 그런데도 여전히 안 됐다.&lt;/p&gt;</description></item><item><title>파이썬 모듈을 여러 파일로 쪼갤 때 만난 __main__ 순환참조 문제</title><link>https://mhub.uk/posts/s9-03-main-circular-import/</link><pubDate>Fri, 25 Sep 2026 07:00:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-03-main-circular-import/</guid><description>&lt;p&gt;지난 글에서 카카오톡 봇 Iris를 관리자·랭킹·경제·경마·미니게임·경매·AI 등 9개 파일로 나눴다는 이야기를 했습니다. 이번 글은 그 과정에서 실제로 부딪힌, 파이썬을 오래 써도 은근히 잘 모르고 지나가는 함정 하나를 기록합니다.&lt;/p&gt;
&lt;h2 id="증상-봇이-두-번-뜬다"&gt;증상: 봇이 두 번 뜬다&lt;/h2&gt;
&lt;p&gt;메인 파일 &lt;code&gt;irispy.py&lt;/code&gt;를 &lt;code&gt;python3 irispy.py&lt;/code&gt;로 직접 실행해서 봇을 띄웠습니다. 문제는 서브 모듈(예: 랭킹 기능 파일)에서 메인의 봇 인스턴스나 DB 헬퍼 함수를 쓰려고 &lt;code&gt;import irispy&lt;/code&gt;를 했을 때 발생했습니다. 봇이 두 개가 뜬 것처럼 동작하거나, 초기화 로직이 중복 실행되는 이상한 증상이 나타났습니다.&lt;/p&gt;</description></item><item><title>코인추천 자동화, Zapier에서 Make로 옮긴 이유</title><link>https://mhub.uk/posts/q2-03-coin-reco-zapier-to-make/</link><pubDate>Thu, 24 Sep 2026 08:01:55 +0900</pubDate><guid>https://mhub.uk/posts/q2-03-coin-reco-zapier-to-make/</guid><description>&lt;p&gt;하루 두세 번, 업비트 시세를 모아 코인 추천 글을 자동으로 쓰고 텔레그램에 발송하는 파이프라인이 있다. 처음엔 이 파이프라인의 연결 고리(시세 조회, 텔레그램 릴레이)를 전부 Zapier로 짰다. 문제는 그 편리함에 비용이 따라온다는 것이었다 — 할당량이 생각보다 빨리 소진됐다.&lt;/p&gt;
&lt;p&gt;같은 일을 Make로 옮기는 작업은 단순 이전이 아니었다. 텔레그램 발송처럼 &amp;ldquo;메시지 하나 보내고 끝&amp;quot;인 시나리오는 쉬웠지만, 업비트 시세 조회는 &amp;ldquo;HTTP 요청을 보내고 결과를 그 자리에서 돌려받아야&amp;rdquo; 하는 요청-응답형 작업이라 난이도가 달랐다. Make의 시나리오는 기본적으로 비동기 워크플로우 모델이라, 이걸 동기 호출처럼 쓰려면 HTTP 모듈로 요청을 구성한 뒤 별도로 응답 인터페이스를 설정하고, &lt;code&gt;responsive: true&lt;/code&gt; 옵션으로 호출하면 결과가 그 자리에서 돌아오게 만드는 구조가 필요했다.&lt;/p&gt;</description></item><item><title>메신저봇R에서 Iris(파이썬)로 카카오톡 봇 포팅하기</title><link>https://mhub.uk/posts/s9-02-messengerbotr-to-iris/</link><pubDate>Thu, 24 Sep 2026 07:00:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-02-messengerbotr-to-iris/</guid><description>&lt;p&gt;지난 글에서 루팅된 갤럭시 S9 위에 Termux + proot-distro로 서버 환경을 만든 이야기를 했습니다. 이번 글은 그 위에 실제로 올린 첫 번째이자 가장 중요한 서비스, 카카오톡 챗봇 &amp;ldquo;Iris&amp;rdquo; 이야기입니다.&lt;/p&gt;
&lt;h2 id="iris란"&gt;Iris란&lt;/h2&gt;
&lt;p&gt;Iris는 루팅된 안드로이드 기기에서 카카오톡 클라이언트의 로컬 메시지 DB를 직접 읽고, &lt;code&gt;app_process&lt;/code&gt;로 띄운 데몬이 HTTP API(&lt;code&gt;127.0.0.1:3000&lt;/code&gt;)와 웹소켓 이벤트를 제공하는 프레임워크입니다. 카카오톡 자체의 공식 API가 아니라, 기기에 이미 로그인되어 있는 카카오톡 앱의 데이터를 활용하는 방식이라 루팅된 기기가 전제 조건입니다.&lt;/p&gt;
&lt;h2 id="왜-포팅이-필요했나"&gt;왜 포팅이 필요했나&lt;/h2&gt;
&lt;p&gt;원래는 &amp;ldquo;메신저봇R&amp;quot;이라는 Node.js 기반 봇으로 채팅 랭킹, 아이템 경제, 경마 게임, 미니게임, 경매, AI 질문 응답 같은 기능들을 이미 운영하고 있었습니다. 이번 작업은 그 기존 코드와 기획을 Iris(파이썬) 환경으로 전면 포팅하는 것이었습니다. 단순히 언어만 바꾸는 게 아니라, 프레임워크가 제공하는 이벤트/API 모델 자체가 다르기 때문에 상당 부분을 다시 설계해야 했습니다.&lt;/p&gt;</description></item><item><title>AI 폴백 서버와 직접 만든 하루 토큰 예산 추적기</title><link>https://mhub.uk/posts/q2-02-ai-fallback-token-budget/</link><pubDate>Wed, 23 Sep 2026 08:02:11 +0900</pubDate><guid>https://mhub.uk/posts/q2-02-ai-fallback-token-budget/</guid><description>&lt;p&gt;무료 AI API 하나만 물려서 쓰면 반드시 하루 어딘가에서 한도에 걸린다. 이걸 해결하려고 만든 게 3단 폴백 구조의 작은 FastAPI 서버였다.&lt;/p&gt;
&lt;p&gt;순서는 Cloudflare Workers AI → Groq → Gemini다. 처음엔 Gemini를 1순위로 뒀는데, 자동응답 기능이 Gemini의 하루 500회 한도를 낮 시간에 다 써버리면 정작 사람이 직접 명령어를 칠 때 쓸 한도가 안 남는 문제가 있었다. 그래서 &amp;ldquo;사실상 무제한&amp;quot;이라고 생각했던 Cloudflare Workers AI를 앞으로 보내고, 응답 형식이 엄격하게 필요한 자동응답 호출만 별도로 Groq부터 시작하도록 순서를 분기했다.&lt;/p&gt;</description></item><item><title>안드로이드가 Termux를 자꾸 죽이는 문제, MacroDroid로 해결하기</title><link>https://mhub.uk/posts/s9-01b-termux-macrodroid/</link><pubDate>Wed, 23 Sep 2026 07:00:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-01b-termux-macrodroid/</guid><description>&lt;p&gt;루팅을 마치고 Termux + proot-distro로 서버 환경까지 만들었는데, 정작 그 위에서 tmux 세션들이 가만히 있다가 조용히 죽어있는 일이 반복됐습니다. 카카오톡 봇이 이유 없이 응답을 멈추길래 확인해보면, Termux 프로세스 자체가 안드로이드 시스템에 의해 강제 종료돼 있었습니다. 이번 글은 그 문제를 해결하기까지의 과정입니다.&lt;/p&gt;
&lt;h2 id="증상-화면을-안-보고-있으면-죽는다"&gt;증상: 화면을 안 보고 있으면 죽는다&lt;/h2&gt;
&lt;p&gt;패턴은 명확했습니다. 화면을 켜서 Termux를 보고 있을 때는 멀쩡하다가, 다른 앱을 쓰거나 화면을 꺼두고 시간이 좀 지나면 tmux 세션 안의 프로세스들이 통째로 죽어있는 경우가 잦았습니다. 전형적인 안드로이드의 백그라운드 프로세스 관리 — 특히 삼성 기기의 배터리 최적화가 공격적이기로 유명합니다 — 에 의해 Termux가 &amp;ldquo;안 쓰는 앱&amp;quot;으로 분류되어 정리되는 것으로 보였습니다.&lt;/p&gt;</description></item><item><title>루팅된 갤럭시 S9 위에 상시 구동 서버 만들기</title><link>https://mhub.uk/posts/s9-01-rooted-server-environment/</link><pubDate>Tue, 22 Sep 2026 07:00:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-01-rooted-server-environment/</guid><description>&lt;p&gt;이 블로그 자체가 유휴 휴대폰을 서버로 쓰는 프로젝트인데, 사실 이 방식을 먼저 시도해본 기기가 하나 더 있습니다. 오래된 갤럭시 S9입니다. 이 시리즈는 그 S9 위에서 카카오톡 챗봇 &amp;ldquo;Iris&amp;quot;를 돌리고, 여러 기능을 개발하면서 겪은 과정을 기록합니다.&lt;/p&gt;
&lt;h2 id="루팅-이후-서버-환경부터"&gt;루팅 이후, 서버 환경부터&lt;/h2&gt;
&lt;p&gt;지난 글에서 이 S9을 루팅하기까지의 과정(그리고 일반적인 방법이 안 통해 TWRP로 돌아갔던 이야기)을 다뤘습니다. 이번 글은 그 이후, 루팅된 안드로이드 위에 실제로 상시 구동되는 서버 환경을 어떻게 쌓아올렸는지부터 시작합니다.&lt;/p&gt;
&lt;h2 id="termux--proot-distro-조합"&gt;Termux + proot-distro 조합&lt;/h2&gt;
&lt;p&gt;기본 구조는 이 블로그의 다른 서버(휴대폰 서버)와 동일합니다. Termux를 리눅스 유저랜드로 쓰고, 그 위에 &lt;code&gt;proot-distro&lt;/code&gt;로 우분투 컨테이너를 하나 더 얹습니다. 루트 권한이 있는 기기라고 해도 Claude CLI가 안드로이드 환경에서 구동되지 않기 때문에 이 기기를 agent로 두고 Claude를 통해 이 휴대폰 서버에서 돌아가는 모든 서비스를 원격으로 통제하기 위해서는 우분투 환경이 필수적이었습니다.&lt;/p&gt;</description></item><item><title>코인추천 자동화 세션 회고 — 탄생부터 인수인계까지 13일</title><link>https://mhub.uk/posts/20260921-235357/</link><pubDate>Mon, 21 Sep 2026 23:53:00 +0900</pubDate><guid>https://mhub.uk/posts/20260921-235357/</guid><description>&lt;p&gt;2026년 9월 8일 만들어져 9월 21일 인수인계로 임무를 마친 코인추천 자동화 세션의 13일간 기록입니다. 클라우드 라우틴 실험으로 시작해 포지션 추적표, 개인 블로그 미러링, SEO 개선을 거쳐 새 서버(S9)로 넘어가기까지의 과정을 정리했습니다.&lt;/p&gt;
&lt;h2 id="탄생--라우틴-하나로-시작된-실험"&gt;탄생 — 라우틴 하나로 시작된 실험&lt;/h2&gt;
&lt;p&gt;2026년 9월 8일, 업비트 코인 시세를 하루 여러 번 분석해 워드프레스에 자동으로 포스팅하는 &amp;ldquo;코인추천&amp;rdquo; 자동화를 위해 이 세션이 만들어졌습니다. 당시로선 다소 실험적인 방식이었습니다 — claude.ai의 &lt;code&gt;RemoteTrigger&lt;/code&gt;(클라우드 라우틴) 기능에 &lt;code&gt;persist_session&lt;/code&gt;이라는, 문서화되지 않은 옵션을 걸어서 매번 새 클라우드 샌드박스를 띄우는 대신 &lt;strong&gt;하나의 세션을 계속 재사용&lt;/strong&gt;하도록 만든 것입니다. 스케줄은 하루 3번, 한국시간 오전 7시·오후 1시·저녁 7시였습니다.&lt;/p&gt;</description></item><item><title>폰 한 대로 상시 구동 서버 만들기 — Remote Control로 시작한 quantum2</title><link>https://mhub.uk/posts/q2-01-remote-control-server/</link><pubDate>Mon, 21 Sep 2026 16:46:24 +0900</pubDate><guid>https://mhub.uk/posts/q2-01-remote-control-server/</guid><description>&lt;p&gt;폰 한 대로 상시 구동 서버를 만들 수 있을까. 답은 &amp;ldquo;된다&amp;quot;였지만, 그 과정은 생각보다 여러 겹의 장애물을 넘는 일이었다.&lt;/p&gt;
&lt;p&gt;시작은 Termux였다. 안드로이드 폰 안에 리눅스 환경을 올려서 코드를 짜고 서버를 띄우는 건 어렵지 않다. 문제는 그 위에 Claude Code를 올리려는 순간 터졌다 — &lt;code&gt;npm install -g @anthropic-ai/claude-code&lt;/code&gt;가 &lt;code&gt;linux-arm64-android&lt;/code&gt; 네이티브 바이너리가 없다는 이유로 실패했다. 안드로이드용 Node.js 바이너리 생태계가 표준 리눅스 배포판과 미묘하게 다르다는 걸 이때 처음 체감했다.&lt;/p&gt;
&lt;p&gt;해법은 한 겹 더 감싸는 것이었다. &lt;code&gt;proot-distro&lt;/code&gt;로 Termux 안에 진짜 우분투 컨테이너를 하나 더 띄우고, 그 안에서 Claude Code를 설치했다. &lt;code&gt;proot-distro login ubuntu&lt;/code&gt;로 들어가면 완전한 리눅스 환경이라 패키지 생태계 문제가 사라진다. 대신 파일 경로가 두 겹이 됐다 — Termux 쪽에서 보는 &lt;code&gt;/data/data/com.termux/files/home/phoneserver&lt;/code&gt;와 우분투 컨테이너 안에서 보는 &lt;code&gt;/root/termux-home/phoneserver&lt;/code&gt;가 바인드 마운트로 연결된 같은 파일이다. 어느 쪽에서 편집해도 같은 파일이 바뀌지만, 이 이중 구조를 모르면 &amp;ldquo;분명히 저장했는데 안 보인다&amp;quot;는 혼란에 빠지기 딱 좋다.&lt;/p&gt;</description></item><item><title>갤럭시 S9 루팅기 — 일반적인 방법이 안 통해서 TWRP로 뚫은 이야기</title><link>https://mhub.uk/posts/s9-00-rooting-twrp/</link><pubDate>Mon, 21 Sep 2026 07:00:00 +0900</pubDate><guid>https://mhub.uk/posts/s9-00-rooting-twrp/</guid><description>&lt;p&gt;카카오톡 챗봇 Iris를 돌리려면 루팅된 안드로이드 기기가 필요합니다. 이 시리즈의 무대인 갤럭시 S9도 처음엔 그냥 평범한 완제품 폰이었습니다. 이번 글은 이 폰을 루팅하기까지 겪은 과정 — 정확히는, &amp;ldquo;보통 이렇게 하면 된다&amp;quot;는 방법이 이 기종에서는 안 통해서 다른 길로 돌아간 이야기입니다.&lt;/p&gt;
&lt;h2 id="1차-시도-magisk-패치--오딘"&gt;1차 시도: Magisk 패치 + 오딘&lt;/h2&gt;
&lt;p&gt;삼성 기기를 루팅하는 가장 흔한 방법은 이렇습니다. 기기의 순정 AP(부트 이미지가 포함된 펌웨어) 파일을 받아서, Magisk 앱으로 그 이미지를 패치한 뒤, 그 패치된 AP를 오딘(Odin)으로 다시 기기에 플래시하는 방식입니다. 커스텀 리커버리를 따로 설치할 필요가 없고, 절차도 비교적 간단해서 여러 기종에서 표준처럼 쓰이는 방법입니다.&lt;/p&gt;</description></item><item><title>무료로 볼 수 있는 금융 데이터 사이트 vs 크롤링 막힌 사이트 정리</title><link>https://mhub.uk/posts/it-12-finance-data-scraping-check/</link><pubDate>Sun, 20 Sep 2026 20:40:00 +0900</pubDate><guid>https://mhub.uk/posts/it-12-finance-data-scraping-check/</guid><description>&lt;p&gt;미국 증시 마감 시황 루틴을 운영하다 보면 자연스럽게 &amp;ldquo;이 데이터를 더 정확하고 안정적으로 가져올 방법이 없을까&amp;quot;라는 질문을 하게 됩니다. 유료 금융 데이터 API는 대부분 비용이 상당하지만, 웹페이지 형태로는 무료로 볼 수 있는 곳도 많습니다. 문제는 그런 사이트일수록 크롤링을 막아둔 경우가 많다는 점입니다. 실제로 몇몇 후보 사이트를 대상으로 간단히 점검해본 결과를 정리합니다.&lt;/p&gt;
&lt;h2 id="점검-방법"&gt;점검 방법&lt;/h2&gt;
&lt;p&gt;사이트마다 &lt;code&gt;robots.txt&lt;/code&gt;를 먼저 확인하고, 이어서 일반적인 페이지 요청(&lt;code&gt;curl&lt;/code&gt;)이 실제로 콘텐츠를 돌려주는지 테스트했습니다. 별도의 브라우저 자동화나 로그인 없이, 가장 단순한 방식으로 접근 가능한지만 확인하는 수준의 점검입니다.&lt;/p&gt;</description></item><item><title>이미지에 한글 텍스트 그릴 때 겪은 폰트 문제와 해결 과정</title><link>https://mhub.uk/posts/it-11-pil-korean-font-bug/</link><pubDate>Sun, 20 Sep 2026 20:20:00 +0900</pubDate><guid>https://mhub.uk/posts/it-11-pil-korean-font-bug/</guid><description>&lt;p&gt;이 블로그의 글마다 붙어 있는 썸네일 이미지들은 전부 Python의 Pillow(PIL) 라이브러리로 직접 그린 것입니다. 그런데 이 기능을 처음 만들 때, 정작 가장 중요한 한글 텍스트가 화면에 텅 빈 네모 박스로만 나오는 문제를 겪었습니다. 이번 글은 그 원인과 해결 과정입니다.&lt;/p&gt;
&lt;h2 id="증상-글자가-전부-빈-네모로-나온다"&gt;증상: 글자가 전부 빈 네모로 나온다&lt;/h2&gt;
&lt;p&gt;썸네일 생성 스크립트를 처음 실행했을 때, 제목이나 카테고리 배지에 들어가야 할 한글 텍스트가 화면에 아예 보이지 않거나 깨진 네모(이른바 &amp;ldquo;tofu&amp;rdquo;)로만 표시됐습니다. 처음엔 좌표 계산이 잘못됐나 의심했지만, 텍스트의 bbox(경계 상자)를 찍어보니 높이가 0으로 나오는 경우까지 있어 좌표 문제가 아니라는 걸 알게 됐습니다.&lt;/p&gt;</description></item><item><title>Claude Code 예약 루틴(Cloud Trigger)으로 알아서 돌아가는 자동화 만들기</title><link>https://mhub.uk/posts/it-10-claude-code-cloud-trigger/</link><pubDate>Sun, 20 Sep 2026 20:00:00 +0900</pubDate><guid>https://mhub.uk/posts/it-10-claude-code-cloud-trigger/</guid><description>&lt;p&gt;지금까지 다룬 코인 추천, 시황 브리핑 같은 루틴들은 전부 휴대폰 안에서 직접 크론(cron)으로 도는 게 아니라, Claude Code의 예약 루틴(Cloud Trigger) 기능을 통해 클라우드에서 독립적으로 실행됩니다. 이번 글은 그 구조가 로컬 크론과 어떻게 다른지, 어떤 장단점이 있었는지 정리합니다.&lt;/p&gt;
&lt;h2 id="로컬-크론이-아니라-완전히-격리된-클라우드-세션"&gt;로컬 크론이 아니라 완전히 격리된 클라우드 세션&lt;/h2&gt;
&lt;p&gt;이 방식의 가장 큰 특징은 실행 주체가 이 휴대폰이 아니라는 점입니다. 정해진 cron 표현식(예: 매일 06:55 UTC 기준 변환)에 맞춰 클라우드에서 새로운 세션이 열리고, 그 세션이 지정된 저장소(GitHub repo)의 지침 문서를 읽어 작업을 수행합니다. 휴대폰이 꺼져 있거나 네트워크가 끊겨 있어도 예약된 실행 자체는 영향을 받지 않습니다.&lt;/p&gt;</description></item><item><title>워드프레스와 자체 블로그에 동시 포스팅하고 짧은 링크만 텔레그램으로 보내는 구조</title><link>https://mhub.uk/posts/it-09-wordpress-blog-crosspost/</link><pubDate>Sun, 20 Sep 2026 19:40:00 +0900</pubDate><guid>https://mhub.uk/posts/it-09-wordpress-blog-crosspost/</guid><description>&lt;p&gt;코인 추천과 시황 브리핑 루틴이 글을 다 쓰고 나면, 그다음은 발행입니다. 이 서버는 글 하나를 두 플랫폼(워드프레스 + 이 블로그)에 동시에 올리고, 알림은 텔레그램으로 짧은 링크만 보내는 구조로 되어 있습니다. 왜 굳이 이렇게 이중으로 구성했는지 정리합니다.&lt;/p&gt;
&lt;h2 id="왜-두-플랫폼에-동시-발행하나"&gt;왜 두 플랫폼에 동시 발행하나&lt;/h2&gt;
&lt;p&gt;이 블로그(blog.mhub.uk)는 직접 만든 Hugo 사이트라 레이아웃과 카테고리를 완전히 통제할 수 있지만, 아직 검색엔진이나 구독자 기반이 약합니다. 반면 워드프레스는 이미 도달 범위와 검색 노출이 어느 정도 갖춰진 플랫폼입니다. 같은 콘텐츠를 두 곳에 올려 각 플랫폼의 장점을 동시에 취하는 쪽을 택했습니다.&lt;/p&gt;</description></item><item><title>매일 미국 증시 마감 시황을 자동으로 조사·검증·발행하는 파이프라인</title><link>https://mhub.uk/posts/it-08-market-brief-automation/</link><pubDate>Sun, 20 Sep 2026 19:20:00 +0900</pubDate><guid>https://mhub.uk/posts/it-08-market-brief-automation/</guid><description>&lt;p&gt;&amp;ldquo;미국 시황&amp;rdquo; 카테고리의 글들도 매일 아침 자동으로 실행되는 루틴의 결과물입니다. 코인 추천 루틴과 달리 이쪽은 실시간 시세 API보다 웹 검색과 교차검증에 훨씬 더 의존한다는 점이 특징입니다. 이번 글은 그 파이프라인 구조를 정리합니다.&lt;/p&gt;
&lt;h2 id="매일-아침-6시-55분kst에-실행"&gt;매일 아침 6시 55분(KST)에 실행&lt;/h2&gt;
&lt;p&gt;미국 증시는 동부시간 오후 4시에 마감하는데, 이는 한국시간 기준 다음날 새벽~아침입니다. 그래서 이 루틴은 매일 아침 6시 55분경 실행되어, 직전 미국 마감 세션의 지수·채권·환율·원자재·경제지표를 조사합니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="시황 브리핑 흐름" loading="lazy" src="https://mhub.uk/images/it-series/it-08-market-brief-automation-flow.png"&gt;&lt;/p&gt;
&lt;h2 id="가장-중요한-규칙-같은-수치를-2개-이상-출처로-교차검증"&gt;가장 중요한 규칙: 같은 수치를 2개 이상 출처로 교차검증&lt;/h2&gt;
&lt;p&gt;자동화된 조사에서 가장 위험한 건 &amp;ldquo;그럴듯하지만 틀린 숫자&amp;quot;를 그대로 받아쓰는 것입니다. 그래서 이 루틴은 지수·금리·환율 등 핵심 수치를 반드시 서로 다른 두 곳 이상의 출처에서 확인한 뒤에만 최종 글에 반영하도록 설계돼 있습니다. 실제로 검색 결과가 날짜를 착각해 예전 수치를 다시 보여주거나, 출처마다 미묘하게 다른 숫자를 내놓는 경우가 꽤 있었는데, 이런 경우는 재검색하거나 &amp;ldquo;확인 불가&amp;quot;로 명시하고 넘어갑니다.&lt;/p&gt;</description></item><item><title>업비트 시세를 스크리닝해 코인 추천 글을 자동 발행하는 루틴 만들기</title><link>https://mhub.uk/posts/it-07-coin-reco-automation/</link><pubDate>Sun, 20 Sep 2026 19:00:00 +0900</pubDate><guid>https://mhub.uk/posts/it-07-coin-reco-automation/</guid><description>&lt;p&gt;이 블로그의 &amp;ldquo;코인 분석&amp;rdquo; 카테고리에 올라오는 글들은 사람이 매번 손으로 쓴 게 아니라, 하루 세 번(07시·13시·19시) 자동으로 실행되는 루틴이 작성해 발행한 것입니다. 이번 글은 그 자동화가 어떻게 만들어졌는지, IT 시리즈 관점에서 정리합니다.&lt;/p&gt;
&lt;h2 id="루틴이-하는-일"&gt;루틴이 하는 일&lt;/h2&gt;
&lt;p&gt;정해진 시각이 되면 업비트 시세를 스크리닝해서 특정 기술적 조건(변동성 패턴, 박스권 이탈, 반등 신호 등)을 만족하는 종목을 후보로 골라내고, 각 후보에 대해 손절가와 목표가를 계산합니다. 그 결과를 정해진 형식의 글로 작성해 자동으로 발행까지 마칩니다.&lt;/p&gt;
&lt;p&gt;&lt;img alt="추천 루틴 흐름" loading="lazy" src="https://mhub.uk/images/it-series/it-07-coin-reco-automation-flow.png"&gt;&lt;/p&gt;</description></item><item><title>AI 모델별 무료 한도 관리 — 토큰을 아끼기 위해 실제로 적용한 것들</title><link>https://mhub.uk/posts/it-06-ai-quota-management/</link><pubDate>Sun, 20 Sep 2026 18:40:00 +0900</pubDate><guid>https://mhub.uk/posts/it-06-ai-quota-management/</guid><description>&lt;p&gt;앞선 글에서 다룬 다중 AI 모델 폴백 구조가 제대로 작동하려면, 지금 각 모델의 한도가 얼마나 남았는지를 정확히 알아야 합니다. 이번 글은 무료 티어 한도 안에서 챗봇을 24시간 돌리기 위해 실제로 적용한 관리 방법들입니다.&lt;/p&gt;
&lt;h2 id="사용량을-모델별로-따로-기록한다"&gt;사용량을 모델별로 따로 기록한다&lt;/h2&gt;
&lt;p&gt;모든 AI 호출 결과를 로그 파일에 남기되, 모델별로 분리해서 관리합니다. Groq는 일일 토큰 총량을 별도 파일에 누적 기록하고, 다른 모델들도 각자의 한도 체계(요청 수 기준, 토큰 수 기준 등)에 맞춰 따로 추적합니다. 이렇게 모델별로 분리해두지 않으면 폴백 순서를 정할 때 &amp;ldquo;지금 어떤 모델이 여유가 있는지&amp;quot;를 판단할 근거가 없습니다.&lt;/p&gt;</description></item><item><title>카카오톡 챗봇에 여러 AI 모델을 자동 폴백으로 엮은 구조</title><link>https://mhub.uk/posts/it-05-kakao-bot-ai-fallback/</link><pubDate>Sun, 20 Sep 2026 18:20:00 +0900</pubDate><guid>https://mhub.uk/posts/it-05-kakao-bot-ai-fallback/</guid><description>&lt;p&gt;이 휴대폰 서버의 핵심 기능 중 하나는 카카오톡으로 말을 걸면 AI가 답해주는 개인용 챗봇입니다. 처음엔 모델 하나만 붙여도 될 줄 알았는데, 무료 티어의 일일/분당 한도에 자주 걸리면서 &amp;ldquo;한도가 다 찼을 때 챗봇이 그냥 조용해지는&amp;rdquo; 문제를 겪었고, 이를 해결하기 위해 여러 AI 모델을 자동으로 오가는 폴백 구조를 만들게 됐습니다.&lt;/p&gt;
&lt;h2 id="문제-모델-하나에-의존하면-챗봇이-먹통이-된다"&gt;문제: 모델 하나에 의존하면 챗봇이 먹통이 된다&lt;/h2&gt;
&lt;p&gt;무료 API 한도는 모델마다 다르게 책정되어 있고, 사용량이 몰리는 시간대에는 순식간에 소진됩니다. 한 모델만 쓰면 한도를 넘는 순간부터 챗봇이 응답하지 않는 시간대가 생깁니다. 개인용으로 24시간 돌리는 챗봇이라면 이건 꽤 치명적인 문제였습니다.&lt;/p&gt;</description></item><item><title>apt-get이 자꾸 멈출 때: GitHub Releases .deb 직접 설치로 우회하기</title><link>https://mhub.uk/posts/it-04-apt-get-dpkg-workaround/</link><pubDate>Sun, 20 Sep 2026 18:00:00 +0900</pubDate><guid>https://mhub.uk/posts/it-04-apt-get-dpkg-workaround/</guid><description>&lt;p&gt;이 서버를 세팅하면서 가장 자주 반복된 문제 하나를 꼽으라면 단연 &lt;code&gt;apt-get&lt;/code&gt;입니다. Hugo, Caddy, 한글 폰트 패키지까지 — 덩치가 좀 있는 패키지를 설치하려 할 때마다 모바일 네트워크 환경에서 다운로드가 멈춘 채로 응답이 없는 상황을 여러 번 겪었습니다. 이번 글은 그 문제를 어떻게 우회했는지 기록입니다.&lt;/p&gt;
&lt;h2 id="증상-멈췄는지-느린-건지-구분이-안-된다"&gt;증상: 멈췄는지 느린 건지 구분이 안 된다&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;apt-get install -y &amp;lt;패키지&amp;gt;&lt;/code&gt;를 실행하면 진행률 표시 없이 그냥 멈춘 것처럼 보이는 경우가 많았습니다. &lt;code&gt;timeout 60&lt;/code&gt;으로 감싸서 실행해도 60초 안에 끝나지 않고 그대로 종료(exit code 143)되는 일이 반복됐는데, 재시도해도 같은 패턴이 이어졌습니다. 원인은 명확히 특정하지는 못했지만, 모바일 데이터/와이파이 전환 구간에서 저장소 미러 서버와의 연결이 불안정했던 것으로 추정합니다.&lt;/p&gt;</description></item><item><title>Hugo + PaperMod로 블로그 세팅하고 커스텀 홈 레이아웃 얹기</title><link>https://mhub.uk/posts/it-03-hugo-papermod-layout/</link><pubDate>Sun, 20 Sep 2026 17:40:00 +0900</pubDate><guid>https://mhub.uk/posts/it-03-hugo-papermod-layout/</guid><description>&lt;p&gt;지금 보고 계신 이 블로그(blog.mhub.uk) 자체가 이 시리즈의 소재입니다. Hugo 정적 사이트 생성기와 PaperMod 테마로 시작해서, 최근에는 카테고리 사이드바·추천글·전체 글 목록으로 이루어진 3분할 홈 화면으로 직접 레이아웃을 손봤습니다.&lt;/p&gt;
&lt;h2 id="왜-hugo인가"&gt;왜 Hugo인가&lt;/h2&gt;
&lt;p&gt;Hugo는 빌드마다 서버 자원을 거의 쓰지 않는 정적 사이트 생성기입니다. 휴대폰처럼 자원이 넉넉하지 않은 환경에서는 매 요청마다 서버 사이드 렌더링을 하는 방식보다, 미리 HTML을 만들어 두고 Caddy가 그냥 파일을 서빙하는 쪽이 훨씬 가볍습니다. 글을 쓰거나 설정을 바꿀 때만 &lt;code&gt;hugo --minify&lt;/code&gt;로 다시 빌드하면 끝입니다.&lt;/p&gt;</description></item><item><title>Cloudflare Tunnel과 Caddy로 집 밖에서도 접속되는 개인 서버 만들기</title><link>https://mhub.uk/posts/it-02-cloudflare-tunnel-caddy/</link><pubDate>Sun, 20 Sep 2026 17:20:00 +0900</pubDate><guid>https://mhub.uk/posts/it-02-cloudflare-tunnel-caddy/</guid><description>&lt;p&gt;휴대폰 안에 리눅스 환경을 만들었다고 해도, 그 안에서 돌아가는 웹사이트나 API를 외부에서 접속할 수 없다면 서버로서 의미가 없습니다. 문제는 이 폰이 가정용 모바일/와이파이 네트워크 뒤에 있어서 고정 공인 IP도, 포트포워딩도 쓸 수 없다는 점이었습니다. 이번 글은 이 문제를 Cloudflare Tunnel과 Caddy 조합으로 해결한 과정입니다.&lt;/p&gt;
&lt;h2 id="cloudflare-tunnel-포트를-열지-않고-밖으로-나가는-통로"&gt;Cloudflare Tunnel: 포트를 열지 않고 밖으로 나가는 통로&lt;/h2&gt;
&lt;p&gt;Cloudflare Tunnel(cloudflared)은 서버 쪽에서 Cloudflare로 나가는 아웃바운드 연결만 유지하면, 외부 요청이 그 터널을 통해 들어오는 구조입니다. 방화벽에서 인바운드 포트를 하나도 열 필요가 없다는 게 핵심입니다. 도메인(blog.mhub.uk 등)을 Cloudflare에 연결해두고, &lt;code&gt;cloudflared&lt;/code&gt;가 로컬에서 실행 중인 서비스로 트래픽을 넘겨주도록 설정했습니다.&lt;/p&gt;</description></item><item><title>Termux + proot-Ubuntu로 휴대폰에 리눅스 환경 만들기</title><link>https://mhub.uk/posts/it-01-termux-proot/</link><pubDate>Sun, 20 Sep 2026 17:00:00 +0900</pubDate><guid>https://mhub.uk/posts/it-01-termux-proot/</guid><description>&lt;p&gt;서랍 속 유휴 휴대폰을 서버로 쓰기로 결심한 다음 가장 먼저 부딪힌 질문은 &amp;ldquo;루트 권한도 없는 안드로이드 폰에서 어떻게 진짜 서버 환경을 만들 것인가&amp;quot;였습니다. 이번 글에서는 실제로 이 서버를 세팅할 때 사용한 Termux + proot-distro 조합을 정리합니다.&lt;/p&gt;
&lt;h2 id="왜-termux인가"&gt;왜 Termux인가&lt;/h2&gt;
&lt;p&gt;Termux는 안드로이드 위에서 동작하는 터미널 에뮬레이터이자 패키지 관리 환경입니다. 루트 권한 없이도 &lt;code&gt;apt&lt;/code&gt; 기반 패키지 설치, SSH, Python, Node.js 실행이 가능합니다. 다만 Termux 자체의 리눅스 환경은 안드로이드 샌드박스 제약 때문에 일반 리눅스 배포판과 100% 동일하지는 않습니다. 그래서 그 위에 &lt;code&gt;proot-distro&lt;/code&gt;를 얹어 진짜 우분투 환경을 통째로 격리 실행하는 방식을 선택했습니다.&lt;/p&gt;</description></item><item><title>유휴 휴대폰으로 서버를 만든 이유</title><link>https://mhub.uk/posts/hello-world/</link><pubDate>Sun, 13 Sep 2026 20:48:40 +0900</pubDate><guid>https://mhub.uk/posts/hello-world/</guid><description>&lt;p&gt;서랍 속에 수년째 잠들어 있던 아주 오래된 구형 안드로이드 폰이 있었습니다. 업데이트도 중단되고 최근 앱을 실행하기에는 메모리도 부족해서 사용할 곳이 없는 고철덩어리였습니다. 서랍 속에서 완전히 죽은 기기로 남아 있던 이 휴대폰을 사용할 수 있을 방법이 없을까 고민했습니다. 이 블로그의 &amp;ldquo;IT&amp;rdquo; 카테고리는 그 폰을 어떻게든 다시 살려보자는 생각에서 시작됐습니다.&lt;/p&gt;
&lt;h2 id="왜-하필-휴대폰이었나"&gt;왜 하필 휴대폰이었나&lt;/h2&gt;
&lt;p&gt;집에 굴러다니는 구형 휴대폰보다는 라즈베리파이나 미니PC를 쓰는 게 훨씬 정석적인 방법이라는 건 알고 있었습니다. 하지만 따로 하드웨어를 사야 하고, 전용 전원과 공간도 필요합니다. 반면 안드로이드 폰은 이미 배터리(=UPS 역할)와 와이파이·모바일 데이터(=이중 네트워크)를 갖춘 완결된 소형 컴퓨터입니다. 성능은 요즘 서버용 CPU에 비할 바는 아니지만, 개인이 돌리는 자동화 스크립트나 소규모 챗봇 정도는 감당하기에 충분하다고 판단했습니다. &amp;ldquo;안 쓰는 자원을 최대한 활용해보자&amp;quot;는 것이 가장 솔직한 동기였습니다.&lt;/p&gt;</description></item></channel></rss>