옵시디언에 QWEN3.6-35B AI를 심었다 — Mac Mini M4 Pro + vLLM-MLX + Qwen3.6 로컬 설치 실전기

3,000개가 넘는 Obsidian 메모를 누군가의 서버에 올리는 건, 솔직히 마음이 불편하다.

ChatGPT나 Claude에게 “내 노트 기반으로 요약해줘”를 시키려면 결국 업로드가 필요하고,
그 순간 내 사고의 원석이 외부 서버로 떠나기 때문이다. 그래서 직접 만들었다 😎.

Mac Mini M4 Pro 한 대 + vLLM-MLX + Qwen3.6-35B + Obsidian 플러그인.

이 조합이면 메모 검색, 요약, 작성, 웹 검색까지 — 모든 토큰이 책상 위 알루미늄 박스 안에서 끝난다!

📌 이 글의 핵심 요약

  • Qwen3.6-35B는 MoE 구조 덕분에 19GB 가중치, 3B 수준 속도, 35B 수준 품질을 제공한다 💡
  • vLLM-MLX는 Mac의 통합 메모리를 직접 활용해 PCIe 복사 병목이 없다.
  • Obsidian 플러그인은 ReAct 루프로 7개 도구(검색·요약·작성·웹 등)를 자동으로 연결해서 처리한다.
  • 모든 처리가 로컬에서 끝나 프라이버시와 오프라인 사용이 동시에 보장된다.

1. 왜 로컬 LLM인가? — 클라우드로 보낼 수 없는 이유

로컬 LLM 설치를 선택하는 세 가지 현실적인 이유가 뭘까?

클라우드 LLM이 뛰어난 성능을 제공한다는 사실은 부정할 수 없다.
그럼에도 로컬 LLM 설치를 선택하는 데는 명확한 이유가 있다.

🔐 프라이버시

Obsidian 볼트에는 미발표 아이디어, 개인 일정, 업무 전략이 섞여 있다. OpenAI나 Anthropic의 서버 정책이 아무리 좋아도, 토큰 자체가 외부로 나가는 구조는 기업 환경에서 허용되지 않는 경우가 많기 때문이다.

💸 비용

GPT-4o 기준 입력 1M 토큰당 $2.50다. 하루 10회 긴 문서를 처리하면 월 수십 달러가 나간다. 반면, 로컬 서버는 초기 하드웨어 비용 이후 전기세만 발생한다.

📡 오프라인 사용

출장 중 인터넷이 불안정한 환경에서도 Obsidian 볼트와 AI가 동시에 작동할 수 있다(물론 로컬에 설치한 경우). 클라우드 의존성 없이 완결된 개인 AI 인프라가 된다는 것이다.

2. 전체 스택 구조 한눈에 보기

Obsidian + vLLM-MLX + Qwen3.6의 전체 아키텍처는 어떻게 구성될까?

전체 흐름은 단순하다.
Obsidian 사이드바가 에이전트 UI 역할을 하고, 요청이 Caddy 리버스 프록시를 거쳐 vLLM-MLX 서버로 전달되며, Qwen3.6-35B가 추론을 처리한다.

▶ 요청 흐름

Obsidian 사이드바 (Mac / iPhone / Windows)
    │ HTTPS :8443 + Bearer API Key
    ▼
Caddy (TLS internal, 리버스 프록시)
    │ HTTP :8001
    ▼
vLLM-MLX (OpenAI 호환 /v1/* 엔드포인트)
    │
    ▼
Qwen3.6-35B-A3B-4bit (mlx-community, ~19GB)

단계 도구 역할
🎙 입력 Obsidian 플러그인
(vault-agent)
사이드바 채팅 UI + ReAct 루프
🔒 보안 Caddy TLS + Bearer 토큰 인증 + 리버스 프록시
⚙️ 추론엔진 vLLM-MLX Continuous batching + Prefix cache + MCP 통합
🧠 언어모델 Qwen3.6-35B-A3B-4bit MoE 35B, 활성 파라미터 3B, 262K 컨텍스트

3. vLLM-MLX 설치 및 설정 — Mac에서 로컬 LLM 서버 구동하기

Mac에서 vLLM-MLX로 로컬 LLM을 설치하는 방법은? 🛠

설치 자체는 어렵지 않다. Python 3.11 가상환경을 만들고, vllm-mlx를 설치한 뒤 모델을 지정해 서버를 기동하면 된다. 핵심은 상시 가동을 위한 launchd 설정이다.

# 1. Python 가상환경 생성

python3.11 -m venv .venv
source .venv/bin/activate

# 2. vLLM-MLX 설치

pip install vllm-mlx

# 3. 서버 기동 (OpenAI 호환 엔드포인트)

python -m vllm.entrypoints.openai.api_server
  –model mlx-community/Qwen3.6-35B-A3B-4bit
  –port 8001
  –host 0.0.0.0

launchd로 Mac 부팅 시 자동 실행되도록 설정하는 방법은? 🛠

서버가 꺼져도 자동으로 재시작되게 하려면 macOS의 launchd를 활용한다.
아래 plist 파일을 ~/Library/LaunchAgents/에 저장해두면 된다.

# ~/Library/LaunchAgents/com.moai.vllm-mlx.plist

<?xml version=”1.0″ encoding=”UTF-8″?>
<plist version=”1.0″>
<dict>
  <key>Label</key>
  <string>com.moai.vllm-mlx</string>
  <key>KeepAlive</key><true/>
  <key>RunAtLoad</key><true/>
  <key>ThrottleInterval</key><integer>30</integer>
</dict>
</plist>

launchd 적용 명령어:

launchctl load ~/Library/LaunchAgents/com.moai.vllm-mlx.plist

4. Qwen3.6-35B를 선택한 이유 — 로컬 LLM 사용법의 핵심

Qwen3.6 35B 모델이 로컬 환경에 적합한 이유가 뭘까?

단순히 “좋은 모델”이 아니다.
로컬 하드웨어의 제약 조건을 정확히 겨냥한 설계 덕분에 선택했다.
세 가지 핵심 특성이 결정적이었다.

① MoE(Mixture-of-Experts) 구조 — “지식은 35B, 속도는 3B”

Qwen3.6-35B-A3B는 Mixture-of-Experts 아키텍처다.
총 파라미터는 35B이지만, 토큰 생성 시 실제 활성화되는 파라미터는 약 3B 수준이다.
즉, 추론 품질은 35B 모델과 유사하면서 생성 속도는 3B 모델에 가깝다.
Mac Mini M4 Pro에서 실측 기준 20~50 tokens/s 속도가 이 구조 덕분에 가능하다.

② 4-bit 양자화 — 19GB로 35B를 담는다

mlx-community/Qwen3.6-35B-A3B-4bit 모델은 4-bit 양자화를 통해 가중치 크기가 약 19GB이다. Apple Silicon의 통합 메모리 64GB 기준으로 전체 메모리의 25%만 차지하므로, 나머지 40GB를 IDE, 브라우저, Claude Code, Obsidian이 공유할 수 있다.
그렇기 때문에 스왑이 발생하지 않는 안정적인 운영이 가능하다.

③ 262K 컨텍스트 — 이전 세대 대비 8배

Qwen3.5-35B의 최대 컨텍스트는 32K였다.
Qwen3.6은 이를 262,144 토큰으로 확장했다고 한다. 이 차이는 실무에서 매우 크다고 본다.

활용 시나리오 Qwen3.5 (32K) Qwen3.6 (262K)
긴 문서 전체 분석 ❌ 분할 필요 ✅ 통째로 가능
Obsidian 볼트 컨텍스트 주입 ❌ 일부만 ✅ 수백 개 노트
멀티스텝 에이전틱 작업 ⚠️ 제한적 ✅ 안정적

vLLM-MLX가 Mac에서 다른 추론 엔진보다 유리한 이유가 뭘까?

Ollama, LM Studio와 비교해서 vLLM-MLX를 선택한 실질적인 근거다.

⚡

Unified Memory 직접 활용

CPU와 GPU가 동일한 물리 RAM을 공유한다. 전통적인 GPU 추론처럼 PCIe를 통한 VRAM 복사 단계가 없어, 19GB 가중치가 Metal 연산에 즉시 접근할 수 있다.

🔧

chat_template_kwargs 지원 — Thinking 모드 서버 단 제어

Ollama는 이 필드를 무시하지만, vLLM-MLX는 읽고 동작한다. Qwen3.6의 <think>...</think> 내부 추론 출력을 서버 단에서 직접 차단할 수 있다.

🗂

Prefix Cache — TTFT 절감

재기동 직후 디스크에서 4.6GB KV 캐시가 메모리로 복구된다. 반복되는 시스템 프롬프트나 MCP 툴 스키마의 첫 토큰 생성 시간이 수 초 단위로 단축된다.

🔌

OpenAI 완전 호환 API

/v1/chat/completions, /v1/models가 바로 준비된다. 클라이언트 코드 변경 없이 클라우드 API와 로컬 서버를 URL 하나로 전환할 수 있다.

5. Obsidian 플러그인 연동 — ReAct 루프로 7개 도구를 자동 체인

Obsidian에서 로컬 LLM을 에이전트로 활용하는 구조는 어떻게 될까??

핵심은 ReAct(Reason + Act) 루프다. 모델이 단순히 질문에 답하는 것이 아니라, 필요한 도구를 스스로 선택하고 실행하며 결과를 종합한다. 플러그인 이름은 vault-agent (v0.2.4)이며, TypeScript로 작성되어 있다.

📁 플러그인 구조

src/
├── agent/
│   ├── AgentController.ts   // ReAct 루프 (최대 3라운드)
│   ├── ToolRegistry.ts      // 7개 도구 등록/실행
│   ├── PromptBuilder.ts     // 시스템 프롬프트 조립
│   └── ConversationManager.ts
├── llm/
│   ├── LLMService.ts        // OpenAI-compat 클라이언트
│   └── QwenAdapter.ts
├── tools/                   // 7개 도구 구현
└── ui/ChatView.ts

7개 도구는 어떤 작업을 자동화하나요?

도구명 역할 실제 활용 예시
vault_search 볼트 내 파일 검색 “n8n 관련 메모 찾아줘”
vault_read_contents 특정 파일 내용 읽기 검색 결과 파일 상세 조회
vault_summarize 파일 요약 생성 “이 글의 핵심 3줄로 요약해줘”
write_to_file 새 파일 작성 “오늘 회의 내용으로 새 노트 만들어줘”
replace_in_file 기존 파일 수정 “이 섹션 내용 업데이트해줘”
web_search 실시간 웹 검색 “최신 vLLM 릴리즈 정보 검색해줘”
youtube_transcript 유튜브 자막 추출 “이 영상 내용 요약해서 노트에 저장해줘”

ReAct 루프를 3라운드로 제한한 이유가 뭘까?

무한 루프 방지가 목적이다. 모델이 “한 번만 더 검색해볼게요”를 반복하다 발산하는 사례를 실제로 경험했다. MAX_TOOL_ROUNDS = 3으로 설정하면 대부분의 작업이 “검색 → 읽기 → 요약/작성” 3단계 사이클 안에서 자연스럽게 완료된다.

6. 실전 삽질 로그 — 모델 업그레이드 후 부서진 것들

Qwen3.5에서 3.6으로 업그레이드 시 발생하는 주요 호환성 문제는?

Qwen 3.5에서 3.6으로 올린 날, 플러그인이 네 곳에서 동시에 터졌다. 흥미로운 점은 모두 “모델이 더 똑똑해져서 생긴 회귀“라는 공통점이다.

🔴 문제 1: arguments가 객체로 옵니다 (JSON 문자열이 아닌)

3.5까지는 tool_calls[0].function.arguments가 항상 JSON 문자열이었다. 3.6은 일부 응답에서 이미 파싱된 객체를 반환하기 시작했고, 기존 JSON.parse(args)가 실패하면서 빈 인자로 도구가 호출되었다.

// 수정 전

function safeParseJSON(input: unknown): unknown {‘{‘}
  try {‘{‘} return JSON.parse(input as string); {‘}’}
  catch {‘{‘} return {‘{‘} raw: input {‘}’}; {‘}’}
{‘}’}

// 수정 후 — 타입 가드 한 줄 추가

function safeParseJSON(input: unknown): unknown {‘{‘}
  if (typeof input !== ‘string’) return input; // ← 이 줄
  try {‘{‘} return JSON.parse(input); {‘}’}
  catch {‘{‘} return {‘{‘} raw: input {‘}’}; {‘}’}
{‘}’}

💡 한 줄 수정에 회귀 테스트 3개(문자열/객체/비JSON 경로)를 붙여 두었다.
      다음 업그레이드 때 같은 함정에 빠지지 않기 위해서다.

🔴 문제 2: <think>…</think> 태그가 응답에 그대로 노출돼

Qwen3.6의 Thinking Mode가 활성화되면 내부 추론 과정이 답변에 섞여 나온다.
단순한 프롬프트 지시 한 줄로는 막히지 않아, 4중 방어 레이어를 구성했다.

레이어 방법
① 시스템 프롬프트 ## THINKING MODE RULE 블록 + /no_think 토큰 삽입
② API 요청 body chat_template_kwargs.enable_thinking: false 추가
③ 클라이언트 후처리 <think>, <thinking>, <reasoning> 태그 정규식 제거
④ 메타 문장 제거 “Final answer.”, “Done.” 등 서두 문장 정리

💡 교훈: 로컬 모델에서 “설정 하나면 된다”는 문서는 절반만 맞는다.
      서버 토글 + 프롬프트 + 클라이언트 후처리 세 레이어가 모두 필요하다 😤

🔴 문제 3: 한국어 응답에 한자·가나가 섞여나와

Qwen은 중국어 기반 모델이다. 샘플링 단계에서 저빈도 한자나 히라가나가 산발적으로 혼입된다. 단순 언어 설정으로는 해결이 안 되어, 시스템 프롬프트에 ## LANGUAGE RULE (STRICT) 블록으로 간체/번체 한자 + 히라가나/가타카나 사용 금지를 명시했다. 이후 혼입 사례가 사실상 사라졌다.

🟡 Caddy TLS 문제 — 외부 접근 설정에서 이틀을 소모했어 😤

iPhone에서도 볼트에 접근하기 위해 DDNS + Caddy 리버스 프록시를 구성하다 세 가지 함정을 만났다.

  • :8443 포트만 지정하면 TLS 인증서가 발급 안 됨 → 호스트명을 명시적으로 등록해야 해.
  • health_uri를 /로 설정하면 vLLM의 404 응답으로 모든 요청이 503 → /v1/models로 변경한다.
  • 외부 DDNS에서 body가 빈 채로 200 응답 반환 → Caddyfile의 site block에 DDNS 호스트명이 누락된 것이 원인이었다. 응답 헤더의 Via: 1.1 Caddy 유무로 reverse_proxy 실행 여부를 판별할 수 있다.

7. 성능 및 비용 비교 — Mac Mini M4 Pro vs RTX 4090 데스크탑

로컬 LLM 서버를 위한 Mac Mini M4 Pro와 RTX 4090 데스크탑의 실제 차이는?

항목 Mac Mini M4 Pro (64GB) 데스크탑 + RTX 4090급
아이들 전력 10~20W 80~150W
추론 피크 전력 60~90W 400~550W
팬 소음 대부분 무음 부하 시 분명히 들림
설치 공간 13 × 13 cm ATX 타워
초기 비용 약 250~300만원 400~500만원+
35B 모델 가용 메모리 64GB 통합 메모리 24GB VRAM
추론 속도 (35B 4bit) 20~50 tokens/s 더 빠름 (배치 처리)
✅ 결론

“늘 켜놓고 로컬 LLM을 API로 쓰고 싶다”는 용도라면, 전력·소음·공간·비용 네 축 모두 Mac Mini가 유리하다. 단, 수백만 토큰 배치 처리나 절대 속도가 중요한 경우는 GPU 데스크탑이 필요하다.

8. 자주 묻는 질문 (FAQ)

Q. Mac Mini M4 Pro 64GB 외에 다른 사양에서도 Qwen3.6-35B가 작동할까?

최소 사양은 32GB 통합 메모리다. 32GB에서는 4-bit 양자화 모델(19GB)을 올릴 수 있지만, 다른 애플리케이션과의 메모리 경합으로 스왑이 발생할 수 있다. 16GB에서는 small 모델(7B~14B)로 다운그레이드해야 할 것이다. 64GB가 여유로운 운영의 최적점.

Q. vLLM-MLX 대신 Ollama를 써도 될까?

Obsidian 플러그인의 기본 채팅 기능은 Ollama에서도 동작한다. 단, chat_template_kwargs.enable_thinking 필드를 Ollama는 무시하기 때문에 Qwen3.6의 think 태그 누수를 서버 단에서 차단할 수 없다. 또한 MCP 통합, Prefix Cache, Reasoning Parser 등 vLLM의 서비스 기능을 사용하려면 vLLM-MLX가 필요.

Q. iPhone에서 Obsidian 볼트에 AI로 접근하는 것이 실제로 가능할까?

가능하다. Caddy를 통해 DDNS + TLS 인증서를 설정하면 외부 네트워크에서도 접근된다. 단, SNI 매칭 문제로 LLMService.ts에서 IP 접속 시 servername을 강제 오버라이드하는 처리가 필요하다. 또한 NAT idle timeout으로 인한 ECONNRESET을 막기 위해 매 요청마다 새 소켓을 여는 설정이 권장된다.

Q. 262K 컨텍스트를 실제로 다 사용하면 메모리가 부족하지 않을까?

128K 이상을 본격적으로 사용하면 KV Cache가 메모리를 상당히 점유한다. 64GB 기준으로는 128K까지 안정적이고, 그 이상에서는 --cache-memory-percent 옵션 조정이 필요하다. 일반적인 에이전틱 작업(검색·요약·작성)은 32K 이내에서 충분히 처리된다.

Q. vault-agent 플러그인을 직접 설치하고 사용할 수 있을까?

GitHub 저장소(github.com/cyans/rocal_llm_obsidian_plugin)에서 소스를 받아 npm run build 한 번으로 빌드할 수 있다. esbuild 번들 후 자동으로 Obsidian 플러그인 폴더에 배포된다. TypeScript와 Node.js 환경만 갖춰져 있으면 충분하다.

마치며 — 로컬 LLM을 “믿을 만하게” 쓰는 것의 의미

로컬 LLM을 올리고 구동하는 것은 30분이면 된다. “믿을 만하게” 쓰는 것은 완전히 다른 이야기이다.

arguments가 어느 날부터 객체로 오고, 응답에 <think> 태그가 새어 나오고, Caddy가 Host 매칭을 조용히 실패하고, NAT이 keepalive 소켓을 RST로 끊겼다. 이 모든 것이 qwen3.5에서 qwen3.6으로 올리는 순간에 몰아쳐 왔다.

그 과정을 버티게 해준 것이 vLLM-MLX + Mac Mini M4 Pro 조합이었다. 통합 메모리, OpenAI 호환 API, 서버 단 Thinking 토글, MCP 통합 — 각각은 화려하지 않지만, 합쳐 놓으면 “내 노트에 말을 거는 35B 어시스턴트를 주말에 구축할 수 있는 수준”까지 내려온다.

직접 구축해 볼래?

지금까지 vLLM-MLX 설치부터 Obsidian 플러그인 연동까지의 전체 과정을 단계별로 설명했다.
직접 구축하다가 어려움이 있으면 물어보라!

  • vLLM-MLX GitHub: github.com/mzbac/vllm-mlx
  • Qwen3.6-35B-A3B-4bit: mlx-community/Qwen3.6-35B-A3B-4bit
  • vault-agent 플러그인: github.com/cyans/rocal_llm_obsidian_plugin
  • Qwen3.6 공식 문서: Alibaba Cloud Model Studio

댓글 남기기