1. 왜 Mixin·patch로 나누나요
공개 커뮤니티에서 받은 프로필은 수백 줄짜리 rules와 proxy-groups를 이미 포함한 채로 자주 갱신됩니다. 매번 포크해서 한두 줄을 수정하면 업스트림이 고친 버그를 다시 손으로 따라가야 하고, 머지 충돌이 일상이 됩니다. 그래서 최신 YAML은 그대로 두고, 개인화는 얇은 오버라이드 레이어로 쌓는 패턴이 널리 쓰입니다. 검색창에는 영어로 profile override나 patch, 국내에선 Mixin이라는 말이 함께 붙는데, 실체는 「맵을 덮어쓰는 경로」와 「리스트 줄을 앞뒤로 붙이는 경로」가 한 프로젝트 안에 공존한다는 뜻에 가깝습니다.
Mihomo라는 이름은 코어 포크를 가리키는 경우가 많고, 사용자 입장에선 Clash Meta 기능 묶음과 거의 동의어로 봐도 무방한 경우가 많습니다. 중요한 구분은 브랜드가 아니라 최종적으로 코어가 읽는 파일이 하나라는 점입니다. 데스크톱 클라이언트는 구독을 내려받은 뒤 Mixin·스크립트·GUI 카드 편집값을 합쳐서 그 하나의 YAML을 만들고, 서비스 모드의 바이너리는 이미 합쳐진 결과만 전달받습니다. 따라서 「내 Mixin이 안 먹힌다」는 증상은 반드시 어느 단계에서 병합이 끊겼는지부터 의심해야 합니다.
2. 코어 YAML과 클라이언트 병합 층
가장 아래 층은 업로더가 제공한 베이스 프로필입니다. 여기에 사용자가 GUI에서 연 전역 확장, 특정 구독 카드에만 걸린 지역 덮어쓰기, 그리고 어떤 앱들이 지원한다면 스크립트 변환이 차례로 얹힙니다. 버전마다 메뉴 이름은 조금씩 다르지만 논리는 비슷합니다. 「구독 원문」은 보존하고, 「실행에 쓰는 문자열」은 매 시작 시 다시 조립한다는 인식을 유지하면 우선순위 논쟁이 줄어듭니다.
터미널에서 mihomo -d /etc/mihomo처럼 직접 띄우는 경우엔 이 병합 층 전체가 없을 수 있습니다. 이때는 Ansible·Python·Helm 템플릿 등 외부 도구가 곧 Mixin 역할을 하거나, 단순히 yq로 파일을 이어붙이는 수고를 감수해야 합니다. 반대로 Verge 계열·전통적인 Windows용 클라이언트는 사용자가 Mixin 입력란에 적은 YAML을 베이스 이후에 합칩니다. 어느 쪽이든 최종 산출물만 문법적으로 타당하면 코어는 동일하게 부팅합니다.
이 구조를 알고 있으면 구독은 성공인데 proxies가 비는 문제와도 연결됩니다. 원격 본문이 비었거나 참조 키가 깨진 경우는 병합 순서와 무관하게 먼저 터지기 때문입니다. 반면 Mixin이 잘못되어 중복 키나 잘못된 들여쓰기가 생기면 파서가 초기 단계에서 멈추므로, 로그에는 provider보다 먼저 YAML 오류가 찍힙니다.
3. Mixin과 patch의 동작 차이
Mixin은 흔히 딥 머지라고 부르는 연산에 가깝습니다. 최상위에서 같은 이름의 맵이 만나면 안쪽까지 내려가며 키를 합치고, 스칼라 값이 충돌하면 나중에 온 값이 이깁니다. 그래서 tun 블록 일부, dns의 일부 키, profile 아래 store-selected 같은 옵션을 조용히 덮어쓰기 좋습니다. 반대로 완전히 새로운 리스트를 통째로 바꿔야 한다면, 베이스와 섞이면서 의도치 않은 항목이 남을 수 있으니 최종 결과를 반드시 열어봐야 합니다.
patch는 주로 리스트 계열을 명시적으로 앞이나 뒤에 붙입니다. 대표적으로 prepend-rules와 append-rules가 여기에 해당하며, 일부 클라이언트는 같은 아이디어를 GUI의 「규칙 앞에 추가」 슬롯으로 노출합니다. 핵심은 규칙 엔진이 위에서 아래로 훑으며 처음 매칭에서 멈춘다는 점입니다. 그래서 append로 멋지게 줄을 추가해도, 베이스 쪽에서 이미 MATCH나 거대한 GEOIP 줄에 걸리면 아래까지 내려가지 못합니다. 이 증상은 Mixin이 실패한 것이 아니라 규칙 순서 논리가 우선한 것입니다.
프록시·그룹 관련 prepend·append
클라이언트에 따라 prepend-proxies, append-proxy-groups 같은 필드도 함께 노출됩니다. 이름이 비슷해도 적용 대 targets가 다릅니다. rules는 매 트래픽마다 재평가되지만, 프록시 목록은 존재하지 않는 그룹을 참조하면 곧바로 오류가 나므로 정책 그룹 이름을 문자 단위로 맞추는 습관이 필요합니다. url-test 튜닝을 한다면 그룹 정의 층과 규칙 층을 동시에 열어두고 diff하는 편이 안전합니다.
4. 병합 우선순위 표로 보기
구체적인 키 이름은 릴리스 노트에 따라 조금씩 바뀔 수 있으나, 의사결정용으로는 아래 표를 머릿속에 두면 충분합니다. 아래는 일반적인 데스크톱 클라이언트를 가정한 상대 순서이며, 앱의 공식 문서가 있다면 항상 그쪽 숫자가 우선입니다.
| 단계(대략) | 설명 |
|---|---|
| 베이스 구독 YAML | 원격에서 내려받은 최소 단위. 여기까지는 사용자의 Mixin이 관여하지 않은 원문에 가깝습니다. |
| 전역 Mixin 딥 머지 | 모든 프로필에 공통으로 얹는 맵 덮어쓰기. DNS·TUN·스니퍼 전역 옵션이 흔한 타깃입니다. |
| 구독별 오버라이드 | 특정 구독에만 적용되는 추가 YAML. 여행용 노드처럼 프로필을 여러 개로 나눌 때 편차가 생깁니다. |
| prepend-* 계열 | 리스트의 앞쪽에 삽입. 규칙이라면 베이스 rules보다 먼저 평가되어 사실상 높은 우선권을 갖습니다. |
| append-* 계열 | 리스트의 뒤쪽에 삽입. MATCH 같은 최종 줄 뒤에 붙으면 실행되지 않을 수 있어 배치를 의심해야 합니다. |
5. patch 파일 경로 잡기
경로 문제는 rule-providers의 path 때와 동일한 난이도를 갖습니다. 앱 번들 안의 읽기 전용 디렉터리와 사용자 홈 아래의 쓰기 가능한 프로필 루트가 갈리기 때문입니다. Mixin 파일을 ./mixin.yaml처럼 적어 두었는데 실제로는 다른 작업 디렉터리에서 실행되면, 편집기로 연 파일과 로더가 읽는 파일이 달라집니다. 처음 세팅할 때는 파일을 저장한 직후 타임스탬프가 바뀌는지, 앱 재시작 후 내용이 유지되는지 두 가지만 확인해도 대부분의 유령 경로를 걸러낼 수 있습니다.
여러 기기에 같은 패치를 복사해 쓰는 경우 운영체제마다 홈 경로 표기가 달라 절대 경로를 YAML에 박아 넣었다가 한쪽에서만 실패하는 경우가 잦습니다. 가능하면 클라이언트가 제공하는 프로필 디렉터리 기준 상대 경로나, GUI의 파일 선택기로 경로를 생성하는 방식이 재현성이 좋습니다. WSL과 Windows를 섞어 쓰면 드라이브 문자와 슬래시 방향이 섞이면서 같은 증상이 나므로, 크로스 플랫폼 템플릿을 만들 거라면 세트별로 디렉터리 스킴을 아예 나누는 편이 정신 건강에 이롭습니다.
패치 내용이 길어지면 patches/dns.yaml, patches/rules-personal.yaml처럼 폴더를 쪼개고 루트 Mixin에서는 include 스타일의 지원 여부를 앱 문서로 확인합니다. 코어 자체의 !include 지원은 빌드·버전에 따라 다르고, 클라이언트가 전처리로 include를 펼쳐 주기도 합니다. 어느 쪽이든 「전처리 후 문자열이 유효한 YAML인가」가 최종 판별 기준이므로, include를 쓰면 그 결과까지 미리보기에서 확인하는 습관을 들이세요.
6. Mixin YAML 예시 패턴
아래는 개념을 잡기 위한 최소 예시입니다. 실제 키는 사용 중인 메이저 버전 스키마를 따르고, 정책 그룹 이름은 본인 프로필의 문자열로 바꿔야 합니다. DNS 전역 튜닝을 더 깊게 보고 싶다면 nameserver·fallback 글과 같은 맵을 Mixin에서 덮어쓰는 상상을 하면 연결이 빠릅니다.
# Example only — tune keys to your core version
dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- 223.5.5.5
tun:
enable: true
stack: system
profile:
store-selected: true
이런 조각을 합칠 때 흔한 실수는 들여쓰기 깊이와 중복 키입니다. YAML 1.1 파서는 후속 키가 앞선 키를 조용히 덮는 경우가 있어, 의도치 않게 nameserver 리스트 전체가 뒤쪽 한 줄로 바뀌기도 합니다. 작은 변화를 여러 번 반복한다면 Git으로 스냅샷을 남겨 diff하는 것이 재현 속도 면에서 이득입니다.
7. prepend·append 규칙 예시
개인 규칙을 베이스보다 우선시키고 싶다면 prepend 쪽에 둡니다. 예시는 형식 이해용이며, 실제 정책 그룹 명은 구독 템플릿에 맞춥니다.
prepend-rules:
- DOMAIN,internal.corp.example,DIRECT
- DOMAIN-SUFFIX,telemetry.vendor.example,REJECT
append-rules:
- DOMAIN-SUFFIX,lab.example,🔰 Proxy
만약 베이스에 이미 MATCH,🔰 Final 같은 줄이 앞쪽에 있다면, append에 둔 도메인은 Final 그룹에 들어가기 전에 다른 규칙에 소모되어야 도달합니다. 반대로 prepend에만 의존하면 외부 목록이 정의한 특별 케이스까지 모두 당신의 줄에서 먼저 소모되어 오탐이 생길 수 있습니다. 운영 규모가 커질수록 RULE-SET으로 분리한 목록과 개인 prepend의 경계를 문서로 남기는 편이 안전합니다.
8. 최종 설정 검증 절차
첫 번째 단계는 문법 검증입니다. 로컬에 CLI가 있다면 mihomo -t 류의 테스트 플래그를 쓰거나, 최소한 온라인 YAML 린터로 최종 문자열을 넣어 보세요. 두 번째는 의미 검증으로, 실제 기기에서 소수의 도메인·IP만 대상으로 하여 프록시 계통이 기대와 일치하는지 확인합니다. 세 번째는 재시작 내구성입니다. 앱을 완전히 종료 후 다시 켰을 때 Mixin 경로가 동일하게 로드되는지, 클라우드 동기 폴더가 구버전 파일을 덮어쓰지 않는지까지 보면 운영 사고를 많이 줄일 수 있습니다.
DNS를 Mixin으로 건드렸다면 fake-ip 모드와 redir-host 모드를 혼동하지 않도록 한 번 더 점검합니다. 같은 prepend 규칙이라도 DNS 응답 경로가 다르면 체감 증상이 완전히 달라집니다. 이 지점은 규칙 제공자 글에서 말한 behavior 정합성과 같은 맥락으로, 레이어를 한꺼풀 바꿀 때마다 전체 스택을 다시 훑는 습관이 필요합니다.
마지막으로 권한·샌드박스를 확인합니다. 모바일이나 스토어 배포 빌드는 사용자 파일 접근이 제한되어 Mixin 파일이 있는 실제 경로를 읽지 못하는 경우가 있습니다. 이때는 증상이 「병합 우선순위 이슈」가 아니라 단순한 파일 I/O 실패인데, 로그 레벨이 낮으면 조용히 무시되기도 하므로 권한 설정 화면과 앱 내부 경로 목록을 함께 보는 것이 좋습니다.
9. FAQ·트러블슈팅
Mixin을 넣었는데 값이 전혀 변하지 않는 것 같습니다. 우선 앱이 실제로 그 파일을 읽었는지 타임스탬프와 로그 메시지로 확인하세요. 그 다음 충돌 키가 다른 레이어에서 다시 덮이는지 병합 미리보기로 확인합니다. 마지막으로, OS별로 다른 프로필 슬롯에 저장해 두었는데 실행 중인 프로필과 편집 중인 파일이 달라진 경우가 흔합니다.
append-rules만 쓰면 위험한가요? 위험하다기보다는 도달 가능성이 문제입니다. 베이스 목록이 길고 상단에 광범위한 매칭이 있으면 append는 사실상 장식이 됩니다. 반대로 prepend만 남발하면 업스트림이 의도한 특수 예외까지 당신의 규칙이 가로챌 수 있어, 운영 정책을 문서로 정리하고 범위를 좁히는 편이 낫습니다.
자주 묻는 질문
GUI에서 저장한 값이 재부팅 후 사라집니다. 클라우드 동기·심볼릭 링크·읽기 전용 볼륨을 우선 의심합니다. 또 다른 프로세스가 동시에 같은 YAML을 쓰고 있지 않은지도 확인하세요. 스크립트 오버라이드와 Mixin을 동시에 켜도 되나요? 대부분 허용되지만 실행 순서는 앱 구현에 달려 있습니다. 둘 다 켠 상태에서 이상하면 한쪽만 남기고 재현해 보세요.
10. 마무리
Mixin으로 맵형 설정을 얇게 덮어쓰고, patch로 규칙 리스트의 앞뒤를 조정하면 원격 프로필 업데이트와 개인 실험을 동시에 유지할 수 있습니다. 병합 우선순위는 앱마다 세부 구현이 조금씩 다르지만, 베이스 → 전역 Mixin → 구독별 오버라이드 → prepend → append라는 큰 줄기만 기억해도 대부분의 혼란을 줄일 수 있습니다. 규칙 엔진이 위에서 아래로 첫 매칭에서 멈춘다는 사실을 곁들이면 append가 「안 먹는」 미스터리도 금방 헤쳐 나갈 수 있습니다.
일부 초경량 브라우저 확장이나 단일 패널형 도구는 즉석 전환은 쉬워도, 다층 YAML 병합과 원격 패키지의 장기 추적에는 구조적으로 불리한 경우가 많습니다. 규칙 소스가 자주 바뀌고, 디바이스마다 같은 정책을 재현해야 한다면 파일 기반 생태계가 유지 보수 비용을 낮춥니다. 반면에 Clash·Mihomo 쪽은 rule-providers·Mixin·DNS까지 한 프로젝트 안에서 맞물리게 설계된 사례가 많아, 팀 단위로도 템플릿을 공유하기 쉽습니다. 문서 허브의 모드 설명과 이 블로그의 기능별 글을 묶어 두면 신입 기여자도 레이어 구조를 빠르게 이해할 수 있습니다.
웹 UI만으로 모든 걸 해결하려는 접근은 빠르게 시작하기엔 좋지만, 세밀한 DNS·rules 교차 검증이나 대량 노드 환경에서는 한계가 드러나기 쉽습니다. 반대로 오픈소스 클라이언트와 메타 코어를 함께 쓰면 YAML 한 벌로 여러 OS를 동기화하고, 로그로 원인을 분리하기도 수월합니다. 같은 문제의식이라면 Clash 클라이언트를 무료로 내려받아 Mixin·patch 패턴을 직접 실험해 보시길 권합니다.
→ 문서 허브에서 모드·DNS·외부 컨트롤러 항목을 함께 열어 두면 병합 레이어와 코어 옵션이 서로 부딪치는 지점을 더 빨리 찾을 수 있습니다.